three-usd-robot 0.5.0 → 0.7.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
@@ -1,32 +1,55 @@
1
1
  # three-usd-robot
2
2
 
3
- > **Kinematic OpenUSD robot loader for Three.js.**
4
- > Load Isaac Sim / OpenUSD robot assets, extract joints and links, and control
5
- > articulations directly in the browser no physics engine required.
3
+ > **Kinematic OpenUSD robot loader — and exporter — for Three.js.**
4
+ > Load Isaac Sim / OpenUSD robot assets, control their joints in the browser,
5
+ > and write scenes back out as USD. No physics engine, no OpenUSD/WASM
6
+ > dependency.
6
7
 
7
8
  Think of it as a **USD version of [`urdf-loader`](https://www.npmjs.com/package/urdf-loader)**:
8
- it reads the link / joint / xform / mesh structure out of `UsdPhysics` robot assets
9
+ it reads the link / joint / xform / mesh structure out of `UsdPhysics` assets
9
10
  and drives forward kinematics on a Three.js `Object3D` hierarchy.
10
11
 
11
- > 🚧 **Status: v0.3.** Loads **ASCII `.usda`**, **binary `.usdc` / `.usd`
12
- > (crate)**, and **`.usdz`** robots including **multi-file assets** via
13
- > references / payloads / sublayers (resolved across `.usda` *and* binary layers,
14
- > with relationship-path remapping), **variant selections**, and **instanceable**
15
- > prims drives forward kinematics with meshes, applies flat **`UsdShade`
16
- > material colors** (UsdPreviewSurface / OmniPBR constants) and **diffuse
17
- > textures** (`UsdUVTexture`), normalizes up-axis & units, seeds the initial
18
- > pose, and **plays back time-sampled joint trajectories**. The crate reader is a
19
- > from-scratch TypeScript implementation (no OpenUSD/WASM dependency). Not yet:
20
- > time samples and variant *selection* stored inside binary crate.
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)
17
+ ![A robot cell loaded in the browser](assets/threejs.png)
18
+
19
+ ## Features
20
+
21
+ - **Formats** ASCII `.usda`, binary `.usdc` / `.usd`, and `.usdz`, auto-detected.
22
+ Multi-file assets (references / payloads / sublayers), variant selections and
23
+ instanceable prims are composed for you.
24
+ - **Robots** — links, joints (fixed / revolute / continuous / prismatic), limits,
25
+ drives and the initial pose become a `setJointValue`-able hierarchy.
26
+ - **Rendering** — meshes with `UsdShade` materials (UsdPreviewSurface / OmniPBR)
27
+ and textures; up-axis and units normalized automatically.
28
+ - **Animation** — plays back time-sampled joint trajectories.
29
+ - **Export** — write robots and whole cells back to `.usda` / `.usdz`,
30
+ simulation-ready for Isaac Sim.
31
+ - **React** — declarative `<UsdRobot>` for React Three Fiber.
32
+
33
+ Stock Isaac Sim robot assets load straight from their public CDN — Franka
34
+ Panda, UR10e, Fanuc CRX-10iA/L, Kuka KR210, Shadow Hand, Unitree H1/Go2 and
35
+ friends all compose from their variant-driven, multi-layer form:
21
36
 
22
37
  ```ts
23
- // .usda / .usdc / binary .usd / .usdz are all auto-detected:
24
- const robot = await new ThreeUsdRobotLoader().loadAsync("/assets/robot.usd");
25
- // or from bytes you already have:
26
- const robot = await new ThreeUsdRobotLoader().parseCrate(usdcBytes);
38
+ const ROOT = "https://omniverse-content-production.s3-us-west-2.amazonaws.com/Assets/Isaac/5.1";
39
+ const franka = await new ThreeUsdRobotLoader()
40
+ .loadAsync(`${ROOT}/Isaac/Robots/FrankaRobotics/FrankaPanda/franka.usd`);
41
+
42
+ franka.setJointValues({ panda_joint2: -0.785, panda_joint4: -2.356, panda_joint6: 1.571 });
43
+ franka.getLinkWorldPosition("panda_hand"); // (0.307, 0, 0.590) — the documented ready pose
27
44
  ```
28
45
 
29
- ![demo](assets/anim_demo.gif)
46
+ The CDN is public and CORS-enabled, so this works in the browser too. Try
47
+ `npx tsx scripts/demo-franka.ts` for a console walkthrough (articulation table,
48
+ FK check, and a re-export to one self-contained file), or open the Vite example
49
+ and pick a robot from the preset list.
50
+
51
+ Not yet supported: time samples stored inside binary crate files, non-mesh
52
+ gprims (`Cube`, `Sphere`, …), and full material/shader fidelity.
30
53
 
31
54
  ## Install
32
55
 
@@ -36,11 +59,12 @@ npm install three-usd-robot three
36
59
 
37
60
  `three` is a **peer dependency** (`>=0.160.0`).
38
61
 
39
- ## Usage
62
+ ## Quick start
40
63
 
41
64
  ```ts
42
65
  import { ThreeUsdRobotLoader } from "three-usd-robot";
43
66
 
67
+ // .usda / .usdc / binary .usd / .usdz are all auto-detected.
44
68
  const robot = await new ThreeUsdRobotLoader().loadAsync("/assets/arm.usda");
45
69
  scene.add(robot);
46
70
 
@@ -52,9 +76,12 @@ const handMatrix = robot.getLinkWorldMatrix("tool0"); // THREE.Matrix4
52
76
  const handPos = robot.getLinkWorldPosition("tool0"); // THREE.Vector3
53
77
 
54
78
  robot.getJointNames(); // controllable joints
55
- robot.getKinematicTree(); // root, ordering, loopJoints, ...
79
+ robot.getKinematicTree(); // root, ordering, loop joints, ...
56
80
  ```
57
81
 
82
+ Already have the bytes? Use `parse(usdaText)`, `parseCrate(usdcBytes)` or
83
+ `parseUsdz(bytes)` instead of `loadAsync`.
84
+
58
85
  ### Viewer toggles & helpers
59
86
 
60
87
  ```ts
@@ -67,6 +94,10 @@ import { addJointLimitHelpers } from "three-usd-robot/helpers";
67
94
  addJointLimitHelpers(robot); // arc (revolute) / segment (prismatic) per joint
68
95
  ```
69
96
 
97
+ Loading a whole cell rather than a bare robot? Pass
98
+ `{ loadSceneGeometry: true }` to the loader to draw the static environment
99
+ (floor, guarding, racking, …) around the machines.
100
+
70
101
  ### Joint slider panel (lil-gui)
71
102
 
72
103
  ```ts
@@ -76,26 +107,24 @@ import { createJointSliderPanel } from "three-usd-robot/extras";
76
107
  createJointSliderPanel(robot, new GUI()); // one slider per articulated joint
77
108
  ```
78
109
 
79
- The `extras` panel takes the GUI instance from you, so the library never bundles
80
- `lil-gui`. See [`examples/`](./examples) for runnable Vite demos.
110
+ The panel takes the GUI instance from you, so the library never bundles
111
+ `lil-gui`.
81
112
 
82
113
  ### Animation playback
83
114
 
84
- If the asset has time-sampled joint trajectories (joint-state or drive-target
85
- time samples), the robot plays them back:
115
+ If the asset has time-sampled joint trajectories, the robot plays them back:
86
116
 
87
117
  ```ts
88
- if (robot.hasAnimation()) {
89
- const { start, end } = robot.getTimeRange()!;
90
- const fps = robot.getTimeCodesPerSecond();
91
- // in your render loop, advance a time code and sample:
118
+ const range = robot.getTimeRange();
119
+ if (range) {
120
+ // in your render loop, advance a time code between range.start and range.end:
92
121
  robot.setTime(t); // interpolates every animated joint and updates FK
93
122
  }
94
123
  ```
95
124
 
96
125
  ### React Three Fiber
97
126
 
98
- `three-usd-robot/react` provides a declarative `<UsdRobot>` for R3F (`react` and
127
+ `three-usd-robot/react` provides a declarative `<UsdRobot>` (`react` and
99
128
  `@react-three/fiber` are **optional** peer deps):
100
129
 
101
130
  ```tsx
@@ -117,11 +146,68 @@ import { UsdRobot } from "three-usd-robot/react";
117
146
  </Canvas>;
118
147
  ```
119
148
 
120
- Also exported: `useUsdRobot(url)` (Suspense loader), `useRobotAnimation(robot)`
121
- (per-frame playback), `preloadUsdRobot`, `clearUsdRobotCache`. Pass a `ref` to
122
- `<UsdRobot>` for the imperative `ThreeUsdRobot` API.
149
+ Also exported: `useUsdRobot(url)` (Suspense loader), `useRobotAnimation(robot)`,
150
+ `preloadUsdRobot`, `clearUsdRobotCache`. Pass a `ref` to `<UsdRobot>` for the
151
+ imperative API.
152
+
153
+ ## Export USD
154
+
155
+ The loader's inverse: re-export something you loaded, or author a robot from
156
+ Three.js objects and open the result in Isaac Sim.
123
157
 
124
- ### Inspect without Three.js
158
+ ![The exported cell opened in Isaac Sim](assets/isaacsim.png)
159
+
160
+ ```ts
161
+ import {
162
+ exportThreeUsdRobot,
163
+ RobotBuilder,
164
+ serializeUsda,
165
+ writeUsdz,
166
+ } from "three-usd-robot";
167
+
168
+ // Re-export a loaded robot (meshes harvested from the Three.js scene):
169
+ const usda = serializeUsda(exportThreeUsdRobot(robot));
170
+
171
+ // …or build one from Three.js meshes. Z-up and metres by default; each joint
172
+ // takes ONE world-space frame, and the build-time arrangement is the zero pose.
173
+ const builder = new RobotBuilder({ name: "my_robot" });
174
+ builder.addLink({ name: "base", visuals: [baseMesh] });
175
+ builder.addLink({ name: "arm", frame: armFrame, visuals: [armMesh] });
176
+ builder.addFixedJoint({ name: "root_joint", child: "base" });
177
+ builder.addRevoluteJoint({
178
+ name: "j1", parent: "base", child: "arm",
179
+ frame: jointFrame, axis: "Z", lower: -Math.PI, upper: Math.PI,
180
+ });
181
+
182
+ const file = builder.toUsda();
183
+ writeFileSync("robot.usda", serializeUsda(file));
184
+ writeFileSync("robot.usdz", writeUsdz({ "robot.usda": serializeUsda(file) }));
185
+ ```
186
+
187
+ Joints export as `UsdPhysics` prims with their limits, drives and initial pose.
188
+ For a simulation-ready asset, links also take mass properties, and collision
189
+ meshes take physics materials and a collision approximation:
190
+
191
+ ```ts
192
+ builder.addLink({
193
+ name: "arm",
194
+ visuals: [armMesh],
195
+ collisions: [armCollisionMesh],
196
+ inertial: { mass: 2.5, centerOfMass: [0, 0, 0.2], diagonalInertia: [0.02, 0.02, 0.004] },
197
+ collisionApproximation: "convexHull",
198
+ physicsMaterial: { name: "steel", staticFriction: 0.6, dynamicFriction: 0.5 },
199
+ });
200
+ ```
201
+
202
+ Both screenshots above come from `npx tsx scripts/demo-factory.ts`, which
203
+ authors a complete robot cell — a 7-DOF arm with a gripper, a conveyor, a
204
+ turntable and the surrounding scenery — and exports it to
205
+ `out/factory.usda` / `.usdz`.
206
+
207
+ ## Use it without Three.js
208
+
209
+ `three-usd-robot/core` is a standalone USD parser, writer and robot IR — handy
210
+ for server-side validation or asset tooling:
125
211
 
126
212
  ```ts
127
213
  import { parseUsda, Stage, extractRobotDescription } from "three-usd-robot/core";
@@ -130,15 +216,31 @@ const desc = extractRobotDescription(Stage.OpenFromString(usdaText));
130
216
  console.log(desc.rootLink, Object.keys(desc.joints));
131
217
  ```
132
218
 
219
+ Command line: `npx tsx scripts/usdc-to-usda.ts robot.usd` converts binary crate
220
+ and `.usdz` packages to ASCII USDA.
221
+
222
+ ## Examples
223
+
224
+ [`examples/`](./examples) holds runnable Vite demos:
225
+
226
+ - **`vite-joint-slider`** — the [live demo](https://three-usd-robot.vercel.app):
227
+ vanilla Three.js + `lil-gui`, with a robot picker, joint sliders, animation
228
+ playback and USD export.
229
+ - **`vite-basic-viewer`** — the same thing through React Three Fiber.
230
+
231
+ Both take `?asset=<url>` for any asset, or `?isaac=<path under Isaac/>` to pull
232
+ one straight from NVIDIA's CDN. `npm run demo:build` generates the factory cell
233
+ and builds the deployable site.
234
+
133
235
  ## Package entry points
134
236
 
135
237
  | Import | Contents |
136
238
  | --- | --- |
137
- | `three-usd-robot` | Three.js runtime — `ThreeUsdRobotLoader`, `ThreeUsdRobot` |
138
- | `three-usd-robot/core` | Three.js-independent USDA parser, robot IR, forward-kinematics math |
239
+ | `three-usd-robot` | Three.js runtime — `ThreeUsdRobotLoader`, `ThreeUsdRobot`, `RobotBuilder`, export helpers |
240
+ | `three-usd-robot/core` | Three.js-independent USD parser & writer, robot IR, forward-kinematics math |
139
241
  | `three-usd-robot/helpers` | Viewer helpers (joint axes, link frames, joint limits) |
140
- | `three-usd-robot/extras` | Heavier convenience utilities (e.g. joint slider panel) |
141
- | `three-usd-robot/react` | React Three Fiber `<UsdRobot>` component + hooks (optional `react` / `@react-three/fiber` peers) |
242
+ | `three-usd-robot/extras` | Joint slider panel (bring your own `lil-gui`) |
243
+ | `three-usd-robot/react` | React Three Fiber `<UsdRobot>` component + hooks |
142
244
 
143
245
  ## Development
144
246
 
@@ -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-3oKG00Sc.js';
2
+ import { J as JointType, A as Axis, a as JointDescription, L as LinkDescription, R as RobotDescription, K as KinematicTree } from './buildKinematicTree-Ccd2DIKH.js';
3
3
 
4
4
  /**
5
5
  * The articulated "motion" node of a joint, inserted between the joint's two
@@ -1,6 +1,6 @@
1
- import { R as RobotDescription } from './buildKinematicTree-3oKG00Sc.js';
1
+ import { R as RobotDescription } from './buildKinematicTree-Ccd2DIKH.js';
2
2
  import { A as AssetResolver } from './AssetResolver-CpIJNgWZ.js';
3
- import { T as ThreeUsdRobot } from './ThreeUsdRobot-D9AOUaVh.js';
3
+ import { T as ThreeUsdRobot } from './ThreeUsdRobot-Ljh4PDVD.js';
4
4
 
5
5
  type ThreeUsdRobotLoaderOptions = {
6
6
  /** Resolver for references / payloads / sublayers (default {@link DefaultAssetResolver}). */
@@ -9,6 +9,12 @@ type ThreeUsdRobotLoaderOptions = {
9
9
  loadVisuals?: boolean;
10
10
  /** Render collision meshes (M6). */
11
11
  loadCollisions?: boolean;
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`.
16
+ */
17
+ loadSceneGeometry?: boolean;
12
18
  /** Load diffuse textures referenced by materials (default `true`). */
13
19
  loadTextures?: boolean;
14
20
  /** Up-axis correction strategy (M9). */
@@ -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 = {
@@ -125,6 +130,25 @@ type RelationshipSpec = {
125
130
  line: number;
126
131
  };
127
132
 
133
+ /**
134
+ * Time-sample interpolation for animated values (e.g. joint trajectories).
135
+ *
136
+ * USD time samples are keyed by time code. We linearly interpolate between the
137
+ * two bracketing samples and hold the endpoints outside the authored range
138
+ * (matching USD's held extrapolation).
139
+ */
140
+ /** A sorted time-sampled scalar channel. */
141
+ type SampleChannel = {
142
+ /** Sample times, ascending. */
143
+ times: number[];
144
+ /** Sample values, parallel to {@link times} (SI units). */
145
+ values: number[];
146
+ };
147
+ /** Linearly sample `channel` at time `t` (held outside the range). */
148
+ declare function interpolate(channel: SampleChannel, t: number): number;
149
+ /** Build a sorted {@link SampleChannel} from `(time → value)` pairs via `map`. */
150
+ declare function channelFromSamples(samples: Map<number, number>): SampleChannel;
151
+
128
152
  /**
129
153
  * Minimal 4×4 matrix math, Three.js-independent.
130
154
  *
@@ -176,25 +200,21 @@ declare function makeEuler(angles: Vec3, order: string): Mat4;
176
200
  declare function fromUsdMatrix(m: UsdMatrix): Mat4;
177
201
  /** Extract the translation component `[x, y, z]` from a {@link Mat4}. */
178
202
  declare function getTranslation(m: Mat4): Vec3;
179
-
203
+ /** Copy a {@link Mat4} into a USD `matrix4d` — same flat layout (see module docs). */
204
+ declare function toUsdMatrix(m: Mat4): UsdMatrix;
180
205
  /**
181
- * Time-sample interpolation for animated values (e.g. joint trajectories).
206
+ * Decompose a rigid transform into translation + orientation (M13 export).
182
207
  *
183
- * USD time samples are keyed by time code. We linearly interpolate between the
184
- * two bracketing samples and hold the endpoints outside the authored range
185
- * (matching USD's held extrapolation).
208
+ * The rotation basis is orthonormalized (Gram-Schmidt, right-handed) before
209
+ * quaternion extraction; `rigid` reports whether the input already was rigid
210
+ * within tolerance, so callers can warn that scale/shear/reflection was
211
+ * discarded.
186
212
  */
187
- /** A sorted time-sampled scalar channel. */
188
- type SampleChannel = {
189
- /** Sample times, ascending. */
190
- times: number[];
191
- /** Sample values, parallel to {@link times} (SI units). */
192
- values: number[];
213
+ declare function decomposeRigid(m: Mat4): {
214
+ position: Vec3;
215
+ orientation: Quat;
216
+ rigid: boolean;
193
217
  };
194
- /** Linearly sample `channel` at time `t` (held outside the range). */
195
- declare function interpolate(channel: SampleChannel, t: number): number;
196
- /** Build a sorted {@link SampleChannel} from `(time → value)` pairs via `map`. */
197
- declare function channelFromSamples(samples: Map<number, number>): SampleChannel;
198
218
 
199
219
  /**
200
220
  * Robot intermediate representation (IR) — Three.js-independent.
@@ -240,6 +260,28 @@ type LinkDescription = {
240
260
  visualPrims: string[];
241
261
  /** Mesh prim paths flagged with a collision API. */
242
262
  collisionPrims?: string[];
263
+ /** Mass properties (`UsdPhysicsMassAPI`), if authored (M16). */
264
+ inertial?: LinkInertialDescription;
265
+ /**
266
+ * Authored stage (world) transform of the link prim, when not identity.
267
+ * Joint chains place tree links; this places floating roots and links that
268
+ * are isolated from the chosen tree (e.g. other machines / free bodies on a
269
+ * multi-articulation stage).
270
+ */
271
+ worldTransform?: Mat4;
272
+ };
273
+ /** Mass properties as authored by `UsdPhysicsMassAPI` (stage units). */
274
+ type LinkInertialDescription = {
275
+ /** Mass in kilograms. */
276
+ mass?: number;
277
+ /** Center of mass in the link frame (stage linear units). */
278
+ centerOfMass?: Vec3;
279
+ /** Principal moments of inertia. */
280
+ diagonalInertia?: Vec3;
281
+ /** Orientation of the principal inertia axes in the link frame. */
282
+ principalAxes?: Quat;
283
+ /** Density — the physics engine derives mass from it when `mass` is absent. */
284
+ density?: number;
243
285
  };
244
286
  type JointDescription = {
245
287
  /** Display name (leaf prim name). */
@@ -319,4 +361,4 @@ type BuildTreeOptions = {
319
361
  };
320
362
  declare function buildKinematicTree(robot: RobotDescription, options?: BuildTreeOptions): KinematicTree;
321
363
 
322
- export { type Axis as A, type BuildTreeOptions as B, type CompositionArc as C, DEG2RAD as D, makeRotationFromQuat as E, makeRotationX as F, makeRotationY as G, makeRotationZ as H, makeScale as I, type JointType as J, type KinematicTree as K, type LinkDescription as L, type Mat4 as M, makeTranslation as N, multiply as O, type PrimSpec as P, Quat as Q, type RobotDescription as R, type SampleChannel as S, type TreeEdge as T, type UsdDictionary as U, type Vec2 as V, multiplyAll as W, type JointDescription as a, type Vec3 as b, AssetPath as c, type AttributeSpec as d, type JointDriveDescription as e, type KinematicNode as f, type ListOp as g, type MetadataMap as h, type PropertySpec as i, RAD2DEG as j, type RelationshipSpec as k, type SdfPath as l, type Specifier as m, UsdMatrix as n, type UsdValue as o, type UsdaFile as p, type Variability as q, type Vec4 as r, buildKinematicTree as s, channelFromSamples as t, fromUsdMatrix as u, getTranslation as v, identity4 as w, interpolate as x, invert as y, makeEuler as z };
364
+ export { type Axis as A, type BuildTreeOptions as B, type CompositionArc as C, DEG2RAD as D, invert as E, makeEuler as F, makeRotationFromQuat as G, makeRotationX as H, makeRotationY as I, type JointType as J, type KinematicTree as K, type LinkDescription as L, type Mat4 as M, makeRotationZ as N, makeScale as O, type PrimSpec as P, Quat as Q, type RobotDescription as R, type SampleChannel as S, type TreeEdge as T, type UsdaFile as U, type Vec2 as V, makeTranslation as W, multiply as X, multiplyAll as Y, toUsdMatrix as Z, type JointDescription as a, type Vec3 as b, type JointDriveDescription as c, type LinkInertialDescription as d, AssetPath as e, type AttributeSpec as f, type KinematicNode as g, type ListOp as h, type MetadataMap as i, type PropertySpec as j, RAD2DEG as k, type RelationshipSpec as l, type SdfPath as m, type Specifier as n, type UsdDictionary as o, UsdMatrix as p, type UsdValue as q, type Variability as r, type Vec4 as s, buildKinematicTree as t, channelFromSamples as u, decomposeRigid as v, fromUsdMatrix as w, getTranslation as x, identity4 as y, interpolate as z };