three-usd-robot 0.10.0 → 0.11.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
@@ -27,10 +27,12 @@ exported back to `.usda` / `.usdz` in the browser.
27
27
  `Capsule` / `Cone`), point clouds (`Points`) and curves (`BasisCurves`:
28
28
  linear / bezier / bspline / catmullRom, periodic wrap, and an opt-in
29
29
  `curveTubes` mode that turns authored widths into tube meshes) with
30
- `UsdShade` materials (UsdPreviewSurface / OmniPBR) and textures; up-axis
30
+ `UsdShade` materials (UsdPreviewSurface / Omniverse MDL) and textures; up-axis
31
31
  and units normalized automatically. Articulation-free stages load as
32
32
  static scenes.
33
- - **Animation** — plays back time-sampled joint trajectories.
33
+ - **Animation** — plays back time-sampled joint trajectories, and replays
34
+ baked body-transform recordings through `setLinkTransforms` with
35
+ constraint diagnostics.
34
36
  - **Export** — write robots and whole cells back to `.usda` / `.usdz`,
35
37
  simulation-ready for Isaac Sim.
36
38
  - **React** — declarative `<UsdRobot>` for React Three Fiber.
@@ -53,13 +55,28 @@ The CDN is public and CORS-enabled, so this works in the browser too. Try
53
55
  FK check, and a re-export to one self-contained file), or open the Vite example
54
56
  and pick a robot from the preset list.
55
57
 
56
- Materials target **UsdPreviewSurface fidelity plus an OmniPBR mapping**:
58
+ Materials target **UsdPreviewSurface fidelity plus an Omniverse MDL mapping**:
57
59
  constant and textured inputs, faceVarying / indexed UVs, multiple UV sets,
58
60
  per-vertex display colors, physical extensions (`ior` / `clearcoat` /
59
61
  specular workflow → `MeshPhysicalMaterial`), packed ORM maps,
60
- `sourceColorSpace`, and purpose/strength-aware bindings. Executing MDL or
61
- MaterialX shader graphs is out of scope. Not yet supported:
62
- collection-based material bindings, and the exotic curve schemas
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
+ Executing MDL or MaterialX shader graphs is out of scope; unknown MDL
78
+ materials fall back to the OmniPBR mapping with a warning. Not yet
79
+ supported: collection-based material bindings, and the exotic curve schemas
63
80
  (`NurbsCurves`, `HermiteCurves`, `NurbsPatch`) which load with a warning and
64
81
  are skipped.
65
82
 
@@ -212,6 +229,48 @@ if (range) {
212
229
  }
213
230
  ```
214
231
 
232
+ ### Baked link transforms (recorded playback)
233
+
234
+ Recordings baked as **body transforms** (Isaac Sim stage-recorder output,
235
+ maximal-coordinate solver playback) can drive link poses directly, bypassing
236
+ the joints — usdview-style display semantics:
237
+
238
+ ```ts
239
+ // A world-pose track keyed by prim path (or link key), quaternion in [x, y, z, w]:
240
+ robot.setLinkTransforms({
241
+ "/World/link1": { position: [0, 0, 1], quaternion: [0, 0, 0, 1] },
242
+ "/World/link2": { position: [0.3, 0, 2], quaternion: [0, 0, 0, 1] },
243
+ }); // batched — one matrix update; unlisted links keep their current world pose
244
+
245
+ robot.displayMode; // "baked": joint values are untouched and no longer place links
246
+ robot.setJointValues(liveValues); // recompute all links from joint values → "fk"
247
+ ```
248
+
249
+ Poses are read in the **three.js scene world after `worldUp` normalization** —
250
+ pair a Z-up meter track (Isaac / ROS convention) with `worldUp: "Z"`; feeding
251
+ it into the default Y-up normalization lays the robot on its side. Transforms
252
+ on the `robot` object itself (placement, uniform scaling) are accounted for,
253
+ and `{ space: "stage" }` reads authored stage coordinates instead.
254
+
255
+ Constraint deviations — a recording from a different model version, solver
256
+ drift, a coordinate-convention bug — are shown, never silently corrected.
257
+ Measure them, or project onto the joints instead:
258
+
259
+ ```ts
260
+ robot.validateLinkTransforms(poses);
261
+ // { "/World/joint1": { anchorError /* m */, axisError /* rad */, q, limitExceeded }, … }
262
+ // covers every joint: fixed joints, loop joints dropped from the FK tree,
263
+ // and the world-fixed root attachment
264
+
265
+ const { values, residuals } = robot.jointValuesFromLinkTransforms(poses, {
266
+ previous: lastValues, // ±π branch continuity, frame to frame
267
+ });
268
+ robot.setJointValues(values); // constraint-respecting playback of the same track
269
+ ```
270
+
271
+ `new ThreeUsdRobotLoader({ debugBakedTransforms: true })` warns once per baked
272
+ session when poses deviate beyond 1 mm / 0.01 rad.
273
+
215
274
  ### React Three Fiber
216
275
 
217
276
  `three-usd-robot/react` provides a declarative `<UsdRobot>` (`react` and
@@ -0,0 +1,347 @@
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, S as Stage } from './buildKinematicTree-BCySuZZn.js';
3
+
4
+ /**
5
+ * The articulated "motion" node of a joint, inserted between the joint's two
6
+ * fixed frames (`jointFrame0` and `inverse(jointFrame1)`). {@link setValue}
7
+ * rotates it about the axis (revolute/continuous) or slides it along the axis
8
+ * (prismatic); fixed joints are inert.
9
+ */
10
+ declare class JointObject extends THREE.Object3D {
11
+ readonly isJointObject = true;
12
+ readonly jointName: string;
13
+ /** Full USD prim path of the joint — the collision-proof address. */
14
+ readonly primPath: string;
15
+ readonly jointType: JointType;
16
+ readonly axisToken: Axis;
17
+ readonly axis: THREE.Vector3;
18
+ readonly lower: number | undefined;
19
+ readonly upper: number | undefined;
20
+ private _value;
21
+ constructor(joint: JointDescription);
22
+ get value(): number;
23
+ get articulated(): boolean;
24
+ /**
25
+ * Set the joint value (radians for revolute/continuous, length for prismatic).
26
+ * Optionally clamps to authored limits. Returns the value actually applied.
27
+ */
28
+ setValue(value: number, clampToLimits?: boolean): number;
29
+ }
30
+
31
+ /**
32
+ * Three.js node for a robot link. Its frame is the USD link prim's frame;
33
+ * visual/collision meshes (M6) attach as children relative to it. Placement
34
+ * relative to the parent is supplied by the joint chain, so a link's own local
35
+ * matrix is identity (except the root, which carries the world-fixed placement).
36
+ */
37
+ declare class LinkObject extends THREE.Object3D {
38
+ readonly isLinkObject = true;
39
+ readonly linkName: string;
40
+ readonly primPath: string;
41
+ constructor(link: LinkDescription);
42
+ }
43
+
44
+ /** Target world up-axis: normalize to Y-up / Z-up, or keep the authored orientation. */
45
+ type WorldUpAxis = "Y" | "Z" | "keep";
46
+ type ThreeUsdRobotOptions = {
47
+ /** Clamp `setJointValue` to authored limits (default `true`). */
48
+ clampJointLimits?: boolean;
49
+ /** Size of the built-in joint-axis / link-frame helpers (stage units, default `0.15`). */
50
+ helperSize?: number;
51
+ /**
52
+ * Target world up-axis. The root is rotated so the stage's authored `upAxis`
53
+ * lands in that convention: `"Y"` for a standard three.js scene, `"Z"` for a
54
+ * robotics-style Z-up world, `"keep"` for no correction. Takes precedence
55
+ * over the deprecated {@link ThreeUsdRobotOptions.upAxisConversion}.
56
+ */
57
+ worldUp?: WorldUpAxis;
58
+ /**
59
+ * Legacy up-axis correction: `"auto"` ≡ `worldUp: "Y"`, `"Y"` / `"none"` ≡
60
+ * `worldUp: "keep"`, and `"Z"` forces the Z-up→Y-up rotation regardless of
61
+ * stage metadata. Default `"none"` (the loader defaults to `"auto"`).
62
+ * @deprecated Use {@link ThreeUsdRobotOptions.worldUp}.
63
+ */
64
+ upAxisConversion?: "auto" | "Y" | "Z" | "none";
65
+ /** Extra uniform scale multiplied with the stage `metersPerUnit` (default `1`). */
66
+ unitScale?: number;
67
+ /** Seed joints from their authored initial value (drive target / joint state). Default `true`. */
68
+ applyInitialPose?: boolean;
69
+ /**
70
+ * Diagnose {@link ThreeUsdRobot.setLinkTransforms} poses against the joint
71
+ * constraints and `console.warn` once per baked session when one deviates
72
+ * beyond tolerance (default 1 mm anchor / 0.01 rad axis). `true` uses the
73
+ * defaults; pass an object to tune them.
74
+ */
75
+ debugBakedTransforms?: boolean | {
76
+ anchorTolerance?: number;
77
+ axisTolerance?: number;
78
+ };
79
+ };
80
+ /**
81
+ * A rigid world pose for {@link ThreeUsdRobot.setLinkTransforms} —
82
+ * `quaternion` in Three.js `[x, y, z, w]` order (USD authors quatf as
83
+ * `(w, x, y, z)`; reorder when reading recorded USD by hand).
84
+ */
85
+ type LinkPose = {
86
+ position: [number, number, number];
87
+ quaternion: [number, number, number, number];
88
+ };
89
+ /**
90
+ * Coordinate space of {@link LinkPose} batches. `"world"` (default) is the
91
+ * Three.js scene world *after* `worldUp` / unit normalization — with
92
+ * `worldUp: "Z"` you hand in Z-up poses — including any transform on the
93
+ * robot object itself (which must stay a similarity: uniform scale, no
94
+ * shear). `"stage"` is the authored USD stage space (before up-axis rotation
95
+ * and `metersPerUnit` scaling), as prim world transforms are written in the
96
+ * file.
97
+ */
98
+ type LinkPoseSpace = "world" | "stage";
99
+ type LinkPosesOptions = {
100
+ /** Interpretation of the poses (default `"world"`). */
101
+ space?: LinkPoseSpace;
102
+ };
103
+ type JointValuesFromLinkTransformsOptions = LinkPosesOptions & {
104
+ /**
105
+ * Previous joint values (keyed like the returned `values`): each
106
+ * revolute/continuous joint picks the 2πk branch of its projection nearest
107
+ * this, for frame-to-frame continuity past ±π.
108
+ */
109
+ previous?: Record<string, number>;
110
+ /** Clamp `values` to authored limits (default `false` — deviations are reported, not hidden). */
111
+ clampLimits?: boolean;
112
+ };
113
+ /**
114
+ * Per-joint constraint residual of a link-pose batch, keyed by joint prim
115
+ * path. The ideal parent→child transform of a joint is a pure motion along
116
+ * its DOF; whatever the poses leave over splits into `anchorError` /
117
+ * `axisError` (see {@link ThreeUsdRobot.validateLinkTransforms}).
118
+ */
119
+ type JointResidual = {
120
+ /** Translation residual at the joint anchor, in meters (`metersPerUnit` applied). */
121
+ anchorError: number;
122
+ /** Rotation residual off the joint DOF, in radians. */
123
+ axisError: number;
124
+ /** Projected joint value (SI: radians / stage length units; `0` for fixed joints). */
125
+ q: number;
126
+ /** Whether `q` lies outside the authored limits. */
127
+ limitExceeded: boolean;
128
+ };
129
+ /**
130
+ * A Three.js `Object3D` that realizes a {@link RobotDescription} as a kinematic
131
+ * hierarchy and drives forward kinematics via {@link setJointValue}.
132
+ *
133
+ * Per joint the hierarchy is
134
+ * `parentLink → jointFrame0 → jointMotion → jointFrame1⁻¹ → childLink`,
135
+ * where only `jointMotion` (a {@link JointObject}) changes with the joint value;
136
+ * world poses then fall out of Three.js's `updateMatrixWorld`.
137
+ *
138
+ * **Naming contract** — a link/joint's key is its prim's leaf name when that
139
+ * is unique across the robot, else its full prim path (deterministic; see the
140
+ * extractor). Every accessor taking a name equally accepts the full prim path,
141
+ * which is stable regardless of collisions; {@link getLinkObjectsByPath} /
142
+ * {@link getJointObjectsByPath} enumerate the path-keyed tables.
143
+ */
144
+ declare class ThreeUsdRobot extends THREE.Object3D {
145
+ readonly isThreeUsdRobot = true;
146
+ readonly robot: RobotDescription;
147
+ readonly tree: KinematicTree;
148
+ readonly clampJointLimits: boolean;
149
+ /**
150
+ * The composed USD stage this robot was built from — the full prim tree, for
151
+ * inspection tooling (structure panels, attribute browsers). Attached by
152
+ * {@link ThreeUsdRobotLoader}; `undefined` for programmatically-built robots.
153
+ */
154
+ stage?: Stage;
155
+ private readonly linkObjects;
156
+ private readonly jointObjects;
157
+ private readonly linkKeyByPath;
158
+ private readonly jointKeyByPath;
159
+ private dirty;
160
+ /** Constructed (fk rest) local matrix of every link, for baked→fk restore. */
161
+ private readonly restLocal;
162
+ private _displayMode;
163
+ /** Stage-space link worlds while baked (`null` in fk mode). */
164
+ private bakedStageWorld;
165
+ /** Frozen `frame0 · motion · frame1⁻¹` per tree joint while baked. */
166
+ private bakedChainRel;
167
+ private readonly debugBaked;
168
+ private bakedDebugWarned;
169
+ private readonly warnedPoseKeys;
170
+ private warnedNonUniformScale;
171
+ private readonly helperSize;
172
+ private _showVisual;
173
+ private _showCollision;
174
+ private _showJointAxes;
175
+ private _showLinkFrames;
176
+ private jointAxesHelpers;
177
+ private linkFrameHelpers;
178
+ constructor(robot: RobotDescription, tree: KinematicTree, options?: ThreeUsdRobotOptions);
179
+ /** Orient (authored upAxis → target world up) and scale (metersPerUnit × unitScale) the root. */
180
+ private applyStageNormalization;
181
+ /** Apply each joint's authored initial value, if any. */
182
+ private applyInitialPose;
183
+ private attachRoot;
184
+ private attachTreeEdges;
185
+ /** Build `parent → frame0 → motion → frame1⁻¹ → child` for one joint. */
186
+ private attachJointChain;
187
+ private attachIsolatedLinks;
188
+ /** Resolve a link reference — key or full prim path — to the extractor key. */
189
+ private linkKey;
190
+ /** Resolve a joint reference — key or full prim path — to the extractor key. */
191
+ private jointKey;
192
+ /**
193
+ * Set one joint value, addressed by key or full prim path. Unknown joints
194
+ * are ignored. Returns whether it applied. Always restores `"fk"` display
195
+ * mode first (see {@link setLinkTransforms}).
196
+ */
197
+ setJointValue(name: string, value: number): boolean;
198
+ /**
199
+ * Set several joint values at once (matrix update is coalesced). Always
200
+ * restores `"fk"` display mode first, recomputing every link purely from
201
+ * joint values — even an empty batch returns from baked playback (see
202
+ * {@link setLinkTransforms}).
203
+ */
204
+ setJointValues(values: Record<string, number>): void;
205
+ getJointValue(name: string): number | undefined;
206
+ /** Recompute world matrices. Called lazily by the getters; safe to call directly. */
207
+ updateKinematics(): void;
208
+ private ensureUpdated;
209
+ /**
210
+ * `"fk"` (default): link placements derive from joint values. `"baked"`:
211
+ * {@link setLinkTransforms} wrote link poses directly and the joints no
212
+ * longer constrain the display; any `setJointValue`-family call restores fk.
213
+ */
214
+ get displayMode(): "fk" | "baked";
215
+ /**
216
+ * Drive link world poses directly — the display path for baked recordings
217
+ * (Isaac Sim body-transform time samples, maximal-coordinate playback).
218
+ * usdview-like semantics: constraint deviations are shown, never corrected —
219
+ * {@link validateLinkTransforms} measures them,
220
+ * {@link jointValuesFromLinkTransforms} projects onto the joints instead.
221
+ *
222
+ * Enters `"baked"` display mode; joint values stay untouched. Return to fk
223
+ * with {@link setJointValues} (any batch, even `{}`), which recomputes
224
+ * every link purely from joint values.
225
+ *
226
+ * Keys are link keys or full prim paths; unknown keys warn once and are
227
+ * skipped. Unspecified links KEEP their current world pose — a track that
228
+ * omits a link means "it did not move". Poses are rigid, `quaternion` in
229
+ * `[x, y, z, w]` order, interpreted per `opts.space` (default `"world"`:
230
+ * the Three.js scene world after `worldUp` normalization — pair a Z-up
231
+ * meter track with `worldUp: "Z"`). Matrix updates are coalesced into the
232
+ * next render / world query. Returns the number of poses applied.
233
+ */
234
+ setLinkTransforms(poses: Record<string, LinkPose>, opts?: LinkPosesOptions): number;
235
+ /**
236
+ * Measure how far a link-pose batch deviates from the joint constraints,
237
+ * without touching the display. Unspecified links resolve to their current
238
+ * displayed pose, so the report predicts exactly what
239
+ * {@link setLinkTransforms} with the same batch would show.
240
+ *
241
+ * Keyed by joint prim path; covers every joint — fixed joints (`q` = 0
242
+ * check), loop joints dropped from the fk tree (closure error) and the
243
+ * world-fixed root attachment (a moved base against a fixed-base model).
244
+ * Typical signatures: a constant `anchorError` offset on every joint —
245
+ * recording/model mismatch (wrong version or scale); growth over time —
246
+ * maximal-coordinate solver drift; large uniform `axisError` — a
247
+ * coordinate-convention bug (Y/Z-up or quaternion order).
248
+ */
249
+ validateLinkTransforms(poses: Record<string, LinkPose>, opts?: LinkPosesOptions): Record<string, JointResidual>;
250
+ /**
251
+ * Project a link-pose batch onto the joint manifold: the closed-form 1-DOF
252
+ * 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:
256
+ *
257
+ * ```ts
258
+ * robot.setJointValues(robot.jointValuesFromLinkTransforms(poses, { previous }).values);
259
+ * ```
260
+ *
261
+ * Residual `q` / `limitExceeded` always report the unclamped projection,
262
+ * also when `clampLimits` clamps `values`.
263
+ */
264
+ jointValuesFromLinkTransforms(poses: Record<string, LinkPose>, opts?: JointValuesFromLinkTransformsOptions): {
265
+ values: Record<string, number>;
266
+ residuals: Record<string, JointResidual>;
267
+ };
268
+ /** Restore the constructed fk link placements (no-op when already `"fk"`). */
269
+ private exitBakedMode;
270
+ /** Freeze the fk state a baked session builds on (joints cannot move while baked). */
271
+ private enterBakedMode;
272
+ /** `frame0 · motion(q) · frame1⁻¹` of every tree joint, from live joint values. */
273
+ private computeChainRels;
274
+ /** Stage-space world transform of every link under the current display state. */
275
+ private computeStageWorlds;
276
+ private currentStageWorlds;
277
+ /** Resolve pose keys to link keys and convert each pose to a rigid stage-space matrix. */
278
+ private resolvePoseTargets;
279
+ /**
280
+ * Scene world → stage space, undoing the robot's own world transform
281
+ * (up-axis rotation, unit scale, any user placement) as a similarity — so
282
+ * link locals stay rigid and the root keeps carrying the scale.
283
+ */
284
+ private sceneToStageConverter;
285
+ /**
286
+ * Decompose every joint's parent→child transform under a pose batch
287
+ * (unspecified links resolve to their current displayed pose, mirroring
288
+ * {@link setLinkTransforms}). Pure — the display is untouched.
289
+ */
290
+ private decomposeJoints;
291
+ private buildResidual;
292
+ private warnUnknownPoseKey;
293
+ /** `debugBakedTransforms`: warn once per baked session when poses break the constraints. */
294
+ private warnBakedDeviationOnce;
295
+ /** World matrix of a link, addressed by key or full prim path. */
296
+ getLinkWorldMatrix(name: string): THREE.Matrix4;
297
+ getLinkWorldPosition(name: string): THREE.Vector3;
298
+ /** Link object by key or full prim path. */
299
+ getLinkObject(name: string): LinkObject | undefined;
300
+ /** Joint object by key or full prim path. */
301
+ getJointObject(name: string): JointObject | undefined;
302
+ /**
303
+ * Table of link prim path → {@link LinkObject}. Prim paths are the
304
+ * collision-proof way to pin a link (e.g. to attach tools or gizmos).
305
+ */
306
+ getLinkObjectsByPath(): Map<string, LinkObject>;
307
+ /**
308
+ * Table of joint prim path → {@link JointObject}, covering the joints
309
+ * realized in the kinematic tree (loop joints have no motion node).
310
+ */
311
+ getJointObjectsByPath(): Map<string, JointObject>;
312
+ getJoints(): JointDescription[];
313
+ getLinks(): LinkDescription[];
314
+ /** Names of the articulated (controllable) joints. */
315
+ getJointNames(): string[];
316
+ getLinkNames(): string[];
317
+ getKinematicTree(): KinematicTree;
318
+ /** Authored stage up-axis (`"Y"` or `"Z"`) — unaffected by `worldUp` normalization. */
319
+ get upAxis(): "Y" | "Z";
320
+ /** Authored stage scale in meters per unit (already applied to the root). */
321
+ get metersPerUnit(): number;
322
+ /** Playback rate in time codes per second (from the stage; default 24). */
323
+ getTimeCodesPerSecond(): number;
324
+ /** Whether any joint has a time-sampled trajectory. */
325
+ hasAnimation(): boolean;
326
+ /**
327
+ * Animation range in time codes: the union of authored joint sample ranges,
328
+ * falling back to the stage `startTimeCode`/`endTimeCode`. `null` if neither.
329
+ */
330
+ getTimeRange(): {
331
+ start: number;
332
+ end: number;
333
+ } | null;
334
+ /** Sample every animated joint at time code `t` and apply the values (an fk drive — leaves baked mode). */
335
+ setTime(t: number): void;
336
+ get showVisual(): boolean;
337
+ set showVisual(v: boolean);
338
+ get showCollision(): boolean;
339
+ set showCollision(v: boolean);
340
+ get showJointAxes(): boolean;
341
+ set showJointAxes(v: boolean);
342
+ get showLinkFrames(): boolean;
343
+ set showLinkFrames(v: boolean);
344
+ private setKindVisibility;
345
+ }
346
+
347
+ export { JointObject as J, LinkObject as L, ThreeUsdRobot as T, type WorldUpAxis as W, type ThreeUsdRobotOptions as a, type JointResidual as b, type JointValuesFromLinkTransformsOptions as c, type LinkPose as d, type LinkPoseSpace as e, type LinkPosesOptions as f };
@@ -1,6 +1,6 @@
1
1
  import { R as RobotDescription } from './buildKinematicTree-BCySuZZn.js';
2
2
  import { A as AssetResolver, U as UsdSource, B as BinarySource } from './bytes-MOJ2oN-u.js';
3
- import { W as WorldUpAxis, T as ThreeUsdRobot } from './ThreeUsdRobot-B7Z4oORO.js';
3
+ import { W as WorldUpAxis, a as ThreeUsdRobotOptions, T as ThreeUsdRobot } from './ThreeUsdRobot-BxnCluuK.js';
4
4
 
5
5
  type ThreeUsdRobotLoaderOptions = {
6
6
  /** Resolver for references / payloads / sublayers (default {@link DefaultAssetResolver}). */
@@ -41,6 +41,12 @@ type ThreeUsdRobotLoaderOptions = {
41
41
  clampJointLimits?: boolean;
42
42
  /** Seed joints from drive targets / joint state (M9). */
43
43
  applyDriveTargetsAsInitialPose?: boolean;
44
+ /**
45
+ * Diagnose {@link ThreeUsdRobot.setLinkTransforms} poses against the joint
46
+ * constraints and warn once per baked session past tolerance (default
47
+ * 1 mm anchor / 0.01 rad axis).
48
+ */
49
+ debugBakedTransforms?: ThreeUsdRobotOptions["debugBakedTransforms"];
44
50
  /** Override the robot name. */
45
51
  robotName?: string;
46
52
  /** Receives non-fatal load diagnostics. */
@@ -1,6 +1,6 @@
1
- import { getMaterialSubsets, resolveBoundMaterial, isRenderableGprim, COLLISION_API, isNonVisualPurpose, computeLocalTransform, DefaultAssetResolver, toBytes, extractRobotDescription, isZip, CrateReader, openUsdz, buildKinematicTree, isUnsupportedGprim, Stage, composeLayer, composeFile, crateToUsdaFile } from './chunk-NFKK5LJK.js';
2
- import { ThreeUsdRobot } from './chunk-WROYLUSS.js';
3
- import { identity4, multiply } from './chunk-IYKPVUZ2.js';
1
+ import { getMaterialSubsets, resolveBoundMaterial, isRenderableGprim, COLLISION_API, isNonVisualPurpose, computeLocalTransform, DefaultAssetResolver, toBytes, extractRobotDescription, isZip, CrateReader, openUsdz, buildKinematicTree, loadMdlModules, isUnsupportedGprim, Stage, composeLayer, composeFile, crateToUsdaFile } from './chunk-AXPMXEQC.js';
2
+ import { ThreeUsdRobot } from './chunk-ENWFOYHU.js';
3
+ import { identity4, multiply } from './chunk-LDO5FKQS.js';
4
4
  import * as THREE from 'three';
5
5
 
6
6
  var DEFAULT_COLOR = 10132122;
@@ -237,22 +237,22 @@ function buildGprimGeometry(prim) {
237
237
  function buildGprimObject(prim, stage, options = {}) {
238
238
  switch (prim.GetTypeName()) {
239
239
  case "Points":
240
- return buildPointsObject(prim, stage);
240
+ return buildPointsObject(prim, stage, options.mdl);
241
241
  case "BasisCurves":
242
- return buildBasisCurvesObject(prim, stage, options.curveTubes ?? false);
242
+ return buildBasisCurvesObject(prim, stage, options.curveTubes ?? false, options.mdl);
243
243
  default: {
244
244
  const geometry = buildGprimGeometry(prim);
245
245
  if (!geometry) return null;
246
246
  return new THREE.Mesh(
247
247
  geometry,
248
- buildMeshMaterials(prim, stage, options.textureProvider, options.onWarn)
248
+ buildMeshMaterials(prim, stage, options.textureProvider, options.onWarn, options.mdl)
249
249
  );
250
250
  }
251
251
  }
252
252
  }
253
- function resolveFlatColor(prim, stage) {
253
+ function resolveFlatColor(prim, stage, mdl) {
254
254
  const color = new THREE.Color(DEFAULT_COLOR);
255
- const bound = stage ? resolveBoundMaterial(stage, prim) : void 0;
255
+ const bound = stage ? resolveBoundMaterial(stage, prim, mdl ? { mdl } : {}) : void 0;
256
256
  if (bound?.color) {
257
257
  color.setRGB(bound.color[0], bound.color[1], bound.color[2]);
258
258
  } else {
@@ -273,12 +273,12 @@ function meanWidth(prim) {
273
273
  if (!isNumberArray(widths) || widths.length === 0) return void 0;
274
274
  return widths.reduce((a, b) => a + b, 0) / widths.length;
275
275
  }
276
- function buildPointsObject(prim, stage) {
276
+ function buildPointsObject(prim, stage, mdl) {
277
277
  const points = prim.GetAttribute("points").Get();
278
278
  if (!isVec3Array(points) || points.length === 0) return null;
279
279
  const geometry = new THREE.BufferGeometry();
280
280
  geometry.setAttribute("position", new THREE.Float32BufferAttribute(flat3(points), 3));
281
- const flat = resolveFlatColor(prim, stage);
281
+ const flat = resolveFlatColor(prim, stage, mdl);
282
282
  const material = new THREE.PointsMaterial({ color: flat.color });
283
283
  if (flat.opacity < 1) {
284
284
  material.transparent = true;
@@ -367,14 +367,14 @@ function polylinePath(samples, closed) {
367
367
  if (closed) path.add(new THREE.LineCurve3(vecs[vecs.length - 1], vecs[0]));
368
368
  return path;
369
369
  }
370
- function buildBasisCurvesObject(prim, stage, curveTubes) {
370
+ function buildBasisCurvesObject(prim, stage, curveTubes, mdl) {
371
371
  const points = prim.GetAttribute("points").Get();
372
372
  const counts = prim.GetAttribute("curveVertexCounts").Get();
373
373
  if (!isVec3Array(points) || !isNumberArray(counts) || counts.length === 0) return null;
374
374
  const curveType = prim.GetAttribute("type").Get() ?? "cubic";
375
375
  const basisName = prim.GetAttribute("basis").Get() ?? "bezier";
376
376
  const periodic = prim.GetAttribute("wrap").Get() === "periodic";
377
- const flat = resolveFlatColor(prim, stage);
377
+ const flat = resolveFlatColor(prim, stage, mdl);
378
378
  const width = meanWidth(prim);
379
379
  const asTubes = curveTubes && width !== void 0 && width > 0;
380
380
  const material = asTubes ? new THREE.MeshStandardMaterial({ color: flat.color, roughness: 0.8, metalness: 0.1 }) : new THREE.LineBasicMaterial({ color: flat.color });
@@ -420,11 +420,15 @@ function orientSpine(geometry, prim) {
420
420
  if (axis === "X") return geometry.rotateZ(-Math.PI / 2);
421
421
  return geometry.rotateX(Math.PI / 2);
422
422
  }
423
- function buildMeshMaterial(meshPrim, stage, textures, bindingPrim, onWarn) {
423
+ function buildMeshMaterial(meshPrim, stage, textures, bindingPrim, onWarn, mdl) {
424
424
  const color = new THREE.Color(DEFAULT_COLOR);
425
425
  let opacity = 1;
426
426
  let vertexColors = false;
427
- const bound = stage ? resolveBoundMaterial(stage, bindingPrim ?? meshPrim) : void 0;
427
+ const resolveOptions = {
428
+ ...mdl ? { mdl } : {},
429
+ ...onWarn ? { onWarn } : {}
430
+ };
431
+ const bound = stage ? resolveBoundMaterial(stage, bindingPrim ?? meshPrim, resolveOptions) : void 0;
428
432
  if (bound?.color) {
429
433
  color.setRGB(bound.color[0], bound.color[1], bound.color[2]);
430
434
  } else {
@@ -511,7 +515,7 @@ function buildMeshMaterial(meshPrim, stage, textures, bindingPrim, onWarn) {
511
515
  ...aoMap ? { aoMap } : {},
512
516
  ...emissiveMap ? { emissiveMap } : {}
513
517
  };
514
- const physical = bound !== void 0 && (bound.ior !== void 0 || bound.clearcoat !== void 0 || bound.clearcoatRoughness !== void 0 || bound.specularColor !== void 0);
518
+ const physical = bound !== void 0 && (bound.ior !== void 0 || bound.clearcoat !== void 0 || bound.clearcoatRoughness !== void 0 || bound.specularColor !== void 0 || bound.transmission !== void 0 || bound.clearcoatNormalTexture !== void 0);
515
519
  const material = physical ? new THREE.MeshPhysicalMaterial(params) : new THREE.MeshStandardMaterial(params);
516
520
  if (material instanceof THREE.MeshPhysicalMaterial && bound) {
517
521
  if (bound.ior !== void 0) material.ior = bound.ior;
@@ -525,6 +529,10 @@ function buildMeshMaterial(meshPrim, stage, textures, bindingPrim, onWarn) {
525
529
  bound.specularColor[2]
526
530
  );
527
531
  }
532
+ if (bound.transmission !== void 0) material.transmission = bound.transmission;
533
+ if (bound.thickness !== void 0) material.thickness = bound.thickness;
534
+ const clearcoatNormalMap = tex(bound.clearcoatNormalTexture, "linear");
535
+ if (clearcoatNormalMap) material.clearcoatNormalMap = clearcoatNormalMap;
528
536
  }
529
537
  if (bound?.emissiveIntensity !== void 0) material.emissiveIntensity = bound.emissiveIntensity;
530
538
  if (normalMap && bound?.normalTexture?.scale) {
@@ -539,7 +547,8 @@ function bindRobotMeshes(stage, robot3d, desc, options = {}) {
539
547
  const gprimOptions = {
540
548
  ...options.textureProvider ? { textureProvider: options.textureProvider } : {},
541
549
  ...options.curveTubes ? { curveTubes: true } : {},
542
- ...options.onWarn ? { onWarn: options.onWarn } : {}
550
+ ...options.onWarn ? { onWarn: options.onWarn } : {},
551
+ ...options.mdl ? { mdl: options.mdl } : {}
543
552
  };
544
553
  for (const [key, link] of Object.entries(desc.links)) {
545
554
  const linkObj = robot3d.getLinkObject(key);
@@ -585,7 +594,8 @@ function bindSceneMeshes(stage, robot3d, desc, options = {}) {
585
594
  const gprimOptions = {
586
595
  ...options.textureProvider ? { textureProvider: options.textureProvider } : {},
587
596
  ...options.curveTubes ? { curveTubes: true } : {},
588
- ...options.onWarn ? { onWarn: options.onWarn } : {}
597
+ ...options.onWarn ? { onWarn: options.onWarn } : {},
598
+ ...options.mdl ? { mdl: options.mdl } : {}
589
599
  };
590
600
  let attached = 0;
591
601
  for (const prim of stage.Traverse()) {
@@ -609,12 +619,13 @@ function placeAtLocal(object, prim) {
609
619
  object.matrix.fromArray(computeLocalTransform(prim).matrix);
610
620
  object.matrixWorldNeedsUpdate = true;
611
621
  }
612
- function buildMeshMaterials(meshPrim, stage, textures, onWarn) {
622
+ function buildMeshMaterials(meshPrim, stage, textures, onWarn, mdl) {
613
623
  const subsets = stage ? getMaterialSubsets(meshPrim) : [];
614
- if (subsets.length === 0) return buildMeshMaterial(meshPrim, stage, textures, void 0, onWarn);
624
+ if (subsets.length === 0)
625
+ return buildMeshMaterial(meshPrim, stage, textures, void 0, onWarn, mdl);
615
626
  return [
616
- ...subsets.map((s) => buildMeshMaterial(meshPrim, stage, textures, s.prim, onWarn)),
617
- buildMeshMaterial(meshPrim, stage, textures, void 0, onWarn)
627
+ ...subsets.map((s) => buildMeshMaterial(meshPrim, stage, textures, s.prim, onWarn, mdl)),
628
+ buildMeshMaterial(meshPrim, stage, textures, void 0, onWarn, mdl)
618
629
  ];
619
630
  }
620
631
  function attachGprim(stage, linkPrim, meshPath, parent, kind, options) {
@@ -864,7 +875,7 @@ var ThreeUsdRobotLoader = class {
864
875
  const stage = await this.composeStageFromBytes(rootBytes, pkg.rootEntry, pkg.resolver);
865
876
  return { stage, baseUrl: pkg.rootEntry, resolver: pkg.resolver };
866
877
  }
867
- buildFromStage({ stage, baseUrl, resolver }) {
878
+ async buildFromStage({ stage, baseUrl, resolver }) {
868
879
  const robot = extractRobotDescription(stage, this.extractOptions());
869
880
  const tree = buildKinematicTree(robot);
870
881
  const robot3d = new ThreeUsdRobot(robot, tree, this.robotOptions());
@@ -882,20 +893,23 @@ var ThreeUsdRobotLoader = class {
882
893
  const textureProvider = this.options.loadTextures ?? true ? createTextureProvider(resolver, baseUrl) : void 0;
883
894
  const curveTubes = this.options.curveTubes ?? false;
884
895
  const onWarn = this.options.onWarn;
896
+ const mdl = await loadMdlModules(stage, resolver, baseUrl);
885
897
  if (loadVisuals || loadCollisions) {
886
898
  bindRobotMeshes(stage, robot3d, robot, {
887
899
  loadVisuals,
888
900
  loadCollisions,
889
901
  ...textureProvider ? { textureProvider } : {},
890
902
  ...curveTubes ? { curveTubes } : {},
891
- ...onWarn ? { onWarn } : {}
903
+ ...onWarn ? { onWarn } : {},
904
+ ...mdl ? { mdl } : {}
892
905
  });
893
906
  }
894
907
  if (loadScene) {
895
908
  bindSceneMeshes(stage, robot3d, robot, {
896
909
  ...textureProvider ? { textureProvider } : {},
897
910
  ...curveTubes ? { curveTubes } : {},
898
- ...onWarn ? { onWarn } : {}
911
+ ...onWarn ? { onWarn } : {},
912
+ ...mdl ? { mdl } : {}
899
913
  });
900
914
  }
901
915
  this.warnUnsupportedGprims(stage);
@@ -935,7 +949,8 @@ var ThreeUsdRobotLoader = class {
935
949
  ...this.options.worldUp ? { worldUp: this.options.worldUp } : { upAxisConversion: this.options.upAxisConversion ?? "auto" },
936
950
  ...this.options.clampJointLimits !== void 0 ? { clampJointLimits: this.options.clampJointLimits } : {},
937
951
  ...this.options.unitScale !== void 0 ? { unitScale: this.options.unitScale } : {},
938
- ...this.options.applyDriveTargetsAsInitialPose !== void 0 ? { applyInitialPose: this.options.applyDriveTargetsAsInitialPose } : {}
952
+ ...this.options.applyDriveTargetsAsInitialPose !== void 0 ? { applyInitialPose: this.options.applyDriveTargetsAsInitialPose } : {},
953
+ ...this.options.debugBakedTransforms !== void 0 ? { debugBakedTransforms: this.options.debugBakedTransforms } : {}
939
954
  };
940
955
  }
941
956
  extractOptions() {
@@ -947,5 +962,5 @@ var ThreeUsdRobotLoader = class {
947
962
  };
948
963
 
949
964
  export { ThreeUsdRobotLoader, bindRobotMeshes, bindSceneMeshes, buildGprimGeometry, buildGprimObject, buildMeshGeometry, buildMeshMaterial, createTextureProvider };
950
- //# sourceMappingURL=chunk-CDQPWWMQ.js.map
951
- //# sourceMappingURL=chunk-CDQPWWMQ.js.map
965
+ //# sourceMappingURL=chunk-5SVRTKS4.js.map
966
+ //# sourceMappingURL=chunk-5SVRTKS4.js.map