mujoco-react 12.1.1 → 12.2.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 +200 -180
- package/dist/{chunk-XD3AY7XZ.js → chunk-SP72GTOD.js} +192 -92
- package/dist/chunk-SP72GTOD.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +25 -3
- package/dist/index.js.map +1 -1
- package/dist/onnx.d.ts +1 -1
- package/dist/spark.d.ts +1 -1
- package/dist/spark.js +25 -16
- package/dist/spark.js.map +1 -1
- package/dist/{types-CSi3fIVs.d.ts → types-CF-BZXtW.d.ts} +32 -1
- package/package.json +2 -2
- package/src/core/MujocoSimProvider.tsx +9 -1
- package/src/hooks/useCameraFrameCapture.ts +17 -0
- package/src/hooks/useCameraStream.ts +1 -1
- package/src/index.ts +2 -0
- package/src/rendering/cameraFrameCapture.ts +141 -68
- package/src/rendering/metricDepth.ts +56 -0
- package/src/spark.tsx +24 -15
- package/src/types.ts +9 -0
- package/dist/chunk-XD3AY7XZ.js.map +0 -1
package/README.md
CHANGED
|
@@ -16,19 +16,6 @@ Composable [React Three Fiber](https://docs.pmnd.rs/react-three-fiber) wrapper a
|
|
|
16
16
|
npm install mujoco-react three @react-three/fiber @react-three/drei
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
## Running policies and simulation workloads
|
|
20
|
-
|
|
21
|
-
Use `const runtime = await api.createRuntime()` after `onReady`, then await
|
|
22
|
-
`runtime.observe()` and `runtime.advance({ steps, controls })`. Physics runs in a
|
|
23
|
-
dedicated worker; commands reject when busy instead of accumulating. The runtime
|
|
24
|
-
supports cancellation, reset, full snapshots, synchronized camera observations,
|
|
25
|
-
and separate physics/capture timing. Use `<CameraView>` for GPU-only previews.
|
|
26
|
-
|
|
27
|
-
The viewer has an 8 ms / 100-step per-frame budget. Equivalent inline scene
|
|
28
|
-
configs preserve the loaded model. See the [simulation runtime guide](https://dadd.mintlify.app/guides/simulation-runtime)
|
|
29
|
-
for ownership, VLA loops, bundling, and performance limits, or try the
|
|
30
|
-
[worker demo](https://mujoco-react-example.pages.dev/?demo=runtime).
|
|
31
|
-
|
|
32
19
|
## Vite Plugin and Type-Safe Names
|
|
33
20
|
|
|
34
21
|
Use the Vite plugin to generate declaration merging for actuator, sensor, body, joint, site, geom, and keyframe names:
|
|
@@ -156,173 +143,6 @@ Use it as a child of `<MujocoCanvas>`:
|
|
|
156
143
|
</MujocoCanvas>
|
|
157
144
|
```
|
|
158
145
|
|
|
159
|
-
## Gaussian Splat Environments
|
|
160
|
-
|
|
161
|
-
Gaussian splats are visual context; MuJoCo XML remains the source of physics, contacts, and task fixtures. Use visual-only splats when you only need rendered environment context, and add collision proxy metadata when a workflow needs simplified contact geometry.
|
|
162
|
-
|
|
163
|
-
Use `VisualScenarioEffects` when the same MuJoCo task should render under
|
|
164
|
-
different camera exposure, fog/background, and deterministic material variants:
|
|
165
|
-
|
|
166
|
-
```tsx
|
|
167
|
-
import { ScenarioLighting, VisualScenarioEffects } from "mujoco-react";
|
|
168
|
-
|
|
169
|
-
<MujocoCanvas config={sceneConfig}>
|
|
170
|
-
<VisualScenarioEffects
|
|
171
|
-
scenario={scenario}
|
|
172
|
-
materialFilter={({ object }) => object.name.startsWith("prop_")}
|
|
173
|
-
/>
|
|
174
|
-
<ScenarioLighting preset={scenario.lighting} />
|
|
175
|
-
</MujocoCanvas>;
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
Use the renderer-agnostic boundary from the main package. If your app stores
|
|
179
|
-
visual scenarios as data, pass the scenario directly; the component resolves the
|
|
180
|
-
splat asset and any paired MJCF collision proxy metadata for you. Visual-only
|
|
181
|
-
splats are valid, and readiness tells you whether a collision proxy is required
|
|
182
|
-
for your training/physics handoff.
|
|
183
|
-
|
|
184
|
-
```tsx
|
|
185
|
-
import { MujocoCanvas, SplatEnvironment, useSplatSceneConfig } from "mujoco-react";
|
|
186
|
-
|
|
187
|
-
const splat = useSplatSceneConfig({ sceneConfig, scenario });
|
|
188
|
-
|
|
189
|
-
<MujocoCanvas config={splat.sceneConfig}>
|
|
190
|
-
{splat.environment ? (
|
|
191
|
-
<SplatEnvironment environment={splat.environment} renderer="custom">
|
|
192
|
-
<MySplatRenderer src={splat.environment.splat.src} />
|
|
193
|
-
</SplatEnvironment>
|
|
194
|
-
) : null}
|
|
195
|
-
</MujocoCanvas>;
|
|
196
|
-
```
|
|
197
|
-
|
|
198
|
-
When a splat scenario includes paired MJCF collision proxy metadata, render a
|
|
199
|
-
generic wireframe preview from that XML with `SplatCollisionProxyPreview`. The
|
|
200
|
-
component parses MJCF primitives such as planes, boxes, spheres, capsules, and
|
|
201
|
-
mesh placeholders from any fetchable proxy XML; the tabletop example is just one
|
|
202
|
-
possible environment.
|
|
203
|
-
|
|
204
|
-
```tsx
|
|
205
|
-
import {
|
|
206
|
-
SplatCollisionProxyPreview,
|
|
207
|
-
SplatEnvironment,
|
|
208
|
-
useSplatCollisionProxyGeoms,
|
|
209
|
-
useSplatSceneConfig,
|
|
210
|
-
} from "mujoco-react";
|
|
211
|
-
|
|
212
|
-
const splat = useSplatSceneConfig({ sceneConfig, scenario });
|
|
213
|
-
const proxy = splat.environment?.collisionProxy;
|
|
214
|
-
|
|
215
|
-
<SplatEnvironment
|
|
216
|
-
environment={splat.environment}
|
|
217
|
-
collisionProxy={
|
|
218
|
-
proxy ? <SplatCollisionProxyPreview collisionProxy={proxy} /> : undefined
|
|
219
|
-
}
|
|
220
|
-
/>;
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
Use `useSplatCollisionProxyGeoms()` when your app wants to inspect or style the
|
|
224
|
-
proxy primitives itself:
|
|
225
|
-
|
|
226
|
-
```tsx
|
|
227
|
-
const proxyPreview = useSplatCollisionProxyGeoms({
|
|
228
|
-
collisionProxy: splat.environment?.collisionProxy,
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
proxyPreview.geoms.map((geom) => geom.type);
|
|
232
|
-
```
|
|
233
|
-
|
|
234
|
-
Use `splat.readiness` or `getSplatEnvironmentReadiness(scenario)` to gate
|
|
235
|
-
authoring and import flows. The status distinguishes disabled scenarios,
|
|
236
|
-
missing visual assets, missing collision proxies, unsupported renderer formats,
|
|
237
|
-
and ready environments.
|
|
238
|
-
|
|
239
|
-
Use `createSplatSceneConfig()` when the same scene composition needs to run
|
|
240
|
-
outside React, such as codegen, import validation, backend handoff metadata, or
|
|
241
|
-
tests:
|
|
242
|
-
|
|
243
|
-
```tsx
|
|
244
|
-
import { createSplatSceneConfig } from "mujoco-react";
|
|
245
|
-
|
|
246
|
-
const splat = createSplatSceneConfig({
|
|
247
|
-
sceneConfig,
|
|
248
|
-
scenario,
|
|
249
|
-
renderer: "spark",
|
|
250
|
-
});
|
|
251
|
-
```
|
|
252
|
-
|
|
253
|
-
Use `createVisualScenarioExecutionContext()` or
|
|
254
|
-
`useVisualScenarioExecutionContext()` when recording rollouts or exporting
|
|
255
|
-
LeRobot/HF Jobs handoff artifacts. It resolves the scenario seed, camera
|
|
256
|
-
exposure/noise/blur/jitter, material randomization, splat source, collision
|
|
257
|
-
proxy, and readiness into one serializable object.
|
|
258
|
-
|
|
259
|
-
```tsx
|
|
260
|
-
import { createVisualScenarioExecutionContext } from "mujoco-react";
|
|
261
|
-
|
|
262
|
-
const visualContext = createVisualScenarioExecutionContext({
|
|
263
|
-
scenario,
|
|
264
|
-
renderer: "spark",
|
|
265
|
-
variantId,
|
|
266
|
-
});
|
|
267
|
-
|
|
268
|
-
writeEpisodeManifest({
|
|
269
|
-
task,
|
|
270
|
-
visualExecutionContext: visualContext,
|
|
271
|
-
});
|
|
272
|
-
```
|
|
273
|
-
|
|
274
|
-
For MuJoCo + 3DGS composition, derive the optional collision environment from
|
|
275
|
-
the same splat metadata and pass the resulting config to `<MujocoCanvas>`:
|
|
276
|
-
|
|
277
|
-
```tsx
|
|
278
|
-
const sceneConfig = withSplatEnvironment(
|
|
279
|
-
{
|
|
280
|
-
src: "/models/xlerobot/",
|
|
281
|
-
sceneFile: "xlerobot.xml",
|
|
282
|
-
},
|
|
283
|
-
kitchenScenario
|
|
284
|
-
);
|
|
285
|
-
```
|
|
286
|
-
|
|
287
|
-
For first-class Spark rendering, install Spark and import the optional adapter:
|
|
288
|
-
|
|
289
|
-
```bash
|
|
290
|
-
npm install @sparkjsdev/spark
|
|
291
|
-
```
|
|
292
|
-
|
|
293
|
-
```tsx
|
|
294
|
-
import {
|
|
295
|
-
SparkSplatEnvironment,
|
|
296
|
-
useSparkSplatEnvironment,
|
|
297
|
-
} from "mujoco-react/spark";
|
|
298
|
-
|
|
299
|
-
function Scene() {
|
|
300
|
-
const splat = useSparkSplatEnvironment({ sceneConfig, scenario });
|
|
301
|
-
|
|
302
|
-
return (
|
|
303
|
-
<MujocoCanvas config={splat.sceneConfig} gl={{ preserveDrawingBuffer: true }}>
|
|
304
|
-
{splat.environment ? (
|
|
305
|
-
<SparkSplatEnvironment hideGroundMeshes {...splat.props} />
|
|
306
|
-
) : null}
|
|
307
|
-
<StatusBadge status={splat.lifecycle.status} error={splat.lifecycle.error} />
|
|
308
|
-
</MujocoCanvas>
|
|
309
|
-
);
|
|
310
|
-
}
|
|
311
|
-
```
|
|
312
|
-
|
|
313
|
-
`SparkSplatEnvironment` currently renders `.spz` assets. Use the renderer-agnostic
|
|
314
|
-
`SplatEnvironment` for `.ply`/`.splat` metadata or when wiring a different renderer.
|
|
315
|
-
Tune live rendering and snapshots separately with `renderTuning` and
|
|
316
|
-
`captureTuning`:
|
|
317
|
-
|
|
318
|
-
```tsx
|
|
319
|
-
<SparkSplatEnvironment
|
|
320
|
-
{...splat.props}
|
|
321
|
-
renderTuning={{ lodSplatScale: 0.75, minSortIntervalMs: 50 }}
|
|
322
|
-
captureTuning={{ lodSplatScale: 1.4, lodRenderScale: 0.45, maxWarmupFrames: 6 }}
|
|
323
|
-
/>
|
|
324
|
-
```
|
|
325
|
-
|
|
326
146
|
## Write Controllers
|
|
327
147
|
|
|
328
148
|
```tsx
|
|
@@ -470,6 +290,34 @@ function PolicyDriver() {
|
|
|
470
290
|
|
|
471
291
|
Use `output: "float64"` when a downstream model expects double precision. Named resources are skipped when absent, so `obs.layout` is the source of truth for the current model.
|
|
472
292
|
|
|
293
|
+
## Metric RGB-D Capture
|
|
294
|
+
|
|
295
|
+
Capture aligned RGB and metric depth for browser robotics observations, including
|
|
296
|
+
LIBERO. Use a camera name from your MJCF:
|
|
297
|
+
|
|
298
|
+
```tsx
|
|
299
|
+
const capture = useCameraFrameCapture({
|
|
300
|
+
cameraName: "agentview",
|
|
301
|
+
width: 256,
|
|
302
|
+
height: 256,
|
|
303
|
+
mujocoCameraCompatibility: true,
|
|
304
|
+
});
|
|
305
|
+
const frame = await capture.captureRGBD();
|
|
306
|
+
// frame.rgb: Uint8Array, sRGB RGB bytes, top-left origin
|
|
307
|
+
// frame.depth: Float32Array, meters along camera -Z, Infinity for background
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
Also available as `api.captureCameraRGBD(options)`, the standalone
|
|
311
|
+
`captureCameraRGBD(renderer, scene, camera, options)`, and
|
|
312
|
+
`session.captureRGBD(options)` on reusable camera capture sessions. One render
|
|
313
|
+
produces both arrays; each result owns its buffers and camera matrix snapshots.
|
|
314
|
+
Scene coordinates must be meters. Custom renderers, logarithmic depth, and
|
|
315
|
+
reversed depth fail explicitly. The example app's `?demo=rgbd` route shows
|
|
316
|
+
reference objects and aligned RGB/depth previews.
|
|
317
|
+
|
|
318
|
+
See [camera capture docs](docs/hooks/use-camera-frame-capture.mdx#metric-rgb-d-capture)
|
|
319
|
+
for projection metadata, moving-camera sessions, and coordinate conventions.
|
|
320
|
+
|
|
473
321
|
## WebSocket Control
|
|
474
322
|
|
|
475
323
|
Stream actuator commands over a WebSocket and send simulation state back. Use a schema validator such as Zod at this boundary because socket messages are untrusted app input (`npm install zod` for this example):
|
|
@@ -1405,6 +1253,173 @@ function ClickSelectOverlay() {
|
|
|
1405
1253
|
|
|
1406
1254
|
See [Click-to-Select](https://dadd.mintlify.app/guides/click-to-select) for the full implementation.
|
|
1407
1255
|
|
|
1256
|
+
## Gaussian Splat Environments
|
|
1257
|
+
|
|
1258
|
+
Gaussian splats are visual context; MuJoCo XML remains the source of physics, contacts, and task fixtures. Use visual-only splats when you only need rendered environment context, and add collision proxy metadata when a workflow needs simplified contact geometry.
|
|
1259
|
+
|
|
1260
|
+
Use `VisualScenarioEffects` when the same MuJoCo task should render under
|
|
1261
|
+
different camera exposure, fog/background, and deterministic material variants:
|
|
1262
|
+
|
|
1263
|
+
```tsx
|
|
1264
|
+
import { ScenarioLighting, VisualScenarioEffects } from "mujoco-react";
|
|
1265
|
+
|
|
1266
|
+
<MujocoCanvas config={sceneConfig}>
|
|
1267
|
+
<VisualScenarioEffects
|
|
1268
|
+
scenario={scenario}
|
|
1269
|
+
materialFilter={({ object }) => object.name.startsWith("prop_")}
|
|
1270
|
+
/>
|
|
1271
|
+
<ScenarioLighting preset={scenario.lighting} />
|
|
1272
|
+
</MujocoCanvas>;
|
|
1273
|
+
```
|
|
1274
|
+
|
|
1275
|
+
Use the renderer-agnostic boundary from the main package. If your app stores
|
|
1276
|
+
visual scenarios as data, pass the scenario directly; the component resolves the
|
|
1277
|
+
splat asset and any paired MJCF collision proxy metadata for you. Visual-only
|
|
1278
|
+
splats are valid, and readiness tells you whether a collision proxy is required
|
|
1279
|
+
for your training/physics handoff.
|
|
1280
|
+
|
|
1281
|
+
```tsx
|
|
1282
|
+
import { MujocoCanvas, SplatEnvironment, useSplatSceneConfig } from "mujoco-react";
|
|
1283
|
+
|
|
1284
|
+
const splat = useSplatSceneConfig({ sceneConfig, scenario });
|
|
1285
|
+
|
|
1286
|
+
<MujocoCanvas config={splat.sceneConfig}>
|
|
1287
|
+
{splat.environment ? (
|
|
1288
|
+
<SplatEnvironment environment={splat.environment} renderer="custom">
|
|
1289
|
+
<MySplatRenderer src={splat.environment.splat.src} />
|
|
1290
|
+
</SplatEnvironment>
|
|
1291
|
+
) : null}
|
|
1292
|
+
</MujocoCanvas>;
|
|
1293
|
+
```
|
|
1294
|
+
|
|
1295
|
+
When a splat scenario includes paired MJCF collision proxy metadata, render a
|
|
1296
|
+
generic wireframe preview from that XML with `SplatCollisionProxyPreview`. The
|
|
1297
|
+
component parses MJCF primitives such as planes, boxes, spheres, capsules, and
|
|
1298
|
+
mesh placeholders from any fetchable proxy XML; the tabletop example is just one
|
|
1299
|
+
possible environment.
|
|
1300
|
+
|
|
1301
|
+
```tsx
|
|
1302
|
+
import {
|
|
1303
|
+
SplatCollisionProxyPreview,
|
|
1304
|
+
SplatEnvironment,
|
|
1305
|
+
useSplatCollisionProxyGeoms,
|
|
1306
|
+
useSplatSceneConfig,
|
|
1307
|
+
} from "mujoco-react";
|
|
1308
|
+
|
|
1309
|
+
const splat = useSplatSceneConfig({ sceneConfig, scenario });
|
|
1310
|
+
const proxy = splat.environment?.collisionProxy;
|
|
1311
|
+
|
|
1312
|
+
<SplatEnvironment
|
|
1313
|
+
environment={splat.environment}
|
|
1314
|
+
collisionProxy={
|
|
1315
|
+
proxy ? <SplatCollisionProxyPreview collisionProxy={proxy} /> : undefined
|
|
1316
|
+
}
|
|
1317
|
+
/>;
|
|
1318
|
+
```
|
|
1319
|
+
|
|
1320
|
+
Use `useSplatCollisionProxyGeoms()` when your app wants to inspect or style the
|
|
1321
|
+
proxy primitives itself:
|
|
1322
|
+
|
|
1323
|
+
```tsx
|
|
1324
|
+
const proxyPreview = useSplatCollisionProxyGeoms({
|
|
1325
|
+
collisionProxy: splat.environment?.collisionProxy,
|
|
1326
|
+
});
|
|
1327
|
+
|
|
1328
|
+
proxyPreview.geoms.map((geom) => geom.type);
|
|
1329
|
+
```
|
|
1330
|
+
|
|
1331
|
+
Use `splat.readiness` or `getSplatEnvironmentReadiness(scenario)` to gate
|
|
1332
|
+
authoring and import flows. The status distinguishes disabled scenarios,
|
|
1333
|
+
missing visual assets, missing collision proxies, unsupported renderer formats,
|
|
1334
|
+
and ready environments.
|
|
1335
|
+
|
|
1336
|
+
Use `createSplatSceneConfig()` when the same scene composition needs to run
|
|
1337
|
+
outside React, such as codegen, import validation, backend handoff metadata, or
|
|
1338
|
+
tests:
|
|
1339
|
+
|
|
1340
|
+
```tsx
|
|
1341
|
+
import { createSplatSceneConfig } from "mujoco-react";
|
|
1342
|
+
|
|
1343
|
+
const splat = createSplatSceneConfig({
|
|
1344
|
+
sceneConfig,
|
|
1345
|
+
scenario,
|
|
1346
|
+
renderer: "spark",
|
|
1347
|
+
});
|
|
1348
|
+
```
|
|
1349
|
+
|
|
1350
|
+
Use `createVisualScenarioExecutionContext()` or
|
|
1351
|
+
`useVisualScenarioExecutionContext()` when recording rollouts or exporting
|
|
1352
|
+
LeRobot/HF Jobs handoff artifacts. It resolves the scenario seed, camera
|
|
1353
|
+
exposure/noise/blur/jitter, material randomization, splat source, collision
|
|
1354
|
+
proxy, and readiness into one serializable object.
|
|
1355
|
+
|
|
1356
|
+
```tsx
|
|
1357
|
+
import { createVisualScenarioExecutionContext } from "mujoco-react";
|
|
1358
|
+
|
|
1359
|
+
const visualContext = createVisualScenarioExecutionContext({
|
|
1360
|
+
scenario,
|
|
1361
|
+
renderer: "spark",
|
|
1362
|
+
variantId,
|
|
1363
|
+
});
|
|
1364
|
+
|
|
1365
|
+
writeEpisodeManifest({
|
|
1366
|
+
task,
|
|
1367
|
+
visualExecutionContext: visualContext,
|
|
1368
|
+
});
|
|
1369
|
+
```
|
|
1370
|
+
|
|
1371
|
+
For MuJoCo + 3DGS composition, derive the optional collision environment from
|
|
1372
|
+
the same splat metadata and pass the resulting config to `<MujocoCanvas>`:
|
|
1373
|
+
|
|
1374
|
+
```tsx
|
|
1375
|
+
const sceneConfig = withSplatEnvironment(
|
|
1376
|
+
{
|
|
1377
|
+
src: "/models/xlerobot/",
|
|
1378
|
+
sceneFile: "xlerobot.xml",
|
|
1379
|
+
},
|
|
1380
|
+
kitchenScenario
|
|
1381
|
+
);
|
|
1382
|
+
```
|
|
1383
|
+
|
|
1384
|
+
For first-class Spark rendering, install Spark and import the optional adapter:
|
|
1385
|
+
|
|
1386
|
+
```bash
|
|
1387
|
+
npm install @sparkjsdev/spark
|
|
1388
|
+
```
|
|
1389
|
+
|
|
1390
|
+
```tsx
|
|
1391
|
+
import {
|
|
1392
|
+
SparkSplatEnvironment,
|
|
1393
|
+
useSparkSplatEnvironment,
|
|
1394
|
+
} from "mujoco-react/spark";
|
|
1395
|
+
|
|
1396
|
+
function Scene() {
|
|
1397
|
+
const splat = useSparkSplatEnvironment({ sceneConfig, scenario });
|
|
1398
|
+
|
|
1399
|
+
return (
|
|
1400
|
+
<MujocoCanvas config={splat.sceneConfig} gl={{ preserveDrawingBuffer: true }}>
|
|
1401
|
+
{splat.environment ? (
|
|
1402
|
+
<SparkSplatEnvironment hideGroundMeshes {...splat.props} />
|
|
1403
|
+
) : null}
|
|
1404
|
+
<StatusBadge status={splat.lifecycle.status} error={splat.lifecycle.error} />
|
|
1405
|
+
</MujocoCanvas>
|
|
1406
|
+
);
|
|
1407
|
+
}
|
|
1408
|
+
```
|
|
1409
|
+
|
|
1410
|
+
`SparkSplatEnvironment` currently renders `.spz` assets. Use the renderer-agnostic
|
|
1411
|
+
`SplatEnvironment` for `.ply`/`.splat` metadata or when wiring a different renderer.
|
|
1412
|
+
Tune live rendering and snapshots separately with `renderTuning` and
|
|
1413
|
+
`captureTuning`:
|
|
1414
|
+
|
|
1415
|
+
```tsx
|
|
1416
|
+
<SparkSplatEnvironment
|
|
1417
|
+
{...splat.props}
|
|
1418
|
+
renderTuning={{ lodSplatScale: 0.75, minSortIntervalMs: 50 }}
|
|
1419
|
+
captureTuning={{ lodSplatScale: 1.4, lodRenderScale: 0.45, maxWarmupFrames: 6 }}
|
|
1420
|
+
/>
|
|
1421
|
+
```
|
|
1422
|
+
|
|
1408
1423
|
## useFrame Priority
|
|
1409
1424
|
|
|
1410
1425
|
| Priority | Owner | Purpose |
|
|
@@ -1434,3 +1449,8 @@ Apache-2.0
|
|
|
1434
1449
|
## Contacts and plasticity
|
|
1435
1450
|
|
|
1436
1451
|
Use MJCF speculative contacts for fast rigid objects and yielding springs for permanent deformation. The existing model/data interface exposes enable flags and plastic state; full snapshots preserve plastic history. See the [guide](docs/guides/engine-features.mdx) and [interactive demos](https://mujoco-react-example.pages.dev/?demo=speculative).
|
|
1452
|
+
|
|
1453
|
+
Live splat camera streams use background sorting and reuse prepared splats, so
|
|
1454
|
+
robot motion does not force a splat rebuild on every preview frame. IK gizmos
|
|
1455
|
+
remain visible in the main view and excluded from captures. One-off and policy
|
|
1456
|
+
captures keep snapshot behavior; `renderMode: 'snapshot'` opts a stream into it.
|