mujoco-react 10.3.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
@@ -860,6 +860,7 @@ Visualization overlays:
860
860
  | `showJoints` | `boolean?` | `false` | Joint axes |
861
861
  | `showContacts` | `boolean?` | `false` | Contact force vectors |
862
862
  | `showCameras` | `boolean?` | `false` | MuJoCo camera positions, frustums, and forward rays |
863
+ | `virtualCameras` | `DebugVirtualCamera[]?` | `[]` | Explicit virtual policy/offscreen render camera poses to draw alongside MuJoCo cameras |
863
864
  | `showCOM` | `boolean?` | `false` | Center of mass markers |
864
865
  | `showInertia` | `boolean?` | `false` | Inertia ellipsoids |
865
866
  | `showTendons` | `boolean?` | `false` | Tendon paths |
@@ -868,7 +869,7 @@ Visualization overlays:
868
869
  | `contactColor` | `string?` | `"#ff4444"` | Color for contact force arrows |
869
870
  | `comColor` | `string?` | `"#ff0000"` | Color for COM markers |
870
871
 
871
- Camera debug overlays use the live MuJoCo `cam_xpos` / `cam_xmat` frame, so the frustum matches mounted camera captures and follows parent body motion.
872
+ Camera debug overlays use the live MuJoCo `cam_xpos` / `cam_xmat` frame, so the frustum matches mounted camera captures and follows parent body motion. Use `virtualCameras` for synthetic fixed policy/offscreen render viewpoints that are not declared as MJCF `<camera>` elements. Debug camera overlays are excluded from camera captures.
872
873
 
873
874
  ### `<TendonRenderer />`
874
875
 
@@ -1094,29 +1095,20 @@ const video = useVideoRecorder({ fps: 30, mimeType: "video/webm" });
1094
1095
  // video.start(), video.stop() -> returns Blob
1095
1096
  ```
1096
1097
 
1097
- ### Frame Capture
1098
+ ### Camera Frames, Streams, and Tensors
1098
1099
 
1099
- 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**.
1100
1103
 
1101
- ```tsx
1102
- const apiRef = useRef<MujocoSimAPI>(null);
1103
-
1104
- const frame = await apiRef.current?.captureFrame({ type: "image/png" });
1105
- const blob = await apiRef.current?.captureFrameBlob({ type: "image/png" });
1106
- ```
1107
-
1108
- Use `useFrameCapture()` or the standalone `captureFrame()` helpers when you own
1109
- the canvas or want to capture a custom container.
1110
-
1111
- Use `captureCameraFrame()` / `captureCameraFrameBlob()` when dataset generation
1112
- needs an offscreen camera render at a stable resolution without moving the
1113
- user's interactive viewport. Pass `cameraName`, `siteName`, or `bodyName` to
1114
- record true MuJoCo-mounted camera frames; the returned image includes
1115
- `source.kind` so dataset pipelines can reject fallback or synthetic fixed poses.
1116
- For named MuJoCo cameras, set `mujocoCameraCompatibility` when you want the
1117
- Three.js offscreen camera to inherit the MJCF camera's `resolution`, `fovy`,
1118
- near/far clipping, and calibrated intrinsics when the WASM model exposes
1119
- `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.
1120
1112
 
1121
1113
  ```tsx
1122
1114
  const frame = await apiRef.current?.captureCameraFrame({
@@ -1127,138 +1119,84 @@ const frame = await apiRef.current?.captureCameraFrame({
1127
1119
  });
1128
1120
  ```
1129
1121
 
1130
- This is still rendered by Three.js. It is useful for browser policy payloads and
1131
- dataset debugging until you have native MuJoCo framebuffer bindings available.
1132
- For canonical policy/training captures, use `visualOverrides` to temporarily
1133
- override scene background, environment, fog, shadow maps, tone mapping, or color
1134
- space, and `renderIsolation` to render with an independent offscreen
1135
- `WebGLRenderer` instead of inheriting viewer renderer state.
1136
-
1137
- Use `recordCameraSequence()` / `useCameraSequenceRecorder()` to step policy
1138
- rollouts and capture synchronized per-camera frames from one or more MuJoCo
1139
- camera configs. Sequence recording requires mounted MuJoCo camera, site, or
1140
- body selectors by default; use still capture APIs for synthetic debug poses.
1141
-
1142
- For LeRobot-style datasets, prefer the named-camera wrapper. It resolves task
1143
- camera keys to MuJoCo cameras/sites/bodies, records the sequence, and returns
1144
- 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:
1145
1126
 
1146
1127
  ```tsx
1147
- const sequence = await recordMountedCameraFrameSequence(api, {
1148
- cameraKeys: ["head", "left_wrist", "right_wrist"],
1149
- aliases: {
1150
- head: [{ siteName: "head_camera_rgb_optical_frame" }],
1151
- left_wrist: [{ siteName: "left_wrist_camera_optical_frame" }],
1152
- right_wrist: [{ siteName: "right_wrist_camera_optical_frame" }],
1153
- },
1154
- defaults: {
1155
- width: 640,
1156
- height: 480,
1157
- type: "image/png",
1158
- fov: 45,
1159
- near: 0.01,
1160
- far: 100,
1161
- },
1162
- frames: 16,
1163
- stepsPerFrame: 1,
1164
- retainFrames: false,
1165
- requireMountedSources: true,
1166
- onFrame: ({ frameIndex, cameras }) => {
1167
- queueLeRobotImages(frameIndex, cameras);
1168
- },
1169
- });
1170
-
1171
- sequence.readiness.ready; // true when every requested stream resolved
1172
- sequence.plan.missingKeys; // unresolved task cameras, if requireAll is false
1173
- sequence.cameraSummaries.head.source; // mounted source provenance
1174
-
1175
- const manifest = createMountedCameraFrameSequenceManifest(sequence);
1176
- manifest.streamSummaries.head.complete; // per-camera frame coverage
1177
- manifest.status; // "complete", "partial", or "missing"
1128
+ function WristStream({ canvasRef }) {
1129
+ useCameraStream(canvasRef, { cameraName: "wrist_cam", width: 256, height: 192 });
1130
+ return null;
1131
+ }
1178
1132
  ```
1179
1133
 
1180
- `recordMountedCameraFrameSequence()` requires all requested `cameraKeys` by
1181
- default so dataset recording cannot silently omit a camera stream. Set
1182
- `requireAll: false` only for exploratory tooling that can tolerate partial
1183
- 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:
1184
1139
 
1185
- Use `createMountedCameraFrameSequenceManifest()` after recording to persist a
1186
- stable dataset-facing manifest with readiness, source targets, dimensions,
1187
- 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
+ ```
1188
1145
 
1189
- Inside `<MujocoCanvas>` children, `useMountedCameraSequenceRecorder()` exposes
1190
- the same planning and recording surface with React status/error/result state.
1191
- Use `checkReadiness()` before recording when the UI needs a preflight gate for
1192
- 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:
1193
1149
 
1194
1150
  ```tsx
1195
- function DatasetRecorder() {
1196
- const recorder = useMountedCameraSequenceRecorder({
1197
- defaults: { width: 640, height: 480, type: "image/png" },
1198
- aliases: {
1199
- head: { cameraName: "head" },
1200
- left_wrist: { siteName: "left_wrist_camera_optical_frame" },
1201
- right_wrist: { siteName: "right_wrist_camera_optical_frame" },
1202
- },
1203
- });
1204
-
1205
- async function recordDatasetEpisode() {
1206
- const cameraKeys = ["head", "left_wrist", "right_wrist"];
1207
- const readiness = recorder.checkReadiness(cameraKeys);
1208
- if (!readiness.ready) return;
1209
-
1210
- await recorder.record({
1211
- cameraKeys,
1212
- frames: 16,
1213
- retainFrames: false,
1214
- onFrame: ({ frameIndex, cameras }) => {
1215
- queueLeRobotImages(frameIndex, cameras);
1216
- },
1217
- });
1218
- }
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
+ });
1219
1157
 
1220
- return (
1221
- <button disabled={recorder.isRecording} onClick={recordDatasetEpisode}>
1222
- Record camera streams
1223
- </button>
1224
- );
1225
- }
1158
+ useAfterPhysicsStep(() => {
1159
+ const { tensors } = cams.capture();
1160
+ const wrist = new ort.Tensor("float32", tensors.wrist.data, [1, ...tensors.wrist.shape]);
1161
+ });
1226
1162
  ```
1227
1163
 
1228
- `recorder.readiness` keeps the latest preflight result, and
1229
- `recorder.result?.readiness` keeps the readiness that shipped with the most
1230
- 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.
1231
1170
 
1232
- Use `resolveMountedCameraFrameSource()` when dataset feature names need to map
1233
- to named MuJoCo cameras, sites, or bodies before recording. The helper honors
1234
- aliases first, then prefers camera matches over sites and bodies, then falls
1235
- back to exact body/site names. This keeps streams such as `wrist` mapped to a
1236
- real `<camera name="wrist_cam">` even when the model also has a body named
1237
- `wrist`. It also handles normalized/prefix/suffix matches such as
1238
- `left_wrist` -> `left_wrist_camera_optical_frame`, and token-contained
1239
- imported-model names such as `observation.images.head` ->
1240
- `robot_head_camera`. It returns both the capture selector and the mounted-source
1241
- 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:
1242
1176
 
1243
1177
  ```tsx
1244
- const resolved = resolveMountedCameraFrameSource("head", {
1245
- cameras: api.getCameras(),
1246
- sites: api.getSites(),
1247
- bodies: api.getBodies(),
1248
- });
1249
-
1250
- if (!resolved) throw new Error("head does not resolve to a MuJoCo source");
1251
-
1252
- 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 },
1253
1186
  frames: 16,
1254
- requireMountedSources: true,
1255
- cameras: [{ key: "head", width: 640, height: 480, ...resolved.selector }],
1187
+ onFrame: ({ frameIndex, cameras }) => queueLeRobotImages(frameIndex, cameras),
1256
1188
  });
1189
+
1190
+ const manifest = createMountedCameraFrameSequenceManifest(sequence); // dataset-facing manifest
1257
1191
  ```
1258
1192
 
1259
- Pass `aliases` when multiple MuJoCo resources could match a dataset stream key
1260
- or when the model uses names that do not share a normalized prefix/suffix with
1261
- 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.
1262
1200
 
1263
1201
  ### `useCtrlNoise(config)`
1264
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-6AZEFI6A.js.map
1296
- //# sourceMappingURL=chunk-6AZEFI6A.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