three-usd-robot 0.7.0 → 0.8.1

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,81 +1,14 @@
1
- import { getMaterialSubsets, resolveBoundMaterial, COLLISION_API, isNonVisualPurpose, computeWorldTransform, identity4, multiply, computeLocalTransform, invert, interpolate, DefaultAssetResolver, CrateReader, openUsdz, extractRobotDescription, buildKinematicTree, Stage, composeLayer, composeFile, crateToUsdaFile } from './chunk-4GPCBXUS.js';
2
- import * as THREE4 from 'three';
1
+ import { getMaterialSubsets, resolveBoundMaterial, isRenderableGprim, COLLISION_API, isNonVisualPurpose, computeWorldTransform, computeLocalTransform, DefaultAssetResolver, toBytes, extractRobotDescription, isZip, CrateReader, openUsdz, buildKinematicTree, Stage, composeLayer, composeFile, crateToUsdaFile } from './chunk-PHHMWMCA.js';
2
+ import { ThreeUsdRobot } from './chunk-FUPFJOXW.js';
3
+ import { identity4, multiply } from './chunk-IYKPVUZ2.js';
4
+ import * as THREE from 'three';
3
5
 
4
- function axisVector(axis) {
5
- switch (axis) {
6
- case "X":
7
- return new THREE4.Vector3(1, 0, 0);
8
- case "Y":
9
- return new THREE4.Vector3(0, 1, 0);
10
- case "Z":
11
- return new THREE4.Vector3(0, 0, 1);
12
- }
13
- }
14
- var JointObject = class extends THREE4.Object3D {
15
- isJointObject = true;
16
- jointName;
17
- jointType;
18
- axisToken;
19
- axis;
20
- lower;
21
- upper;
22
- _value = 0;
23
- constructor(joint) {
24
- super();
25
- this.name = joint.name;
26
- this.jointName = joint.name;
27
- this.jointType = joint.type;
28
- this.axisToken = joint.axis;
29
- this.axis = axisVector(joint.axis);
30
- this.lower = joint.lower;
31
- this.upper = joint.upper;
32
- }
33
- get value() {
34
- return this._value;
35
- }
36
- get articulated() {
37
- return this.jointType !== "fixed";
38
- }
39
- /**
40
- * Set the joint value (radians for revolute/continuous, length for prismatic).
41
- * Optionally clamps to authored limits. Returns the value actually applied.
42
- */
43
- setValue(value, clampToLimits = true) {
44
- if (!this.articulated) return this._value;
45
- let v = value;
46
- if (clampToLimits) {
47
- if (this.lower !== void 0 && v < this.lower) v = this.lower;
48
- if (this.upper !== void 0 && v > this.upper) v = this.upper;
49
- }
50
- this._value = v;
51
- if (this.jointType === "prismatic") {
52
- this.position.copy(this.axis).multiplyScalar(v);
53
- this.quaternion.identity();
54
- } else {
55
- this.quaternion.setFromAxisAngle(this.axis, v);
56
- this.position.set(0, 0, 0);
57
- }
58
- return v;
59
- }
60
- };
61
- var LinkObject = class extends THREE4.Object3D {
62
- isLinkObject = true;
63
- linkName;
64
- primPath;
65
- constructor(link) {
66
- super();
67
- this.name = link.name;
68
- this.linkName = link.name;
69
- this.primPath = link.primPath;
70
- this.matrixAutoUpdate = false;
71
- }
72
- };
73
6
  var DEFAULT_COLOR = 10132122;
74
7
  function buildMeshGeometry(meshPrim) {
75
8
  const points = meshPrim.GetAttribute("points").Get();
76
9
  if (!isVec3Array(points) || points.length === 0) return null;
77
- const geometry = new THREE4.BufferGeometry();
78
- geometry.setAttribute("position", new THREE4.Float32BufferAttribute(flat3(points), 3));
10
+ const geometry = new THREE.BufferGeometry();
11
+ geometry.setAttribute("position", new THREE.Float32BufferAttribute(flat3(points), 3));
79
12
  const counts = meshPrim.GetAttribute("faceVertexCounts").Get();
80
13
  const indices = meshPrim.GetAttribute("faceVertexIndices").Get();
81
14
  if (isNumberArray(counts) && isNumberArray(indices)) {
@@ -90,18 +23,57 @@ function buildMeshGeometry(meshPrim) {
90
23
  }
91
24
  const normals = meshPrim.GetAttribute("normals").Get();
92
25
  if (isVec3Array(normals) && normals.length === points.length) {
93
- geometry.setAttribute("normal", new THREE4.Float32BufferAttribute(flat3(normals), 3));
26
+ geometry.setAttribute("normal", new THREE.Float32BufferAttribute(flat3(normals), 3));
94
27
  } else {
95
28
  geometry.computeVertexNormals();
96
29
  }
97
30
  const st = meshPrim.GetAttribute("primvars:st").Get();
98
31
  if (isVec2Array(st) && st.length === points.length) {
99
- geometry.setAttribute("uv", new THREE4.Float32BufferAttribute(flat2(st), 2));
32
+ geometry.setAttribute("uv", new THREE.Float32BufferAttribute(flat2(st), 2));
100
33
  }
101
34
  return geometry;
102
35
  }
36
+ function buildGprimGeometry(prim) {
37
+ switch (prim.GetTypeName()) {
38
+ case "Mesh":
39
+ return buildMeshGeometry(prim);
40
+ case "Cube": {
41
+ const size = getNumber(prim, "size") ?? 2;
42
+ return new THREE.BoxGeometry(size, size, size);
43
+ }
44
+ case "Sphere":
45
+ return new THREE.SphereGeometry(getNumber(prim, "radius") ?? 1, 32, 16);
46
+ case "Cylinder": {
47
+ const radius = getNumber(prim, "radius") ?? 1;
48
+ const height = getNumber(prim, "height") ?? 2;
49
+ return orientSpine(new THREE.CylinderGeometry(radius, radius, height, 32), prim);
50
+ }
51
+ case "Capsule": {
52
+ const radius = getNumber(prim, "radius") ?? 0.5;
53
+ const height = getNumber(prim, "height") ?? 1;
54
+ return orientSpine(new THREE.CapsuleGeometry(radius, height, 8, 32), prim);
55
+ }
56
+ case "Cone": {
57
+ const radius = getNumber(prim, "radius") ?? 1;
58
+ const height = getNumber(prim, "height") ?? 2;
59
+ return orientSpine(new THREE.ConeGeometry(radius, height, 32), prim);
60
+ }
61
+ default:
62
+ return null;
63
+ }
64
+ }
65
+ function getNumber(prim, name) {
66
+ const v = prim.GetAttribute(name).Get();
67
+ return typeof v === "number" ? v : void 0;
68
+ }
69
+ function orientSpine(geometry, prim) {
70
+ const axis = prim.GetAttribute("axis").Get();
71
+ if (axis === "Y") return geometry;
72
+ if (axis === "X") return geometry.rotateZ(-Math.PI / 2);
73
+ return geometry.rotateX(Math.PI / 2);
74
+ }
103
75
  function buildMeshMaterial(meshPrim, stage, textures, bindingPrim) {
104
- const color = new THREE4.Color(DEFAULT_COLOR);
76
+ const color = new THREE.Color(DEFAULT_COLOR);
105
77
  let opacity = 1;
106
78
  const bound = stage ? resolveBoundMaterial(stage, bindingPrim ?? meshPrim) : void 0;
107
79
  if (bound?.color) {
@@ -133,7 +105,7 @@ function buildMeshMaterial(meshPrim, stage, textures, bindingPrim) {
133
105
  const emissiveMap = tex(bound?.emissiveTexture, "srgb");
134
106
  const metalness = bound?.metalness ?? bound?.metalnessTexture?.scale?.[0] ?? (metalnessMap ? 1 : 0.1);
135
107
  const roughness = bound?.roughness ?? bound?.roughnessTexture?.scale?.[0] ?? (roughnessMap ? 1 : 0.8);
136
- const emissive = new THREE4.Color(0);
108
+ const emissive = new THREE.Color(0);
137
109
  if (bound?.emissiveColor) {
138
110
  emissive.setRGB(bound.emissiveColor[0], bound.emissiveColor[1], bound.emissiveColor[2]);
139
111
  } else if (emissiveMap) {
@@ -147,7 +119,7 @@ function buildMeshMaterial(meshPrim, stage, textures, bindingPrim) {
147
119
  const alphaTest = threshold !== void 0 && threshold > 0 ? threshold : 0;
148
120
  const transparent = alphaTest === 0 && hasAlphaSource;
149
121
  const doubleSided = meshPrim.GetAttribute("doubleSided").Get() === true;
150
- const material = new THREE4.MeshStandardMaterial({
122
+ const material = new THREE.MeshStandardMaterial({
151
123
  color,
152
124
  metalness,
153
125
  roughness,
@@ -155,7 +127,7 @@ function buildMeshMaterial(meshPrim, stage, textures, bindingPrim) {
155
127
  transparent,
156
128
  opacity,
157
129
  ...alphaTest > 0 ? { alphaTest } : {},
158
- side: doubleSided ? THREE4.DoubleSide : THREE4.FrontSide,
130
+ side: doubleSided ? THREE.DoubleSide : THREE.FrontSide,
159
131
  ...map ? { map } : {},
160
132
  ...alphaMap ? { alphaMap } : {},
161
133
  ...normalMap ? { normalMap } : {},
@@ -198,12 +170,12 @@ function bindSceneMeshes(stage, robot3d, desc, options = {}) {
198
170
  }
199
171
  let attached = 0;
200
172
  for (const prim of stage.Traverse()) {
201
- if (prim.GetTypeName() !== "Mesh" || owned.has(prim.GetPath())) continue;
173
+ if (!isRenderableGprim(prim) || owned.has(prim.GetPath())) continue;
202
174
  if (prim.HasAPI(COLLISION_API) || isNonVisualPurpose(prim)) continue;
203
175
  if ([...owned].some((path) => prim.GetPath().startsWith(`${path}/`))) continue;
204
- const geometry = buildMeshGeometry(prim);
176
+ const geometry = buildGprimGeometry(prim);
205
177
  if (!geometry) continue;
206
- const mesh = new THREE4.Mesh(geometry, buildMeshMaterial(prim, stage, options.textureProvider));
178
+ const mesh = new THREE.Mesh(geometry, buildMeshMaterial(prim, stage, options.textureProvider));
207
179
  mesh.name = prim.GetName();
208
180
  mesh.userData.kind = "scene";
209
181
  mesh.userData.primPath = prim.GetPath();
@@ -226,9 +198,9 @@ function buildMeshMaterials(meshPrim, stage, textures) {
226
198
  function attachMesh(stage, linkPrim, meshPath, parent, kind, textures) {
227
199
  const meshPrim = stage.GetPrimAtPath(meshPath);
228
200
  if (!meshPrim) return;
229
- const geometry = buildMeshGeometry(meshPrim);
201
+ const geometry = buildGprimGeometry(meshPrim);
230
202
  if (!geometry) return;
231
- const mesh = new THREE4.Mesh(geometry, buildMeshMaterials(meshPrim, stage, textures));
203
+ const mesh = new THREE.Mesh(geometry, buildMeshMaterials(meshPrim, stage, textures));
232
204
  mesh.name = meshPrim.GetName();
233
205
  mesh.userData.kind = kind;
234
206
  mesh.userData.primPath = meshPath;
@@ -348,8 +320,8 @@ function createTextureProvider(resolver, baseUrl) {
348
320
  } catch {
349
321
  return null;
350
322
  }
351
- const texture = new THREE4.Texture();
352
- texture.colorSpace = options.colorSpace === "linear" ? THREE4.NoColorSpace : THREE4.SRGBColorSpace;
323
+ const texture = new THREE.Texture();
324
+ texture.colorSpace = options.colorSpace === "linear" ? THREE.NoColorSpace : THREE.SRGBColorSpace;
353
325
  texture.wrapS = toThreeWrap(options.wrapS);
354
326
  texture.wrapT = toThreeWrap(options.wrapT);
355
327
  applyTransform(texture, options.transform);
@@ -380,11 +352,11 @@ function toThreeWrap(wrap) {
380
352
  switch (wrap) {
381
353
  case "clamp":
382
354
  case "black":
383
- return THREE4.ClampToEdgeWrapping;
355
+ return THREE.ClampToEdgeWrapping;
384
356
  case "mirror":
385
- return THREE4.MirroredRepeatWrapping;
357
+ return THREE.MirroredRepeatWrapping;
386
358
  default:
387
- return THREE4.RepeatWrapping;
359
+ return THREE.RepeatWrapping;
388
360
  }
389
361
  }
390
362
  function applyTransform(texture, t) {
@@ -401,254 +373,6 @@ function mimeOf(url) {
401
373
  if (/\.webp$/i.test(url)) return "image/webp";
402
374
  return "image/png";
403
375
  }
404
- var ThreeUsdRobot = class extends THREE4.Object3D {
405
- isThreeUsdRobot = true;
406
- robot;
407
- tree;
408
- clampJointLimits;
409
- linkObjects = /* @__PURE__ */ new Map();
410
- jointObjects = /* @__PURE__ */ new Map();
411
- dirty = true;
412
- helperSize;
413
- _showVisual = true;
414
- _showCollision = false;
415
- _showJointAxes = false;
416
- _showLinkFrames = false;
417
- jointAxesHelpers = [];
418
- linkFrameHelpers = [];
419
- constructor(robot, tree, options = {}) {
420
- super();
421
- this.name = robot.name;
422
- this.robot = robot;
423
- this.tree = tree;
424
- this.clampJointLimits = options.clampJointLimits ?? true;
425
- this.helperSize = options.helperSize ?? 0.15;
426
- for (const [key, link] of Object.entries(robot.links)) {
427
- this.linkObjects.set(key, new LinkObject(link));
428
- }
429
- this.attachRoot();
430
- this.attachTreeEdges();
431
- this.attachIsolatedLinks();
432
- this.applyStageNormalization(robot, options);
433
- if (options.applyInitialPose ?? true) this.applyInitialPose(robot);
434
- }
435
- /** Orient (Z-up → Y-up) and scale (metersPerUnit × unitScale) the robot root. */
436
- applyStageNormalization(robot, options) {
437
- const scale = (robot.metersPerUnit || 1) * (options.unitScale ?? 1);
438
- if (scale !== 1) this.scale.setScalar(scale);
439
- const conv = options.upAxisConversion ?? "none";
440
- const toY = conv === "Z" || conv === "auto" && robot.upAxis === "Z";
441
- if (toY) this.quaternion.setFromAxisAngle(new THREE4.Vector3(1, 0, 0), -Math.PI / 2);
442
- }
443
- /** Apply each joint's authored initial value, if any. */
444
- applyInitialPose(robot) {
445
- for (const [key, joint] of Object.entries(robot.joints)) {
446
- if (joint.initialValue === void 0) continue;
447
- this.jointObjects.get(key)?.setValue(joint.initialValue, this.clampJointLimits);
448
- }
449
- this.dirty = true;
450
- }
451
- attachRoot() {
452
- const rootObj = this.linkObjects.get(this.tree.root);
453
- if (!rootObj) return;
454
- const rootJointKey = this.tree.rootJoint;
455
- const rootLink = this.robot.links[this.tree.root];
456
- if (rootJointKey) {
457
- const j = this.robot.joints[rootJointKey];
458
- if (j) setMatrix(rootObj, multiply(j.jointFrame0, invert(j.jointFrame1)));
459
- } else if (rootLink?.worldTransform) {
460
- setMatrix(rootObj, rootLink.worldTransform);
461
- }
462
- this.add(rootObj);
463
- }
464
- attachTreeEdges() {
465
- for (const linkKey of this.tree.order) {
466
- const node = this.tree.nodes[linkKey];
467
- if (!node || node.parent === null || node.jointToParent === null) continue;
468
- const parentObj = this.linkObjects.get(node.parent);
469
- const childObj = this.linkObjects.get(linkKey);
470
- const joint = this.robot.joints[node.jointToParent];
471
- if (!parentObj || !childObj || !joint) continue;
472
- this.attachJointChain(parentObj, childObj, node.jointToParent, joint);
473
- }
474
- }
475
- /** Build `parent → frame0 → motion → frame1⁻¹ → child` for one joint. */
476
- attachJointChain(parent, child, jointKey, joint) {
477
- const frame0 = new THREE4.Group();
478
- frame0.name = `${joint.name}:frame0`;
479
- setMatrix(frame0, joint.jointFrame0);
480
- const motion = new JointObject(joint);
481
- const frame1Inv = new THREE4.Group();
482
- frame1Inv.name = `${joint.name}:frame1Inv`;
483
- setMatrix(frame1Inv, invert(joint.jointFrame1));
484
- parent.add(frame0);
485
- frame0.add(motion);
486
- motion.add(frame1Inv);
487
- frame1Inv.add(child);
488
- this.jointObjects.set(jointKey, motion);
489
- }
490
- attachIsolatedLinks() {
491
- for (const key of this.tree.isolatedLinks) {
492
- const obj = this.linkObjects.get(key);
493
- if (!obj || obj.parent) continue;
494
- const worldTransform = this.robot.links[key]?.worldTransform;
495
- if (worldTransform) setMatrix(obj, worldTransform);
496
- this.add(obj);
497
- }
498
- }
499
- // -- Joint control -------------------------------------------------------
500
- /** Set one joint value. Unknown joints are ignored. Returns whether it applied. */
501
- setJointValue(name, value) {
502
- const joint = this.jointObjects.get(name);
503
- if (!joint) return false;
504
- joint.setValue(value, this.clampJointLimits);
505
- this.dirty = true;
506
- return true;
507
- }
508
- /** Set several joint values at once (matrix update is coalesced). */
509
- setJointValues(values) {
510
- for (const [name, value] of Object.entries(values)) {
511
- const joint = this.jointObjects.get(name);
512
- if (joint) {
513
- joint.setValue(value, this.clampJointLimits);
514
- this.dirty = true;
515
- }
516
- }
517
- }
518
- getJointValue(name) {
519
- return this.jointObjects.get(name)?.value;
520
- }
521
- /** Recompute world matrices. Called lazily by the getters; safe to call directly. */
522
- updateKinematics() {
523
- this.updateMatrixWorld(true);
524
- this.dirty = false;
525
- }
526
- ensureUpdated() {
527
- if (this.dirty) this.updateKinematics();
528
- }
529
- // -- Queries -------------------------------------------------------------
530
- getLinkWorldMatrix(name) {
531
- const obj = this.linkObjects.get(name);
532
- if (!obj) throw new Error(`unknown link "${name}"`);
533
- this.ensureUpdated();
534
- return obj.matrixWorld.clone();
535
- }
536
- getLinkWorldPosition(name) {
537
- return new THREE4.Vector3().setFromMatrixPosition(this.getLinkWorldMatrix(name));
538
- }
539
- getLinkObject(name) {
540
- return this.linkObjects.get(name);
541
- }
542
- getJointObject(name) {
543
- return this.jointObjects.get(name);
544
- }
545
- getJoints() {
546
- return Object.values(this.robot.joints);
547
- }
548
- getLinks() {
549
- return Object.values(this.robot.links);
550
- }
551
- /** Names of the articulated (controllable) joints. */
552
- getJointNames() {
553
- return [...this.jointObjects.keys()];
554
- }
555
- getLinkNames() {
556
- return [...this.linkObjects.keys()];
557
- }
558
- getKinematicTree() {
559
- return this.tree;
560
- }
561
- // -- Animation playback --------------------------------------------------
562
- /** Playback rate in time codes per second (from the stage; default 24). */
563
- getTimeCodesPerSecond() {
564
- return this.robot.timeCodesPerSecond ?? 24;
565
- }
566
- /** Whether any joint has a time-sampled trajectory. */
567
- hasAnimation() {
568
- return Object.values(this.robot.joints).some((j) => j.valueSamples !== void 0);
569
- }
570
- /**
571
- * Animation range in time codes: the union of authored joint sample ranges,
572
- * falling back to the stage `startTimeCode`/`endTimeCode`. `null` if neither.
573
- */
574
- getTimeRange() {
575
- let start = Number.POSITIVE_INFINITY;
576
- let end = Number.NEGATIVE_INFINITY;
577
- for (const joint of Object.values(this.robot.joints)) {
578
- const times = joint.valueSamples?.times;
579
- if (!times || times.length === 0) continue;
580
- start = Math.min(start, times[0]);
581
- end = Math.max(end, times[times.length - 1]);
582
- }
583
- if (start <= end) return { start, end };
584
- const { startTimeCode, endTimeCode } = this.robot;
585
- if (startTimeCode !== void 0 && endTimeCode !== void 0) {
586
- return { start: startTimeCode, end: endTimeCode };
587
- }
588
- return null;
589
- }
590
- /** Sample every animated joint at time code `t` and apply the values. */
591
- setTime(t) {
592
- for (const [key, joint] of Object.entries(this.robot.joints)) {
593
- if (joint.valueSamples) this.setJointValue(key, interpolate(joint.valueSamples, t));
594
- }
595
- }
596
- // -- Display toggles -----------------------------------------------------
597
- get showVisual() {
598
- return this._showVisual;
599
- }
600
- set showVisual(v) {
601
- this._showVisual = v;
602
- this.setKindVisibility("visual", v);
603
- }
604
- get showCollision() {
605
- return this._showCollision;
606
- }
607
- set showCollision(v) {
608
- this._showCollision = v;
609
- this.setKindVisibility("collision", v);
610
- }
611
- get showJointAxes() {
612
- return this._showJointAxes;
613
- }
614
- set showJointAxes(v) {
615
- this._showJointAxes = v;
616
- if (v && this.jointAxesHelpers.length === 0) {
617
- for (const joint of this.jointObjects.values()) {
618
- const h = new THREE4.AxesHelper(this.helperSize);
619
- h.name = `${joint.jointName}:axes`;
620
- joint.add(h);
621
- this.jointAxesHelpers.push(h);
622
- }
623
- }
624
- for (const h of this.jointAxesHelpers) h.visible = v;
625
- }
626
- get showLinkFrames() {
627
- return this._showLinkFrames;
628
- }
629
- set showLinkFrames(v) {
630
- this._showLinkFrames = v;
631
- if (v && this.linkFrameHelpers.length === 0) {
632
- for (const link of this.linkObjects.values()) {
633
- const h = new THREE4.AxesHelper(this.helperSize);
634
- h.name = `${link.linkName}:frame`;
635
- link.add(h);
636
- this.linkFrameHelpers.push(h);
637
- }
638
- }
639
- for (const h of this.linkFrameHelpers) h.visible = v;
640
- }
641
- setKindVisibility(kind, visible) {
642
- this.traverse((o) => {
643
- if (o.userData.kind === kind) o.visible = visible;
644
- });
645
- }
646
- };
647
- function setMatrix(obj, m) {
648
- obj.matrixAutoUpdate = false;
649
- obj.matrix.fromArray(m);
650
- obj.matrixWorldNeedsUpdate = true;
651
- }
652
376
 
653
377
  // src/three/ThreeUsdRobotLoader.ts
654
378
  var ThreeUsdRobotLoader = class {
@@ -661,52 +385,76 @@ var ThreeUsdRobotLoader = class {
661
385
  }
662
386
  /**
663
387
  * Fetch an asset by URL and build the robot. `.usdz` packages are unzipped and
664
- * composed from their entries; everything else is sniffed for the crate magic
665
- * and parsed as binary USDC or USDA text.
388
+ * composed from their entries; everything else is sniffed for the zip / crate
389
+ * magic and parsed as USDZ, binary USDC, or USDA text.
666
390
  */
667
391
  async loadAsync(url) {
668
392
  const bytes = await this.fetchRootBytes(url);
669
393
  if (/\.usdz$/i.test(url)) return this.parseUsdz(bytes);
670
- if (CrateReader.isCrate(bytes)) return this.parseCrate(bytes, url);
671
- return this.parse(new TextDecoder().decode(bytes), url);
394
+ return this.parse(bytes, url);
672
395
  }
673
396
  async fetchRootBytes(url) {
674
397
  const resolver = this.resolver;
675
398
  if (resolver.fetchBytes) return resolver.fetchBytes(url);
676
399
  return new TextEncoder().encode(await resolver.fetchText(url));
677
400
  }
678
- /** Build a robot (composed, with meshes) from USDA source text. */
679
- async parse(text, baseUrl = "") {
680
- return this.buildFromStage(
681
- await this.composeStage(text, baseUrl, this.resolver),
682
- baseUrl,
683
- this.resolver
684
- );
401
+ /**
402
+ * Build a robot from in-memory content — no fetch involved. Accepts USDA
403
+ * source text, or an `ArrayBuffer` / typed array / `Blob` (e.g. a dropped
404
+ * `File`) holding USDA text, a binary crate, or a `.usdz` package; binary
405
+ * input is sniffed for the zip / crate magic. `baseUrl` anchors relative
406
+ * references, payloads, and texture paths of non-package input.
407
+ */
408
+ async parse(data, baseUrl = "") {
409
+ return this.buildFromStage(await this.openSource(data, baseUrl));
685
410
  }
686
- /** Build a robot from the bytes of a `.usdz` package. */
687
- async parseUsdz(bytes) {
688
- const pkg = openUsdz(bytes);
689
- const rootBytes = await pkg.resolver.fetchBytes(pkg.rootEntry);
690
- const stage = await this.composeStageFromBytes(rootBytes, pkg.rootEntry, pkg.resolver);
691
- return this.buildFromStage(stage, pkg.rootEntry, pkg.resolver);
411
+ /** Build a robot from a `.usdz` package (bytes, `ArrayBuffer`, or `Blob`). */
412
+ async parseUsdz(data) {
413
+ return this.buildFromStage(await this.openUsdzStage(await toBytes(data)));
692
414
  }
693
- /** Build a robot from the bytes of a binary crate (`.usdc` / binary `.usd`). */
694
- async parseCrate(bytes, baseUrl = "") {
695
- const stage = await this.composeStageFromBytes(bytes, baseUrl, this.resolver);
696
- return this.buildFromStage(stage, baseUrl, this.resolver);
415
+ /** Build a robot from a binary crate (`.usdc` / binary `.usd`). */
416
+ async parseCrate(data, baseUrl = "") {
417
+ const stage = await this.composeStageFromBytes(await toBytes(data), baseUrl, this.resolver);
418
+ return this.buildFromStage({ stage, baseUrl, resolver: this.resolver });
697
419
  }
698
- /** Parse + compose USDA source into the Three.js-independent robot IR. */
699
- async parseRobotDescription(text, baseUrl = "") {
700
- const stage = await this.composeStage(text, baseUrl, this.resolver);
420
+ /**
421
+ * Parse + compose in-memory content (same formats as {@link parse}) into the
422
+ * Three.js-independent robot IR.
423
+ */
424
+ async parseRobotDescription(data, baseUrl = "") {
425
+ const { stage } = await this.openSource(data, baseUrl);
701
426
  return extractRobotDescription(stage, this.extractOptions());
702
427
  }
703
- buildFromStage(stage, baseUrl, resolver) {
428
+ /** Sniff in-memory content (text / zip / crate) and compose it into a stage. */
429
+ async openSource(data, baseUrl) {
430
+ const resolver = this.resolver;
431
+ if (typeof data === "string") {
432
+ return { stage: await this.composeStage(data, baseUrl, resolver), baseUrl, resolver };
433
+ }
434
+ const bytes = await toBytes(data);
435
+ if (isZip(bytes)) return this.openUsdzStage(bytes);
436
+ const stage = CrateReader.isCrate(bytes) ? await this.composeStageFromBytes(bytes, baseUrl, resolver) : await this.composeStage(new TextDecoder().decode(bytes), baseUrl, resolver);
437
+ return { stage, baseUrl, resolver };
438
+ }
439
+ async openUsdzStage(bytes) {
440
+ const pkg = openUsdz(bytes);
441
+ const rootBytes = await pkg.resolver.fetchBytes(pkg.rootEntry);
442
+ const stage = await this.composeStageFromBytes(rootBytes, pkg.rootEntry, pkg.resolver);
443
+ return { stage, baseUrl: pkg.rootEntry, resolver: pkg.resolver };
444
+ }
445
+ buildFromStage({ stage, baseUrl, resolver }) {
704
446
  const robot = extractRobotDescription(stage, this.extractOptions());
705
447
  const tree = buildKinematicTree(robot);
706
448
  const robot3d = new ThreeUsdRobot(robot, tree, this.robotOptions());
707
449
  const loadVisuals = this.options.loadVisuals ?? true;
708
450
  const loadCollisions = this.options.loadCollisions ?? false;
709
- const loadScene = this.options.loadSceneGeometry ?? false;
451
+ const isStaticScene = Object.keys(robot.links).length === 0 && Object.keys(robot.joints).length === 0;
452
+ if (isStaticScene && this.options.loadSceneGeometry === void 0) {
453
+ this.options.onWarn?.(
454
+ "no articulation found (0 links / 0 joints); rendering the stage as static scene geometry"
455
+ );
456
+ }
457
+ const loadScene = this.options.loadSceneGeometry ?? isStaticScene;
710
458
  if (loadVisuals || loadCollisions || loadScene) {
711
459
  const textureProvider = this.options.loadTextures ?? true ? createTextureProvider(resolver, baseUrl) : void 0;
712
460
  if (loadVisuals || loadCollisions) {
@@ -741,7 +489,7 @@ var ThreeUsdRobotLoader = class {
741
489
  }
742
490
  robotOptions() {
743
491
  return {
744
- upAxisConversion: this.options.upAxisConversion ?? "auto",
492
+ ...this.options.worldUp ? { worldUp: this.options.worldUp } : { upAxisConversion: this.options.upAxisConversion ?? "auto" },
745
493
  ...this.options.clampJointLimits !== void 0 ? { clampJointLimits: this.options.clampJointLimits } : {},
746
494
  ...this.options.unitScale !== void 0 ? { unitScale: this.options.unitScale } : {},
747
495
  ...this.options.applyDriveTargetsAsInitialPose !== void 0 ? { applyInitialPose: this.options.applyDriveTargetsAsInitialPose } : {}
@@ -755,6 +503,6 @@ var ThreeUsdRobotLoader = class {
755
503
  }
756
504
  };
757
505
 
758
- export { JointObject, LinkObject, ThreeUsdRobot, ThreeUsdRobotLoader, axisVector, bindRobotMeshes, bindSceneMeshes, buildMeshGeometry, buildMeshMaterial, createTextureProvider };
759
- //# sourceMappingURL=chunk-FL554IS5.js.map
760
- //# sourceMappingURL=chunk-FL554IS5.js.map
506
+ export { ThreeUsdRobotLoader, bindRobotMeshes, bindSceneMeshes, buildGprimGeometry, buildMeshGeometry, buildMeshMaterial, createTextureProvider };
507
+ //# sourceMappingURL=chunk-JHOX3VUW.js.map
508
+ //# sourceMappingURL=chunk-JHOX3VUW.js.map