mujoco-react 8.11.0 → 9.1.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 +34 -17
- package/dist/{chunk-SEWQULWO.js → chunk-33CV6HSV.js} +3 -3
- package/dist/chunk-33CV6HSV.js.map +1 -0
- package/dist/index.d.ts +13 -41
- package/dist/index.js +185 -152
- package/dist/index.js.map +1 -1
- package/dist/spark.d.ts +1 -1
- package/dist/spark.js +1 -1
- package/dist/{types-BmneHLBM.d.ts → types-C5gTvR7b.d.ts} +83 -13
- package/package.json +1 -1
- package/src/components/DragInteraction.tsx +1 -1
- package/src/components/IkGizmo.tsx +2 -2
- package/src/components/SceneRenderer.tsx +1 -1
- package/src/components/TrajectoryPlayer.tsx +4 -1
- package/src/components/VisualScenario.tsx +1 -1
- package/src/core/MujocoPhysics.tsx +10 -4
- package/src/core/MujocoSimProvider.tsx +42 -13
- package/src/core/createController.tsx +2 -2
- package/src/hooks/useBodyState.ts +1 -1
- package/src/hooks/useContacts.ts +1 -1
- package/src/hooks/useCtrlNoise.ts +1 -1
- package/src/hooks/useFrameCapture.ts +8 -42
- package/src/hooks/useGamepad.ts +1 -1
- package/src/hooks/useGravityCompensation.ts +1 -1
- package/src/hooks/useIkController.ts +22 -13
- package/src/hooks/useJointState.ts +1 -1
- package/src/hooks/useKeyboardTeleop.ts +1 -1
- package/src/hooks/usePolicy.ts +1 -1
- package/src/hooks/useSensor.ts +1 -1
- package/src/hooks/useTrajectoryPlayer.ts +4 -4
- package/src/hooks/useTrajectoryRecorder.ts +1 -1
- package/src/index.ts +18 -9
- package/src/types.ts +106 -18
- package/dist/chunk-SEWQULWO.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { ScenarioLighting, SplatEnvironment, VisualScenarioEffects, createPairedSplatEnvironment, createSparkSplatViewerUrl, createSplatEnvironmentUserData, getScenarioBackground, getScenarioCameraPosition, useSplatEnvironment, useVisualScenarioEffects, withSplatEnvironment } from './chunk-
|
|
1
|
+
export { ScenarioLighting, SplatEnvironment, VisualScenarioEffects, createPairedSplatEnvironment, createSparkSplatViewerUrl, createSplatEnvironmentUserData, getScenarioBackground, getScenarioCameraPosition, useSplatEnvironment, useVisualScenarioEffects, withSplatEnvironment } from './chunk-33CV6HSV.js';
|
|
2
2
|
import loadMujoco from '@mujoco/mujoco';
|
|
3
3
|
import defaultMujocoWasmUrl from '@mujoco/mujoco/mujoco.wasm?url';
|
|
4
4
|
import { createContext, forwardRef, useEffect, useContext, useState, useRef, useCallback, useMemo, useLayoutEffect } from 'react';
|
|
@@ -1285,7 +1285,7 @@ function SceneRenderer(props) {
|
|
|
1285
1285
|
const model = mjModelRef.current;
|
|
1286
1286
|
if (model && bodyID < model.nbody && onSelectionRef.current) {
|
|
1287
1287
|
const name = getName(model, model.name_bodyadr[bodyID]);
|
|
1288
|
-
onSelectionRef.current(bodyID, name);
|
|
1288
|
+
onSelectionRef.current({ bodyId: bodyID, name });
|
|
1289
1289
|
}
|
|
1290
1290
|
}
|
|
1291
1291
|
}
|
|
@@ -1294,6 +1294,114 @@ function SceneRenderer(props) {
|
|
|
1294
1294
|
}
|
|
1295
1295
|
var _previousQuat = new THREE11.Quaternion();
|
|
1296
1296
|
var _currentQuat = new THREE11.Quaternion();
|
|
1297
|
+
function isTargetRef(target) {
|
|
1298
|
+
return Boolean(target && typeof target === "object" && "current" in target);
|
|
1299
|
+
}
|
|
1300
|
+
function resolveCanvasTarget(target) {
|
|
1301
|
+
const resolvedTarget = isTargetRef(target) ? target.current : target;
|
|
1302
|
+
if (!resolvedTarget) {
|
|
1303
|
+
throw new Error("No frame capture target is available.");
|
|
1304
|
+
}
|
|
1305
|
+
if (resolvedTarget instanceof HTMLCanvasElement) {
|
|
1306
|
+
return resolvedTarget;
|
|
1307
|
+
}
|
|
1308
|
+
const canvas = resolvedTarget.querySelector("canvas");
|
|
1309
|
+
if (!canvas) {
|
|
1310
|
+
throw new Error("Frame capture target does not contain a canvas.");
|
|
1311
|
+
}
|
|
1312
|
+
return canvas;
|
|
1313
|
+
}
|
|
1314
|
+
function waitForNextAnimationFrame() {
|
|
1315
|
+
return new Promise((resolve) => {
|
|
1316
|
+
requestAnimationFrame(() => resolve());
|
|
1317
|
+
});
|
|
1318
|
+
}
|
|
1319
|
+
async function captureFrame(options) {
|
|
1320
|
+
const type = options.type ?? "image/png";
|
|
1321
|
+
const canvas = resolveCanvasTarget(options.target);
|
|
1322
|
+
if (options.waitForAnimationFrame ?? true) {
|
|
1323
|
+
await waitForNextAnimationFrame();
|
|
1324
|
+
}
|
|
1325
|
+
return {
|
|
1326
|
+
canvas,
|
|
1327
|
+
dataUrl: canvas.toDataURL(type, options.quality),
|
|
1328
|
+
type
|
|
1329
|
+
};
|
|
1330
|
+
}
|
|
1331
|
+
async function captureFrameBlob(options) {
|
|
1332
|
+
const type = options.type ?? "image/png";
|
|
1333
|
+
const canvas = resolveCanvasTarget(options.target);
|
|
1334
|
+
if (options.waitForAnimationFrame ?? true) {
|
|
1335
|
+
await waitForNextAnimationFrame();
|
|
1336
|
+
}
|
|
1337
|
+
const blob = await new Promise((resolve, reject) => {
|
|
1338
|
+
canvas.toBlob(
|
|
1339
|
+
(nextBlob) => {
|
|
1340
|
+
if (nextBlob) {
|
|
1341
|
+
resolve(nextBlob);
|
|
1342
|
+
} else {
|
|
1343
|
+
reject(new Error("Canvas frame capture did not produce a Blob."));
|
|
1344
|
+
}
|
|
1345
|
+
},
|
|
1346
|
+
type,
|
|
1347
|
+
options.quality
|
|
1348
|
+
);
|
|
1349
|
+
});
|
|
1350
|
+
return { canvas, blob, type };
|
|
1351
|
+
}
|
|
1352
|
+
function useFrameCapture(defaultOptions = {}) {
|
|
1353
|
+
const [status, setStatus] = useState("idle");
|
|
1354
|
+
const [error, setError] = useState(null);
|
|
1355
|
+
const reset = useCallback(() => {
|
|
1356
|
+
setStatus("idle");
|
|
1357
|
+
setError(null);
|
|
1358
|
+
}, []);
|
|
1359
|
+
const capture = useCallback(
|
|
1360
|
+
async (options = {}) => {
|
|
1361
|
+
setStatus("capturing");
|
|
1362
|
+
setError(null);
|
|
1363
|
+
try {
|
|
1364
|
+
const result = await captureFrame({ ...defaultOptions, ...options });
|
|
1365
|
+
setStatus("captured");
|
|
1366
|
+
return result;
|
|
1367
|
+
} catch (nextError) {
|
|
1368
|
+
const error2 = nextError instanceof Error ? nextError : new Error("Unable to capture the current canvas frame.");
|
|
1369
|
+
setError(error2);
|
|
1370
|
+
setStatus("error");
|
|
1371
|
+
throw error2;
|
|
1372
|
+
}
|
|
1373
|
+
},
|
|
1374
|
+
[defaultOptions]
|
|
1375
|
+
);
|
|
1376
|
+
const captureBlob = useCallback(
|
|
1377
|
+
async (options = {}) => {
|
|
1378
|
+
setStatus("capturing");
|
|
1379
|
+
setError(null);
|
|
1380
|
+
try {
|
|
1381
|
+
const result = await captureFrameBlob({
|
|
1382
|
+
...defaultOptions,
|
|
1383
|
+
...options
|
|
1384
|
+
});
|
|
1385
|
+
setStatus("captured");
|
|
1386
|
+
return result;
|
|
1387
|
+
} catch (nextError) {
|
|
1388
|
+
const error2 = nextError instanceof Error ? nextError : new Error("Unable to capture the current canvas frame.");
|
|
1389
|
+
setError(error2);
|
|
1390
|
+
setStatus("error");
|
|
1391
|
+
throw error2;
|
|
1392
|
+
}
|
|
1393
|
+
},
|
|
1394
|
+
[defaultOptions]
|
|
1395
|
+
);
|
|
1396
|
+
return {
|
|
1397
|
+
status,
|
|
1398
|
+
error,
|
|
1399
|
+
isCapturing: status === "capturing",
|
|
1400
|
+
capture,
|
|
1401
|
+
captureBlob,
|
|
1402
|
+
reset
|
|
1403
|
+
};
|
|
1404
|
+
}
|
|
1297
1405
|
var JOINT_TYPE_NAMES2 = ["free", "ball", "slide", "hinge"];
|
|
1298
1406
|
var GEOM_TYPE_NAMES = ["plane", "hfield", "sphere", "capsule", "ellipsoid", "cylinder", "box", "mesh"];
|
|
1299
1407
|
var SENSOR_TYPE_NAMES = {
|
|
@@ -1420,7 +1528,7 @@ function useBeforePhysicsStep(callback) {
|
|
|
1420
1528
|
const callbackRef = useRef(callback);
|
|
1421
1529
|
callbackRef.current = callback;
|
|
1422
1530
|
useEffect(() => {
|
|
1423
|
-
const wrapped = (
|
|
1531
|
+
const wrapped = (input) => callbackRef.current(input);
|
|
1424
1532
|
beforeStepCallbacks.current.add(wrapped);
|
|
1425
1533
|
return () => {
|
|
1426
1534
|
beforeStepCallbacks.current.delete(wrapped);
|
|
@@ -1432,7 +1540,7 @@ function useAfterPhysicsStep(callback) {
|
|
|
1432
1540
|
const callbackRef = useRef(callback);
|
|
1433
1541
|
callbackRef.current = callback;
|
|
1434
1542
|
useEffect(() => {
|
|
1435
|
-
const wrapped = (
|
|
1543
|
+
const wrapped = (input) => callbackRef.current(input);
|
|
1436
1544
|
afterStepCallbacks.current.add(wrapped);
|
|
1437
1545
|
return () => {
|
|
1438
1546
|
afterStepCallbacks.current.delete(wrapped);
|
|
@@ -1576,7 +1684,7 @@ function MujocoSimProvider({
|
|
|
1576
1684
|
useEffect(() => {
|
|
1577
1685
|
if (status === "ready") {
|
|
1578
1686
|
const api2 = apiRef.current;
|
|
1579
|
-
if (onReady) onReady(api2);
|
|
1687
|
+
if (onReady) onReady({ api: api2 });
|
|
1580
1688
|
if (externalApiRef) {
|
|
1581
1689
|
if (typeof externalApiRef === "function") {
|
|
1582
1690
|
externalApiRef(api2);
|
|
@@ -1596,7 +1704,7 @@ function MujocoSimProvider({
|
|
|
1596
1704
|
data.qfrc_applied[i] = 0;
|
|
1597
1705
|
}
|
|
1598
1706
|
for (const cb of beforeStepCallbacks.current) {
|
|
1599
|
-
cb(model, data);
|
|
1707
|
+
cb({ model, data });
|
|
1600
1708
|
}
|
|
1601
1709
|
const numSubsteps = substepsRef.current;
|
|
1602
1710
|
if (!interpolateRef.current) {
|
|
@@ -1647,14 +1755,14 @@ function MujocoSimProvider({
|
|
|
1647
1755
|
interpolationStateRef.current.alpha = Math.min(Math.max(physicsAccumulatorRef.current / stepDt, 0), 1);
|
|
1648
1756
|
interpolationStateRef.current.valid = true;
|
|
1649
1757
|
if (!stepped) {
|
|
1650
|
-
onStepRef.current?.(data.time);
|
|
1758
|
+
onStepRef.current?.({ time: data.time, model, data });
|
|
1651
1759
|
return;
|
|
1652
1760
|
}
|
|
1653
1761
|
}
|
|
1654
1762
|
for (const cb of afterStepCallbacks.current) {
|
|
1655
|
-
cb(model, data);
|
|
1763
|
+
cb({ model, data });
|
|
1656
1764
|
}
|
|
1657
|
-
onStepRef.current?.(data.time);
|
|
1765
|
+
onStepRef.current?.({ time: data.time, model, data });
|
|
1658
1766
|
}, -1);
|
|
1659
1767
|
function ensureInterpolationBuffers(model) {
|
|
1660
1768
|
const state = interpolationStateRef.current;
|
|
@@ -1685,7 +1793,7 @@ function MujocoSimProvider({
|
|
|
1685
1793
|
}
|
|
1686
1794
|
}
|
|
1687
1795
|
}
|
|
1688
|
-
configRef.current.onReset?.(model, data);
|
|
1796
|
+
configRef.current.onReset?.({ model, data });
|
|
1689
1797
|
mujoco.mj_forward(model, data);
|
|
1690
1798
|
for (const cb of resetCallbacks.current) {
|
|
1691
1799
|
cb();
|
|
@@ -2145,6 +2253,21 @@ function MujocoSimProvider({
|
|
|
2145
2253
|
},
|
|
2146
2254
|
[gl]
|
|
2147
2255
|
);
|
|
2256
|
+
const getCanvas = useCallback(() => {
|
|
2257
|
+
return gl.domElement ?? null;
|
|
2258
|
+
}, [gl]);
|
|
2259
|
+
const captureFrameApi = useCallback(
|
|
2260
|
+
(options = {}) => {
|
|
2261
|
+
return captureFrame({ ...options, target: gl.domElement });
|
|
2262
|
+
},
|
|
2263
|
+
[gl]
|
|
2264
|
+
);
|
|
2265
|
+
const captureFrameBlobApi = useCallback(
|
|
2266
|
+
(options = {}) => {
|
|
2267
|
+
return captureFrameBlob({ ...options, target: gl.domElement });
|
|
2268
|
+
},
|
|
2269
|
+
[gl]
|
|
2270
|
+
);
|
|
2148
2271
|
const project2DTo3D = useCallback(
|
|
2149
2272
|
(x, y, cameraPos, lookAt) => {
|
|
2150
2273
|
const virtCam = camera.clone();
|
|
@@ -2251,7 +2374,10 @@ function MujocoSimProvider({
|
|
|
2251
2374
|
addBody: addBodyApi,
|
|
2252
2375
|
removeBody: removeBodyApi,
|
|
2253
2376
|
recompile: recompileApi,
|
|
2377
|
+
getCanvas,
|
|
2254
2378
|
getCanvasSnapshot,
|
|
2379
|
+
captureFrame: captureFrameApi,
|
|
2380
|
+
captureFrameBlob: captureFrameBlobApi,
|
|
2255
2381
|
project2DTo3D,
|
|
2256
2382
|
setBodyMass,
|
|
2257
2383
|
setGeomFriction,
|
|
@@ -2303,7 +2429,10 @@ function MujocoSimProvider({
|
|
|
2303
2429
|
addBodyApi,
|
|
2304
2430
|
removeBodyApi,
|
|
2305
2431
|
recompileApi,
|
|
2432
|
+
getCanvas,
|
|
2306
2433
|
getCanvasSnapshot,
|
|
2434
|
+
captureFrameApi,
|
|
2435
|
+
captureFrameBlobApi,
|
|
2307
2436
|
project2DTo3D,
|
|
2308
2437
|
setBodyMass,
|
|
2309
2438
|
setGeomFriction,
|
|
@@ -2841,9 +2970,9 @@ var useIkController = createControllerHook(
|
|
|
2841
2970
|
}
|
|
2842
2971
|
}, [config?.siteName, config?.numJoints, config?.joints, config?.actuators, status, mjModelRef, mjDataRef, config]);
|
|
2843
2972
|
const ikSolveFn = useCallback(
|
|
2844
|
-
(
|
|
2973
|
+
({ position, quaternion, currentQ, context }) => {
|
|
2845
2974
|
if (!config) return null;
|
|
2846
|
-
if (config.ikSolveFn) return config.ikSolveFn(
|
|
2975
|
+
if (config.ikSolveFn) return config.ikSolveFn({ position, quaternion, currentQ, context });
|
|
2847
2976
|
const model = mjModelRef.current;
|
|
2848
2977
|
const data = mjDataRef.current;
|
|
2849
2978
|
const controlGroup = controlGroupRef.current;
|
|
@@ -2853,8 +2982,8 @@ var useIkController = createControllerHook(
|
|
|
2853
2982
|
data,
|
|
2854
2983
|
siteIdRef.current,
|
|
2855
2984
|
controlGroup.qposAdr,
|
|
2856
|
-
|
|
2857
|
-
|
|
2985
|
+
position,
|
|
2986
|
+
quaternion,
|
|
2858
2987
|
currentQ,
|
|
2859
2988
|
{ damping: config.damping, maxIterations: config.maxIterations }
|
|
2860
2989
|
);
|
|
@@ -2883,7 +3012,7 @@ var useIkController = createControllerHook(
|
|
|
2883
3012
|
target.quaternion.slerpQuaternions(ga.startRot, ga.endRot, ease);
|
|
2884
3013
|
if (t >= 1) ga.active = false;
|
|
2885
3014
|
});
|
|
2886
|
-
useBeforePhysicsStep((model, data) => {
|
|
3015
|
+
useBeforePhysicsStep(({ model, data }) => {
|
|
2887
3016
|
if (!config || !ikEnabledRef.current) {
|
|
2888
3017
|
ikCalculatingRef.current = false;
|
|
2889
3018
|
return;
|
|
@@ -2894,12 +3023,21 @@ var useIkController = createControllerHook(
|
|
|
2894
3023
|
const controlGroup = controlGroupRef.current;
|
|
2895
3024
|
if (!controlGroup) return;
|
|
2896
3025
|
const currentQ = Array.from(controlGroup.readQpos(data));
|
|
2897
|
-
const solution = config.ikSolveFn ? config.ikSolveFn(
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
3026
|
+
const solution = config.ikSolveFn ? config.ikSolveFn({
|
|
3027
|
+
position: target.position,
|
|
3028
|
+
quaternion: target.quaternion,
|
|
3029
|
+
currentQ,
|
|
3030
|
+
context: {
|
|
3031
|
+
model,
|
|
3032
|
+
data,
|
|
3033
|
+
siteId: siteIdRef.current,
|
|
3034
|
+
controlGroup
|
|
3035
|
+
}
|
|
3036
|
+
}) : ikSolveFnRef.current({
|
|
3037
|
+
position: target.position,
|
|
3038
|
+
quaternion: target.quaternion,
|
|
3039
|
+
currentQ
|
|
3040
|
+
});
|
|
2903
3041
|
if (solution) {
|
|
2904
3042
|
controlGroup.writeCtrl(data, solution);
|
|
2905
3043
|
}
|
|
@@ -2938,8 +3076,8 @@ var useIkController = createControllerHook(
|
|
|
2938
3076
|
if (data && target) syncGizmoToSite(data, siteIdRef.current, target);
|
|
2939
3077
|
}, [mjDataRef]);
|
|
2940
3078
|
const solveIK = useCallback(
|
|
2941
|
-
(
|
|
2942
|
-
return ikSolveFnRef.current(
|
|
3079
|
+
(input) => {
|
|
3080
|
+
return ikSolveFnRef.current(input);
|
|
2943
3081
|
},
|
|
2944
3082
|
[]
|
|
2945
3083
|
);
|
|
@@ -3153,7 +3291,7 @@ function IkGizmo({ controller, siteName, scale = 0.18, onDrag }) {
|
|
|
3153
3291
|
onDrag: (_l, _dl, world) => {
|
|
3154
3292
|
world.decompose(_pos, _quat, _scale);
|
|
3155
3293
|
if (onDrag) {
|
|
3156
|
-
onDrag(_pos.clone(), _quat.clone());
|
|
3294
|
+
onDrag({ position: _pos.clone(), quaternion: _quat.clone() });
|
|
3157
3295
|
} else {
|
|
3158
3296
|
const target = ikTargetRef.current;
|
|
3159
3297
|
if (target) {
|
|
@@ -3327,7 +3465,7 @@ function DragInteraction({
|
|
|
3327
3465
|
window.removeEventListener("pointercancel", onPointerUp);
|
|
3328
3466
|
};
|
|
3329
3467
|
}, [gl, camera, scene, controls, mjDataRef]);
|
|
3330
|
-
useBeforePhysicsStep((model, data) => {
|
|
3468
|
+
useBeforePhysicsStep(({ model, data }) => {
|
|
3331
3469
|
if (!draggingRef.current || bodyIdRef.current <= 0) return;
|
|
3332
3470
|
const bid = bodyIdRef.current;
|
|
3333
3471
|
const mujoco = mujocoRef.current;
|
|
@@ -4118,7 +4256,7 @@ function useContacts(bodyName, callback) {
|
|
|
4118
4256
|
bodyIdRef.current = findBodyByName(model, bodyName);
|
|
4119
4257
|
bodyResolvedRef.current = true;
|
|
4120
4258
|
}, [bodyName, status, mjModelRef]);
|
|
4121
|
-
useAfterPhysicsStep((model, data) => {
|
|
4259
|
+
useAfterPhysicsStep(({ model, data }) => {
|
|
4122
4260
|
if (bodyName && !bodyResolvedRef.current) {
|
|
4123
4261
|
bodyIdRef.current = findBodyByName(model, bodyName);
|
|
4124
4262
|
bodyResolvedRef.current = true;
|
|
@@ -4224,7 +4362,7 @@ function useTrajectoryPlayer(trajectory, options = {}) {
|
|
|
4224
4362
|
const setState = useCallback((next) => {
|
|
4225
4363
|
if (stateRef.current === next) return;
|
|
4226
4364
|
stateRef.current = next;
|
|
4227
|
-
optionsRef.current.onStateChange?.(next);
|
|
4365
|
+
optionsRef.current.onStateChange?.({ state: next });
|
|
4228
4366
|
}, []);
|
|
4229
4367
|
const play = useCallback(() => {
|
|
4230
4368
|
const traj = trajectoryRef.current;
|
|
@@ -4306,7 +4444,7 @@ function useTrajectoryPlayer(trajectory, options = {}) {
|
|
|
4306
4444
|
}
|
|
4307
4445
|
}
|
|
4308
4446
|
});
|
|
4309
|
-
useBeforePhysicsStep((model, data) => {
|
|
4447
|
+
useBeforePhysicsStep(({ model, data }) => {
|
|
4310
4448
|
if (stateRef.current !== "playing") return;
|
|
4311
4449
|
if ((optionsRef.current.mode ?? "kinematic") !== "physics") return;
|
|
4312
4450
|
const traj = trajectoryRef.current;
|
|
@@ -4391,7 +4529,10 @@ function TrajectoryPlayer({
|
|
|
4391
4529
|
const currentFrame = player.frame;
|
|
4392
4530
|
if (currentFrame !== lastReportedFrameRef.current && player.playing) {
|
|
4393
4531
|
lastReportedFrameRef.current = currentFrame;
|
|
4394
|
-
onFrameRef.current(
|
|
4532
|
+
onFrameRef.current({
|
|
4533
|
+
frameIndex: currentFrame,
|
|
4534
|
+
frame: trajectory[currentFrame]
|
|
4535
|
+
});
|
|
4395
4536
|
}
|
|
4396
4537
|
});
|
|
4397
4538
|
return null;
|
|
@@ -4465,7 +4606,7 @@ function useSitePosition(siteName) {
|
|
|
4465
4606
|
|
|
4466
4607
|
// src/hooks/useGravityCompensation.ts
|
|
4467
4608
|
function useGravityCompensation(enabled = true) {
|
|
4468
|
-
useBeforePhysicsStep((model, data) => {
|
|
4609
|
+
useBeforePhysicsStep(({ model, data }) => {
|
|
4469
4610
|
if (!enabled) return;
|
|
4470
4611
|
for (let i = 0; i < model.nv; i++) {
|
|
4471
4612
|
data.qfrc_applied[i] += data.qfrc_bias[i];
|
|
@@ -4492,7 +4633,7 @@ function useSensor(name) {
|
|
|
4492
4633
|
}
|
|
4493
4634
|
sensorIdRef.current = -1;
|
|
4494
4635
|
}, [name, status, mjModelRef]);
|
|
4495
|
-
useAfterPhysicsStep((
|
|
4636
|
+
useAfterPhysicsStep(({ data }) => {
|
|
4496
4637
|
if (sensorIdRef.current < 0) return;
|
|
4497
4638
|
const adr = sensorAdrRef.current;
|
|
4498
4639
|
const dim = sensorDimRef.current;
|
|
@@ -4589,7 +4730,7 @@ function useJointState(name) {
|
|
|
4589
4730
|
}
|
|
4590
4731
|
jointIdRef.current = -1;
|
|
4591
4732
|
}, [name, status, mjModelRef]);
|
|
4592
|
-
useAfterPhysicsStep((
|
|
4733
|
+
useAfterPhysicsStep(({ data }) => {
|
|
4593
4734
|
if (jointIdRef.current < 0) return;
|
|
4594
4735
|
const qa = qposAdrRef.current;
|
|
4595
4736
|
const da = dofAdrRef.current;
|
|
@@ -4619,7 +4760,7 @@ function useBodyState(name) {
|
|
|
4619
4760
|
if (!model || status !== "ready") return;
|
|
4620
4761
|
bodyIdRef.current = findBodyByName(model, name);
|
|
4621
4762
|
}, [name, status, mjModelRef]);
|
|
4622
|
-
useAfterPhysicsStep((
|
|
4763
|
+
useAfterPhysicsStep(({ data }) => {
|
|
4623
4764
|
const bid = bodyIdRef.current;
|
|
4624
4765
|
if (bid < 0) return;
|
|
4625
4766
|
const i3 = bid * 3;
|
|
@@ -4715,7 +4856,7 @@ function useKeyboardTeleop(config) {
|
|
|
4715
4856
|
window.removeEventListener("keyup", onKeyUp);
|
|
4716
4857
|
};
|
|
4717
4858
|
}, []);
|
|
4718
|
-
useBeforePhysicsStep((
|
|
4859
|
+
useBeforePhysicsStep(({ data }) => {
|
|
4719
4860
|
if (!enabledRef.current) return;
|
|
4720
4861
|
const bindings = bindingsRef.current;
|
|
4721
4862
|
const cache = actuatorCacheRef.current;
|
|
@@ -4743,7 +4884,7 @@ function usePolicy(config) {
|
|
|
4743
4884
|
const configRef = useRef(config);
|
|
4744
4885
|
configRef.current = config;
|
|
4745
4886
|
isRunningRef.current = config.enabled ?? isRunningRef.current;
|
|
4746
|
-
useBeforePhysicsStep((model, data) => {
|
|
4887
|
+
useBeforePhysicsStep(({ model, data }) => {
|
|
4747
4888
|
if (!isRunningRef.current) return;
|
|
4748
4889
|
const cfg = configRef.current;
|
|
4749
4890
|
model.opt?.timestep ?? 2e-3;
|
|
@@ -4800,7 +4941,7 @@ function useTrajectoryRecorder(options = {}) {
|
|
|
4800
4941
|
const recordingRef = useRef(false);
|
|
4801
4942
|
const framesRef = useRef([]);
|
|
4802
4943
|
const fields = options.fields ?? ["qpos"];
|
|
4803
|
-
useAfterPhysicsStep((
|
|
4944
|
+
useAfterPhysicsStep(({ data }) => {
|
|
4804
4945
|
if (!recordingRef.current) return;
|
|
4805
4946
|
const frame = {
|
|
4806
4947
|
time: data.time,
|
|
@@ -4889,7 +5030,7 @@ function useGamepad(config) {
|
|
|
4889
5030
|
buttonCacheRef.current.set(Number(idx), findActuatorByName(model, name));
|
|
4890
5031
|
}
|
|
4891
5032
|
}, [config.axes, config.buttons, status, mjModelRef]);
|
|
4892
|
-
useBeforePhysicsStep((
|
|
5033
|
+
useBeforePhysicsStep(({ data }) => {
|
|
4893
5034
|
const cfg = configRef.current;
|
|
4894
5035
|
if (cfg.enabled === false) return;
|
|
4895
5036
|
const gamepads = navigator.getGamepads?.();
|
|
@@ -4967,120 +5108,12 @@ function useVideoRecorder(options = {}) {
|
|
|
4967
5108
|
}
|
|
4968
5109
|
};
|
|
4969
5110
|
}
|
|
4970
|
-
function isTargetRef(target) {
|
|
4971
|
-
return Boolean(target && typeof target === "object" && "current" in target);
|
|
4972
|
-
}
|
|
4973
|
-
function resolveCanvasTarget(target) {
|
|
4974
|
-
const resolvedTarget = isTargetRef(target) ? target.current : target;
|
|
4975
|
-
if (!resolvedTarget) {
|
|
4976
|
-
throw new Error("No frame capture target is available.");
|
|
4977
|
-
}
|
|
4978
|
-
if (resolvedTarget instanceof HTMLCanvasElement) {
|
|
4979
|
-
return resolvedTarget;
|
|
4980
|
-
}
|
|
4981
|
-
const canvas = resolvedTarget.querySelector("canvas");
|
|
4982
|
-
if (!canvas) {
|
|
4983
|
-
throw new Error("Frame capture target does not contain a canvas.");
|
|
4984
|
-
}
|
|
4985
|
-
return canvas;
|
|
4986
|
-
}
|
|
4987
|
-
function waitForNextAnimationFrame() {
|
|
4988
|
-
return new Promise((resolve) => {
|
|
4989
|
-
requestAnimationFrame(() => resolve());
|
|
4990
|
-
});
|
|
4991
|
-
}
|
|
4992
|
-
async function captureFrame(options) {
|
|
4993
|
-
const type = options.type ?? "image/png";
|
|
4994
|
-
const canvas = resolveCanvasTarget(options.target);
|
|
4995
|
-
if (options.waitForAnimationFrame ?? true) {
|
|
4996
|
-
await waitForNextAnimationFrame();
|
|
4997
|
-
}
|
|
4998
|
-
return {
|
|
4999
|
-
canvas,
|
|
5000
|
-
dataUrl: canvas.toDataURL(type, options.quality),
|
|
5001
|
-
type
|
|
5002
|
-
};
|
|
5003
|
-
}
|
|
5004
|
-
async function captureFrameBlob(options) {
|
|
5005
|
-
const type = options.type ?? "image/png";
|
|
5006
|
-
const canvas = resolveCanvasTarget(options.target);
|
|
5007
|
-
if (options.waitForAnimationFrame ?? true) {
|
|
5008
|
-
await waitForNextAnimationFrame();
|
|
5009
|
-
}
|
|
5010
|
-
const blob = await new Promise((resolve, reject) => {
|
|
5011
|
-
canvas.toBlob(
|
|
5012
|
-
(nextBlob) => {
|
|
5013
|
-
if (nextBlob) {
|
|
5014
|
-
resolve(nextBlob);
|
|
5015
|
-
} else {
|
|
5016
|
-
reject(new Error("Canvas frame capture did not produce a Blob."));
|
|
5017
|
-
}
|
|
5018
|
-
},
|
|
5019
|
-
type,
|
|
5020
|
-
options.quality
|
|
5021
|
-
);
|
|
5022
|
-
});
|
|
5023
|
-
return { canvas, blob, type };
|
|
5024
|
-
}
|
|
5025
|
-
function useFrameCapture(defaultOptions = {}) {
|
|
5026
|
-
const [status, setStatus] = useState("idle");
|
|
5027
|
-
const [error, setError] = useState(null);
|
|
5028
|
-
const reset = useCallback(() => {
|
|
5029
|
-
setStatus("idle");
|
|
5030
|
-
setError(null);
|
|
5031
|
-
}, []);
|
|
5032
|
-
const capture = useCallback(
|
|
5033
|
-
async (options = {}) => {
|
|
5034
|
-
setStatus("capturing");
|
|
5035
|
-
setError(null);
|
|
5036
|
-
try {
|
|
5037
|
-
const result = await captureFrame({ ...defaultOptions, ...options });
|
|
5038
|
-
setStatus("captured");
|
|
5039
|
-
return result;
|
|
5040
|
-
} catch (nextError) {
|
|
5041
|
-
const error2 = nextError instanceof Error ? nextError : new Error("Unable to capture the current canvas frame.");
|
|
5042
|
-
setError(error2);
|
|
5043
|
-
setStatus("error");
|
|
5044
|
-
throw error2;
|
|
5045
|
-
}
|
|
5046
|
-
},
|
|
5047
|
-
[defaultOptions]
|
|
5048
|
-
);
|
|
5049
|
-
const captureBlob = useCallback(
|
|
5050
|
-
async (options = {}) => {
|
|
5051
|
-
setStatus("capturing");
|
|
5052
|
-
setError(null);
|
|
5053
|
-
try {
|
|
5054
|
-
const result = await captureFrameBlob({
|
|
5055
|
-
...defaultOptions,
|
|
5056
|
-
...options
|
|
5057
|
-
});
|
|
5058
|
-
setStatus("captured");
|
|
5059
|
-
return result;
|
|
5060
|
-
} catch (nextError) {
|
|
5061
|
-
const error2 = nextError instanceof Error ? nextError : new Error("Unable to capture the current canvas frame.");
|
|
5062
|
-
setError(error2);
|
|
5063
|
-
setStatus("error");
|
|
5064
|
-
throw error2;
|
|
5065
|
-
}
|
|
5066
|
-
},
|
|
5067
|
-
[defaultOptions]
|
|
5068
|
-
);
|
|
5069
|
-
return {
|
|
5070
|
-
status,
|
|
5071
|
-
error,
|
|
5072
|
-
isCapturing: status === "capturing",
|
|
5073
|
-
capture,
|
|
5074
|
-
captureBlob,
|
|
5075
|
-
reset
|
|
5076
|
-
};
|
|
5077
|
-
}
|
|
5078
5111
|
function useCtrlNoise(config = {}) {
|
|
5079
5112
|
const { mjModelRef } = useMujocoContext();
|
|
5080
5113
|
const configRef = useRef(config);
|
|
5081
5114
|
configRef.current = config;
|
|
5082
5115
|
const noiseRef = useRef(null);
|
|
5083
|
-
useBeforePhysicsStep((
|
|
5116
|
+
useBeforePhysicsStep(({ data }) => {
|
|
5084
5117
|
const cfg = configRef.current;
|
|
5085
5118
|
if (cfg.enabled === false) return;
|
|
5086
5119
|
const rate = cfg.rate ?? 0.01;
|
|
@@ -5227,6 +5260,12 @@ function useCameraAnimation() {
|
|
|
5227
5260
|
* @license
|
|
5228
5261
|
* SPDX-License-Identifier: Apache-2.0
|
|
5229
5262
|
*/
|
|
5263
|
+
/**
|
|
5264
|
+
* @license
|
|
5265
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5266
|
+
*
|
|
5267
|
+
* useFrameCapture — still-frame capture for canvas-backed MuJoCo/R3F scenes.
|
|
5268
|
+
*/
|
|
5230
5269
|
/**
|
|
5231
5270
|
* @license
|
|
5232
5271
|
* SPDX-License-Identifier: Apache-2.0
|
|
@@ -5360,12 +5399,6 @@ function useCameraAnimation() {
|
|
|
5360
5399
|
*
|
|
5361
5400
|
* useVideoRecorder — canvas video recording hook (spec 13.3)
|
|
5362
5401
|
*/
|
|
5363
|
-
/**
|
|
5364
|
-
* @license
|
|
5365
|
-
* SPDX-License-Identifier: Apache-2.0
|
|
5366
|
-
*
|
|
5367
|
-
* useFrameCapture — still-frame capture for canvas-backed MuJoCo/R3F scenes.
|
|
5368
|
-
*/
|
|
5369
5402
|
/**
|
|
5370
5403
|
* @license
|
|
5371
5404
|
* SPDX-License-Identifier: Apache-2.0
|