three-usd-robot 0.10.0 → 0.11.0

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,4 +1,4 @@
1
- import { Quat, multiply, makeTranslation, makeRotationFromQuat, identity4, invert, makeEuler, DEG2RAD, makeRotationZ, makeRotationY, makeRotationX, fromUsdMatrix, makeScale, RAD2DEG, AssetPath, UsdMatrix } from './chunk-IYKPVUZ2.js';
1
+ import { Quat, multiply, makeTranslation, makeRotationFromQuat, identity4, invert, makeEuler, DEG2RAD, makeRotationZ, makeRotationY, makeRotationX, fromUsdMatrix, makeScale, RAD2DEG, AssetPath, UsdMatrix } from './chunk-LDO5FKQS.js';
2
2
  import { unzipSync } from 'fflate';
3
3
 
4
4
  // src/schemas/usdGeom.ts
@@ -282,122 +282,594 @@ function asMatrix(v, where) {
282
282
  throw new Error(`${where}: expected a matrix`);
283
283
  }
284
284
 
285
+ // src/usd/AssetResolver.ts
286
+ var DefaultAssetResolver = class {
287
+ resolve(assetPath, baseUrl) {
288
+ try {
289
+ return new URL(assetPath, baseUrl || void 0).href;
290
+ } catch {
291
+ return joinPosix(baseUrl, assetPath);
292
+ }
293
+ }
294
+ async fetchText(url) {
295
+ const res = await fetch(url);
296
+ if (!res.ok) throw new Error(`${res.status} ${res.statusText}`);
297
+ return res.text();
298
+ }
299
+ async fetchBytes(url) {
300
+ const res = await fetch(url);
301
+ if (!res.ok) throw new Error(`${res.status} ${res.statusText}`);
302
+ return new Uint8Array(await res.arrayBuffer());
303
+ }
304
+ };
305
+ function createMemoryResolver(files) {
306
+ const decoder = new TextDecoder();
307
+ const encoder = new TextEncoder();
308
+ return {
309
+ resolve(assetPath, baseUrl) {
310
+ return joinPosix(baseUrl, assetPath);
311
+ },
312
+ fetchText(url) {
313
+ const v = files[url];
314
+ if (v === void 0) return Promise.reject(new Error(`asset not found: ${url}`));
315
+ return Promise.resolve(typeof v === "string" ? v : decoder.decode(v));
316
+ },
317
+ fetchBytes(url) {
318
+ const v = files[url];
319
+ if (v === void 0) return Promise.reject(new Error(`asset not found: ${url}`));
320
+ return Promise.resolve(typeof v === "string" ? encoder.encode(v) : v);
321
+ }
322
+ };
323
+ }
324
+ function joinPosix(baseUrl, rel) {
325
+ if (rel.startsWith("/")) return normalizePosix(rel);
326
+ const dir = baseUrl.slice(0, baseUrl.lastIndexOf("/") + 1);
327
+ return normalizePosix(dir + rel);
328
+ }
329
+ function normalizePosix(path) {
330
+ const isAbsolute = path.startsWith("/");
331
+ const out = [];
332
+ for (const part of path.split("/")) {
333
+ if (part === "" || part === ".") continue;
334
+ if (part === "..") out.pop();
335
+ else out.push(part);
336
+ }
337
+ return (isAbsolute ? "/" : "") + out.join("/");
338
+ }
339
+
340
+ // src/usd/mdl/parseMdl.ts
341
+ function isMdlTexture(value) {
342
+ return typeof value === "object" && value !== null && !Array.isArray(value) && "assetPath" in value;
343
+ }
344
+ function parseMdl(text) {
345
+ const src = stripNoise(text);
346
+ const materials = /* @__PURE__ */ new Map();
347
+ const declRe = /\bexport\s+material\s+([A-Za-z_]\w*)\s*\(/g;
348
+ let match = declRe.exec(src);
349
+ while (match) {
350
+ const open = match.index + match[0].length - 1;
351
+ const close = matchParen(src, open);
352
+ if (close === -1) break;
353
+ const decl = {
354
+ name: match[1],
355
+ defaults: parseParamDefaults(src.slice(open + 1, close - 1)),
356
+ args: /* @__PURE__ */ new Map()
357
+ };
358
+ let next = close;
359
+ let i = close;
360
+ while (i < src.length && /\s/.test(src[i])) i++;
361
+ if (src[i] === "=") {
362
+ const end = statementEnd(src, i + 1);
363
+ const body = parseWrapperBody(src.slice(i + 1, end));
364
+ if (body.base) decl.base = body.base;
365
+ decl.args = body.args;
366
+ next = end;
367
+ }
368
+ materials.set(decl.name, decl);
369
+ declRe.lastIndex = next;
370
+ match = declRe.exec(src);
371
+ }
372
+ return { materials };
373
+ }
374
+ var NUMBER_RE = /^[+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?[fFdD]?$/;
375
+ var CALL_RE = /^(?:::)?([A-Za-z_]\w*(?:::[A-Za-z_]\w*)*)\s*\(/;
376
+ var VECTOR_RE = /^(?:color|(?:float|double|int)([234]))$/;
377
+ var IDENTIFIER_RE = /^[A-Za-z_]\w*$/;
378
+ function parseMdlLiteral(src) {
379
+ const s = src.trim();
380
+ if (s === "true") return true;
381
+ if (s === "false") return false;
382
+ if (NUMBER_RE.test(s)) return Number.parseFloat(s.replace(/[fFdD]$/, ""));
383
+ if (s.startsWith('"')) {
384
+ if (skipString(s, 0) !== s.length) return void 0;
385
+ return s.slice(1, -1).replace(/\\(.)/g, "$1");
386
+ }
387
+ const call = CALL_RE.exec(s);
388
+ if (!call) return void 0;
389
+ const open = call[0].length - 1;
390
+ if (matchParen(s, open) !== s.length) return void 0;
391
+ const name = call[1].split("::").pop();
392
+ const args = splitTopLevel(s.slice(open + 1, s.length - 1), ",").map((a) => a.trim()).filter((a) => a.length > 0);
393
+ if (name === "texture_2d") {
394
+ const path = args[0] === void 0 ? void 0 : parseMdlLiteral(args[0]);
395
+ if (typeof path !== "string" || path.length === 0) return void 0;
396
+ const texture = { assetPath: path };
397
+ const gamma = args.slice(1).join(",");
398
+ if (gamma.includes("gamma_srgb")) texture.sourceColorSpace = "sRGB";
399
+ else if (gamma.includes("gamma_linear")) texture.sourceColorSpace = "raw";
400
+ return texture;
401
+ }
402
+ const vector = VECTOR_RE.exec(name);
403
+ if (vector) {
404
+ const width = vector[1] ? Number(vector[1]) : 3;
405
+ const values = args.map((a) => parseMdlLiteral(a));
406
+ if (values.length === 0 || !values.every((v) => typeof v === "number")) return void 0;
407
+ if (values.length === 1) return new Array(width).fill(values[0]);
408
+ return values.length === width ? values : void 0;
409
+ }
410
+ return void 0;
411
+ }
412
+ function parseParamDefaults(src) {
413
+ const defaults = /* @__PURE__ */ new Map();
414
+ const body = src.trim();
415
+ if (body === "" || body === "*") return defaults;
416
+ for (const param of splitTopLevel(src, ",")) {
417
+ const assign = topLevelAssign(param);
418
+ if (assign === -1) continue;
419
+ const name = param.slice(0, assign).trim().split(/\s+/).pop();
420
+ if (!name || !IDENTIFIER_RE.test(name)) continue;
421
+ const value = parseMdlLiteral(param.slice(assign + 1));
422
+ if (value !== void 0) defaults.set(name, value);
423
+ }
424
+ return defaults;
425
+ }
426
+ function parseWrapperBody(src) {
427
+ const args = /* @__PURE__ */ new Map();
428
+ const body = src.trim();
429
+ const call = CALL_RE.exec(body);
430
+ if (!call) return { args };
431
+ const open = call[0].length - 1;
432
+ if (matchParen(body, open) !== body.length) return { args };
433
+ const base = call[1].split("::").pop();
434
+ if (base === "material") return { args };
435
+ for (const arg of splitTopLevel(body.slice(open + 1, body.length - 1), ",")) {
436
+ const colon = topLevelColon(arg);
437
+ if (colon === -1) continue;
438
+ const name = arg.slice(0, colon).trim();
439
+ if (!IDENTIFIER_RE.test(name)) continue;
440
+ const value = parseMdlLiteral(arg.slice(colon + 1));
441
+ if (value !== void 0) args.set(name, value);
442
+ }
443
+ return { base, args };
444
+ }
445
+ function stripNoise(src) {
446
+ let out = "";
447
+ let i = 0;
448
+ while (i < src.length) {
449
+ const c = src[i];
450
+ if (c === '"') {
451
+ const end = skipString(src, i);
452
+ out += src.slice(i, end);
453
+ i = end;
454
+ } else if (c === "/" && src[i + 1] === "/") {
455
+ while (i < src.length && src[i] !== "\n") i++;
456
+ } else if (c === "/" && src[i + 1] === "*") {
457
+ const end = src.indexOf("*/", i + 2);
458
+ i = end === -1 ? src.length : end + 2;
459
+ out += " ";
460
+ } else if (c === "[" && src[i + 1] === "[") {
461
+ i = skipAnnotation(src, i);
462
+ out += " ";
463
+ } else {
464
+ out += c;
465
+ i++;
466
+ }
467
+ }
468
+ return out;
469
+ }
470
+ function skipString(src, i) {
471
+ let j = i + 1;
472
+ while (j < src.length) {
473
+ if (src[j] === "\\") j += 2;
474
+ else if (src[j] === '"') return j + 1;
475
+ else j++;
476
+ }
477
+ return j;
478
+ }
479
+ function skipAnnotation(src, i) {
480
+ let depth = 0;
481
+ let j = i;
482
+ while (j < src.length) {
483
+ if (src[j] === '"') {
484
+ j = skipString(src, j);
485
+ } else if (src[j] === "[" && src[j + 1] === "[") {
486
+ depth++;
487
+ j += 2;
488
+ } else if (src[j] === "]" && src[j + 1] === "]") {
489
+ depth--;
490
+ j += 2;
491
+ if (depth === 0) return j;
492
+ } else {
493
+ j++;
494
+ }
495
+ }
496
+ return j;
497
+ }
498
+ function matchParen(src, open) {
499
+ let depth = 0;
500
+ let i = open;
501
+ while (i < src.length) {
502
+ const c = src[i];
503
+ if (c === '"') {
504
+ i = skipString(src, i);
505
+ continue;
506
+ }
507
+ if (c === "(") depth++;
508
+ else if (c === ")") {
509
+ depth--;
510
+ if (depth === 0) return i + 1;
511
+ }
512
+ i++;
513
+ }
514
+ return -1;
515
+ }
516
+ function isOpener(c) {
517
+ return c === "(" || c === "[" || c === "{";
518
+ }
519
+ function isCloser(c) {
520
+ return c === ")" || c === "]" || c === "}";
521
+ }
522
+ function splitTopLevel(src, separator) {
523
+ const parts = [];
524
+ let depth = 0;
525
+ let start = 0;
526
+ let i = 0;
527
+ while (i < src.length) {
528
+ const c = src[i];
529
+ if (c === '"') {
530
+ i = skipString(src, i);
531
+ continue;
532
+ }
533
+ if (isOpener(c)) depth++;
534
+ else if (isCloser(c)) depth--;
535
+ else if (c === separator && depth === 0) {
536
+ parts.push(src.slice(start, i));
537
+ start = i + 1;
538
+ }
539
+ i++;
540
+ }
541
+ parts.push(src.slice(start));
542
+ return parts;
543
+ }
544
+ function statementEnd(src, from) {
545
+ let depth = 0;
546
+ let i = from;
547
+ while (i < src.length) {
548
+ const c = src[i];
549
+ if (c === '"') {
550
+ i = skipString(src, i);
551
+ continue;
552
+ }
553
+ if (isOpener(c)) depth++;
554
+ else if (isCloser(c)) depth--;
555
+ else if (c === ";" && depth === 0) return i;
556
+ i++;
557
+ }
558
+ return src.length;
559
+ }
560
+ function topLevelAssign(src) {
561
+ let depth = 0;
562
+ let i = 0;
563
+ while (i < src.length) {
564
+ const c = src[i];
565
+ if (c === '"') {
566
+ i = skipString(src, i);
567
+ continue;
568
+ }
569
+ if (isOpener(c)) depth++;
570
+ else if (isCloser(c)) depth--;
571
+ else if (c === "=" && depth === 0 && src[i + 1] !== "=" && !"<>!=".includes(src[i - 1] ?? "")) {
572
+ return i;
573
+ }
574
+ i++;
575
+ }
576
+ return -1;
577
+ }
578
+ function topLevelColon(src) {
579
+ let depth = 0;
580
+ let i = 0;
581
+ while (i < src.length) {
582
+ const c = src[i];
583
+ if (c === '"') {
584
+ i = skipString(src, i);
585
+ continue;
586
+ }
587
+ if (isOpener(c)) depth++;
588
+ else if (isCloser(c)) depth--;
589
+ else if (c === ":" && depth === 0) {
590
+ if (src[i + 1] === ":") {
591
+ i += 2;
592
+ continue;
593
+ }
594
+ return i;
595
+ }
596
+ i++;
597
+ }
598
+ return -1;
599
+ }
600
+
285
601
  // src/three/MaterialBinding.ts
286
602
  var DIFFUSE_INPUTS = [
287
- "inputs:diffuseColor",
603
+ "diffuseColor",
288
604
  // UsdPreviewSurface
289
- "inputs:diffuse_color_constant",
605
+ "diffuse_color_constant",
290
606
  // OmniPBR
291
- "inputs:diffuse_tint",
292
- "inputs:base_color",
293
- "inputs:baseColor"
607
+ "diffuse_tint",
608
+ "base_color",
609
+ "baseColor"
294
610
  ];
295
- var OPACITY_INPUTS = ["inputs:opacity", "inputs:opacity_constant"];
296
- var OPACITY_THRESHOLD_INPUTS = ["inputs:opacityThreshold", "inputs:opacity_threshold"];
297
- var METALLIC_INPUTS = ["inputs:metallic", "inputs:metallic_constant"];
298
- var ROUGHNESS_INPUTS = ["inputs:roughness", "inputs:reflection_roughness_constant"];
299
- var EMISSIVE_INPUTS = ["inputs:emissiveColor", "inputs:emissive_color"];
611
+ var OPACITY_INPUTS = ["opacity", "opacity_constant"];
612
+ var OPACITY_THRESHOLD_INPUTS = ["opacityThreshold", "opacity_threshold"];
613
+ var METALLIC_INPUTS = ["metallic", "metallic_constant"];
614
+ var ROUGHNESS_INPUTS = ["roughness", "reflection_roughness_constant"];
615
+ var EMISSIVE_INPUTS = ["emissiveColor", "emissive_color"];
300
616
  var SURFACE_OUTPUTS = ["outputs:surface", "outputs:mdl:surface"];
301
617
  var TEXTURE_LOOKUPS = {
302
618
  color: {
303
- surface: ["inputs:diffuseColor"],
304
- direct: ["inputs:diffuse_texture", "inputs:diffuse_color_texture"]
619
+ surface: ["diffuseColor"],
620
+ direct: ["diffuse_texture", "diffuse_color_texture", "glass_color_texture"]
305
621
  },
306
622
  opacity: {
307
- surface: ["inputs:opacity"],
308
- direct: ["inputs:opacity_texture", "inputs:opacity_color_texture"]
623
+ surface: ["opacity"],
624
+ direct: ["opacity_texture", "opacity_color_texture"]
309
625
  },
310
626
  normal: {
311
- surface: ["inputs:normal"],
312
- direct: ["inputs:normalmap_texture", "inputs:normal_texture"]
627
+ surface: ["normal"],
628
+ direct: ["normalmap_texture", "normal_texture", "normal_map_texture"]
313
629
  },
314
630
  roughness: {
315
- surface: ["inputs:roughness"],
316
- direct: ["inputs:reflectionroughness_texture", "inputs:roughness_texture"]
631
+ surface: ["roughness"],
632
+ direct: ["reflectionroughness_texture", "roughness_texture"]
317
633
  },
318
634
  metalness: {
319
- surface: ["inputs:metallic"],
320
- direct: ["inputs:metallic_texture"]
635
+ surface: ["metallic"],
636
+ direct: ["metallic_texture"]
321
637
  },
322
638
  occlusion: {
323
- surface: ["inputs:occlusion"],
324
- direct: ["inputs:ao_texture", "inputs:occlusion_texture"]
639
+ surface: ["occlusion"],
640
+ direct: ["ao_texture", "occlusion_texture"]
325
641
  },
326
642
  emissive: {
327
- surface: ["inputs:emissiveColor"],
328
- direct: ["inputs:emissive_color_texture", "inputs:emissive_mask_texture"]
643
+ surface: ["emissiveColor"],
644
+ direct: ["emissive_color_texture", "emissive_mask_texture"]
329
645
  }
330
646
  };
331
- function resolveBoundMaterial(stage, prim) {
647
+ function resolveBoundMaterial(stage, prim, options = {}) {
332
648
  const materialPath = findBinding(prim);
333
649
  if (!materialPath) return void 0;
334
650
  const material = stage.GetPrimAtPath(materialPath);
335
651
  if (!material) return void 0;
336
652
  const shader = findSurfaceShader(material);
337
653
  if (!shader) return void 0;
654
+ const mdlSource = readMdlShaderSource(shader, options.mdl);
655
+ if (mdlSource && !mdlSource.family) {
656
+ options.onWarn?.(
657
+ `${material.GetPath()}: unknown MDL material "${mdlSource.id}" (${mdlSource.assetPath}); applying the best-effort OmniPBR mapping`
658
+ );
659
+ }
660
+ const sv = { shader };
661
+ if (mdlSource?.values) sv.mdl = mdlSource.values;
662
+ if (mdlSource) sv.mdlAssetPath = mdlSource.assetPath;
338
663
  const result = { name: material.GetName() };
339
- const color = firstColor(shader, DIFFUSE_INPUTS);
664
+ const color = firstColor(sv, DIFFUSE_INPUTS);
340
665
  if (color) result.color = color;
341
- const opacity = firstNumber(shader, OPACITY_INPUTS);
666
+ const opacity = firstNumber(sv, OPACITY_INPUTS);
342
667
  if (opacity !== void 0) result.opacity = opacity;
343
- const opacityThreshold = firstNumber(shader, OPACITY_THRESHOLD_INPUTS);
668
+ const opacityThreshold = firstNumber(sv, OPACITY_THRESHOLD_INPUTS);
344
669
  if (opacityThreshold !== void 0) result.opacityThreshold = opacityThreshold;
345
- const metalness = firstNumber(shader, METALLIC_INPUTS);
670
+ const metalness = firstNumber(sv, METALLIC_INPUTS);
346
671
  if (metalness !== void 0) result.metalness = metalness;
347
- const roughness = firstNumber(shader, ROUGHNESS_INPUTS);
672
+ const roughness = firstNumber(sv, ROUGHNESS_INPUTS);
348
673
  if (roughness !== void 0) result.roughness = roughness;
349
- const emissive = firstColor(shader, EMISSIVE_INPUTS);
350
- if (emissive && shader.GetAttribute("inputs:enable_emission").Get() !== false) {
674
+ const emissive = firstColor(sv, EMISSIVE_INPUTS);
675
+ if (emissive && svBoolean(sv, "enable_emission") !== false) {
351
676
  result.emissiveColor = emissive;
352
- const intensity = firstNumber(shader, ["inputs:emissive_intensity"]);
677
+ const intensity = svNumber(sv, "emissive_intensity");
353
678
  if (intensity !== void 0) result.emissiveIntensity = intensity;
354
679
  }
355
- const ior = firstNumber(shader, ["inputs:ior"]);
680
+ const ior = svNumber(sv, "ior");
356
681
  if (ior !== void 0) result.ior = ior;
357
- const clearcoat = firstNumber(shader, ["inputs:clearcoat"]);
682
+ const clearcoat = svNumber(sv, "clearcoat");
358
683
  if (clearcoat !== void 0) result.clearcoat = clearcoat;
359
- const clearcoatRoughness = firstNumber(shader, ["inputs:clearcoatRoughness"]);
684
+ const clearcoatRoughness = svNumber(sv, "clearcoatRoughness");
360
685
  if (clearcoatRoughness !== void 0) result.clearcoatRoughness = clearcoatRoughness;
361
- if (shader.GetAttribute("inputs:useSpecularWorkflow").Get() === 1) {
362
- const specular = firstColor(shader, ["inputs:specularColor"]);
686
+ if (svNumber(sv, "useSpecularWorkflow") === 1) {
687
+ const specular = svColor(sv, "specularColor");
363
688
  if (specular) result.specularColor = specular;
364
689
  }
365
- const colorTex = findTexture(shader, TEXTURE_LOOKUPS.color);
690
+ const colorTex = findTexture(sv, TEXTURE_LOOKUPS.color);
366
691
  if (colorTex !== void 0) result.colorTexture = colorTex;
367
- const opacityTex = findTexture(shader, TEXTURE_LOOKUPS.opacity);
692
+ const opacityTex = findTexture(sv, TEXTURE_LOOKUPS.opacity);
368
693
  if (opacityTex !== void 0) result.opacityTexture = opacityTex;
369
- const normal = findTexture(shader, TEXTURE_LOOKUPS.normal);
694
+ const normal = findTexture(sv, TEXTURE_LOOKUPS.normal);
370
695
  if (normal !== void 0) result.normalTexture = normal;
371
- const roughTex = findTexture(shader, TEXTURE_LOOKUPS.roughness);
696
+ const roughTex = findTexture(sv, TEXTURE_LOOKUPS.roughness);
372
697
  if (roughTex !== void 0) result.roughnessTexture = roughTex;
373
- const metalTex = findTexture(shader, TEXTURE_LOOKUPS.metalness);
698
+ const metalTex = findTexture(sv, TEXTURE_LOOKUPS.metalness);
374
699
  if (metalTex !== void 0) result.metalnessTexture = metalTex;
375
- const aoTex = findTexture(shader, TEXTURE_LOOKUPS.occlusion);
700
+ const aoTex = findTexture(sv, TEXTURE_LOOKUPS.occlusion);
376
701
  if (aoTex !== void 0) result.occlusionTexture = aoTex;
377
- const emissiveTex = findTexture(shader, TEXTURE_LOOKUPS.emissive);
702
+ const emissiveTex = findTexture(sv, TEXTURE_LOOKUPS.emissive);
378
703
  if (emissiveTex !== void 0) result.emissiveTexture = emissiveTex;
379
- const ormFile = shader.GetAttribute("inputs:ORM_texture").Get();
380
- if (shader.GetAttribute("inputs:enable_ORM_texture").Get() === true && ormFile instanceof AssetPath && ormFile.path) {
381
- const transform = mdlTextureTransform(shader);
382
- const orm = { path: ormFile.path, ...transform ? { transform } : {} };
704
+ const orm = directTexture(sv, "ORM_texture") ?? mdlValueTexture(sv, "ORM_texture");
705
+ if (svBoolean(sv, "enable_ORM_texture") === true && orm) {
383
706
  result.occlusionTexture = { ...orm, outputChannel: "r" };
384
707
  result.roughnessTexture = { ...orm, outputChannel: "g" };
385
708
  result.metalnessTexture = { ...orm, outputChannel: "b" };
386
709
  }
710
+ switch (mdlSource?.family) {
711
+ case "glass":
712
+ readOmniGlass(sv, result);
713
+ break;
714
+ case "clearcoat":
715
+ readOmniClearCoat(sv, result);
716
+ break;
717
+ case "surface":
718
+ readOmniSurface(sv, result);
719
+ break;
720
+ }
387
721
  return result;
388
722
  }
389
- function findTexture(shader, lookup) {
723
+ function classifyOmniMdl(name) {
724
+ if (!name) return void 0;
725
+ if (name.startsWith("OmniGlass")) return "glass";
726
+ if (name.startsWith("OmniPBR_ClearCoat")) return "clearcoat";
727
+ if (name.startsWith("OmniPBR")) return "pbr";
728
+ if (name.startsWith("OmniSurface")) return "surface";
729
+ return void 0;
730
+ }
731
+ function readMdlShaderSource(shader, provider) {
732
+ const asset = shader.GetAttribute("info:mdl:sourceAsset").Get();
733
+ if (!(asset instanceof AssetPath) || !asset.path) return void 0;
734
+ const sub = shader.GetAttribute("info:mdl:sourceAsset:subIdentifier").Get();
735
+ const stem = (asset.path.split("/").pop() ?? "").replace(/\.mdl$/i, "");
736
+ const id = typeof sub === "string" && sub.length > 0 ? sub : stem;
737
+ const decl = provider?.(asset.path)?.materials.get(id);
738
+ const family = classifyOmniMdl(decl?.base) ?? classifyOmniMdl(id) ?? classifyOmniMdl(stem);
739
+ const source = { assetPath: asset.path, id };
740
+ if (family) source.family = family;
741
+ if (decl && (decl.defaults.size > 0 || decl.args.size > 0)) {
742
+ source.values = new Map([...decl.defaults, ...decl.args]);
743
+ }
744
+ return source;
745
+ }
746
+ function readOmniGlass(sv, result) {
747
+ result.transmission = 1;
748
+ result.metalness = 0;
749
+ result.color = svColor(sv, "glass_color") ?? [1, 1, 1];
750
+ result.ior = svNumber(sv, "glass_ior") ?? 1.491;
751
+ result.roughness = svNumber(sv, "frosting_roughness") ?? 0;
752
+ const depth = svNumber(sv, "depth");
753
+ if (svBoolean(sv, "thin_walled") !== true && depth !== void 0) result.thickness = depth;
754
+ const cutout = svNumber(sv, "cutout_opacity");
755
+ if (cutout !== void 0 && cutout < 1) result.opacity = cutout;
756
+ }
757
+ function readOmniClearCoat(sv, result) {
758
+ if (svBoolean(sv, "enable_clearcoat") === false) return;
759
+ result.clearcoat = 1;
760
+ const roughness = svNumber(sv, "clearcoat_reflection_roughness");
761
+ if (roughness !== void 0) result.clearcoatRoughness = roughness;
762
+ const normal = directTexture(sv, "clearcoat_normalmap_texture") ?? mdlValueTexture(sv, "clearcoat_normalmap_texture");
763
+ if (normal && svBoolean(sv, "enable_clearcoat_normalmap_texture") !== false) {
764
+ result.clearcoatNormalTexture = normal;
765
+ }
766
+ }
767
+ function readOmniSurface(sv, result) {
768
+ const color = svColor(sv, "diffuse_reflection_color");
769
+ if (color) result.color = color;
770
+ const metalness = svNumber(sv, "metalness");
771
+ if (metalness !== void 0) result.metalness = metalness;
772
+ const roughness = svNumber(sv, "specular_reflection_roughness");
773
+ if (roughness !== void 0) result.roughness = roughness;
774
+ const ior = svNumber(sv, "specular_reflection_ior");
775
+ if (ior !== void 0) result.ior = ior;
776
+ const coat = svNumber(sv, "coat_weight");
777
+ if (coat !== void 0 && coat > 0) {
778
+ result.clearcoat = coat;
779
+ const coatRoughness = svNumber(sv, "coat_roughness");
780
+ if (coatRoughness !== void 0) result.clearcoatRoughness = coatRoughness;
781
+ }
782
+ const emissionWeight = svNumber(sv, "emission_weight");
783
+ const emissionColor = svColor(sv, "emission_color");
784
+ if (emissionWeight !== void 0 && emissionWeight > 0 && emissionColor) {
785
+ result.emissiveColor = emissionColor;
786
+ result.emissiveIntensity = emissionWeight;
787
+ }
788
+ if (svBoolean(sv, "enable_opacity") === true) {
789
+ const opacity = svNumber(sv, "geometry_opacity");
790
+ if (opacity !== void 0) result.opacity = opacity;
791
+ }
792
+ }
793
+ function authored(sv, name) {
794
+ return sv.shader.GetAttribute(`inputs:${name}`).Get();
795
+ }
796
+ function asNumber2(v) {
797
+ return typeof v === "number" ? v : void 0;
798
+ }
799
+ function asBoolean(v) {
800
+ return typeof v === "boolean" ? v : void 0;
801
+ }
802
+ function asVec(v, length) {
803
+ if (Array.isArray(v) && v.length >= length && v.every((n) => typeof n === "number")) {
804
+ return v.slice(0, length);
805
+ }
806
+ return void 0;
807
+ }
808
+ function svNumber(sv, name) {
809
+ return asNumber2(authored(sv, name)) ?? asNumber2(sv.mdl?.get(name));
810
+ }
811
+ function svBoolean(sv, name) {
812
+ return asBoolean(authored(sv, name)) ?? asBoolean(sv.mdl?.get(name));
813
+ }
814
+ function svColor(sv, name) {
815
+ const v = asVec(authored(sv, name), 3) ?? asVec(sv.mdl?.get(name), 3);
816
+ return v ? v : void 0;
817
+ }
818
+ function svVec2(sv, name) {
819
+ const v = asVec(authored(sv, name), 2) ?? asVec(sv.mdl?.get(name), 2);
820
+ return v ? v : void 0;
821
+ }
822
+ function firstNumber(sv, names) {
823
+ for (const name of names) {
824
+ const v = asNumber2(authored(sv, name));
825
+ if (v !== void 0) return v;
826
+ }
827
+ for (const name of names) {
828
+ const v = asNumber2(sv.mdl?.get(name));
829
+ if (v !== void 0) return v;
830
+ }
831
+ return void 0;
832
+ }
833
+ function firstColor(sv, names) {
834
+ for (const name of names) {
835
+ const v = asVec(authored(sv, name), 3);
836
+ if (v) return v;
837
+ }
838
+ for (const name of names) {
839
+ const v = asVec(sv.mdl?.get(name), 3);
840
+ if (v) return v;
841
+ }
842
+ return void 0;
843
+ }
844
+ function directTexture(sv, name) {
845
+ const v = authored(sv, name);
846
+ if (v instanceof AssetPath && v.path) return withMdlTransform(sv, { path: v.path });
847
+ return void 0;
848
+ }
849
+ function mdlValueTexture(sv, name) {
850
+ const v = sv.mdl?.get(name);
851
+ if (!isMdlTexture(v)) return void 0;
852
+ const texture = { path: resolveMdlRelative(sv.mdlAssetPath, v.assetPath) };
853
+ if (v.sourceColorSpace) texture.sourceColorSpace = v.sourceColorSpace;
854
+ return withMdlTransform(sv, texture);
855
+ }
856
+ function resolveMdlRelative(modulePath, path) {
857
+ if (!modulePath || path.startsWith("/") || /^[a-z][a-z0-9+.-]*:/i.test(path)) return path;
858
+ return joinPosix(modulePath, path);
859
+ }
860
+ function withMdlTransform(sv, texture) {
861
+ const transform = mdlTextureTransform(sv);
862
+ return transform ? { ...texture, transform } : texture;
863
+ }
864
+ function findTexture(sv, lookup) {
390
865
  for (const name of lookup.direct) {
391
- const v = shader.GetAttribute(name).Get();
392
- if (v instanceof AssetPath && v.path) {
393
- const transform = mdlTextureTransform(shader);
394
- return { path: v.path, ...transform ? { transform } : {} };
395
- }
866
+ const texture = directTexture(sv, name);
867
+ if (texture) return texture;
396
868
  }
397
869
  for (const name of lookup.surface) {
398
- const conn = shader.GetAttribute(name).GetConnections()[0];
870
+ const conn = sv.shader.GetAttribute(`inputs:${name}`).GetConnections()[0];
399
871
  if (!conn) continue;
400
- const texPrim = shader.GetStage().GetPrimAtPath(conn.split(".")[0]);
872
+ const texPrim = sv.shader.GetStage().GetPrimAtPath(conn.split(".")[0]);
401
873
  if (!texPrim) continue;
402
874
  const file = texPrim.GetAttribute("inputs:file").Get();
403
875
  if (file instanceof AssetPath && file.path) {
@@ -407,6 +879,10 @@ function findTexture(shader, lookup) {
407
879
  return tex;
408
880
  }
409
881
  }
882
+ for (const name of lookup.direct) {
883
+ const texture = mdlValueTexture(sv, name);
884
+ if (texture) return texture;
885
+ }
410
886
  return void 0;
411
887
  }
412
888
  function outputChannelOf(connection) {
@@ -424,14 +900,14 @@ function outputChannelOf(connection) {
424
900
  return void 0;
425
901
  }
426
902
  }
427
- function mdlTextureTransform(shader) {
903
+ function mdlTextureTransform(sv) {
428
904
  const transform = {};
429
- const translation = numArray(shader, "inputs:texture_translate", 2);
905
+ const translation = svVec2(sv, "texture_translate");
430
906
  if (translation) transform.translation = translation;
431
- const scale = numArray(shader, "inputs:texture_scale", 2);
907
+ const scale = svVec2(sv, "texture_scale");
432
908
  if (scale) transform.scale = scale;
433
- const rotation = shader.GetAttribute("inputs:texture_rotate").Get();
434
- if (typeof rotation === "number") transform.rotation = rotation;
909
+ const rotation = svNumber(sv, "texture_rotate");
910
+ if (rotation !== void 0) transform.rotation = rotation;
435
911
  return Object.keys(transform).length > 0 ? transform : void 0;
436
912
  }
437
913
  var WRAP_VALUES = /* @__PURE__ */ new Set(["repeat", "clamp", "mirror", "black"]);
@@ -480,11 +956,7 @@ function readStChain(texPrim) {
480
956
  return out;
481
957
  }
482
958
  function numArray(prim, name, length) {
483
- const v = prim.GetAttribute(name).Get();
484
- if (Array.isArray(v) && v.length >= length && v.every((n) => typeof n === "number")) {
485
- return v.slice(0, length);
486
- }
487
- return void 0;
959
+ return asVec(prim.GetAttribute(name).Get(), length);
488
960
  }
489
961
  function findBinding(prim) {
490
962
  let chosen;
@@ -513,22 +985,6 @@ function findSurfaceShader(material) {
513
985
  }
514
986
  return material.GetChildren().find((c) => c.GetTypeName() === "Shader") ?? void 0;
515
987
  }
516
- function firstColor(shader, names) {
517
- for (const name of names) {
518
- const v = shader.GetAttribute(name).Get();
519
- if (Array.isArray(v) && v.length >= 3 && v.every((n) => typeof n === "number")) {
520
- return [v[0], v[1], v[2]];
521
- }
522
- }
523
- return void 0;
524
- }
525
- function firstNumber(shader, names) {
526
- for (const name of names) {
527
- const v = shader.GetAttribute(name).Get();
528
- if (typeof v === "number") return v;
529
- }
530
- return void 0;
531
- }
532
988
 
533
989
  // src/robot/buildKinematicTree.ts
534
990
  var WORLD = "";
@@ -810,61 +1266,6 @@ function leafName(path) {
810
1266
  return parts[parts.length - 1] ?? path;
811
1267
  }
812
1268
 
813
- // src/usd/AssetResolver.ts
814
- var DefaultAssetResolver = class {
815
- resolve(assetPath, baseUrl) {
816
- try {
817
- return new URL(assetPath, baseUrl || void 0).href;
818
- } catch {
819
- return joinPosix(baseUrl, assetPath);
820
- }
821
- }
822
- async fetchText(url) {
823
- const res = await fetch(url);
824
- if (!res.ok) throw new Error(`${res.status} ${res.statusText}`);
825
- return res.text();
826
- }
827
- async fetchBytes(url) {
828
- const res = await fetch(url);
829
- if (!res.ok) throw new Error(`${res.status} ${res.statusText}`);
830
- return new Uint8Array(await res.arrayBuffer());
831
- }
832
- };
833
- function createMemoryResolver(files) {
834
- const decoder = new TextDecoder();
835
- const encoder = new TextEncoder();
836
- return {
837
- resolve(assetPath, baseUrl) {
838
- return joinPosix(baseUrl, assetPath);
839
- },
840
- fetchText(url) {
841
- const v = files[url];
842
- if (v === void 0) return Promise.reject(new Error(`asset not found: ${url}`));
843
- return Promise.resolve(typeof v === "string" ? v : decoder.decode(v));
844
- },
845
- fetchBytes(url) {
846
- const v = files[url];
847
- if (v === void 0) return Promise.reject(new Error(`asset not found: ${url}`));
848
- return Promise.resolve(typeof v === "string" ? encoder.encode(v) : v);
849
- }
850
- };
851
- }
852
- function joinPosix(baseUrl, rel) {
853
- if (rel.startsWith("/")) return normalizePosix(rel);
854
- const dir = baseUrl.slice(0, baseUrl.lastIndexOf("/") + 1);
855
- return normalizePosix(dir + rel);
856
- }
857
- function normalizePosix(path) {
858
- const isAbsolute = path.startsWith("/");
859
- const out = [];
860
- for (const part of path.split("/")) {
861
- if (part === "" || part === ".") continue;
862
- if (part === "..") out.pop();
863
- else out.push(part);
864
- }
865
- return (isAbsolute ? "/" : "") + out.join("/");
866
- }
867
-
868
1269
  // src/parser/reader.ts
869
1270
  var ParseError = class extends Error {
870
1271
  constructor(message, line, col) {
@@ -951,7 +1352,7 @@ var TokenizeError = class extends Error {
951
1352
  };
952
1353
  var IDENT_START = /[A-Za-z_]/;
953
1354
  var IDENT_CONT = /[A-Za-z0-9_:]/;
954
- var NUMBER_RE = /^[-+]?(?:\d+\.?\d*|\.\d+)(?:[eE][-+]?\d+)?/;
1355
+ var NUMBER_RE2 = /^[-+]?(?:\d+\.?\d*|\.\d+)(?:[eE][-+]?\d+)?/;
955
1356
  var INF_NAN_RE = /^[-+]?(?:inf|nan)\b/i;
956
1357
  function tokenize(src) {
957
1358
  const tokens = [];
@@ -1023,7 +1424,7 @@ function tokenize(src) {
1023
1424
  continue;
1024
1425
  }
1025
1426
  if (/[0-9]/.test(c) || (c === "-" || c === "+" || c === ".") && /[0-9.]/.test(src[i + 1] ?? "")) {
1026
- const m = NUMBER_RE.exec(src.slice(i));
1427
+ const m = NUMBER_RE2.exec(src.slice(i));
1027
1428
  if (m) {
1028
1429
  const text = m[0];
1029
1430
  push("number", text, startCol, Number(text));
@@ -3124,7 +3525,7 @@ function crateToUsdaFile(crate) {
3124
3525
  if (spec.specType !== SPEC_PRIM) continue;
3125
3526
  const fm = fieldsOf(spec.fieldSetIndex);
3126
3527
  primByPath.set(path, {
3127
- specifier: SPECIFIERS2[asNumber2(crate, fm.get("specifier")) ?? 0] ?? "def",
3528
+ specifier: SPECIFIERS2[asNumber3(crate, fm.get("specifier")) ?? 0] ?? "def",
3128
3529
  typeName: asString(crate, fm.get("typeName")) ?? "",
3129
3530
  name: leaf(path),
3130
3531
  metadata: buildPrimMetadata(crate, fm),
@@ -3178,7 +3579,7 @@ function buildAttribute(crate, name, fm) {
3178
3579
  name,
3179
3580
  typeName: isArrayType ? rawType.slice(0, -2) : rawType,
3180
3581
  isArray: isArrayType || (defaultRep !== void 0 ? decodeRepBits(defaultRep).isArray : false),
3181
- variability: asNumber2(crate, fm.get("variability")) === 1 ? "uniform" : "varying",
3582
+ variability: asNumber3(crate, fm.get("variability")) === 1 ? "uniform" : "varying",
3182
3583
  custom: customRep !== void 0 && crate.getValue(customRep) === true,
3183
3584
  metadata: {},
3184
3585
  line: 0
@@ -3250,10 +3651,10 @@ function buildLayerMetadata(crate, fm) {
3250
3651
  if (upAxis !== void 0) meta.upAxis = upAxis;
3251
3652
  const defaultPrim2 = asString(crate, fm.get("defaultPrim"));
3252
3653
  if (defaultPrim2 !== void 0) meta.defaultPrim = defaultPrim2;
3253
- const metersPerUnit = asNumber2(crate, fm.get("metersPerUnit"));
3654
+ const metersPerUnit = asNumber3(crate, fm.get("metersPerUnit"));
3254
3655
  if (metersPerUnit !== void 0) meta.metersPerUnit = metersPerUnit;
3255
3656
  for (const key of ["startTimeCode", "endTimeCode", "timeCodesPerSecond", "framesPerSecond"]) {
3256
- const value = asNumber2(crate, fm.get(key));
3657
+ const value = asNumber3(crate, fm.get(key));
3257
3658
  if (value !== void 0) meta[key] = value;
3258
3659
  }
3259
3660
  const customLayerData = fm.has("customLayerData") ? crate.getValue(fm.get("customLayerData")) : void 0;
@@ -3267,7 +3668,7 @@ function asString(crate, rep) {
3267
3668
  const v = crate.getValue(rep);
3268
3669
  return typeof v === "string" ? v : void 0;
3269
3670
  }
3270
- function asNumber2(crate, rep) {
3671
+ function asNumber3(crate, rep) {
3271
3672
  if (rep === void 0) return void 0;
3272
3673
  const v = crate.getValue(rep);
3273
3674
  return typeof v === "number" ? v : void 0;
@@ -3584,6 +3985,33 @@ function stripKeys(meta, keys) {
3584
3985
  }
3585
3986
  return out;
3586
3987
  }
3988
+
3989
+ // src/usd/mdl/loadMdlModules.ts
3990
+ function collectMdlAssetPaths(stage) {
3991
+ const paths = /* @__PURE__ */ new Set();
3992
+ for (const prim of stage.Traverse()) {
3993
+ if (prim.GetTypeName() !== "Shader") continue;
3994
+ const asset = prim.GetAttribute("info:mdl:sourceAsset").Get();
3995
+ if (asset instanceof AssetPath && asset.path) paths.add(asset.path);
3996
+ }
3997
+ return [...paths];
3998
+ }
3999
+ async function loadMdlModules(stage, resolver, baseUrl) {
4000
+ const paths = collectMdlAssetPaths(stage);
4001
+ if (paths.length === 0) return void 0;
4002
+ const modules = /* @__PURE__ */ new Map();
4003
+ await Promise.all(
4004
+ paths.map(async (path) => {
4005
+ try {
4006
+ const module = parseMdl(await resolver.fetchText(resolver.resolve(path, baseUrl)));
4007
+ if (module.materials.size > 0) modules.set(path, module);
4008
+ } catch {
4009
+ }
4010
+ })
4011
+ );
4012
+ if (modules.size === 0) return void 0;
4013
+ return (assetPath) => modules.get(assetPath);
4014
+ }
3587
4015
  var USD_ENTRY = /\.(usda|usdc|usd)$/i;
3588
4016
  function isZip(bytes) {
3589
4017
  if (bytes.length < 4 || bytes[0] !== 80 || bytes[1] !== 75) return false;
@@ -3620,6 +4048,6 @@ function openUsdz(bytes) {
3620
4048
  return { rootEntry, resolver };
3621
4049
  }
3622
4050
 
3623
- export { ARTICULATION_ROOT_API, Attribute, COLLISION_API, CrateReader, DEFAULT_METERS_PER_UNIT, DefaultAssetResolver, Layer, MASS_API, MESH_COLLISION_API, PHYSICS_MATERIAL_API, ParseError, Prim, RIGID_BODY_API, Relationship, Stage, TokenizeError, buildKinematicTree, composeFile, composeLayer, computeLocalTransform, computeWorldTransform, crateToUsdaFile, createMemoryResolver, driveKindFor, extractRobotDescription, gatherGprimDescendants, gatherMeshDescendants, getJointAxis, getJointBodies, getJointDrive, getJointLimits, getJointLocalFrame, getJointStatePosition, getJointType, getMassProperties, getMaterialSubsets, hasArticulationRootAPI, hasCollisionAPI, hasRigidBodyAPI, isBasisCurves, isMesh, isNonVisualPurpose, isPoints, isRenderableGprim, isScope, isSolidGprim, isUnsupportedGprim, isXform, isZip, iterDescendants, joinPosix, jointValueFromSI, jointValueToSI, normalizeJointLimits, openUsdz, parseOpType, parseUsda, refineJointType, resolveBoundMaterial, serializeUsda, toBytes, tokenize };
3624
- //# sourceMappingURL=chunk-NFKK5LJK.js.map
3625
- //# sourceMappingURL=chunk-NFKK5LJK.js.map
4051
+ export { ARTICULATION_ROOT_API, Attribute, COLLISION_API, CrateReader, DEFAULT_METERS_PER_UNIT, DefaultAssetResolver, Layer, MASS_API, MESH_COLLISION_API, PHYSICS_MATERIAL_API, ParseError, Prim, RIGID_BODY_API, Relationship, Stage, TokenizeError, buildKinematicTree, collectMdlAssetPaths, composeFile, composeLayer, computeLocalTransform, computeWorldTransform, crateToUsdaFile, createMemoryResolver, driveKindFor, extractRobotDescription, gatherGprimDescendants, gatherMeshDescendants, getJointAxis, getJointBodies, getJointDrive, getJointLimits, getJointLocalFrame, getJointStatePosition, getJointType, getMassProperties, getMaterialSubsets, hasArticulationRootAPI, hasCollisionAPI, hasRigidBodyAPI, isBasisCurves, isMdlTexture, isMesh, isNonVisualPurpose, isPoints, isRenderableGprim, isScope, isSolidGprim, isUnsupportedGprim, isXform, isZip, iterDescendants, joinPosix, jointValueFromSI, jointValueToSI, loadMdlModules, normalizeJointLimits, openUsdz, parseMdl, parseMdlLiteral, parseOpType, parseUsda, refineJointType, resolveBoundMaterial, serializeUsda, toBytes, tokenize };
4052
+ //# sourceMappingURL=chunk-AXPMXEQC.js.map
4053
+ //# sourceMappingURL=chunk-AXPMXEQC.js.map