mujoco-react 10.4.0 → 10.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
@@ -1,5 +1,5 @@
1
1
  <p align="center">
2
- <img src="docs/images/mj2.gif" alt="mujoco-react demo" width="100%" />
2
+ <img width="941" height="598" alt="Screenshot 2026-06-24 at 5 44 13 PM" src="https://github.com/user-attachments/assets/09999f02-c093-473e-aaee-519fe2e4cdd4" />
3
3
  </p>
4
4
 
5
5
  # mujoco-react
@@ -1095,29 +1095,20 @@ const video = useVideoRecorder({ fps: 30, mimeType: "video/webm" });
1095
1095
  // video.start(), video.stop() -> returns Blob
1096
1096
  ```
1097
1097
 
1098
- ### Frame Capture
1098
+ ### Camera Frames, Streams, and Tensors
1099
1099
 
1100
- Capture dataset/debug stills from the rendered MuJoCo canvas:
1100
+ A camera pose the viewer camera, a MuJoCo `cameraName`/`siteName`/`bodyName`, or
1101
+ an explicit `position`+`lookAt` — can be turned into three outputs: a **snapshot**
1102
+ (PNG/JPEG), a **live on-screen stream**, or a **policy tensor**.
1101
1103
 
1102
- ```tsx
1103
- const apiRef = useRef<MujocoSimAPI>(null);
1104
-
1105
- const frame = await apiRef.current?.captureFrame({ type: "image/png" });
1106
- const blob = await apiRef.current?.captureFrameBlob({ type: "image/png" });
1107
- ```
1108
-
1109
- Use `useFrameCapture()` or the standalone `captureFrame()` helpers when you own
1110
- the canvas or want to capture a custom container.
1111
-
1112
- Use `captureCameraFrame()` / `captureCameraFrameBlob()` when dataset generation
1113
- needs an offscreen camera render at a stable resolution without moving the
1114
- user's interactive viewport. Pass `cameraName`, `siteName`, or `bodyName` to
1115
- record true MuJoCo-mounted camera frames; the returned image includes
1116
- `source.kind` so dataset pipelines can reject fallback or synthetic fixed poses.
1117
- For named MuJoCo cameras, set `mujocoCameraCompatibility` when you want the
1118
- Three.js offscreen camera to inherit the MJCF camera's `resolution`, `fovy`,
1119
- near/far clipping, and calibrated intrinsics when the WASM model exposes
1120
- `cam_intrinsic` plus `cam_sensorsize`:
1104
+ **Snapshots.** `captureFrame()`/`captureFrameBlob()` grab the live canvas (or use
1105
+ `useFrameCapture()` / the standalone `captureFrame()` when you own the canvas).
1106
+ `captureCameraFrame()`/`captureCameraFrameBlob()` render a chosen camera offscreen
1107
+ at a stable resolution without moving the viewport; pass `cameraName`/`siteName`/
1108
+ `bodyName` for true mounted frames (`source.kind` lets dataset pipelines reject
1109
+ fallback/synthetic poses). Set `mujocoCameraCompatibility` to inherit a MJCF
1110
+ camera's `resolution`, `fovy`, clipping, and calibrated intrinsics; use
1111
+ `visualOverrides`/`renderIsolation` for canonical training captures.
1121
1112
 
1122
1113
  ```tsx
1123
1114
  const frame = await apiRef.current?.captureCameraFrame({
@@ -1128,138 +1119,84 @@ const frame = await apiRef.current?.captureCameraFrame({
1128
1119
  });
1129
1120
  ```
1130
1121
 
1131
- This is still rendered by Three.js. It is useful for browser policy payloads and
1132
- dataset debugging until you have native MuJoCo framebuffer bindings available.
1133
- For canonical policy/training captures, use `visualOverrides` to temporarily
1134
- override scene background, environment, fog, shadow maps, tone mapping, or color
1135
- space, and `renderIsolation` to render with an independent offscreen
1136
- `WebGLRenderer` instead of inheriting viewer renderer state.
1137
-
1138
- Use `recordCameraSequence()` / `useCameraSequenceRecorder()` to step policy
1139
- rollouts and capture synchronized per-camera frames from one or more MuJoCo
1140
- camera configs. Sequence recording requires mounted MuJoCo camera, site, or
1141
- body selectors by default; use still capture APIs for synthetic debug poses.
1142
-
1143
- For LeRobot-style datasets, prefer the named-camera wrapper. It resolves task
1144
- camera keys to MuJoCo cameras/sites/bodies, records the sequence, and returns
1145
- the plan and readiness summary alongside frame provenance:
1122
+ **Live streams.** `useCameraStream(canvasRef, options)` renders a camera into a
1123
+ DOM `<canvas>` every frame (offscreen render blit: no PNG round-trip, no render-
1124
+ loop takeover, splat scenes stream safely). Call it inside `<MujocoCanvas>`; the
1125
+ canvas can live anywhere in your DOM:
1146
1126
 
1147
1127
  ```tsx
1148
- const sequence = await recordMountedCameraFrameSequence(api, {
1149
- cameraKeys: ["head", "left_wrist", "right_wrist"],
1150
- aliases: {
1151
- head: [{ siteName: "head_camera_rgb_optical_frame" }],
1152
- left_wrist: [{ siteName: "left_wrist_camera_optical_frame" }],
1153
- right_wrist: [{ siteName: "right_wrist_camera_optical_frame" }],
1154
- },
1155
- defaults: {
1156
- width: 640,
1157
- height: 480,
1158
- type: "image/png",
1159
- fov: 45,
1160
- near: 0.01,
1161
- far: 100,
1162
- },
1163
- frames: 16,
1164
- stepsPerFrame: 1,
1165
- retainFrames: false,
1166
- requireMountedSources: true,
1167
- onFrame: ({ frameIndex, cameras }) => {
1168
- queueLeRobotImages(frameIndex, cameras);
1169
- },
1170
- });
1171
-
1172
- sequence.readiness.ready; // true when every requested stream resolved
1173
- sequence.plan.missingKeys; // unresolved task cameras, if requireAll is false
1174
- sequence.cameraSummaries.head.source; // mounted source provenance
1175
-
1176
- const manifest = createMountedCameraFrameSequenceManifest(sequence);
1177
- manifest.streamSummaries.head.complete; // per-camera frame coverage
1178
- manifest.status; // "complete", "partial", or "missing"
1128
+ function WristStream({ canvasRef }) {
1129
+ useCameraStream(canvasRef, { cameraName: "wrist_cam", width: 256, height: 192 });
1130
+ return null;
1131
+ }
1179
1132
  ```
1180
1133
 
1181
- `recordMountedCameraFrameSequence()` requires all requested `cameraKeys` by
1182
- default so dataset recording cannot silently omit a camera stream. Set
1183
- `requireAll: false` only for exploratory tooling that can tolerate partial
1184
- camera coverage.
1134
+ For a transparent picture-in-picture overlay, use `<CameraView>` (or
1135
+ `useCameraViewport` to track your own element). It scissors into the main canvas —
1136
+ cheaper, but it takes over the render loop while mounted (incompatible with
1137
+ postprocessing, occluded by opaque DOM), so prefer `useCameraStream` for panel
1138
+ tiles:
1185
1139
 
1186
- Use `createMountedCameraFrameSequenceManifest()` after recording to persist a
1187
- stable dataset-facing manifest with readiness, source targets, dimensions,
1188
- first/last frame indices, timestamps, and per-camera missing-frame counts.
1140
+ ```tsx
1141
+ <MujocoCanvas config={config}>
1142
+ <CameraView cameraName="wrist_cam" style={{ right: 16, bottom: 16, width: 240, height: 180 }} />
1143
+ </MujocoCanvas>
1144
+ ```
1189
1145
 
1190
- Inside `<MujocoCanvas>` children, `useMountedCameraSequenceRecorder()` exposes
1191
- the same planning and recording surface with React status/error/result state.
1192
- Use `checkReadiness()` before recording when the UI needs a preflight gate for
1193
- LeRobot camera streams:
1146
+ **Policy tensors.** For in-browser inference, capture straight into a
1147
+ `Float32Array` no canvas, no PNG. `usePolicyCameraTensors` keeps one reusable
1148
+ session per camera and re-aims it to the live pose each step:
1194
1149
 
1195
1150
  ```tsx
1196
- function DatasetRecorder() {
1197
- const recorder = useMountedCameraSequenceRecorder({
1198
- defaults: { width: 640, height: 480, type: "image/png" },
1199
- aliases: {
1200
- head: { cameraName: "head" },
1201
- left_wrist: { siteName: "left_wrist_camera_optical_frame" },
1202
- right_wrist: { siteName: "right_wrist_camera_optical_frame" },
1203
- },
1204
- });
1205
-
1206
- async function recordDatasetEpisode() {
1207
- const cameraKeys = ["head", "left_wrist", "right_wrist"];
1208
- const readiness = recorder.checkReadiness(cameraKeys);
1209
- if (!readiness.ready) return;
1210
-
1211
- await recorder.record({
1212
- cameraKeys,
1213
- frames: 16,
1214
- retainFrames: false,
1215
- onFrame: ({ frameIndex, cameras }) => {
1216
- queueLeRobotImages(frameIndex, cameras);
1217
- },
1218
- });
1219
- }
1151
+ const cams = usePolicyCameraTensors({
1152
+ streams: [
1153
+ { key: "wrist", cameraName: "wrist_cam", width: 96, height: 96, layout: "CHW" },
1154
+ { key: "front", cameraName: "front", width: 96, height: 96, layout: "CHW" },
1155
+ ],
1156
+ });
1220
1157
 
1221
- return (
1222
- <button disabled={recorder.isRecording} onClick={recordDatasetEpisode}>
1223
- Record camera streams
1224
- </button>
1225
- );
1226
- }
1158
+ useAfterPhysicsStep(() => {
1159
+ const { tensors } = cams.capture();
1160
+ const wrist = new ort.Tensor("float32", tensors.wrist.data, [1, ...tensors.wrist.shape]);
1161
+ });
1227
1162
  ```
1228
1163
 
1229
- `recorder.readiness` keeps the latest preflight result, and
1230
- `recorder.result?.readiness` keeps the readiness that shipped with the most
1231
- recent recording.
1164
+ Use `usePolicyCameraTensorsFromMountedStreams` for dataset-name resolution,
1165
+ `captureCameraFrameTensor()` for one-offs, or `createCameraFrameCaptureSession()`
1166
+ + `pixelsToPolicyImageTensor()` for lower-level control. The optional
1167
+ `mujoco-react/onnx` entry point wraps ONNX Runtime Web: `createOnnxPolicySession()`
1168
+ loads a manifest plus model, and `onnxTensorToPolicyActionChunk()` decodes the
1169
+ output into action chunks.
1232
1170
 
1233
- Use `resolveMountedCameraFrameSource()` when dataset feature names need to map
1234
- to named MuJoCo cameras, sites, or bodies before recording. The helper honors
1235
- aliases first, then prefers camera matches over sites and bodies, then falls
1236
- back to exact body/site names. This keeps streams such as `wrist` mapped to a
1237
- real `<camera name="wrist_cam">` even when the model also has a body named
1238
- `wrist`. It also handles normalized/prefix/suffix matches such as
1239
- `left_wrist` -> `left_wrist_camera_optical_frame`, and token-contained
1240
- imported-model names such as `observation.images.head` ->
1241
- `robot_head_camera`. It returns both the capture selector and the mounted-source
1242
- provenance that should be stored beside the dataset:
1171
+ **Dataset recording.** `recordMountedCameraFrameSequence()` steps a rollout and
1172
+ captures synchronized per-camera frames, resolving LeRobot-style task camera keys
1173
+ to MuJoCo cameras/sites/bodies. It requires every requested `cameraKey` by default
1174
+ (set `requireAll: false` for exploratory tooling) and returns a plan, readiness,
1175
+ and per-camera source provenance:
1243
1176
 
1244
1177
  ```tsx
1245
- const resolved = resolveMountedCameraFrameSource("head", {
1246
- cameras: api.getCameras(),
1247
- sites: api.getSites(),
1248
- bodies: api.getBodies(),
1249
- });
1250
-
1251
- if (!resolved) throw new Error("head does not resolve to a MuJoCo source");
1252
-
1253
- await api.recordCameraSequence({
1178
+ const sequence = await recordMountedCameraFrameSequence(api, {
1179
+ cameraKeys: ["head", "left_wrist", "right_wrist"],
1180
+ aliases: {
1181
+ head: [{ siteName: "head_camera_rgb_optical_frame" }],
1182
+ left_wrist: [{ siteName: "left_wrist_camera_optical_frame" }],
1183
+ right_wrist: [{ siteName: "right_wrist_camera_optical_frame" }],
1184
+ },
1185
+ defaults: { width: 640, height: 480, type: "image/png", fov: 45 },
1254
1186
  frames: 16,
1255
- requireMountedSources: true,
1256
- cameras: [{ key: "head", width: 640, height: 480, ...resolved.selector }],
1187
+ onFrame: ({ frameIndex, cameras }) => queueLeRobotImages(frameIndex, cameras),
1257
1188
  });
1189
+
1190
+ const manifest = createMountedCameraFrameSequenceManifest(sequence); // dataset-facing manifest
1258
1191
  ```
1259
1192
 
1260
- Pass `aliases` when multiple MuJoCo resources could match a dataset stream key
1261
- or when the model uses names that do not share a normalized prefix/suffix with
1262
- the LeRobot camera feature.
1193
+ Inside `<MujocoCanvas>`, `useMountedCameraSequenceRecorder()` exposes the same
1194
+ planning/recording with React status and a `checkReadiness()` preflight gate.
1195
+ `resolveMountedCameraFrameSource()` maps a single dataset feature name to a MuJoCo
1196
+ source (aliases first, then camera > site > body, then normalized prefix/suffix
1197
+ matches) when you need the selector before recording. The lower-level
1198
+ `recordCameraSequence()` / `useCameraSequenceRecorder()` take explicit camera
1199
+ configs.
1263
1200
 
1264
1201
  ### `useCtrlNoise(config)`
1265
1202
 
@@ -85,6 +85,95 @@ var SplatEnvironmentReadinessStatus = {
85
85
  UnsupportedFormat: "unsupported-format",
86
86
  Ready: "ready"
87
87
  };
88
+
89
+ // src/policyImageTensors.ts
90
+ function resolveTensorOptions(options) {
91
+ return {
92
+ channels: 3,
93
+ layout: "CHW",
94
+ range: [0, 1],
95
+ ...options
96
+ };
97
+ }
98
+ function normalizeChannel(value, range) {
99
+ const [min, max] = range;
100
+ if (min === 0 && max === 255) return value;
101
+ return min + value / 255 * (max - min);
102
+ }
103
+ function pixelsToPolicyImageTensor(pixels, options) {
104
+ const resolved = resolveTensorOptions(options);
105
+ const { width, height, channels, layout, range } = resolved;
106
+ const expected = width * height * 4;
107
+ if (pixels.length < expected) {
108
+ throw new Error(
109
+ `Pixel buffer of length ${pixels.length} is too small for ${width}x${height} RGBA data (${expected} bytes).`
110
+ );
111
+ }
112
+ const flipY = options.sourceOrigin === "bottom-left";
113
+ const flipX = options.flipX ?? false;
114
+ const pixelCount = width * height;
115
+ const data = new Float32Array(pixelCount * channels);
116
+ for (let y = 0; y < height; y += 1) {
117
+ const sourceY = flipY ? height - y - 1 : y;
118
+ for (let x = 0; x < width; x += 1) {
119
+ const sourceX = flipX ? width - x - 1 : x;
120
+ const source = (sourceY * width + sourceX) * 4;
121
+ const target = y * width + x;
122
+ for (let channel = 0; channel < channels; channel += 1) {
123
+ const value = normalizeChannel(pixels[source + channel], range);
124
+ if (layout === "CHW") {
125
+ data[channel * pixelCount + target] = value;
126
+ } else {
127
+ data[target * channels + channel] = value;
128
+ }
129
+ }
130
+ }
131
+ }
132
+ return {
133
+ data,
134
+ shape: layout === "CHW" ? [channels, height, width] : [height, width, channels],
135
+ width,
136
+ height,
137
+ channels,
138
+ layout,
139
+ range
140
+ };
141
+ }
142
+ function imageDataToPolicyImageTensor(imageData, options) {
143
+ const resolved = resolveTensorOptions(options);
144
+ if (imageData.width !== resolved.width || imageData.height !== resolved.height) {
145
+ throw new Error(
146
+ `ImageData size ${imageData.width}x${imageData.height} does not match tensor size ${resolved.width}x${resolved.height}.`
147
+ );
148
+ }
149
+ return pixelsToPolicyImageTensor(imageData.data, {
150
+ ...resolved,
151
+ sourceOrigin: "top-left"
152
+ });
153
+ }
154
+ async function decodeImageSource(dataUrl) {
155
+ const image = new Image();
156
+ image.decoding = "async";
157
+ image.src = dataUrl;
158
+ await image.decode();
159
+ return image;
160
+ }
161
+ async function dataUrlToPolicyImageTensor(dataUrl, options) {
162
+ const resolved = resolveTensorOptions(options);
163
+ const image = await decodeImageSource(dataUrl);
164
+ const canvas = document.createElement("canvas");
165
+ canvas.width = resolved.width;
166
+ canvas.height = resolved.height;
167
+ const context = canvas.getContext("2d", { willReadFrequently: true });
168
+ if (!context) {
169
+ throw new Error("Unable to create a 2D canvas context for policy image tensor conversion.");
170
+ }
171
+ context.drawImage(image, 0, 0, resolved.width, resolved.height);
172
+ return imageDataToPolicyImageTensor(
173
+ context.getImageData(0, 0, resolved.width, resolved.height),
174
+ resolved
175
+ );
176
+ }
88
177
  var CAMERA_FRAME_CAPTURE_RENDER_USER_DATA_KEY = "mujocoReactCameraFrameCaptureRender";
89
178
  var CAMERA_FRAME_CAPTURE_PRE_RENDER_USER_DATA_KEY = "mujocoReactCameraFrameCapturePreRender";
90
179
  var CAPTURE_EXCLUDE_KEY = "mujoco.capture.exclude";
@@ -465,7 +554,7 @@ function createCameraFrameCaptureSession(renderer, scene, fallbackCamera, option
465
554
  );
466
555
  return captureOptions;
467
556
  }
468
- function renderPreparedCapture(captureOptions) {
557
+ function renderCaptureToTarget(captureOptions, readback) {
469
558
  const previousState = saveRendererState(sessionRenderer);
470
559
  const previousSceneState = applyCaptureVisualOverrides(
471
560
  sessionRenderer,
@@ -494,6 +583,15 @@ function createCameraFrameCaptureSession(renderer, scene, fallbackCamera, option
494
583
  }
495
584
  sessionRenderer.clear();
496
585
  sessionRenderer.render(scene, camera);
586
+ readback();
587
+ } finally {
588
+ restoreObjectVisibility(hidden);
589
+ if (previousSceneState) restoreSceneVisualState(scene, previousSceneState);
590
+ restoreRendererState(sessionRenderer, previousState);
591
+ }
592
+ }
593
+ function renderPreparedCapture(captureOptions) {
594
+ renderCaptureToTarget(captureOptions, () => {
497
595
  readRenderTargetToCanvas(
498
596
  sessionRenderer,
499
597
  target,
@@ -506,22 +604,44 @@ function createCameraFrameCaptureSession(renderer, scene, fallbackCamera, option
506
604
  sessionRenderer.outputColorSpace,
507
605
  captureOptions.flipX ?? false
508
606
  );
509
- return {
510
- canvas,
511
- camera,
512
- width,
513
- height,
514
- source: getCameraFrameCaptureSource(captureOptions)
515
- };
516
- } finally {
517
- restoreObjectVisibility(hidden);
518
- if (previousSceneState) restoreSceneVisualState(scene, previousSceneState);
519
- restoreRendererState(sessionRenderer, previousState);
520
- }
607
+ });
608
+ return {
609
+ canvas,
610
+ camera,
611
+ width,
612
+ height,
613
+ source: getCameraFrameCaptureSource(captureOptions)
614
+ };
521
615
  }
522
616
  function capture(nextOptions = {}) {
523
617
  return renderPreparedCapture(resolveCaptureOptions(nextOptions));
524
618
  }
619
+ function capturePixels(nextOptions = {}) {
620
+ const captureOptions = resolveCaptureOptions(nextOptions);
621
+ renderCaptureToTarget(captureOptions, () => {
622
+ sessionRenderer.readRenderTargetPixels(target, 0, 0, width, height, pixels);
623
+ });
624
+ return {
625
+ pixels,
626
+ camera,
627
+ width,
628
+ height,
629
+ source: getCameraFrameCaptureSource(captureOptions)
630
+ };
631
+ }
632
+ function captureTensor(nextOptions = {}) {
633
+ const result = capturePixels(nextOptions);
634
+ const tensor = pixelsToPolicyImageTensor(pixels, {
635
+ width,
636
+ height,
637
+ channels: nextOptions.channels,
638
+ layout: nextOptions.layout,
639
+ range: nextOptions.range,
640
+ sourceOrigin: "bottom-left",
641
+ flipX: nextOptions.flipX
642
+ });
643
+ return { ...tensor, camera, source: result.source };
644
+ }
525
645
  async function captureAsync(nextOptions = {}) {
526
646
  const captureOptions = resolveCaptureOptions(nextOptions);
527
647
  runCapturePreRenderHooks(scene);
@@ -594,6 +714,8 @@ function createCameraFrameCaptureSession(renderer, scene, fallbackCamera, option
594
714
  height,
595
715
  capture,
596
716
  captureAsync,
717
+ capturePixels,
718
+ captureTensor,
597
719
  captureDataUrl(nextOptions = {}) {
598
720
  const type = nextOptions.type ?? options.type ?? "image/png";
599
721
  const result = capture(nextOptions);
@@ -686,6 +808,19 @@ async function captureCameraFrameBlob(renderer, scene, fallbackCamera, options =
686
808
  session.dispose();
687
809
  }
688
810
  }
811
+ function captureCameraFrameTensor(renderer, scene, fallbackCamera, options = {}) {
812
+ const session = createCameraFrameCaptureSession(
813
+ renderer,
814
+ scene,
815
+ fallbackCamera,
816
+ options
817
+ );
818
+ try {
819
+ return session.captureTensor(options);
820
+ } finally {
821
+ session.dispose();
822
+ }
823
+ }
689
824
  var DEFAULT_BACKGROUND = "#181a1f";
690
825
  function ScenarioLighting({
691
826
  preset = "studio",
@@ -1284,6 +1419,12 @@ function clamp01(value) {
1284
1419
  * @license
1285
1420
  * SPDX-License-Identifier: Apache-2.0
1286
1421
  */
1422
+ /**
1423
+ * @license
1424
+ * SPDX-License-Identifier: Apache-2.0
1425
+ *
1426
+ * Helpers for turning browser camera captures into policy image tensors.
1427
+ */
1287
1428
  /**
1288
1429
  * @license
1289
1430
  * SPDX-License-Identifier: Apache-2.0
@@ -1291,6 +1432,6 @@ function clamp01(value) {
1291
1432
  * Offscreen camera-frame capture for R3F/MuJoCo scenes.
1292
1433
  */
1293
1434
 
1294
- export { CAMERA_FRAME_CAPTURE_PRE_RENDER_USER_DATA_KEY, CAMERA_FRAME_CAPTURE_RENDER_USER_DATA_KEY, CAPTURE_EXCLUDE_KEY, ModelActuators, ModelBodies, ModelCameras, ModelGeoms, ModelJoints, ModelKeyframes, ModelResources, ModelSensors, ModelSites, ScenarioLighting, SplatEnvironment, SplatEnvironmentReadinessStatus, VisualScenarioEffects, captureCameraFrame, captureCameraFrameBlob, createCameraFrameCaptureSession, createPairedSplatEnvironment, createSparkSplatViewerUrl, createSplatEnvironmentUserData, createSplatSceneConfig, createVisualScenarioExecutionContext, getContact, getScenarioBackground, getScenarioCameraPosition, getSplatEnvironmentReadiness, registerModelResources, renderCameraFrameToCanvas, useSplatEnvironment, useSplatSceneConfig, useVisualScenarioEffects, useVisualScenarioExecutionContext, withContacts, withSplatEnvironment };
1295
- //# sourceMappingURL=chunk-FBXXXPLQ.js.map
1296
- //# sourceMappingURL=chunk-FBXXXPLQ.js.map
1435
+ export { CAMERA_FRAME_CAPTURE_PRE_RENDER_USER_DATA_KEY, CAMERA_FRAME_CAPTURE_RENDER_USER_DATA_KEY, CAPTURE_EXCLUDE_KEY, ModelActuators, ModelBodies, ModelCameras, ModelGeoms, ModelJoints, ModelKeyframes, ModelResources, ModelSensors, ModelSites, ScenarioLighting, SplatEnvironment, SplatEnvironmentReadinessStatus, VisualScenarioEffects, captureCameraFrame, captureCameraFrameBlob, captureCameraFrameTensor, createCameraFrameCaptureSession, createCaptureCamera, createPairedSplatEnvironment, createSparkSplatViewerUrl, createSplatEnvironmentUserData, createSplatSceneConfig, createVisualScenarioExecutionContext, dataUrlToPolicyImageTensor, getContact, getScenarioBackground, getScenarioCameraPosition, getSplatEnvironmentReadiness, imageDataToPolicyImageTensor, pixelsToPolicyImageTensor, prepareCaptureCamera, registerModelResources, renderCameraFrameToCanvas, useSplatEnvironment, useSplatSceneConfig, useVisualScenarioEffects, useVisualScenarioExecutionContext, withContacts, withSplatEnvironment };
1436
+ //# sourceMappingURL=chunk-KHZ5U36J.js.map
1437
+ //# sourceMappingURL=chunk-KHZ5U36J.js.map