mujoco-react 9.4.0 → 9.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 +22 -6
- package/dist/{chunk-VDSEPZYQ.js → chunk-6MOK6ZWB.js} +397 -4
- package/dist/chunk-6MOK6ZWB.js.map +1 -0
- package/dist/index.d.ts +13 -4
- package/dist/index.js +360 -430
- package/dist/index.js.map +1 -1
- package/dist/spark.d.ts +27 -3
- package/dist/spark.js +156 -3
- package/dist/spark.js.map +1 -1
- package/dist/{types-BuJ4boaq.d.ts → types-BDB9QT6Z.d.ts} +1 -0
- package/package.json +1 -1
- package/src/components/ContactMarkers.tsx +8 -1
- package/src/components/Debug.tsx +154 -3
- package/src/components/DragInteraction.tsx +2 -0
- package/src/components/IkGizmo.tsx +5 -1
- package/src/core/MujocoSimProvider.tsx +5 -5
- package/src/index.ts +1 -0
- package/src/rendering/cameraFrameCapture.ts +259 -28
- package/src/rendering/cameraFrameSource.ts +10 -2
- package/src/spark.tsx +241 -1
- package/src/types.ts +1 -0
- package/dist/chunk-VDSEPZYQ.js.map +0 -1
package/README.md
CHANGED
|
@@ -297,6 +297,16 @@ function Scene() {
|
|
|
297
297
|
|
|
298
298
|
`SparkSplatEnvironment` currently renders `.spz` assets. Use the renderer-agnostic
|
|
299
299
|
`SplatEnvironment` for `.ply`/`.splat` metadata or when wiring a different renderer.
|
|
300
|
+
Tune live rendering and snapshots separately with `renderTuning` and
|
|
301
|
+
`captureTuning`:
|
|
302
|
+
|
|
303
|
+
```tsx
|
|
304
|
+
<SparkSplatEnvironment
|
|
305
|
+
{...splat.props}
|
|
306
|
+
renderTuning={{ lodSplatScale: 0.75, minSortIntervalMs: 50 }}
|
|
307
|
+
captureTuning={{ lodSplatScale: 1.4, lodRenderScale: 0.45, maxWarmupFrames: 6 }}
|
|
308
|
+
/>
|
|
309
|
+
```
|
|
300
310
|
|
|
301
311
|
## Write Controllers
|
|
302
312
|
|
|
@@ -850,6 +860,7 @@ Visualization overlays:
|
|
|
850
860
|
| `showSites` | `boolean?` | `false` | Site markers |
|
|
851
861
|
| `showJoints` | `boolean?` | `false` | Joint axes |
|
|
852
862
|
| `showContacts` | `boolean?` | `false` | Contact force vectors |
|
|
863
|
+
| `showCameras` | `boolean?` | `false` | MuJoCo camera positions, frustums, and forward rays |
|
|
853
864
|
| `showCOM` | `boolean?` | `false` | Center of mass markers |
|
|
854
865
|
| `showInertia` | `boolean?` | `false` | Inertia ellipsoids |
|
|
855
866
|
| `showTendons` | `boolean?` | `false` | Tendon paths |
|
|
@@ -858,6 +869,8 @@ Visualization overlays:
|
|
|
858
869
|
| `contactColor` | `string?` | `"#ff4444"` | Color for contact force arrows |
|
|
859
870
|
| `comColor` | `string?` | `"#ff0000"` | Color for COM markers |
|
|
860
871
|
|
|
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.
|
|
873
|
+
|
|
861
874
|
### `<TendonRenderer />`
|
|
862
875
|
|
|
863
876
|
Renders tendons as tube geometry from wrap paths.
|
|
@@ -1198,12 +1211,15 @@ function DatasetRecorder() {
|
|
|
1198
1211
|
recent recording.
|
|
1199
1212
|
|
|
1200
1213
|
Use `resolveMountedCameraFrameSource()` when dataset feature names need to map
|
|
1201
|
-
to named MuJoCo cameras, sites, or bodies before recording. The helper
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
`
|
|
1206
|
-
|
|
1214
|
+
to named MuJoCo cameras, sites, or bodies before recording. The helper honors
|
|
1215
|
+
aliases first, then prefers camera matches over sites and bodies, then falls
|
|
1216
|
+
back to exact body/site names. This keeps streams such as `wrist` mapped to a
|
|
1217
|
+
real `<camera name="wrist_cam">` even when the model also has a body named
|
|
1218
|
+
`wrist`. It also handles normalized/prefix/suffix matches such as
|
|
1219
|
+
`left_wrist` -> `left_wrist_camera_optical_frame`, and token-contained
|
|
1220
|
+
imported-model names such as `observation.images.head` ->
|
|
1221
|
+
`robot_head_camera`. It returns both the capture selector and the mounted-source
|
|
1222
|
+
provenance that should be stored beside the dataset:
|
|
1207
1223
|
|
|
1208
1224
|
```tsx
|
|
1209
1225
|
const resolved = resolveMountedCameraFrameSource("head", {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import * as THREE from 'three';
|
|
1
2
|
import { useThree } from '@react-three/fiber';
|
|
2
3
|
import { useMemo, useEffect } from 'react';
|
|
3
|
-
import * as THREE from 'three';
|
|
4
4
|
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
5
5
|
|
|
6
6
|
// src/types.ts
|
|
@@ -85,6 +85,393 @@ var SplatEnvironmentReadinessStatus = {
|
|
|
85
85
|
UnsupportedFormat: "unsupported-format",
|
|
86
86
|
Ready: "ready"
|
|
87
87
|
};
|
|
88
|
+
var CAMERA_FRAME_CAPTURE_RENDER_USER_DATA_KEY = "mujocoReactCameraFrameCaptureRender";
|
|
89
|
+
var CAPTURE_EXCLUDE_KEY = "mujoco.capture.exclude";
|
|
90
|
+
function toVector3(value, fallback) {
|
|
91
|
+
if (!value) return fallback.clone();
|
|
92
|
+
return value instanceof THREE.Vector3 ? value.clone() : new THREE.Vector3(value[0], value[1], value[2]);
|
|
93
|
+
}
|
|
94
|
+
function applyCameraPose(camera, options, fallbackCamera) {
|
|
95
|
+
camera.position.copy(toVector3(options.position, fallbackCamera.position));
|
|
96
|
+
camera.up.copy(toVector3(options.up, fallbackCamera.up));
|
|
97
|
+
if (options.quaternion) {
|
|
98
|
+
if (options.quaternion instanceof THREE.Quaternion) {
|
|
99
|
+
camera.quaternion.copy(options.quaternion);
|
|
100
|
+
} else {
|
|
101
|
+
camera.quaternion.set(
|
|
102
|
+
options.quaternion[0],
|
|
103
|
+
options.quaternion[1],
|
|
104
|
+
options.quaternion[2],
|
|
105
|
+
options.quaternion[3]
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
} else if (options.lookAt) {
|
|
109
|
+
camera.lookAt(toVector3(options.lookAt, new THREE.Vector3()));
|
|
110
|
+
} else {
|
|
111
|
+
camera.quaternion.copy(fallbackCamera.quaternion);
|
|
112
|
+
}
|
|
113
|
+
camera.updateMatrixWorld();
|
|
114
|
+
}
|
|
115
|
+
function createCaptureCamera(options, fallbackCamera, width, height) {
|
|
116
|
+
const camera = options.camera ? options.camera.clone() : fallbackCamera instanceof THREE.PerspectiveCamera ? fallbackCamera.clone() : new THREE.PerspectiveCamera(45, width / height, 0.01, 100);
|
|
117
|
+
if (camera instanceof THREE.PerspectiveCamera) {
|
|
118
|
+
camera.aspect = width / height;
|
|
119
|
+
camera.fov = options.fov ?? camera.fov;
|
|
120
|
+
camera.near = options.near ?? camera.near;
|
|
121
|
+
camera.far = options.far ?? camera.far;
|
|
122
|
+
camera.updateProjectionMatrix();
|
|
123
|
+
}
|
|
124
|
+
applyCameraPose(camera, options, fallbackCamera);
|
|
125
|
+
return camera;
|
|
126
|
+
}
|
|
127
|
+
function getCaptureDimensions(renderer, options) {
|
|
128
|
+
const width = Math.max(
|
|
129
|
+
1,
|
|
130
|
+
Math.floor(options.width ?? renderer.domElement.width)
|
|
131
|
+
);
|
|
132
|
+
const height = Math.max(
|
|
133
|
+
1,
|
|
134
|
+
Math.floor(options.height ?? renderer.domElement.height)
|
|
135
|
+
);
|
|
136
|
+
return { width, height };
|
|
137
|
+
}
|
|
138
|
+
function prepareCaptureCamera(camera, options, fallbackCamera, width, height) {
|
|
139
|
+
if (options.camera) {
|
|
140
|
+
camera.copy(options.camera);
|
|
141
|
+
}
|
|
142
|
+
if (camera instanceof THREE.PerspectiveCamera) {
|
|
143
|
+
camera.aspect = width / height;
|
|
144
|
+
camera.fov = options.fov ?? camera.fov;
|
|
145
|
+
camera.near = options.near ?? camera.near;
|
|
146
|
+
camera.far = options.far ?? camera.far;
|
|
147
|
+
camera.updateProjectionMatrix();
|
|
148
|
+
}
|
|
149
|
+
applyCameraPose(camera, options, fallbackCamera);
|
|
150
|
+
}
|
|
151
|
+
function readRenderTargetToCanvas(renderer, target, canvas, context, pixels, imageData, width, height, outputColorSpace) {
|
|
152
|
+
renderer.readRenderTargetPixels(target, 0, 0, width, height, pixels);
|
|
153
|
+
const rowBytes = width * 4;
|
|
154
|
+
const encodeSrgb = outputColorSpace === THREE.SRGBColorSpace;
|
|
155
|
+
for (let y = 0; y < height; y += 1) {
|
|
156
|
+
const sourceStart = (height - y - 1) * rowBytes;
|
|
157
|
+
const targetStart = y * rowBytes;
|
|
158
|
+
const row = pixels.subarray(sourceStart, sourceStart + rowBytes);
|
|
159
|
+
if (!encodeSrgb) {
|
|
160
|
+
imageData.data.set(row, targetStart);
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
for (let x = 0; x < rowBytes; x += 4) {
|
|
164
|
+
const pixelOffset = targetStart + x;
|
|
165
|
+
imageData.data[pixelOffset] = linearByteToSrgbByte(row[x]);
|
|
166
|
+
imageData.data[pixelOffset + 1] = linearByteToSrgbByte(row[x + 1]);
|
|
167
|
+
imageData.data[pixelOffset + 2] = linearByteToSrgbByte(row[x + 2]);
|
|
168
|
+
imageData.data[pixelOffset + 3] = row[x + 3];
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
context.putImageData(imageData, 0, 0);
|
|
172
|
+
return canvas;
|
|
173
|
+
}
|
|
174
|
+
function linearByteToSrgbByte(value) {
|
|
175
|
+
const normalized = value / 255;
|
|
176
|
+
const encoded = normalized <= 31308e-7 ? normalized * 12.92 : 1.055 * Math.pow(normalized, 1 / 2.4) - 0.055;
|
|
177
|
+
return Math.min(255, Math.max(0, Math.round(encoded * 255)));
|
|
178
|
+
}
|
|
179
|
+
function readPixelsToCanvas(pixels, context, imageData, width, height, flipY = true) {
|
|
180
|
+
const rowBytes = width * 4;
|
|
181
|
+
for (let y = 0; y < height; y += 1) {
|
|
182
|
+
const sourceY = flipY ? height - y - 1 : y;
|
|
183
|
+
const sourceStart = sourceY * rowBytes;
|
|
184
|
+
const targetStart = y * rowBytes;
|
|
185
|
+
imageData.data.set(
|
|
186
|
+
pixels.subarray(sourceStart, sourceStart + rowBytes),
|
|
187
|
+
targetStart
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
context.putImageData(imageData, 0, 0);
|
|
191
|
+
}
|
|
192
|
+
function hideExcludedCaptureObjects(scene) {
|
|
193
|
+
const hidden = [];
|
|
194
|
+
scene.traverse((object) => {
|
|
195
|
+
if (!object.visible) return;
|
|
196
|
+
if (!object.userData[CAPTURE_EXCLUDE_KEY]) return;
|
|
197
|
+
hidden.push({ object, visible: object.visible });
|
|
198
|
+
object.visible = false;
|
|
199
|
+
});
|
|
200
|
+
return hidden;
|
|
201
|
+
}
|
|
202
|
+
function restoreObjectVisibility(hidden) {
|
|
203
|
+
for (const { object, visible } of hidden) {
|
|
204
|
+
object.visible = visible;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
function getCameraFrameCaptureSource(options) {
|
|
208
|
+
if (options.source) return options.source;
|
|
209
|
+
if (options.cameraName) {
|
|
210
|
+
return { kind: "mujoco-camera", cameraName: options.cameraName };
|
|
211
|
+
}
|
|
212
|
+
if (options.siteName) {
|
|
213
|
+
return { kind: "mujoco-site", siteName: options.siteName };
|
|
214
|
+
}
|
|
215
|
+
if (options.bodyName) {
|
|
216
|
+
return { kind: "mujoco-body", bodyName: options.bodyName };
|
|
217
|
+
}
|
|
218
|
+
if (options.camera) return { kind: "custom-camera" };
|
|
219
|
+
if (options.position || options.lookAt || options.quaternion) {
|
|
220
|
+
return { kind: "explicit-pose" };
|
|
221
|
+
}
|
|
222
|
+
return { kind: "fallback-camera" };
|
|
223
|
+
}
|
|
224
|
+
function saveRendererState(renderer) {
|
|
225
|
+
const viewport = new THREE.Vector4();
|
|
226
|
+
const scissor = new THREE.Vector4();
|
|
227
|
+
const clearColor = new THREE.Color();
|
|
228
|
+
renderer.getViewport(viewport);
|
|
229
|
+
renderer.getScissor(scissor);
|
|
230
|
+
renderer.getClearColor(clearColor);
|
|
231
|
+
return {
|
|
232
|
+
target: renderer.getRenderTarget(),
|
|
233
|
+
xrEnabled: renderer.xr.enabled,
|
|
234
|
+
viewport,
|
|
235
|
+
scissor,
|
|
236
|
+
scissorTest: renderer.getScissorTest(),
|
|
237
|
+
clearColor,
|
|
238
|
+
clearAlpha: renderer.getClearAlpha(),
|
|
239
|
+
autoClear: renderer.autoClear
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
function restoreRendererState(renderer, state) {
|
|
243
|
+
renderer.setRenderTarget(state.target);
|
|
244
|
+
renderer.xr.enabled = state.xrEnabled;
|
|
245
|
+
renderer.setViewport(state.viewport);
|
|
246
|
+
renderer.setScissor(state.scissor);
|
|
247
|
+
renderer.setScissorTest(state.scissorTest);
|
|
248
|
+
renderer.setClearColor(state.clearColor, state.clearAlpha);
|
|
249
|
+
renderer.autoClear = state.autoClear;
|
|
250
|
+
}
|
|
251
|
+
function getCaptureRenderer(scene) {
|
|
252
|
+
const renderers = [];
|
|
253
|
+
scene.traverse((object) => {
|
|
254
|
+
if (renderers.length) return;
|
|
255
|
+
const render = object.userData[CAMERA_FRAME_CAPTURE_RENDER_USER_DATA_KEY];
|
|
256
|
+
if (typeof render === "function") renderers.push(render);
|
|
257
|
+
});
|
|
258
|
+
return renderers[0] ?? null;
|
|
259
|
+
}
|
|
260
|
+
function createCameraFrameCaptureSession(renderer, scene, fallbackCamera, options = {}) {
|
|
261
|
+
const { width, height } = getCaptureDimensions(renderer, options);
|
|
262
|
+
const camera = createCaptureCamera(options, fallbackCamera, width, height);
|
|
263
|
+
const target = new THREE.WebGLRenderTarget(width, height, {
|
|
264
|
+
format: THREE.RGBAFormat,
|
|
265
|
+
type: THREE.UnsignedByteType
|
|
266
|
+
});
|
|
267
|
+
const canvas = document.createElement("canvas");
|
|
268
|
+
canvas.width = width;
|
|
269
|
+
canvas.height = height;
|
|
270
|
+
const context = canvas.getContext("2d");
|
|
271
|
+
if (!context) {
|
|
272
|
+
target.dispose();
|
|
273
|
+
throw new Error("Unable to create a 2D canvas for camera frame capture.");
|
|
274
|
+
}
|
|
275
|
+
const drawContext = context;
|
|
276
|
+
const pixels = new Uint8Array(width * height * 4);
|
|
277
|
+
const imageData = drawContext.createImageData(width, height);
|
|
278
|
+
function resolveCaptureOptions(nextOptions = {}) {
|
|
279
|
+
const captureOptions = { ...options, ...nextOptions };
|
|
280
|
+
const nextDimensions = getCaptureDimensions(renderer, captureOptions);
|
|
281
|
+
if (nextDimensions.width !== width || nextDimensions.height !== height) {
|
|
282
|
+
throw new Error(
|
|
283
|
+
"Camera frame capture sessions require stable width and height."
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
prepareCaptureCamera(
|
|
287
|
+
camera,
|
|
288
|
+
captureOptions,
|
|
289
|
+
fallbackCamera,
|
|
290
|
+
width,
|
|
291
|
+
height
|
|
292
|
+
);
|
|
293
|
+
return captureOptions;
|
|
294
|
+
}
|
|
295
|
+
function renderPreparedCapture(captureOptions) {
|
|
296
|
+
const previousState = saveRendererState(renderer);
|
|
297
|
+
const hidden = hideExcludedCaptureObjects(scene);
|
|
298
|
+
scene.updateMatrixWorld(true);
|
|
299
|
+
try {
|
|
300
|
+
renderer.xr.enabled = false;
|
|
301
|
+
renderer.setRenderTarget(target);
|
|
302
|
+
renderer.setViewport(0, 0, width, height);
|
|
303
|
+
renderer.setScissor(0, 0, width, height);
|
|
304
|
+
renderer.setScissorTest(false);
|
|
305
|
+
renderer.clear();
|
|
306
|
+
renderer.render(scene, camera);
|
|
307
|
+
readRenderTargetToCanvas(
|
|
308
|
+
renderer,
|
|
309
|
+
target,
|
|
310
|
+
canvas,
|
|
311
|
+
drawContext,
|
|
312
|
+
pixels,
|
|
313
|
+
imageData,
|
|
314
|
+
width,
|
|
315
|
+
height,
|
|
316
|
+
renderer.outputColorSpace
|
|
317
|
+
);
|
|
318
|
+
return {
|
|
319
|
+
canvas,
|
|
320
|
+
camera,
|
|
321
|
+
width,
|
|
322
|
+
height,
|
|
323
|
+
source: getCameraFrameCaptureSource(captureOptions)
|
|
324
|
+
};
|
|
325
|
+
} finally {
|
|
326
|
+
restoreObjectVisibility(hidden);
|
|
327
|
+
restoreRendererState(renderer, previousState);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
function capture(nextOptions = {}) {
|
|
331
|
+
return renderPreparedCapture(resolveCaptureOptions(nextOptions));
|
|
332
|
+
}
|
|
333
|
+
async function captureAsync(nextOptions = {}) {
|
|
334
|
+
const captureOptions = resolveCaptureOptions(nextOptions);
|
|
335
|
+
scene.updateMatrixWorld(true);
|
|
336
|
+
const captureRenderer = getCaptureRenderer(scene);
|
|
337
|
+
if (captureRenderer) {
|
|
338
|
+
const previousState = saveRendererState(renderer);
|
|
339
|
+
const hidden = hideExcludedCaptureObjects(scene);
|
|
340
|
+
try {
|
|
341
|
+
renderer.xr.enabled = false;
|
|
342
|
+
const captureResult = await captureRenderer({
|
|
343
|
+
renderer,
|
|
344
|
+
scene,
|
|
345
|
+
camera,
|
|
346
|
+
target,
|
|
347
|
+
width,
|
|
348
|
+
height
|
|
349
|
+
});
|
|
350
|
+
if (captureResult) {
|
|
351
|
+
const captureWidth = captureResult.width ?? width;
|
|
352
|
+
const captureHeight = captureResult.height ?? height;
|
|
353
|
+
if (captureWidth !== width || captureHeight !== height) {
|
|
354
|
+
throw new Error(
|
|
355
|
+
"Camera frame capture renderer returned unexpected dimensions."
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
readPixelsToCanvas(
|
|
359
|
+
captureResult.pixels,
|
|
360
|
+
drawContext,
|
|
361
|
+
imageData,
|
|
362
|
+
width,
|
|
363
|
+
height,
|
|
364
|
+
captureResult.flipY ?? true
|
|
365
|
+
);
|
|
366
|
+
return {
|
|
367
|
+
canvas,
|
|
368
|
+
camera,
|
|
369
|
+
width,
|
|
370
|
+
height,
|
|
371
|
+
source: getCameraFrameCaptureSource(captureOptions)
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
} finally {
|
|
375
|
+
restoreObjectVisibility(hidden);
|
|
376
|
+
restoreRendererState(renderer, previousState);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
return renderPreparedCapture(captureOptions);
|
|
380
|
+
}
|
|
381
|
+
return {
|
|
382
|
+
width,
|
|
383
|
+
height,
|
|
384
|
+
capture,
|
|
385
|
+
captureAsync,
|
|
386
|
+
captureDataUrl(nextOptions = {}) {
|
|
387
|
+
const type = nextOptions.type ?? options.type ?? "image/png";
|
|
388
|
+
const result = capture(nextOptions);
|
|
389
|
+
return {
|
|
390
|
+
...result,
|
|
391
|
+
dataUrl: result.canvas.toDataURL(
|
|
392
|
+
type,
|
|
393
|
+
nextOptions.quality ?? options.quality
|
|
394
|
+
),
|
|
395
|
+
type
|
|
396
|
+
};
|
|
397
|
+
},
|
|
398
|
+
async captureDataUrlAsync(nextOptions = {}) {
|
|
399
|
+
const type = nextOptions.type ?? options.type ?? "image/png";
|
|
400
|
+
const result = await captureAsync(nextOptions);
|
|
401
|
+
return {
|
|
402
|
+
...result,
|
|
403
|
+
dataUrl: result.canvas.toDataURL(
|
|
404
|
+
type,
|
|
405
|
+
nextOptions.quality ?? options.quality
|
|
406
|
+
),
|
|
407
|
+
type
|
|
408
|
+
};
|
|
409
|
+
},
|
|
410
|
+
async captureBlob(nextOptions = {}) {
|
|
411
|
+
const type = nextOptions.type ?? options.type ?? "image/png";
|
|
412
|
+
const result = await captureAsync(nextOptions);
|
|
413
|
+
const blob = await new Promise((resolve, reject) => {
|
|
414
|
+
result.canvas.toBlob(
|
|
415
|
+
(nextBlob) => {
|
|
416
|
+
if (nextBlob) resolve(nextBlob);
|
|
417
|
+
else reject(new Error("Camera frame capture did not produce a Blob."));
|
|
418
|
+
},
|
|
419
|
+
type,
|
|
420
|
+
nextOptions.quality ?? options.quality
|
|
421
|
+
);
|
|
422
|
+
});
|
|
423
|
+
return { ...result, blob, type };
|
|
424
|
+
},
|
|
425
|
+
dispose() {
|
|
426
|
+
target.dispose();
|
|
427
|
+
}
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
function renderCameraFrameToCanvas(renderer, scene, fallbackCamera, options = {}) {
|
|
431
|
+
const session = createCameraFrameCaptureSession(
|
|
432
|
+
renderer,
|
|
433
|
+
scene,
|
|
434
|
+
fallbackCamera,
|
|
435
|
+
options
|
|
436
|
+
);
|
|
437
|
+
try {
|
|
438
|
+
return session.capture();
|
|
439
|
+
} finally {
|
|
440
|
+
session.dispose();
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
async function captureCameraFrame(renderer, scene, fallbackCamera, options = {}) {
|
|
444
|
+
const type = options.type ?? "image/png";
|
|
445
|
+
const session = createCameraFrameCaptureSession(
|
|
446
|
+
renderer,
|
|
447
|
+
scene,
|
|
448
|
+
fallbackCamera,
|
|
449
|
+
options
|
|
450
|
+
);
|
|
451
|
+
try {
|
|
452
|
+
const result = await session.captureAsync();
|
|
453
|
+
return {
|
|
454
|
+
...result,
|
|
455
|
+
dataUrl: result.canvas.toDataURL(type, options.quality),
|
|
456
|
+
type
|
|
457
|
+
};
|
|
458
|
+
} finally {
|
|
459
|
+
session.dispose();
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
async function captureCameraFrameBlob(renderer, scene, fallbackCamera, options = {}) {
|
|
463
|
+
const session = createCameraFrameCaptureSession(
|
|
464
|
+
renderer,
|
|
465
|
+
scene,
|
|
466
|
+
fallbackCamera,
|
|
467
|
+
options
|
|
468
|
+
);
|
|
469
|
+
try {
|
|
470
|
+
return await session.captureBlob();
|
|
471
|
+
} finally {
|
|
472
|
+
session.dispose();
|
|
473
|
+
}
|
|
474
|
+
}
|
|
88
475
|
var DEFAULT_BACKGROUND = "#181a1f";
|
|
89
476
|
function ScenarioLighting({
|
|
90
477
|
preset = "studio",
|
|
@@ -683,7 +1070,13 @@ function clamp01(value) {
|
|
|
683
1070
|
* @license
|
|
684
1071
|
* SPDX-License-Identifier: Apache-2.0
|
|
685
1072
|
*/
|
|
1073
|
+
/**
|
|
1074
|
+
* @license
|
|
1075
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
1076
|
+
*
|
|
1077
|
+
* Offscreen camera-frame capture for R3F/MuJoCo scenes.
|
|
1078
|
+
*/
|
|
686
1079
|
|
|
687
|
-
export { RobotActuators, RobotBodies, RobotCameras, RobotGeoms, RobotJoints, RobotKeyframes, RobotResources, RobotSensors, RobotSites, ScenarioLighting, SplatEnvironment, SplatEnvironmentReadinessStatus, VisualScenarioEffects, createPairedSplatEnvironment, createSparkSplatViewerUrl, createSplatEnvironmentUserData, createSplatSceneConfig, createVisualScenarioExecutionContext, getContact, getScenarioBackground, getScenarioCameraPosition, getSplatEnvironmentReadiness, registerRobotResources, useSplatEnvironment, useSplatSceneConfig, useVisualScenarioEffects, useVisualScenarioExecutionContext, withContacts, withSplatEnvironment };
|
|
688
|
-
//# sourceMappingURL=chunk-
|
|
689
|
-
//# sourceMappingURL=chunk-
|
|
1080
|
+
export { CAMERA_FRAME_CAPTURE_RENDER_USER_DATA_KEY, CAPTURE_EXCLUDE_KEY, RobotActuators, RobotBodies, RobotCameras, RobotGeoms, RobotJoints, RobotKeyframes, RobotResources, RobotSensors, RobotSites, ScenarioLighting, SplatEnvironment, SplatEnvironmentReadinessStatus, VisualScenarioEffects, captureCameraFrame, captureCameraFrameBlob, createCameraFrameCaptureSession, createPairedSplatEnvironment, createSparkSplatViewerUrl, createSplatEnvironmentUserData, createSplatSceneConfig, createVisualScenarioExecutionContext, getContact, getScenarioBackground, getScenarioCameraPosition, getSplatEnvironmentReadiness, registerRobotResources, renderCameraFrameToCanvas, useSplatEnvironment, useSplatSceneConfig, useVisualScenarioEffects, useVisualScenarioExecutionContext, withContacts, withSplatEnvironment };
|
|
1081
|
+
//# sourceMappingURL=chunk-6MOK6ZWB.js.map
|
|
1082
|
+
//# sourceMappingURL=chunk-6MOK6ZWB.js.map
|