three-usd-robot 0.3.0 → 0.5.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
@@ -8,15 +8,16 @@ Think of it as a **USD version of [`urdf-loader`](https://www.npmjs.com/package/
8
8
  it reads the link / joint / xform / mesh structure out of `UsdPhysics` robot assets
9
9
  and drives forward kinematics on a Three.js `Object3D` hierarchy.
10
10
 
11
- > 🚧 **Status: v0.2 + USDC.** Loads **ASCII `.usda`**, **binary `.usdc` / `.usd`
12
- > (crate)**, and **`.usdz`** robots — including multi-file assets via
13
- > references/payloads/sublayers, **variant selections**, and **instanceable**
14
- > prims (internal-reference prototypes) drives forward kinematics with meshes,
15
- > applies flat **`UsdShade` material colors** (UsdPreviewSurface / OmniPBR
16
- > constants), normalizes up-axis & units, and seeds the initial pose. The
17
- > crate reader is a from-scratch TypeScript implementation (no OpenUSD/WASM
18
- > dependency). Not yet: variant resolution inside binary crate, and time-sampled
19
- > (animated) values.
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.
20
21
 
21
22
  ```ts
22
23
  // .usda / .usdc / binary .usd / .usdz are all auto-detected:
@@ -25,6 +26,8 @@ const robot = await new ThreeUsdRobotLoader().loadAsync("/assets/robot.usd");
25
26
  const robot = await new ThreeUsdRobotLoader().parseCrate(usdcBytes);
26
27
  ```
27
28
 
29
+ ![demo](assets/anim_demo.gif)
30
+
28
31
  ## Install
29
32
 
30
33
  ```sh
@@ -76,6 +79,48 @@ createJointSliderPanel(robot, new GUI()); // one slider per articulated joint
76
79
  The `extras` panel takes the GUI instance from you, so the library never bundles
77
80
  `lil-gui`. See [`examples/`](./examples) for runnable Vite demos.
78
81
 
82
+ ### Animation playback
83
+
84
+ If the asset has time-sampled joint trajectories (joint-state or drive-target
85
+ time samples), the robot plays them back:
86
+
87
+ ```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:
92
+ robot.setTime(t); // interpolates every animated joint and updates FK
93
+ }
94
+ ```
95
+
96
+ ### React Three Fiber
97
+
98
+ `three-usd-robot/react` provides a declarative `<UsdRobot>` for R3F (`react` and
99
+ `@react-three/fiber` are **optional** peer deps):
100
+
101
+ ```tsx
102
+ import { Canvas } from "@react-three/fiber";
103
+ import { Suspense } from "react";
104
+ import { UsdRobot } from "three-usd-robot/react";
105
+
106
+ <Canvas camera={{ position: [2, 2, 2] }}>
107
+ <Suspense fallback={null}>
108
+ <UsdRobot
109
+ url="/robot.usda"
110
+ jointValues={{ joint1: 0.4 }} // controlled
111
+ showJointAxes
112
+ animate // play time-sampled trajectories
113
+ onLoad={(robot) => console.log(robot.getJointNames())}
114
+ />
115
+ </Suspense>
116
+ <ambientLight />
117
+ </Canvas>;
118
+ ```
119
+
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.
123
+
79
124
  ### Inspect without Three.js
80
125
 
81
126
  ```ts
@@ -93,6 +138,7 @@ console.log(desc.rootLink, Object.keys(desc.joints));
93
138
  | `three-usd-robot/core` | Three.js-independent USDA parser, robot IR, forward-kinematics math |
94
139
  | `three-usd-robot/helpers` | Viewer helpers (joint axes, link frames, joint limits) |
95
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) |
96
142
 
97
143
  ## Development
98
144
 
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Resolves USD asset paths (from references / payloads / sublayers) to URLs and
3
+ * fetches their text. Composition (`composition.ts`) is I/O-agnostic and goes
4
+ * through an {@link AssetResolver}, so the same engine works in the browser, in
5
+ * Node, or against an in-memory file map in tests.
6
+ */
7
+ interface AssetResolver {
8
+ /** Resolve an authored asset path against the layer's base URL to an absolute key. */
9
+ resolve(assetPath: string, baseUrl: string): string;
10
+ /** Fetch the text of a resolved asset; rejects if it cannot be read. */
11
+ fetchText(url: string): Promise<string>;
12
+ /** Fetch the raw bytes of a resolved asset (for binary USDC / USDZ). Optional. */
13
+ fetchBytes?(url: string): Promise<Uint8Array>;
14
+ }
15
+ /** URL-based resolver using the global `fetch` (browser / Node 18+). */
16
+ declare class DefaultAssetResolver implements AssetResolver {
17
+ resolve(assetPath: string, baseUrl: string): string;
18
+ fetchText(url: string): Promise<string>;
19
+ fetchBytes(url: string): Promise<Uint8Array>;
20
+ }
21
+ /**
22
+ * Resolver over an in-memory `{ path: contents }` map (text or bytes), with
23
+ * posix-style joins. Useful for tests and bundled assets.
24
+ */
25
+ declare function createMemoryResolver(files: Record<string, string | Uint8Array>): AssetResolver;
26
+ /** Resolve `rel` against `baseUrl` using posix semantics, normalizing `.`/`..`. */
27
+ declare function joinPosix(baseUrl: string, rel: string): string;
28
+
29
+ export { type AssetResolver as A, DefaultAssetResolver as D, createMemoryResolver as c, joinPosix as j };
@@ -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-BJuC_-5_.js';
2
+ import { J as JointType, A as Axis, a as JointDescription, L as LinkDescription, R as RobotDescription, K as KinematicTree } from './buildKinematicTree-3oKG00Sc.js';
3
3
 
4
4
  /**
5
5
  * The articulated "motion" node of a joint, inserted between the joint's two
@@ -107,6 +107,20 @@ declare class ThreeUsdRobot extends THREE.Object3D {
107
107
  getJointNames(): string[];
108
108
  getLinkNames(): string[];
109
109
  getKinematicTree(): KinematicTree;
110
+ /** Playback rate in time codes per second (from the stage; default 24). */
111
+ getTimeCodesPerSecond(): number;
112
+ /** Whether any joint has a time-sampled trajectory. */
113
+ hasAnimation(): boolean;
114
+ /**
115
+ * Animation range in time codes: the union of authored joint sample ranges,
116
+ * falling back to the stage `startTimeCode`/`endTimeCode`. `null` if neither.
117
+ */
118
+ getTimeRange(): {
119
+ start: number;
120
+ end: number;
121
+ } | null;
122
+ /** Sample every animated joint at time code `t` and apply the values. */
123
+ setTime(t: number): void;
110
124
  get showVisual(): boolean;
111
125
  set showVisual(v: boolean);
112
126
  get showCollision(): boolean;
@@ -0,0 +1,61 @@
1
+ import { R as RobotDescription } from './buildKinematicTree-3oKG00Sc.js';
2
+ import { A as AssetResolver } from './AssetResolver-CpIJNgWZ.js';
3
+ import { T as ThreeUsdRobot } from './ThreeUsdRobot-D9AOUaVh.js';
4
+
5
+ type ThreeUsdRobotLoaderOptions = {
6
+ /** Resolver for references / payloads / sublayers (default {@link DefaultAssetResolver}). */
7
+ assetResolver?: AssetResolver;
8
+ /** Render visual meshes (M6). */
9
+ loadVisuals?: boolean;
10
+ /** Render collision meshes (M6). */
11
+ loadCollisions?: boolean;
12
+ /** Load diffuse textures referenced by materials (default `true`). */
13
+ loadTextures?: boolean;
14
+ /** Up-axis correction strategy (M9). */
15
+ upAxisConversion?: "auto" | "Y" | "Z" | "none";
16
+ /** Extra uniform scale multiplied with `metersPerUnit` (M9). */
17
+ unitScale?: number;
18
+ /** Clamp `setJointValue` to authored limits (default `true`). */
19
+ clampJointLimits?: boolean;
20
+ /** Seed joints from drive targets / joint state (M9). */
21
+ applyDriveTargetsAsInitialPose?: boolean;
22
+ /** Override the robot name. */
23
+ robotName?: string;
24
+ /** Receives non-fatal load diagnostics. */
25
+ onWarn?: (message: string) => void;
26
+ };
27
+ /**
28
+ * Loads Isaac Sim / OpenUSD robot assets into a controllable {@link ThreeUsdRobot}.
29
+ *
30
+ * Composition (references / payloads / sublayers, M8) is resolved through an
31
+ * {@link AssetResolver}. Mesh rendering is M6; unit / up-axis / initial-pose
32
+ * handling is M9; USDC and variants are M10.
33
+ */
34
+ declare class ThreeUsdRobotLoader {
35
+ readonly options: ThreeUsdRobotLoaderOptions;
36
+ constructor(options?: ThreeUsdRobotLoaderOptions);
37
+ private get resolver();
38
+ /**
39
+ * Fetch an asset by URL and build the robot. `.usdz` packages are unzipped and
40
+ * composed from their entries; everything else is sniffed for the crate magic
41
+ * and parsed as binary USDC or USDA text.
42
+ */
43
+ loadAsync(url: string): Promise<ThreeUsdRobot>;
44
+ private fetchRootBytes;
45
+ /** Build a robot (composed, with meshes) from USDA source text. */
46
+ parse(text: string, baseUrl?: string): Promise<ThreeUsdRobot>;
47
+ /** Build a robot from the bytes of a `.usdz` package. */
48
+ parseUsdz(bytes: Uint8Array): Promise<ThreeUsdRobot>;
49
+ /** Build a robot from the bytes of a binary crate (`.usdc` / binary `.usd`). */
50
+ parseCrate(bytes: Uint8Array, baseUrl?: string): Promise<ThreeUsdRobot>;
51
+ /** Parse + compose USDA source into the Three.js-independent robot IR. */
52
+ parseRobotDescription(text: string, baseUrl?: string): Promise<RobotDescription>;
53
+ private buildFromStage;
54
+ private composeStage;
55
+ /** Compose a layer from raw bytes, sniffing binary crate vs USDA text. */
56
+ private composeStageFromBytes;
57
+ private robotOptions;
58
+ private extractOptions;
59
+ }
60
+
61
+ export { type ThreeUsdRobotLoaderOptions as T, ThreeUsdRobotLoader as a };
@@ -177,6 +177,25 @@ declare function fromUsdMatrix(m: UsdMatrix): Mat4;
177
177
  /** Extract the translation component `[x, y, z]` from a {@link Mat4}. */
178
178
  declare function getTranslation(m: Mat4): Vec3;
179
179
 
180
+ /**
181
+ * Time-sample interpolation for animated values (e.g. joint trajectories).
182
+ *
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).
186
+ */
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[];
193
+ };
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
+
180
199
  /**
181
200
  * Robot intermediate representation (IR) — Three.js-independent.
182
201
  *
@@ -205,6 +224,11 @@ type RobotDescription = {
205
224
  upAxis: "Y" | "Z";
206
225
  /** Stage linear unit, for the M9 scale normalization. */
207
226
  metersPerUnit: number;
227
+ /** Playback rate (time codes per second); defaults to 24 when unauthored. */
228
+ timeCodesPerSecond?: number;
229
+ /** Authored animation range (time codes), if any. */
230
+ startTimeCode?: number;
231
+ endTimeCode?: number;
208
232
  /** Non-fatal extraction diagnostics. */
209
233
  warnings?: string[];
210
234
  };
@@ -237,6 +261,8 @@ type JointDescription = {
237
261
  jointFrame1: Mat4;
238
262
  /** Initial joint value (SI) from JointStateAPI or a drive target, if authored. */
239
263
  initialValue?: number;
264
+ /** Time-sampled joint value trajectory (SI), if authored — drives playback. */
265
+ valueSamples?: SampleChannel;
240
266
  drive?: JointDriveDescription;
241
267
  };
242
268
  /** Authored joint drive parameters (`UsdPhysicsDriveAPI`), as read in M3. */
@@ -293,4 +319,4 @@ type BuildTreeOptions = {
293
319
  };
294
320
  declare function buildKinematicTree(robot: RobotDescription, options?: BuildTreeOptions): KinematicTree;
295
321
 
296
- export { type Axis as A, type BuildTreeOptions as B, type CompositionArc as C, DEG2RAD as D, makeRotationZ as E, makeScale as F, makeTranslation as G, multiply as H, multiplyAll as I, type JointType as J, type KinematicTree as K, type LinkDescription as L, type Mat4 as M, type PrimSpec as P, Quat as Q, type RobotDescription as R, type SdfPath as S, type TreeEdge as T, type UsdDictionary as U, type Vec3 as V, type JointDescription as a, AssetPath as b, type AttributeSpec as c, type JointDriveDescription as d, type KinematicNode as e, type ListOp as f, type MetadataMap as g, type PropertySpec as h, RAD2DEG as i, type RelationshipSpec as j, type Specifier as k, UsdMatrix as l, type UsdValue as m, type UsdaFile as n, type Variability as o, type Vec2 as p, type Vec4 as q, buildKinematicTree as r, fromUsdMatrix as s, getTranslation as t, identity4 as u, invert as v, makeEuler as w, makeRotationFromQuat as x, makeRotationX as y, makeRotationY as z };
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 };