three-usd-robot 0.12.0 → 0.13.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.
package/README.md CHANGED
@@ -22,14 +22,12 @@ exported back to `.usda` / `.usdz` in the browser.
22
22
  Multi-file assets (references / payloads / sublayers), variant selections and
23
23
  instanceable prims are composed for you.
24
24
  - **Robots** — links, joints (fixed / revolute / continuous / prismatic), limits,
25
- drives and the initial pose become a `setJointValue`-able hierarchy.
26
- - **Rendering** — meshes, solid gprims (`Cube` / `Sphere` / `Cylinder` /
27
- `Capsule` / `Cone`), point clouds (`Points`) and curves (`BasisCurves`:
28
- linear / bezier / bspline / catmullRom, periodic wrap, and an opt-in
29
- `curveTubes` mode that turns authored widths into tube meshes) with
30
- `UsdShade` materials (UsdPreviewSurface / Omniverse MDL / MaterialX) and textures; up-axis
31
- and units normalized automatically. Articulation-free stages load as
32
- static scenes.
25
+ drives, mimic couplings (gripper finger linkages) and the initial pose become
26
+ a `setJointValue`-able hierarchy.
27
+ - **Rendering** — meshes, solid gprims, point clouds and curves with `UsdShade`
28
+ materials (UsdPreviewSurface / Omniverse MDL / MaterialX, plus optional TSL
29
+ graph execution on WebGPU) and textures; up-axis and units normalized
30
+ automatically. Articulation-free stages load as static scenes.
33
31
  - **Animation** — plays back time-sampled joint trajectories, and replays
34
32
  baked body-transform recordings through `setLinkTransforms` with
35
33
  constraint diagnostics.
@@ -55,71 +53,6 @@ The CDN is public and CORS-enabled, so this works in the browser too. Try
55
53
  FK check, and a re-export to one self-contained file), or open the Vite example
56
54
  and pick a robot from the preset list.
57
55
 
58
- Materials target **UsdPreviewSurface fidelity plus Omniverse MDL and MaterialX mappings**:
59
- constant and textured inputs, faceVarying / indexed UVs, multiple UV sets,
60
- per-vertex display colors, physical extensions (`ior` / `clearcoat` /
61
- specular workflow → `MeshPhysicalMaterial`), packed ORM maps,
62
- `sourceColorSpace`, and purpose/strength-aware bindings. MDL shaders are
63
- identified by `info:mdl:sourceAsset` and mapped **by family** — no shader
64
- execution — and referenced `.mdl` modules are fetched and parsed for their
65
- declaration values, so wrapper materials
66
- (`export material X(*) = OmniPBR(…)`) render correctly even when the USD
67
- shader authors no inputs at all. Value priority: authored USD inputs >
68
- wrapper arguments > declaration defaults.
69
-
70
- | MDL family | three.js mapping |
71
- | --- | --- |
72
- | `OmniPBR` (and derivatives, e.g. `OmniPBR_Opacity`) | color / metalness / roughness / emissive (`emissive_intensity`), per-channel texture maps and packed `ORM_texture`, opacity, normal map, `texture_translate/rotate/scale` |
73
- | `OmniPBR_ClearCoat` | the OmniPBR mapping plus `clearcoat` / `clearcoatRoughness` / `clearcoatNormalMap` |
74
- | `OmniGlass` | `MeshPhysicalMaterial` with `transmission` / `ior` (default 1.491) / `roughness` / `thickness`, glass color + texture |
75
- | `OmniSurface(Lite)` | constants subset: diffuse color / metalness / roughness / IOR / coat / emission / opacity |
76
-
77
- MaterialX networks authored natively in UsdShade (`outputs:mtlx:surface` +
78
- `ND_*` shaders) resolve the same way — by parameter mapping, without graph
79
- execution. `ND_standard_surface_surfaceshader` maps `base × base_color` /
80
- `metalness` / `specular_roughness` / `specular_IOR` / `coat(_roughness)` /
81
- `transmission` / `emission (× emission_color)` / `opacity` / `normal` onto
82
- the standard (or, when coat / transmission / IOR are authored, physical)
83
- three material; the `ND_Usd*` compatibility nodes delegate to the
84
- UsdPreviewSurface readers. Image and UV nodes are supported
85
- (`ND_image_*` address modes, `ND_tiledimage_*` uvtiling/uvoffset,
86
- `ND_texcoord_*` UV-channel index, `ND_geompropvalue_*` primvar name,
87
- `ND_normalmap`), and constant-only `ND_multiply_* / ND_mix_* / ND_convert_*`
88
- (plus `ND_constant_*` / `ND_dot_*`) fold into values. Anything that needs
89
- real evaluation (noise, ramps, …) skips just that channel with a warning —
90
- the rest of the material still renders — unless you opt into the TSL entry
91
- below. External `.mtlx` file references (UsdMtlx) are not parsed; load them
92
- with `loadMaterialXDocument` (a thin wrapper over three's official
93
- `MaterialXLoader`) from `three-usd-robot/nodes`.
94
-
95
- **Executing MaterialX graphs (optional, WebGPU)** — the separate
96
- `three-usd-robot/nodes` entry converts `ND_*` graphs (noise, ramps, math,
97
- images, procedural UV warps — a ~45-node practical subset) into three.js TSL
98
- `MeshPhysicalNodeMaterial`s and plugs in through the loader's
99
- `materialFactory` hook:
100
-
101
- ```ts
102
- import { ThreeUsdRobotLoader } from "three-usd-robot";
103
- import { createMaterialXNodeFactory } from "three-usd-robot/nodes";
104
-
105
- const loader = new ThreeUsdRobotLoader({
106
- materialFactory: createMaterialXNodeFactory({ onWarn: console.warn }),
107
- });
108
- ```
109
-
110
- Requires `WebGPURenderer` (it falls back to WebGL2 internally). Graphs with
111
- nodes outside the conversion table fall back to the parameter mapping with a
112
- warning, and the WebGL core bundles never import `three/webgpu` / `three/tsl`
113
- (verified at build time). See `examples/vite-webgpu-nodes` for a procedural
114
- marble / worley / lava demo.
115
-
116
- Executing MDL remains out of scope (a language, not a graph — it would need
117
- an MDL SDK-class compiler); unknown MDL materials fall back to the OmniPBR
118
- mapping (and unknown `ND_*` surface shaders to the UsdPreviewSurface reads)
119
- with a warning. Not yet supported: collection-based material bindings, and
120
- the exotic curve schemas (`NurbsCurves`, `HermiteCurves`, `NurbsPatch`)
121
- which load with a warning and are skipped.
122
-
123
56
  ## Install
124
57
 
125
58
  ```sh
@@ -144,221 +77,33 @@ robot.setJointValues({ joint1: 0.4, joint2: -0.2 });
144
77
  const handMatrix = robot.getLinkWorldMatrix("tool0"); // THREE.Matrix4
145
78
  const handPos = robot.getLinkWorldPosition("tool0"); // THREE.Vector3
146
79
 
147
- robot.getJointNames(); // controllable joints
80
+ robot.getJointNames(); // commandable joints
148
81
  robot.getKinematicTree(); // root, ordering, loop joints, ...
149
82
  ```
150
83
 
151
- No URL? `parse` also takes in-memory content: USDA source text, or an
152
- `ArrayBuffer` / typed array / `Blob` holding any supported format the zip /
153
- crate magic is sniffed, so a dropped `File` or a response body works as-is:
154
-
155
- ```ts
156
- const loader = new ThreeUsdRobotLoader();
157
- await loader.parse(usdaText); // USDA source string
158
- await loader.parse(file); // File / Blob from drag & drop or <input type="file">
159
- await loader.parse(await res.arrayBuffer()); // a fetch you did yourself
160
- ```
161
-
162
- `parseUsdz(data)` / `parseCrate(data, baseUrl)` stay as explicit-format entries,
163
- and `parseRobotDescription(data)` returns the Three.js-independent IR. Pass a
164
- `baseUrl` as the second `parse` argument if the layer has relative references
165
- or texture paths to resolve.
166
-
167
- ### World up-axis & units
168
-
169
- Stages load normalized: `metersPerUnit` scales the root, and the authored
170
- `upAxis` (`"Y"` or `"Z"`) is rotated into your world convention via `worldUp`:
171
-
172
- ```ts
173
- new ThreeUsdRobotLoader(); // default: "Y" — upright in a stock three.js scene
174
- new ThreeUsdRobotLoader({ worldUp: "Z" }); // robotics-style Z-up world
175
- new ThreeUsdRobotLoader({ worldUp: "keep" }); // leave the authored orientation
176
-
177
- robot.upAxis; // authored stage value ("Y" | "Z"), independent of normalization
178
- robot.metersPerUnit; // authored stage scale (already applied to the root)
179
- ```
180
-
181
- Isaac Sim assets are Z-up, so the default makes them stand upright in a plain
182
- three.js scene; a Z-up app (ROS-style) passes `worldUp: "Z"` once instead of
183
- counter-rotating per asset. The M9 option `upAxisConversion` remains as a
184
- deprecated alias (`"auto"` ≡ `worldUp: "Y"`, `"none"` ≡ `"keep"`).
185
-
186
- ### Stable addressing (naming contract)
187
-
188
- Joints and links are keyed by their prim's **leaf name** while it is unique
189
- across the robot; on a collision (say, two arms each with a `seg` link) the
190
- colliding entries are keyed by their **full prim path** instead —
191
- deterministically. Every accessor also takes the full prim path directly, so
192
- tooling can pin exact prims no matter how the asset is named:
193
-
194
- ```ts
195
- robot.setJointValue("/World/armL/j1", 0.4); // same joint as its key
196
- robot.getLinkWorldMatrix("/World/armL/seg");
197
- robot.getLinkObjectsByPath(); // Map<primPath, LinkObject>
198
- robot.getJointObjectsByPath(); // Map<primPath, JointObject>
199
- ```
200
-
201
- `LinkObject.primPath` / `JointObject.primPath` carry the reverse direction.
202
-
203
- ### Viewer toggles & helpers
204
-
205
- ```ts
206
- robot.showVisual = true;
207
- robot.showCollision = false;
208
- robot.showJointAxes = true; // built-in axes gizmos on each joint
209
- robot.showLinkFrames = false;
210
-
211
- import { addJointLimitHelpers } from "three-usd-robot/helpers";
212
- addJointLimitHelpers(robot); // arc (revolute) / segment (prismatic) per joint
213
- ```
214
-
215
- ### Link highlighting & ghosts
216
-
217
- Per-link appearance helpers cover the common viewer chores — flagging
218
- colliding links, material swaps, and translucent "ghost" pose previews:
219
-
220
- ```ts
221
- import {
222
- createGhostRobot,
223
- highlightLink,
224
- restoreLinkMaterials,
225
- setLinkMaterial,
226
- } from "three-usd-robot/helpers";
227
-
228
- highlightLink(robot, "link1"); // emissive red tint; maps/colors kept
229
- highlightLink(robot, "/World/armL/seg", { color: 0xffaa00, opacity: 0.6 });
230
- setLinkMaterial(robot, "link2", new THREE.MeshBasicMaterial({ wireframe: true }));
231
- restoreLinkMaterials(robot, "link1"); // exact original materials back
232
-
233
- const ghost = createGhostRobot(robot, { jointValues: { joint1: 1.2 } });
234
- scene.add(ghost); // translucent copy previewing the target pose
235
- ghost.setJointValues(ikSolution); // a full ThreeUsdRobot, driveable like the source
236
- ```
237
-
238
- Highlights never stack (each call re-tints from the originals), and ghosts
239
- share the source's geometry — cloning is cheap enough for onion-skinning.
84
+ `parse` also takes in-memory content (USDA text, an `ArrayBuffer` / typed
85
+ array / `Blob` the format is sniffed, so a dropped `File` works as-is), and
86
+ the loader normalizes units and up-axis for you: Isaac's Z-up assets stand
87
+ upright in a Y-up three.js scene by default, or pass `worldUp: "Z"` for a
88
+ robotics-style Z-up world. Details in the [runtime guide](./docs/runtime.md).
240
89
 
241
- Loading a whole cell rather than a bare robot? Pass
242
- `{ loadSceneGeometry: true }` to the loader to draw the static environment
243
- (floor, guarding, racking, …) around the machines. A stage with **no
244
- articulation at all** — a plain static USD scene — is detected and rendered as
245
- scene geometry automatically, with the same unit / up-axis normalization; pass
246
- `loadSceneGeometry: false` to opt out.
90
+ ## Documentation
247
91
 
248
- ### Joint slider panel (lil-gui)
249
-
250
- ```ts
251
- import GUI from "lil-gui";
252
- import { createJointSliderPanel } from "three-usd-robot/extras";
253
-
254
- createJointSliderPanel(robot, new GUI()); // one slider per articulated joint
255
- ```
256
-
257
- The panel takes the GUI instance from you, so the library never bundles
258
- `lil-gui`.
259
-
260
- ### Animation playback
261
-
262
- If the asset has time-sampled joint trajectories, the robot plays them back:
263
-
264
- ```ts
265
- const range = robot.getTimeRange();
266
- if (range) {
267
- // in your render loop, advance a time code between range.start and range.end:
268
- robot.setTime(t); // interpolates every animated joint and updates FK
269
- }
270
- ```
271
-
272
- ### Baked link transforms (recorded playback)
273
-
274
- Recordings baked as **body transforms** (Isaac Sim stage-recorder output,
275
- maximal-coordinate solver playback) can drive link poses directly, bypassing
276
- the joints — usdview-style display semantics:
277
-
278
- ```ts
279
- // A world-pose track keyed by prim path (or link key), quaternion in [x, y, z, w]:
280
- robot.setLinkTransforms({
281
- "/World/link1": { position: [0, 0, 1], quaternion: [0, 0, 0, 1] },
282
- "/World/link2": { position: [0.3, 0, 2], quaternion: [0, 0, 0, 1] },
283
- }); // batched — one matrix update; unlisted links keep their current world pose
284
-
285
- robot.displayMode; // "baked": joint values are untouched and no longer place links
286
- robot.setJointValues(liveValues); // recompute all links from joint values → "fk"
287
- ```
288
-
289
- Poses are read in the **three.js scene world after `worldUp` normalization** —
290
- pair a Z-up meter track (Isaac / ROS convention) with `worldUp: "Z"`; feeding
291
- it into the default Y-up normalization lays the robot on its side. Transforms
292
- on the `robot` object itself (placement, uniform scaling) are accounted for,
293
- and `{ space: "stage" }` reads authored stage coordinates instead.
294
-
295
- Constraint deviations — a recording from a different model version, solver
296
- drift, a coordinate-convention bug — are shown, never silently corrected.
297
- Measure them, or project onto the joints instead:
298
-
299
- ```ts
300
- robot.validateLinkTransforms(poses);
301
- // { "/World/joint1": { anchorError /* m */, axisError /* rad */, q, limitExceeded }, … }
302
- // covers every joint: fixed joints, loop joints dropped from the FK tree,
303
- // and the world-fixed root attachment
304
-
305
- const { values, residuals } = robot.jointValuesFromLinkTransforms(poses, {
306
- previous: lastValues, // ±π branch continuity, frame to frame
307
- });
308
- robot.setJointValues(values); // constraint-respecting playback of the same track
309
- ```
310
-
311
- `new ThreeUsdRobotLoader({ debugBakedTransforms: true })` warns once per baked
312
- session when poses deviate beyond 1 mm / 0.01 rad.
313
-
314
- ### React Three Fiber
315
-
316
- `three-usd-robot/react` provides a declarative `<UsdRobot>` (`react` and
317
- `@react-three/fiber` are **optional** peer deps):
318
-
319
- ```tsx
320
- import { Canvas } from "@react-three/fiber";
321
- import { Suspense } from "react";
322
- import { UsdRobot } from "three-usd-robot/react";
323
-
324
- <Canvas camera={{ position: [2, 2, 2] }}>
325
- <Suspense fallback={null}>
326
- <UsdRobot
327
- url="/robot.usda"
328
- jointValues={{ joint1: 0.4 }} // controlled
329
- showJointAxes
330
- animate // play time-sampled trajectories
331
- onLoad={(robot) => console.log(robot.getJointNames())}
332
- />
333
- </Suspense>
334
- <ambientLight />
335
- </Canvas>;
336
- ```
337
-
338
- Also exported: `useUsdRobot(url)` (Suspense loader), `useRobotAnimation(robot)`,
339
- `preloadUsdRobot`, `clearUsdRobotCache`. Pass a `ref` to `<UsdRobot>` for the
340
- imperative API.
92
+ | Doc | Contents |
93
+ | --- | --- |
94
+ | [Runtime guide](./docs/runtime.md) | Input sources, up-axis & units, naming contract, mimic joints, viewer helpers & ghosts, static scenes, joint sliders, React Three Fiber, using the core without Three.js |
95
+ | [Materials](./docs/materials.md) | UsdPreviewSurface fidelity, Omniverse MDL family mappings, MaterialX, optional TSL graph execution (WebGPU) |
96
+ | [Animation & recorded playback](./docs/recorded-playback.md) | Time-sampled trajectories, baked body-transform recordings, constraint diagnostics & joint projection |
97
+ | [Exporting USD](./docs/export.md) | Re-export, `RobotBuilder` authoring, simulation-ready assets (mass / collision / Isaac Robot Schema), USDZ packaging |
341
98
 
342
99
  ## Export USD
343
100
 
344
- The loader's inverse: re-export something you loaded, or author a robot from
345
- Three.js objects and open the result in Isaac Sim.
346
-
347
- ![The exported cell opened in Isaac Sim](assets/isaacsim.png)
101
+ The loader's inverse author a robot from Three.js meshes and open it in
102
+ Isaac Sim:
348
103
 
349
104
  ```ts
350
- import {
351
- exportThreeUsdRobot,
352
- RobotBuilder,
353
- serializeUsda,
354
- writeUsdz,
355
- } from "three-usd-robot";
356
-
357
- // Re-export a loaded robot (meshes harvested from the Three.js scene):
358
- const usda = serializeUsda(exportThreeUsdRobot(robot));
359
-
360
- // …or build one from Three.js meshes. Z-up and metres by default; each joint
361
- // takes ONE world-space frame, and the build-time arrangement is the zero pose.
105
+ import { RobotBuilder, serializeUsda } from "three-usd-robot";
106
+
362
107
  const builder = new RobotBuilder({ name: "my_robot" });
363
108
  builder.addLink({ name: "base", visuals: [baseMesh] });
364
109
  builder.addLink({ name: "arm", frame: armFrame, visuals: [armMesh] });
@@ -367,46 +112,11 @@ builder.addRevoluteJoint({
367
112
  name: "j1", parent: "base", child: "arm",
368
113
  frame: jointFrame, axis: "Z", lower: -Math.PI, upper: Math.PI,
369
114
  });
370
-
371
- const file = builder.toUsda();
372
- writeFileSync("robot.usda", serializeUsda(file));
373
- writeFileSync("robot.usdz", writeUsdz({ "robot.usda": serializeUsda(file) }));
374
- ```
375
-
376
- Joints export as `UsdPhysics` prims with their limits, drives and initial pose.
377
- For a simulation-ready asset, links also take mass properties, and collision
378
- meshes take physics materials and a collision approximation:
379
-
380
- ```ts
381
- builder.addLink({
382
- name: "arm",
383
- visuals: [armMesh],
384
- collisions: [armCollisionMesh],
385
- inertial: { mass: 2.5, centerOfMass: [0, 0, 0.2], diagonalInertia: [0.02, 0.02, 0.004] },
386
- collisionApproximation: "convexHull",
387
- physicsMaterial: { name: "steel", staticFriction: 0.6, dynamicFriction: 0.5 },
388
- });
389
- ```
390
-
391
- Both screenshots above come from `npx tsx scripts/demo-factory.ts`, which
392
- authors a complete robot cell — a 7-DOF arm with a gripper, a conveyor, a
393
- turntable and the surrounding scenery — and exports it to
394
- `out/factory.usda` / `.usdz`.
395
-
396
- ## Use it without Three.js
397
-
398
- `three-usd-robot/core` is a standalone USD parser, writer and robot IR — handy
399
- for server-side validation or asset tooling:
400
-
401
- ```ts
402
- import { parseUsda, Stage, extractRobotDescription } from "three-usd-robot/core";
403
-
404
- const desc = extractRobotDescription(Stage.OpenFromString(usdaText));
405
- console.log(desc.rootLink, Object.keys(desc.joints));
115
+ writeFileSync("robot.usda", serializeUsda(builder.toUsda()));
406
116
  ```
407
117
 
408
- Command line: `npx tsx scripts/usdc-to-usda.ts robot.usd` converts binary crate
409
- and `.usdz` packages to ASCII USDA.
118
+ Limits, drives, mimic couplings, the initial pose, mass properties and
119
+ collision setup all serialize see [Exporting USD](./docs/export.md).
410
120
 
411
121
  ## Examples
412
122
 
@@ -417,10 +127,11 @@ and `.usdz` packages to ASCII USDA.
417
127
  (prim tree + attribute inspector), a transform gizmo, joint sliders,
418
128
  animation playback and USD export.
419
129
  - **`vite-basic-viewer`** — the same thing through React Three Fiber.
130
+ - **`vite-webgpu-nodes`** — MaterialX graph execution (TSL) on `WebGPURenderer`.
420
131
 
421
- Both take `?asset=<url>` for any asset, or `?isaac=<path under Isaac/>` to pull
422
- one straight from NVIDIA's CDN. `npm run demo:build` generates the factory cell
423
- and builds the deployable site.
132
+ The viewers take `?asset=<url>` for any asset, or `?isaac=<path under Isaac/>`
133
+ to pull one straight from NVIDIA's CDN. `npm run demo:build` generates the
134
+ factory cell and builds the deployable site.
424
135
 
425
136
  ## Package entry points
426
137
 
@@ -428,7 +139,7 @@ and builds the deployable site.
428
139
  | --- | --- |
429
140
  | `three-usd-robot` | Three.js runtime — `ThreeUsdRobotLoader`, `ThreeUsdRobot`, `RobotBuilder`, export helpers |
430
141
  | `three-usd-robot/core` | Three.js-independent USD parser & writer, robot IR, forward-kinematics math |
431
- | `three-usd-robot/helpers` | Viewer helpers (joint axes, link frames, joint limits) |
142
+ | `three-usd-robot/helpers` | Viewer helpers (joint axes, link frames, joint limits, highlights, ghosts) |
432
143
  | `three-usd-robot/extras` | Joint slider panel (bring your own `lil-gui`) |
433
144
  | `three-usd-robot/nodes` | Optional MaterialX → TSL execution (`three/webgpu`) — `createMaterialXNodeFactory`, `loadMaterialXDocument` |
434
145
  | `three-usd-robot/react` | React Three Fiber `<UsdRobot>` component + hooks |
@@ -1,7 +1,7 @@
1
1
  import * as THREE from 'three';
2
- import { x as Vec2, y as Vec3, S as Stage, P as Prim, R as RobotDescription } from './buildKinematicTree-iFqaw0Jl.js';
2
+ import { y as Vec2, z as Vec3, S as Stage, P as Prim, R as RobotDescription } from './buildKinematicTree-Q9JOjAjM.js';
3
3
  import { b as MdlModuleProvider, A as AssetResolver } from './parseMdl-vfzBGoMr.js';
4
- import { T as ThreeUsdRobot } from './ThreeUsdRobot-C3N6-XBJ.js';
4
+ import { T as ThreeUsdRobot } from './ThreeUsdRobot-X1zbpRZo.js';
5
5
 
6
6
  /**
7
7
  * Resolves `UsdShade` material bindings to flat PBR parameters.
@@ -1,5 +1,5 @@
1
1
  import * as THREE from 'three';
2
- import { f as JointType, A as Axis, e as JointDescription, i as LinkDescription, R as RobotDescription, K as KinematicTree, S as Stage } from './buildKinematicTree-iFqaw0Jl.js';
2
+ import { g as JointType, A as Axis, f as JointMimicDescription, e as JointDescription, j as LinkDescription, R as RobotDescription, K as KinematicTree, S as Stage } from './buildKinematicTree-Q9JOjAjM.js';
3
3
 
4
4
  /**
5
5
  * The articulated "motion" node of a joint, inserted between the joint's two
@@ -17,6 +17,8 @@ declare class JointObject extends THREE.Object3D {
17
17
  readonly axis: THREE.Vector3;
18
18
  readonly lower: number | undefined;
19
19
  readonly upper: number | undefined;
20
+ /** Mimic constraint this joint follows, if any (see {@link JointMimicDescription}). */
21
+ readonly mimic: JointMimicDescription | undefined;
20
22
  private _value;
21
23
  constructor(joint: JointDescription);
22
24
  get value(): number;
@@ -156,6 +158,10 @@ declare class ThreeUsdRobot extends THREE.Object3D {
156
158
  private readonly jointObjects;
157
159
  private readonly linkKeyByPath;
158
160
  private readonly jointKeyByPath;
161
+ /** Mimic edges among realized joints: leader key → followers. */
162
+ private readonly mimicFollowers;
163
+ private readonly mimicLeaderByFollower;
164
+ private warnedMimicDrive;
159
165
  private dirty;
160
166
  /** Constructed (fk rest) local matrix of every link, for baked→fk restore. */
161
167
  private readonly restLocal;
@@ -180,6 +186,17 @@ declare class ThreeUsdRobot extends THREE.Object3D {
180
186
  private applyStageNormalization;
181
187
  /** Apply each joint's authored initial value, if any. */
182
188
  private applyInitialPose;
189
+ /** Index the mimic edges realized in the tree (leader and follower both driven). */
190
+ private registerMimicFollowers;
191
+ /** Drive the followers of `leaderKey` from its current value (recursive, cycle-safe). */
192
+ private propagateMimic;
193
+ /** Re-derive every follower from its chain's top-most leader. */
194
+ private propagateAllMimic;
195
+ /** Whether the joint (key or prim path) is a mimic follower, driven by its leader. */
196
+ isMimicFollower(name: string): boolean;
197
+ /** Keys of the mimic-follower joints (excluded from {@link getJointNames}). */
198
+ getMimicJointNames(): string[];
199
+ private warnMimicDriveOnce;
183
200
  private attachRoot;
184
201
  private attachTreeEdges;
185
202
  /** Build `parent → frame0 → motion → frame1⁻¹ → child` for one joint. */
@@ -191,12 +208,15 @@ declare class ThreeUsdRobot extends THREE.Object3D {
191
208
  private jointKey;
192
209
  /**
193
210
  * Set one joint value, addressed by key or full prim path. Unknown joints
194
- * are ignored. Returns whether it applied. Always restores `"fk"` display
211
+ * are ignored, and so are mimic followers (their value derives from the
212
+ * leader; a warning is logged once). Driving a leader also updates its
213
+ * followers. Returns whether it applied. Always restores `"fk"` display
195
214
  * mode first (see {@link setLinkTransforms}).
196
215
  */
197
216
  setJointValue(name: string, value: number): boolean;
198
217
  /**
199
- * Set several joint values at once (matrix update is coalesced). Always
218
+ * Set several joint values at once (matrix update is coalesced). Mimic
219
+ * followers in the batch are skipped like in {@link setJointValue}. Always
200
220
  * restores `"fk"` display mode first, recomputing every link purely from
201
221
  * joint values — even an empty batch returns from baked playback (see
202
222
  * {@link setLinkTransforms}).
@@ -250,9 +270,11 @@ declare class ThreeUsdRobot extends THREE.Object3D {
250
270
  /**
251
271
  * Project a link-pose batch onto the joint manifold: the closed-form 1-DOF
252
272
  * joint values that best reproduce it, plus the same residuals as
253
- * {@link validateLinkTransforms}. `values` covers the articulated tree
254
- * joints, keyed by joint prim path, and feeds {@link setJointValues}
255
- * directly the constraint-respecting playback of the same track:
273
+ * {@link validateLinkTransforms}. `values` covers the commandable
274
+ * articulated tree joints (mimic followers excluded their leaders
275
+ * re-derive them), keyed by joint prim path, and feeds
276
+ * {@link setJointValues} directly — the constraint-respecting playback of
277
+ * the same track:
256
278
  *
257
279
  * ```ts
258
280
  * robot.setJointValues(robot.jointValuesFromLinkTransforms(poses, { previous }).values);
@@ -311,7 +333,11 @@ declare class ThreeUsdRobot extends THREE.Object3D {
311
333
  getJointObjectsByPath(): Map<string, JointObject>;
312
334
  getJoints(): JointDescription[];
313
335
  getLinks(): LinkDescription[];
314
- /** Names of the articulated (controllable) joints. */
336
+ /**
337
+ * Names of the commandable joints — articulated tree joints minus mimic
338
+ * followers, whose values derive from their leader
339
+ * (see {@link getMimicJointNames}).
340
+ */
315
341
  getJointNames(): string[];
316
342
  getLinkNames(): string[];
317
343
  getKinematicTree(): KinematicTree;
@@ -331,7 +357,11 @@ declare class ThreeUsdRobot extends THREE.Object3D {
331
357
  start: number;
332
358
  end: number;
333
359
  } | null;
334
- /** Sample every animated joint at time code `t` and apply the values (an fk drive — leaves baked mode). */
360
+ /**
361
+ * Sample every animated joint at time code `t` and apply the values (an fk
362
+ * drive — leaves baked mode). Samples on mimic followers are ignored; the
363
+ * constraint re-derives them from their leader.
364
+ */
335
365
  setTime(t: number): void;
336
366
  get showVisual(): boolean;
337
367
  set showVisual(v: boolean);
@@ -1,8 +1,8 @@
1
- import { R as RobotDescription } from './buildKinematicTree-iFqaw0Jl.js';
1
+ import { R as RobotDescription } from './buildKinematicTree-Q9JOjAjM.js';
2
2
  import { A as AssetResolver } from './parseMdl-vfzBGoMr.js';
3
3
  import { U as UsdSource, B as BinarySource } from './bytes-CxGRGry_.js';
4
- import { M as MaterialFactory } from './MeshBinding-DQKXuYNr.js';
5
- import { W as WorldUpAxis, a as ThreeUsdRobotOptions, T as ThreeUsdRobot } from './ThreeUsdRobot-C3N6-XBJ.js';
4
+ import { M as MaterialFactory } from './MeshBinding-B_8RhuQE.js';
5
+ import { W as WorldUpAxis, a as ThreeUsdRobotOptions, T as ThreeUsdRobot } from './ThreeUsdRobot-X1zbpRZo.js';
6
6
 
7
7
  type ThreeUsdRobotLoaderOptions = {
8
8
  /** Resolver for references / payloads / sublayers (default {@link DefaultAssetResolver}). */
@@ -223,8 +223,6 @@ declare function decomposeRigid(m: Mat4): {
223
223
  * {@link RobotDescription}; the kinematics builder (M4) trees it; the Three.js
224
224
  * runtime (M5) realizes it as an `Object3D` hierarchy. Keeping the IR free of
225
225
  * Three.js lets the same data drive tooling, validation, and (later) IK.
226
- *
227
- * See concept.md §9 for the rationale behind each field.
228
226
  */
229
227
 
230
228
  type JointType = "fixed" | "revolute" | "continuous" | "prismatic";
@@ -306,6 +304,22 @@ type JointDescription = {
306
304
  /** Time-sampled joint value trajectory (SI), if authored — drives playback. */
307
305
  valueSamples?: SampleChannel;
308
306
  drive?: JointDriveDescription;
307
+ /** Mimic constraint: this joint follows another joint's value. */
308
+ mimic?: JointMimicDescription;
309
+ };
310
+ /**
311
+ * A mimic constraint — `follower = multiplier · leader + offset` (URDF
312
+ * convention, SI units) — read from `NewtonMimicAPI` (Isaac Sim 6 / Newton
313
+ * authoring) or the legacy PhysX `PhysxMimicJointAPI`. Leader and follower
314
+ * must share the motion kind (angular ↔ angular, linear ↔ linear).
315
+ */
316
+ type JointMimicDescription = {
317
+ /** Leader joint key (same key space as {@link RobotDescription.joints}). */
318
+ joint: string;
319
+ /** Scale on the leader value (dimensionless between same-type joints). */
320
+ multiplier: number;
321
+ /** Offset added after scaling, in SI (radians / stage linear units). */
322
+ offset: number;
309
323
  };
310
324
  /** Authored joint drive parameters (`UsdPhysicsDriveAPI`), as read in M3. */
311
325
  type JointDriveDescription = {
@@ -528,4 +542,4 @@ type BuildTreeOptions = {
528
542
  };
529
543
  declare function buildKinematicTree(robot: RobotDescription, options?: BuildTreeOptions): KinematicTree;
530
544
 
531
- export { makeRotationZ as $, type Axis as A, type BuildTreeOptions as B, type CompositionArc as C, DEFAULT_METERS_PER_UNIT as D, buildKinematicTree as E, channelFromSamples as F, decomposeRigid as G, fromUsdMatrix as H, getTranslation as I, type JointDriveDescription as J, type KinematicTree as K, type LinkInertialDescription as L, type Mat4 as M, identity4 as N, interpolate as O, Prim as P, Quat as Q, type RobotDescription as R, Stage as S, type TreeEdge as T, type UsdaFile as U, type Variability as V, invert as W, makeEuler as X, makeRotationFromQuat as Y, makeRotationX as Z, makeRotationY as _, AssetPath as a, makeScale as a0, makeTranslation as a1, multiply as a2, multiplyAll as a3, toUsdMatrix as a4, Attribute as b, type AttributeSpec as c, DEG2RAD as d, type JointDescription as e, type JointType as f, type KinematicNode as g, Layer as h, type LinkDescription as i, type ListOp as j, type MetadataMap as k, type PrimSpec as l, type PropertySpec as m, RAD2DEG as n, Relationship as o, type RelationshipSpec as p, type SampleChannel as q, type SdfPath as r, type Specifier as s, type UpAxis as t, type UsdDictionary as u, UsdMatrix as v, type UsdValue as w, type Vec2 as x, type Vec3 as y, type Vec4 as z };
545
+ export { makeRotationY as $, type Axis as A, type BuildTreeOptions as B, type CompositionArc as C, DEFAULT_METERS_PER_UNIT as D, type Vec4 as E, buildKinematicTree as F, channelFromSamples as G, decomposeRigid as H, fromUsdMatrix as I, type JointDriveDescription as J, type KinematicTree as K, type LinkInertialDescription as L, type Mat4 as M, getTranslation as N, identity4 as O, Prim as P, Quat as Q, type RobotDescription as R, Stage as S, type TreeEdge as T, type UsdaFile as U, type Variability as V, interpolate as W, invert as X, makeEuler as Y, makeRotationFromQuat as Z, makeRotationX as _, AssetPath as a, makeRotationZ as a0, makeScale as a1, makeTranslation as a2, multiply as a3, multiplyAll as a4, toUsdMatrix as a5, Attribute as b, type AttributeSpec as c, DEG2RAD as d, type JointDescription as e, type JointMimicDescription as f, type JointType as g, type KinematicNode as h, Layer as i, type LinkDescription as j, type ListOp as k, type MetadataMap as l, type PrimSpec as m, type PropertySpec as n, RAD2DEG as o, Relationship as p, type RelationshipSpec as q, type SampleChannel as r, type SdfPath as s, type Specifier as t, type UpAxis as u, type UsdDictionary as v, UsdMatrix as w, type UsdValue as x, type Vec2 as y, type Vec3 as z };
@@ -1,5 +1,5 @@
1
- import { getMaterialSubsets, isRenderableGprim, COLLISION_API, isNonVisualPurpose, computeLocalTransform, toBytes, extractRobotDescription, isZip, CrateReader, openUsdz, buildKinematicTree, loadMdlModules, isUnsupportedGprim, Stage, composeLayer, composeFile, crateToUsdaFile } from './chunk-6W4LQVEQ.js';
2
- import { ThreeUsdRobot } from './chunk-KYBHWDX5.js';
1
+ import { getMaterialSubsets, isRenderableGprim, COLLISION_API, isNonVisualPurpose, computeLocalTransform, toBytes, extractRobotDescription, isZip, CrateReader, openUsdz, buildKinematicTree, loadMdlModules, isUnsupportedGprim, Stage, composeLayer, composeFile, crateToUsdaFile } from './chunk-J6ZQWQ2C.js';
2
+ import { ThreeUsdRobot } from './chunk-JN2QPDB3.js';
3
3
  import { identity4, multiply } from './chunk-YGJ23CG3.js';
4
4
  import { resolveBoundMaterial, DefaultAssetResolver } from './chunk-PPPRB6KE.js';
5
5
  import * as THREE from 'three';
@@ -969,5 +969,5 @@ var ThreeUsdRobotLoader = class {
969
969
  };
970
970
 
971
971
  export { ThreeUsdRobotLoader, bindRobotMeshes, bindSceneMeshes, buildGprimGeometry, buildGprimObject, buildMeshGeometry, buildMeshMaterial, createTextureProvider };
972
- //# sourceMappingURL=chunk-7GGSIA6M.js.map
973
- //# sourceMappingURL=chunk-7GGSIA6M.js.map
972
+ //# sourceMappingURL=chunk-5C3VTA56.js.map
973
+ //# sourceMappingURL=chunk-5C3VTA56.js.map