three-usd-robot 0.6.0 → 0.8.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.
package/README.md CHANGED
@@ -9,6 +9,11 @@ Think of it as a **USD version of [`urdf-loader`](https://www.npmjs.com/package/
9
9
  it reads the link / joint / xform / mesh structure out of `UsdPhysics` assets
10
10
  and drives forward kinematics on a Three.js `Object3D` hierarchy.
11
11
 
12
+ **[▶ Live demo](https://three-usd-robot.vercel.app)** — stock Isaac Sim robots
13
+ streamed from NVIDIA's asset CDN, joints driven from a slider panel, and
14
+ exported back to `.usda` / `.usdz` in the browser.
15
+
16
+ ![A franka panda loaded in the browser](assets/franka.png)
12
17
  ![A robot cell loaded in the browser](assets/threejs.png)
13
18
 
14
19
  ## Features
@@ -18,15 +23,35 @@ and drives forward kinematics on a Three.js `Object3D` hierarchy.
18
23
  instanceable prims are composed for you.
19
24
  - **Robots** — links, joints (fixed / revolute / continuous / prismatic), limits,
20
25
  drives and the initial pose become a `setJointValue`-able hierarchy.
21
- - **Rendering** — meshes with `UsdShade` materials (UsdPreviewSurface / OmniPBR)
22
- and textures; up-axis and units normalized automatically.
26
+ - **Rendering** — meshes and solid gprims (`Cube` / `Sphere` / `Cylinder` /
27
+ `Capsule` / `Cone`) with `UsdShade` materials (UsdPreviewSurface / OmniPBR)
28
+ and textures; up-axis and units normalized automatically. Articulation-free
29
+ stages load as static scenes.
23
30
  - **Animation** — plays back time-sampled joint trajectories.
24
31
  - **Export** — write robots and whole cells back to `.usda` / `.usdz`,
25
32
  simulation-ready for Isaac Sim.
26
33
  - **React** — declarative `<UsdRobot>` for React Three Fiber.
27
34
 
28
- Not yet supported: time samples and variant *selections* stored inside binary
29
- crate files, and full material/shader fidelity.
35
+ Stock Isaac Sim robot assets load straight from their public CDN — Franka
36
+ Panda, UR10e, Fanuc CRX-10iA/L, Kuka KR210, Shadow Hand, Unitree H1/Go2 and
37
+ friends all compose from their variant-driven, multi-layer form:
38
+
39
+ ```ts
40
+ const ROOT = "https://omniverse-content-production.s3-us-west-2.amazonaws.com/Assets/Isaac/5.1";
41
+ const franka = await new ThreeUsdRobotLoader()
42
+ .loadAsync(`${ROOT}/Isaac/Robots/FrankaRobotics/FrankaPanda/franka.usd`);
43
+
44
+ franka.setJointValues({ panda_joint2: -0.785, panda_joint4: -2.356, panda_joint6: 1.571 });
45
+ franka.getLinkWorldPosition("panda_hand"); // (0.307, 0, 0.590) — the documented ready pose
46
+ ```
47
+
48
+ The CDN is public and CORS-enabled, so this works in the browser too. Try
49
+ `npx tsx scripts/demo-franka.ts` for a console walkthrough (articulation table,
50
+ FK check, and a re-export to one self-contained file), or open the Vite example
51
+ and pick a robot from the preset list.
52
+
53
+ Not yet supported: time samples stored inside binary crate files, point/curve
54
+ gprims (`Points`, `BasisCurves`, …), and full material/shader fidelity.
30
55
 
31
56
  ## Install
32
57
 
@@ -56,8 +81,38 @@ robot.getJointNames(); // controllable joints
56
81
  robot.getKinematicTree(); // root, ordering, loop joints, ...
57
82
  ```
58
83
 
59
- Already have the bytes? Use `parse(usdaText)`, `parseCrate(usdcBytes)` or
60
- `parseUsdz(bytes)` instead of `loadAsync`.
84
+ No URL? `parse` also takes in-memory content: USDA source text, or an
85
+ `ArrayBuffer` / typed array / `Blob` holding any supported format — the zip /
86
+ crate magic is sniffed, so a dropped `File` or a response body works as-is:
87
+
88
+ ```ts
89
+ const loader = new ThreeUsdRobotLoader();
90
+ await loader.parse(usdaText); // USDA source string
91
+ await loader.parse(file); // File / Blob from drag & drop or <input type="file">
92
+ await loader.parse(await res.arrayBuffer()); // a fetch you did yourself
93
+ ```
94
+
95
+ `parseUsdz(data)` / `parseCrate(data, baseUrl)` stay as explicit-format entries,
96
+ and `parseRobotDescription(data)` returns the Three.js-independent IR. Pass a
97
+ `baseUrl` as the second `parse` argument if the layer has relative references
98
+ or texture paths to resolve.
99
+
100
+ ### Stable addressing (naming contract)
101
+
102
+ Joints and links are keyed by their prim's **leaf name** while it is unique
103
+ across the robot; on a collision (say, two arms each with a `seg` link) the
104
+ colliding entries are keyed by their **full prim path** instead —
105
+ deterministically. Every accessor also takes the full prim path directly, so
106
+ tooling can pin exact prims no matter how the asset is named:
107
+
108
+ ```ts
109
+ robot.setJointValue("/World/armL/j1", 0.4); // same joint as its key
110
+ robot.getLinkWorldMatrix("/World/armL/seg");
111
+ robot.getLinkObjectsByPath(); // Map<primPath, LinkObject>
112
+ robot.getJointObjectsByPath(); // Map<primPath, JointObject>
113
+ ```
114
+
115
+ `LinkObject.primPath` / `JointObject.primPath` carry the reverse direction.
61
116
 
62
117
  ### Viewer toggles & helpers
63
118
 
@@ -71,9 +126,38 @@ import { addJointLimitHelpers } from "three-usd-robot/helpers";
71
126
  addJointLimitHelpers(robot); // arc (revolute) / segment (prismatic) per joint
72
127
  ```
73
128
 
129
+ ### Link highlighting & ghosts
130
+
131
+ Per-link appearance helpers cover the common viewer chores — flagging
132
+ colliding links, material swaps, and translucent "ghost" pose previews:
133
+
134
+ ```ts
135
+ import {
136
+ createGhostRobot,
137
+ highlightLink,
138
+ restoreLinkMaterials,
139
+ setLinkMaterial,
140
+ } from "three-usd-robot/helpers";
141
+
142
+ highlightLink(robot, "link1"); // emissive red tint; maps/colors kept
143
+ highlightLink(robot, "/World/armL/seg", { color: 0xffaa00, opacity: 0.6 });
144
+ setLinkMaterial(robot, "link2", new THREE.MeshBasicMaterial({ wireframe: true }));
145
+ restoreLinkMaterials(robot, "link1"); // exact original materials back
146
+
147
+ const ghost = createGhostRobot(robot, { jointValues: { joint1: 1.2 } });
148
+ scene.add(ghost); // translucent copy previewing the target pose
149
+ ghost.setJointValues(ikSolution); // a full ThreeUsdRobot, driveable like the source
150
+ ```
151
+
152
+ Highlights never stack (each call re-tints from the originals), and ghosts
153
+ share the source's geometry — cloning is cheap enough for onion-skinning.
154
+
74
155
  Loading a whole cell rather than a bare robot? Pass
75
156
  `{ loadSceneGeometry: true }` to the loader to draw the static environment
76
- (floor, guarding, racking, …) around the machines.
157
+ (floor, guarding, racking, …) around the machines. A stage with **no
158
+ articulation at all** — a plain static USD scene — is detected and rendered as
159
+ scene geometry automatically, with the same unit / up-axis normalization; pass
160
+ `loadSceneGeometry: false` to opt out.
77
161
 
78
162
  ### Joint slider panel (lil-gui)
79
163
 
@@ -198,9 +282,16 @@ and `.usdz` packages to ASCII USDA.
198
282
 
199
283
  ## Examples
200
284
 
201
- [`examples/`](./examples) holds runnable Vite demos, including a React Three
202
- Fiber viewer that loads any asset via `?asset=<url>` and exports it back to
203
- `.usda` / `.usdz`.
285
+ [`examples/`](./examples) holds runnable Vite demos:
286
+
287
+ - **`vite-joint-slider`** — the [live demo](https://three-usd-robot.vercel.app):
288
+ vanilla Three.js + `lil-gui`, with a robot picker, joint sliders, animation
289
+ playback and USD export.
290
+ - **`vite-basic-viewer`** — the same thing through React Three Fiber.
291
+
292
+ Both take `?asset=<url>` for any asset, or `?isaac=<path under Isaac/>` to pull
293
+ one straight from NVIDIA's CDN. `npm run demo:build` generates the factory cell
294
+ and builds the deployable site.
204
295
 
205
296
  ## Package entry points
206
297
 
@@ -1,5 +1,5 @@
1
1
  import * as THREE from 'three';
2
- import { J as JointType, A as Axis, a as JointDescription, L as LinkDescription, R as RobotDescription, K as KinematicTree } from './buildKinematicTree-wtZPzy0B.js';
2
+ import { J as JointType, A as Axis, a as JointDescription, L as LinkDescription, R as RobotDescription, K as KinematicTree } from './buildKinematicTree-D4R0hEtN.js';
3
3
 
4
4
  /**
5
5
  * The articulated "motion" node of a joint, inserted between the joint's two
@@ -10,6 +10,8 @@ import { J as JointType, A as Axis, a as JointDescription, L as LinkDescription,
10
10
  declare class JointObject extends THREE.Object3D {
11
11
  readonly isJointObject = true;
12
12
  readonly jointName: string;
13
+ /** Full USD prim path of the joint — the collision-proof address. */
14
+ readonly primPath: string;
13
15
  readonly jointType: JointType;
14
16
  readonly axisToken: Axis;
15
17
  readonly axis: THREE.Vector3;
@@ -63,6 +65,12 @@ type ThreeUsdRobotOptions = {
63
65
  * `parentLink → jointFrame0 → jointMotion → jointFrame1⁻¹ → childLink`,
64
66
  * where only `jointMotion` (a {@link JointObject}) changes with the joint value;
65
67
  * world poses then fall out of Three.js's `updateMatrixWorld`.
68
+ *
69
+ * **Naming contract** — a link/joint's key is its prim's leaf name when that
70
+ * is unique across the robot, else its full prim path (deterministic; see the
71
+ * extractor). Every accessor taking a name equally accepts the full prim path,
72
+ * which is stable regardless of collisions; {@link getLinkObjectsByPath} /
73
+ * {@link getJointObjectsByPath} enumerate the path-keyed tables.
66
74
  */
67
75
  declare class ThreeUsdRobot extends THREE.Object3D {
68
76
  readonly isThreeUsdRobot = true;
@@ -71,6 +79,8 @@ declare class ThreeUsdRobot extends THREE.Object3D {
71
79
  readonly clampJointLimits: boolean;
72
80
  private readonly linkObjects;
73
81
  private readonly jointObjects;
82
+ private readonly linkKeyByPath;
83
+ private readonly jointKeyByPath;
74
84
  private dirty;
75
85
  private readonly helperSize;
76
86
  private _showVisual;
@@ -89,7 +99,14 @@ declare class ThreeUsdRobot extends THREE.Object3D {
89
99
  /** Build `parent → frame0 → motion → frame1⁻¹ → child` for one joint. */
90
100
  private attachJointChain;
91
101
  private attachIsolatedLinks;
92
- /** Set one joint value. Unknown joints are ignored. Returns whether it applied. */
102
+ /** Resolve a link reference key or full prim path to the extractor key. */
103
+ private linkKey;
104
+ /** Resolve a joint reference — key or full prim path — to the extractor key. */
105
+ private jointKey;
106
+ /**
107
+ * Set one joint value, addressed by key or full prim path. Unknown joints
108
+ * are ignored. Returns whether it applied.
109
+ */
93
110
  setJointValue(name: string, value: number): boolean;
94
111
  /** Set several joint values at once (matrix update is coalesced). */
95
112
  setJointValues(values: Record<string, number>): void;
@@ -97,10 +114,23 @@ declare class ThreeUsdRobot extends THREE.Object3D {
97
114
  /** Recompute world matrices. Called lazily by the getters; safe to call directly. */
98
115
  updateKinematics(): void;
99
116
  private ensureUpdated;
117
+ /** World matrix of a link, addressed by key or full prim path. */
100
118
  getLinkWorldMatrix(name: string): THREE.Matrix4;
101
119
  getLinkWorldPosition(name: string): THREE.Vector3;
120
+ /** Link object by key or full prim path. */
102
121
  getLinkObject(name: string): LinkObject | undefined;
122
+ /** Joint object by key or full prim path. */
103
123
  getJointObject(name: string): JointObject | undefined;
124
+ /**
125
+ * Table of link prim path → {@link LinkObject}. Prim paths are the
126
+ * collision-proof way to pin a link (e.g. to attach tools or gizmos).
127
+ */
128
+ getLinkObjectsByPath(): Map<string, LinkObject>;
129
+ /**
130
+ * Table of joint prim path → {@link JointObject}, covering the joints
131
+ * realized in the kinematic tree (loop joints have no motion node).
132
+ */
133
+ getJointObjectsByPath(): Map<string, JointObject>;
104
134
  getJoints(): JointDescription[];
105
135
  getLinks(): LinkDescription[];
106
136
  /** Names of the articulated (controllable) joints. */
@@ -1,6 +1,6 @@
1
- import { R as RobotDescription } from './buildKinematicTree-wtZPzy0B.js';
2
- import { A as AssetResolver } from './AssetResolver-CpIJNgWZ.js';
3
- import { T as ThreeUsdRobot } from './ThreeUsdRobot-CajXJqYU.js';
1
+ import { R as RobotDescription } from './buildKinematicTree-D4R0hEtN.js';
2
+ import { A as AssetResolver, U as UsdSource, B as BinarySource } from './bytes-MOJ2oN-u.js';
3
+ import { T as ThreeUsdRobot } from './ThreeUsdRobot-CsjraHqP.js';
4
4
 
5
5
  type ThreeUsdRobotLoaderOptions = {
6
6
  /** Resolver for references / payloads / sublayers (default {@link DefaultAssetResolver}). */
@@ -10,9 +10,10 @@ type ThreeUsdRobotLoaderOptions = {
10
10
  /** Render collision meshes (M6). */
11
11
  loadCollisions?: boolean;
12
12
  /**
13
- * Also render Mesh prims that belong to no link — the static scenery of a
14
- * cell that contains robots (floor, guarding, racking, …). Placed by their
15
- * authored stage transform. Default `false`.
13
+ * Also render gprims that belong to no link — the static scenery of a cell
14
+ * that contains robots (floor, guarding, racking, …). Placed by their
15
+ * authored stage transform. Default `false`; a stage with no articulation at
16
+ * all (a pure static scene) defaults to `true` so it renders out of the box.
16
17
  */
17
18
  loadSceneGeometry?: boolean;
18
19
  /** Load diffuse textures referenced by materials (default `true`). */
@@ -33,6 +34,8 @@ type ThreeUsdRobotLoaderOptions = {
33
34
  /**
34
35
  * Loads Isaac Sim / OpenUSD robot assets into a controllable {@link ThreeUsdRobot}.
35
36
  *
37
+ * Assets load by URL ({@link loadAsync}) or from in-memory content
38
+ * ({@link parse} — USDA text, `ArrayBuffer`, typed array, or `Blob`/`File`).
36
39
  * Composition (references / payloads / sublayers, M8) is resolved through an
37
40
  * {@link AssetResolver}. Mesh rendering is M6; unit / up-axis / initial-pose
38
41
  * handling is M9; USDC and variants are M10.
@@ -43,19 +46,31 @@ declare class ThreeUsdRobotLoader {
43
46
  private get resolver();
44
47
  /**
45
48
  * Fetch an asset by URL and build the robot. `.usdz` packages are unzipped and
46
- * composed from their entries; everything else is sniffed for the crate magic
47
- * and parsed as binary USDC or USDA text.
49
+ * composed from their entries; everything else is sniffed for the zip / crate
50
+ * magic and parsed as USDZ, binary USDC, or USDA text.
48
51
  */
49
52
  loadAsync(url: string): Promise<ThreeUsdRobot>;
50
53
  private fetchRootBytes;
51
- /** Build a robot (composed, with meshes) from USDA source text. */
52
- parse(text: string, baseUrl?: string): Promise<ThreeUsdRobot>;
53
- /** Build a robot from the bytes of a `.usdz` package. */
54
- parseUsdz(bytes: Uint8Array): Promise<ThreeUsdRobot>;
55
- /** Build a robot from the bytes of a binary crate (`.usdc` / binary `.usd`). */
56
- parseCrate(bytes: Uint8Array, baseUrl?: string): Promise<ThreeUsdRobot>;
57
- /** Parse + compose USDA source into the Three.js-independent robot IR. */
58
- parseRobotDescription(text: string, baseUrl?: string): Promise<RobotDescription>;
54
+ /**
55
+ * Build a robot from in-memory content — no fetch involved. Accepts USDA
56
+ * source text, or an `ArrayBuffer` / typed array / `Blob` (e.g. a dropped
57
+ * `File`) holding USDA text, a binary crate, or a `.usdz` package; binary
58
+ * input is sniffed for the zip / crate magic. `baseUrl` anchors relative
59
+ * references, payloads, and texture paths of non-package input.
60
+ */
61
+ parse(data: UsdSource, baseUrl?: string): Promise<ThreeUsdRobot>;
62
+ /** Build a robot from a `.usdz` package (bytes, `ArrayBuffer`, or `Blob`). */
63
+ parseUsdz(data: BinarySource): Promise<ThreeUsdRobot>;
64
+ /** Build a robot from a binary crate (`.usdc` / binary `.usd`). */
65
+ parseCrate(data: BinarySource, baseUrl?: string): Promise<ThreeUsdRobot>;
66
+ /**
67
+ * Parse + compose in-memory content (same formats as {@link parse}) into the
68
+ * Three.js-independent robot IR.
69
+ */
70
+ parseRobotDescription(data: UsdSource, baseUrl?: string): Promise<RobotDescription>;
71
+ /** Sniff in-memory content (text / zip / crate) and compose it into a stage. */
72
+ private openSource;
73
+ private openUsdzStage;
59
74
  private buildFromStage;
60
75
  private composeStage;
61
76
  /** Compose a layer from raw bytes, sniffing binary crate vs USDA text. */
@@ -89,6 +89,11 @@ type PrimSpec = {
89
89
  type VariantContent = {
90
90
  properties: PropertySpec[];
91
91
  children: PrimSpec[];
92
+ /**
93
+ * Metadata authored on the variant itself. Composition arcs live here — the
94
+ * common "this variant references another layer" pattern in Isaac Sim assets.
95
+ */
96
+ metadata: MetadataMap;
92
97
  };
93
98
  /** `variantSetName → variantName → content`. */
94
99
  type VariantSetMap = {
@@ -251,9 +256,9 @@ type LinkDescription = {
251
256
  /** Display name (leaf prim name). */
252
257
  name: string;
253
258
  primPath: string;
254
- /** Mesh prim paths to render. */
259
+ /** Renderable gprim paths (Mesh / Cube / Sphere / Cylinder / Capsule / Cone). */
255
260
  visualPrims: string[];
256
- /** Mesh prim paths flagged with a collision API. */
261
+ /** Gprim paths flagged with a collision API (or a non-visual purpose). */
257
262
  collisionPrims?: string[];
258
263
  /** Mass properties (`UsdPhysicsMassAPI`), if authored (M16). */
259
264
  inertial?: LinkInertialDescription;
@@ -26,4 +26,17 @@ declare function createMemoryResolver(files: Record<string, string | Uint8Array>
26
26
  /** Resolve `rel` against `baseUrl` using posix semantics, normalizing `.`/`..`. */
27
27
  declare function joinPosix(baseUrl: string, rel: string): string;
28
28
 
29
- export { type AssetResolver as A, DefaultAssetResolver as D, createMemoryResolver as c, joinPosix as j };
29
+ /**
30
+ * In-memory input normalization for the loader entry points. Callers hold USD
31
+ * content in many shapes — a `fetch` response's `ArrayBuffer`, a `Uint8Array`
32
+ * (or any typed-array view), a dropped `File` / `Blob` — and every byte-eating
33
+ * API here funnels through {@link toBytes} so all of them are accepted.
34
+ */
35
+ /** Binary USD content: an `ArrayBuffer`, any typed-array view, or a `Blob`/`File`. */
36
+ type BinarySource = ArrayBuffer | ArrayBufferView | Blob;
37
+ /** In-memory USD content: USDA source text, or {@link BinarySource} bytes. */
38
+ type UsdSource = string | BinarySource;
39
+ /** Normalize a {@link BinarySource} to bytes, honoring a view's offset/length. */
40
+ declare function toBytes(data: BinarySource): Promise<Uint8Array>;
41
+
42
+ export { type AssetResolver as A, type BinarySource as B, DefaultAssetResolver as D, type UsdSource as U, createMemoryResolver as c, joinPosix as j, toBytes as t };