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 +75 -137
- package/dist/{chunk-6AZEFI6A.js → chunk-KHZ5U36J.js} +157 -16
- package/dist/chunk-KHZ5U36J.js.map +1 -0
- package/dist/index.d.ts +180 -49
- package/dist/index.js +627 -19
- package/dist/index.js.map +1 -1
- package/dist/onnx.d.ts +65 -0
- package/dist/onnx.js +58 -0
- package/dist/onnx.js.map +1 -0
- package/dist/spark.d.ts +1 -1
- package/dist/spark.js +1 -1
- package/dist/{types-BOhNDICK.d.ts → types-CViUme8D.d.ts} +157 -1
- package/package.json +14 -3
- package/src/components/CameraView.tsx +245 -0
- package/src/components/Debug.tsx +174 -3
- package/src/core/GenericIK.ts +16 -4
- package/src/core/MujocoSimProvider.tsx +37 -1
- package/src/core/SceneLoader.ts +3 -2
- package/src/hooks/useCameraStream.ts +115 -0
- package/src/hooks/useControlGroup.ts +0 -0
- package/src/hooks/useIkController.ts +3 -0
- package/src/hooks/usePolicyCameraTensors.ts +215 -0
- package/src/index.ts +45 -0
- package/src/onnx.ts +126 -0
- package/src/policyImageTensors.ts +150 -0
- package/src/rendering/cameraFrameCapture.ts +112 -15
- package/src/types.ts +45 -0
- package/dist/chunk-6AZEFI6A.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { withContacts, getContact, captureCameraFrame, captureCameraFrameBlob, createCameraFrameCaptureSession, CAMERA_FRAME_CAPTURE_PRE_RENDER_USER_DATA_KEY, CAPTURE_EXCLUDE_KEY } from './chunk-
|
|
2
|
-
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, withSplatEnvironment } from './chunk-
|
|
1
|
+
import { withContacts, getContact, captureCameraFrame, captureCameraFrameBlob, createCameraFrameCaptureSession, CAMERA_FRAME_CAPTURE_PRE_RENDER_USER_DATA_KEY, CAPTURE_EXCLUDE_KEY, createCaptureCamera, prepareCaptureCamera } from './chunk-KHZ5U36J.js';
|
|
2
|
+
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, createPairedSplatEnvironment, createSparkSplatViewerUrl, createSplatEnvironmentUserData, createSplatSceneConfig, createVisualScenarioExecutionContext, dataUrlToPolicyImageTensor, getContact, getScenarioBackground, getScenarioCameraPosition, getSplatEnvironmentReadiness, imageDataToPolicyImageTensor, pixelsToPolicyImageTensor, registerModelResources, renderCameraFrameToCanvas, useSplatEnvironment, useSplatSceneConfig, useVisualScenarioEffects, useVisualScenarioExecutionContext, withSplatEnvironment } from './chunk-KHZ5U36J.js';
|
|
3
3
|
import loadMujoco from '@mujoco/mujoco';
|
|
4
4
|
import defaultMujocoWasmUrl from '@mujoco/mujoco/mujoco.wasm?url';
|
|
5
|
-
import { createContext, forwardRef, useRef, useEffect, useContext, useState, useCallback, useMemo, useLayoutEffect } from 'react';
|
|
5
|
+
import { createContext, forwardRef, useRef, useEffect, useContext, useState, useCallback, useMemo, useLayoutEffect, useId } from 'react';
|
|
6
6
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
7
7
|
import { Canvas, useThree, useFrame } from '@react-three/fiber';
|
|
8
8
|
import * as THREE11 from 'three';
|
|
@@ -492,8 +492,10 @@ function sceneObjectToXml(obj) {
|
|
|
492
492
|
const solref = obj.solref ? ` solref="${obj.solref}"` : "";
|
|
493
493
|
const solimp = obj.solimp ? ` solimp="${obj.solimp}"` : "";
|
|
494
494
|
const condim = obj.condim ? ` condim="${obj.condim}"` : "";
|
|
495
|
+
const contype = obj.contype ?? 1;
|
|
496
|
+
const conaffinity = obj.conaffinity ?? 1;
|
|
495
497
|
const group = obj.group !== void 0 ? ` group="${obj.group}"` : "";
|
|
496
|
-
return `<body name="${obj.name}" pos="${pos}">${joint}<geom name="${geomName}" type="${obj.type}" size="${size}" rgba="${rgba}" contype="
|
|
498
|
+
return `<body name="${obj.name}" pos="${pos}">${joint}<geom name="${geomName}" type="${obj.type}" size="${size}" rgba="${rgba}" contype="${contype}" conaffinity="${conaffinity}"${mass}${friction}${solref}${solimp}${condim}${group}/></body>`;
|
|
497
499
|
}
|
|
498
500
|
function ensureDir(mujoco, fname) {
|
|
499
501
|
const dirParts = fname.split("/");
|
|
@@ -1170,6 +1172,118 @@ function SceneRenderer({ renderOptions, ...props }) {
|
|
|
1170
1172
|
}
|
|
1171
1173
|
var _previousQuat = new THREE11.Quaternion();
|
|
1172
1174
|
var _currentQuat = new THREE11.Quaternion();
|
|
1175
|
+
var CameraViewportRegistryContext = createContext(null);
|
|
1176
|
+
var nextViewportId = 0;
|
|
1177
|
+
var VIEWPORT_RENDER_PRIORITY = 1;
|
|
1178
|
+
function CameraViewportRenderer({
|
|
1179
|
+
viewportsRef
|
|
1180
|
+
}) {
|
|
1181
|
+
const gl = useThree((state) => state.gl);
|
|
1182
|
+
const scene = useThree((state) => state.scene);
|
|
1183
|
+
const mainCamera = useThree((state) => state.camera);
|
|
1184
|
+
const mujoco = useMujoco();
|
|
1185
|
+
useFrame(() => {
|
|
1186
|
+
const drawWidth = gl.domElement.width;
|
|
1187
|
+
const drawHeight = gl.domElement.height;
|
|
1188
|
+
gl.setScissorTest(false);
|
|
1189
|
+
gl.setViewport(0, 0, drawWidth, drawHeight);
|
|
1190
|
+
gl.render(scene, mainCamera);
|
|
1191
|
+
const api = mujoco.api;
|
|
1192
|
+
if (!api || viewportsRef.current.size === 0) return;
|
|
1193
|
+
const dpr = gl.getPixelRatio();
|
|
1194
|
+
const canvasRect = gl.domElement.getBoundingClientRect();
|
|
1195
|
+
for (const descriptor of viewportsRef.current.values()) {
|
|
1196
|
+
const element = descriptor.getElement();
|
|
1197
|
+
if (!element) continue;
|
|
1198
|
+
const rect = element.getBoundingClientRect();
|
|
1199
|
+
const isOffscreen = rect.bottom < canvasRect.top || rect.top > canvasRect.bottom || rect.right < canvasRect.left || rect.left > canvasRect.right;
|
|
1200
|
+
if (isOffscreen) continue;
|
|
1201
|
+
const width = Math.floor(rect.width * dpr);
|
|
1202
|
+
const height = Math.floor(rect.height * dpr);
|
|
1203
|
+
if (width <= 0 || height <= 0) continue;
|
|
1204
|
+
const left = Math.floor((rect.left - canvasRect.left) * dpr);
|
|
1205
|
+
const bottom = Math.floor((canvasRect.bottom - rect.bottom) * dpr);
|
|
1206
|
+
let resolved;
|
|
1207
|
+
try {
|
|
1208
|
+
resolved = api.resolveCameraCaptureOptions(descriptor.getOptions());
|
|
1209
|
+
} catch {
|
|
1210
|
+
continue;
|
|
1211
|
+
}
|
|
1212
|
+
if (!descriptor.camera) {
|
|
1213
|
+
descriptor.camera = createCaptureCamera(resolved, mainCamera, width, height);
|
|
1214
|
+
} else {
|
|
1215
|
+
prepareCaptureCamera(descriptor.camera, resolved, mainCamera, width, height);
|
|
1216
|
+
}
|
|
1217
|
+
gl.setViewport(left, bottom, width, height);
|
|
1218
|
+
gl.setScissor(left, bottom, width, height);
|
|
1219
|
+
gl.setScissorTest(true);
|
|
1220
|
+
gl.render(scene, descriptor.camera);
|
|
1221
|
+
}
|
|
1222
|
+
gl.setScissorTest(false);
|
|
1223
|
+
gl.setViewport(0, 0, drawWidth, drawHeight);
|
|
1224
|
+
}, VIEWPORT_RENDER_PRIORITY);
|
|
1225
|
+
return null;
|
|
1226
|
+
}
|
|
1227
|
+
function CameraViewportProvider({ children }) {
|
|
1228
|
+
const viewportsRef = useRef(/* @__PURE__ */ new Map());
|
|
1229
|
+
const [count, setCount] = useState(0);
|
|
1230
|
+
const register = useCallback((descriptor) => {
|
|
1231
|
+
const id = nextViewportId++;
|
|
1232
|
+
viewportsRef.current.set(id, descriptor);
|
|
1233
|
+
setCount((value2) => value2 + 1);
|
|
1234
|
+
return () => {
|
|
1235
|
+
viewportsRef.current.delete(id);
|
|
1236
|
+
setCount((value2) => value2 - 1);
|
|
1237
|
+
};
|
|
1238
|
+
}, []);
|
|
1239
|
+
const value = useMemo(() => ({ register }), [register]);
|
|
1240
|
+
return /* @__PURE__ */ jsxs(CameraViewportRegistryContext.Provider, { value, children: [
|
|
1241
|
+
children,
|
|
1242
|
+
count > 0 && /* @__PURE__ */ jsx(CameraViewportRenderer, { viewportsRef })
|
|
1243
|
+
] });
|
|
1244
|
+
}
|
|
1245
|
+
function useCameraViewport(elementRef, options) {
|
|
1246
|
+
const registry = useContext(CameraViewportRegistryContext);
|
|
1247
|
+
if (!registry) {
|
|
1248
|
+
throw new Error("useCameraViewport must be used inside <MujocoCanvas>.");
|
|
1249
|
+
}
|
|
1250
|
+
const optionsRef = useRef(options);
|
|
1251
|
+
optionsRef.current = options;
|
|
1252
|
+
useEffect(() => {
|
|
1253
|
+
const descriptor = {
|
|
1254
|
+
getElement: () => elementRef.current,
|
|
1255
|
+
getOptions: () => optionsRef.current,
|
|
1256
|
+
camera: null
|
|
1257
|
+
};
|
|
1258
|
+
return registry.register(descriptor);
|
|
1259
|
+
}, [registry, elementRef]);
|
|
1260
|
+
}
|
|
1261
|
+
function CameraView({ className, style, ...options }) {
|
|
1262
|
+
const gl = useThree((state) => state.gl);
|
|
1263
|
+
const elementRef = useRef(null);
|
|
1264
|
+
if (!elementRef.current && typeof document !== "undefined") {
|
|
1265
|
+
elementRef.current = document.createElement("div");
|
|
1266
|
+
}
|
|
1267
|
+
useEffect(() => {
|
|
1268
|
+
const element = elementRef.current;
|
|
1269
|
+
const parent = gl.domElement.parentElement;
|
|
1270
|
+
if (!element || !parent) return;
|
|
1271
|
+
element.style.position = "absolute";
|
|
1272
|
+
element.style.overflow = "hidden";
|
|
1273
|
+
parent.appendChild(element);
|
|
1274
|
+
return () => {
|
|
1275
|
+
parent.removeChild(element);
|
|
1276
|
+
};
|
|
1277
|
+
}, [gl]);
|
|
1278
|
+
useEffect(() => {
|
|
1279
|
+
const element = elementRef.current;
|
|
1280
|
+
if (!element) return;
|
|
1281
|
+
element.className = className ?? "";
|
|
1282
|
+
if (style) Object.assign(element.style, style);
|
|
1283
|
+
}, [className, style]);
|
|
1284
|
+
useCameraViewport(elementRef, options);
|
|
1285
|
+
return null;
|
|
1286
|
+
}
|
|
1173
1287
|
function isTargetRef(target) {
|
|
1174
1288
|
return Boolean(target && typeof target === "object" && "current" in target);
|
|
1175
1289
|
}
|
|
@@ -3021,6 +3135,32 @@ function MujocoSimProvider({
|
|
|
3021
3135
|
},
|
|
3022
3136
|
[camera, gl, resolveCameraCaptureOptions, scene]
|
|
3023
3137
|
);
|
|
3138
|
+
const createCameraFrameCaptureSessionApi = useCallback(
|
|
3139
|
+
(options = {}) => createCameraFrameCaptureSession(
|
|
3140
|
+
gl,
|
|
3141
|
+
scene,
|
|
3142
|
+
camera,
|
|
3143
|
+
resolveCameraCaptureOptions(options)
|
|
3144
|
+
),
|
|
3145
|
+
[camera, gl, resolveCameraCaptureOptions, scene]
|
|
3146
|
+
);
|
|
3147
|
+
const captureCameraFrameTensorApi = useCallback(
|
|
3148
|
+
(options = {}) => {
|
|
3149
|
+
const resolved = {
|
|
3150
|
+
...resolveCameraCaptureOptions(options),
|
|
3151
|
+
channels: options.channels,
|
|
3152
|
+
layout: options.layout,
|
|
3153
|
+
range: options.range
|
|
3154
|
+
};
|
|
3155
|
+
const session = createCameraFrameCaptureSession(gl, scene, camera, resolved);
|
|
3156
|
+
try {
|
|
3157
|
+
return session.captureTensor(resolved);
|
|
3158
|
+
} finally {
|
|
3159
|
+
session.dispose();
|
|
3160
|
+
}
|
|
3161
|
+
},
|
|
3162
|
+
[camera, gl, resolveCameraCaptureOptions, scene]
|
|
3163
|
+
);
|
|
3024
3164
|
const recordCameraSequenceApi = useCallback(
|
|
3025
3165
|
async (options) => {
|
|
3026
3166
|
const frameCount = Math.max(0, Math.floor(options.frames));
|
|
@@ -3320,6 +3460,9 @@ function MujocoSimProvider({
|
|
|
3320
3460
|
captureFrameBlob: captureFrameBlobApi,
|
|
3321
3461
|
captureCameraFrame: captureCameraFrameApi,
|
|
3322
3462
|
captureCameraFrameBlob: captureCameraFrameBlobApi,
|
|
3463
|
+
captureCameraFrameTensor: captureCameraFrameTensorApi,
|
|
3464
|
+
createCameraFrameCaptureSession: createCameraFrameCaptureSessionApi,
|
|
3465
|
+
resolveCameraCaptureOptions,
|
|
3323
3466
|
recordCameraSequence: recordCameraSequenceApi,
|
|
3324
3467
|
project2DTo3D,
|
|
3325
3468
|
projectImagePointTo3D: projectImagePointTo3D2,
|
|
@@ -3380,6 +3523,9 @@ function MujocoSimProvider({
|
|
|
3380
3523
|
captureFrameBlobApi,
|
|
3381
3524
|
captureCameraFrameApi,
|
|
3382
3525
|
captureCameraFrameBlobApi,
|
|
3526
|
+
captureCameraFrameTensorApi,
|
|
3527
|
+
createCameraFrameCaptureSessionApi,
|
|
3528
|
+
resolveCameraCaptureOptions,
|
|
3383
3529
|
recordCameraSequenceApi,
|
|
3384
3530
|
project2DTo3D,
|
|
3385
3531
|
projectImagePointTo3D2,
|
|
@@ -3416,7 +3562,7 @@ function MujocoSimProvider({
|
|
|
3416
3562
|
);
|
|
3417
3563
|
return /* @__PURE__ */ jsxs(MujocoSimContext.Provider, { value: contextValue, children: [
|
|
3418
3564
|
/* @__PURE__ */ jsx(SceneRenderer, { renderOptions }),
|
|
3419
|
-
children
|
|
3565
|
+
/* @__PURE__ */ jsx(CameraViewportProvider, { children })
|
|
3420
3566
|
] });
|
|
3421
3567
|
}
|
|
3422
3568
|
var MujocoCanvas = forwardRef(
|
|
@@ -3641,9 +3787,16 @@ function resolveOptions(opts) {
|
|
|
3641
3787
|
tolerance: opts?.tolerance ?? DEFAULTS.tolerance,
|
|
3642
3788
|
epsilon: opts?.epsilon ?? DEFAULTS.epsilon,
|
|
3643
3789
|
posWeight: opts?.posWeight ?? DEFAULTS.posWeight,
|
|
3644
|
-
rotWeight: opts?.rotWeight ?? DEFAULTS.rotWeight
|
|
3790
|
+
rotWeight: opts?.rotWeight ?? DEFAULTS.rotWeight,
|
|
3791
|
+
jointLimits: opts?.jointLimits
|
|
3645
3792
|
};
|
|
3646
3793
|
}
|
|
3794
|
+
function clampJoint(value, limit) {
|
|
3795
|
+
if (!limit) return value;
|
|
3796
|
+
const [min, max] = limit;
|
|
3797
|
+
if (!Number.isFinite(min) || !Number.isFinite(max) || min >= max) return value;
|
|
3798
|
+
return Math.max(min, Math.min(max, value));
|
|
3799
|
+
}
|
|
3647
3800
|
var GenericIK = class {
|
|
3648
3801
|
mujoco;
|
|
3649
3802
|
constructor(mujoco) {
|
|
@@ -3668,7 +3821,7 @@ var GenericIK = class {
|
|
|
3668
3821
|
savedQpos.set(data.qpos);
|
|
3669
3822
|
const R_target = quatToMat3(targetQuat);
|
|
3670
3823
|
const q = new Float64Array(n);
|
|
3671
|
-
for (let i = 0; i < n; i++) q[i] = currentQ[i];
|
|
3824
|
+
for (let i = 0; i < n; i++) q[i] = clampJoint(currentQ[i], o.jointLimits?.[i]);
|
|
3672
3825
|
const J = new Float64Array(6 * n);
|
|
3673
3826
|
const JJt = new Float64Array(36);
|
|
3674
3827
|
const rhs = new Float64Array(6);
|
|
@@ -3745,7 +3898,7 @@ var GenericIK = class {
|
|
|
3745
3898
|
}
|
|
3746
3899
|
dq[j] = sum;
|
|
3747
3900
|
}
|
|
3748
|
-
for (let i = 0; i < n; i++) q[i]
|
|
3901
|
+
for (let i = 0; i < n; i++) q[i] = clampJoint(q[i] + dq[i], o.jointLimits?.[i]);
|
|
3749
3902
|
}
|
|
3750
3903
|
data.qpos.set(savedQpos);
|
|
3751
3904
|
this.mujoco.mj_forward(model, data);
|
|
@@ -3940,19 +4093,20 @@ var useIkController = createControllerHook(
|
|
|
3940
4093
|
if (config.ikSolveFn) return config.ikSolveFn({ position, quaternion, currentQ, context });
|
|
3941
4094
|
const model = mjModelRef.current;
|
|
3942
4095
|
const data = mjDataRef.current;
|
|
3943
|
-
const
|
|
3944
|
-
if (!model || !data || !
|
|
4096
|
+
const controlGroup2 = controlGroupRef.current;
|
|
4097
|
+
if (!model || !data || !controlGroup2 || siteIdRef.current === -1) return null;
|
|
3945
4098
|
return genericIkRef.current.solve(
|
|
3946
4099
|
model,
|
|
3947
4100
|
data,
|
|
3948
4101
|
siteIdRef.current,
|
|
3949
|
-
|
|
4102
|
+
controlGroup2.qposAdr,
|
|
3950
4103
|
position,
|
|
3951
4104
|
quaternion,
|
|
3952
4105
|
currentQ,
|
|
3953
4106
|
{
|
|
3954
4107
|
damping: config.damping,
|
|
3955
4108
|
epsilon: config.epsilon,
|
|
4109
|
+
jointLimits: config.jointLimits ?? controlGroup2.joints.map((joint) => joint.limited ? joint.range : joint.ctrlRange),
|
|
3956
4110
|
maxIterations: config.maxIterations,
|
|
3957
4111
|
posWeight: config.posWeight,
|
|
3958
4112
|
rotWeight: config.rotWeight,
|
|
@@ -3992,9 +4146,9 @@ var useIkController = createControllerHook(
|
|
|
3992
4146
|
const target = ikTargetRef.current;
|
|
3993
4147
|
if (!target) return;
|
|
3994
4148
|
ikCalculatingRef.current = true;
|
|
3995
|
-
const
|
|
3996
|
-
if (!
|
|
3997
|
-
const currentQ = Array.from(
|
|
4149
|
+
const controlGroup2 = controlGroupRef.current;
|
|
4150
|
+
if (!controlGroup2) return;
|
|
4151
|
+
const currentQ = Array.from(controlGroup2.readQpos(data));
|
|
3998
4152
|
const solution = config.ikSolveFn ? config.ikSolveFn({
|
|
3999
4153
|
position: target.position,
|
|
4000
4154
|
quaternion: target.quaternion,
|
|
@@ -4003,7 +4157,7 @@ var useIkController = createControllerHook(
|
|
|
4003
4157
|
model,
|
|
4004
4158
|
data,
|
|
4005
4159
|
siteId: siteIdRef.current,
|
|
4006
|
-
controlGroup
|
|
4160
|
+
controlGroup: controlGroup2
|
|
4007
4161
|
}
|
|
4008
4162
|
}) : ikSolveFnRef.current({
|
|
4009
4163
|
position: target.position,
|
|
@@ -4011,7 +4165,7 @@ var useIkController = createControllerHook(
|
|
|
4011
4165
|
currentQ
|
|
4012
4166
|
});
|
|
4013
4167
|
if (solution) {
|
|
4014
|
-
|
|
4168
|
+
controlGroup2.writeCtrl(data, solution);
|
|
4015
4169
|
}
|
|
4016
4170
|
});
|
|
4017
4171
|
useEffect(() => {
|
|
@@ -4848,6 +5002,69 @@ function parseNumberList(value) {
|
|
|
4848
5002
|
function addProxyVectors(a, b) {
|
|
4849
5003
|
return [a[0] + b[0], a[1] + b[1], a[2] + b[2]];
|
|
4850
5004
|
}
|
|
5005
|
+
function streamSignature(options) {
|
|
5006
|
+
return JSON.stringify({
|
|
5007
|
+
cameraName: options.cameraName,
|
|
5008
|
+
siteName: options.siteName,
|
|
5009
|
+
bodyName: options.bodyName,
|
|
5010
|
+
width: options.width,
|
|
5011
|
+
height: options.height,
|
|
5012
|
+
renderIsolation: options.renderIsolation ?? false
|
|
5013
|
+
});
|
|
5014
|
+
}
|
|
5015
|
+
function useCameraStream(canvasRef, options) {
|
|
5016
|
+
const mujoco = useMujoco();
|
|
5017
|
+
const sessionRef = useRef(null);
|
|
5018
|
+
const signatureRef = useRef("");
|
|
5019
|
+
const optionsRef = useRef(options);
|
|
5020
|
+
optionsRef.current = options;
|
|
5021
|
+
const elapsedRef = useRef(0);
|
|
5022
|
+
const inFlightRef = useRef(false);
|
|
5023
|
+
const mountedRef = useRef(true);
|
|
5024
|
+
useEffect(() => {
|
|
5025
|
+
mountedRef.current = true;
|
|
5026
|
+
return () => {
|
|
5027
|
+
mountedRef.current = false;
|
|
5028
|
+
sessionRef.current?.dispose();
|
|
5029
|
+
sessionRef.current = null;
|
|
5030
|
+
signatureRef.current = "";
|
|
5031
|
+
};
|
|
5032
|
+
}, []);
|
|
5033
|
+
useFrame((_state, delta) => {
|
|
5034
|
+
const api = mujoco.api;
|
|
5035
|
+
if (!api || !canvasRef.current) return;
|
|
5036
|
+
const opts = optionsRef.current;
|
|
5037
|
+
if (opts.paused) return;
|
|
5038
|
+
if (opts.fps && opts.fps > 0) {
|
|
5039
|
+
elapsedRef.current += delta;
|
|
5040
|
+
if (elapsedRef.current < 1 / opts.fps) return;
|
|
5041
|
+
}
|
|
5042
|
+
if (inFlightRef.current) return;
|
|
5043
|
+
elapsedRef.current = 0;
|
|
5044
|
+
const signature = streamSignature(opts);
|
|
5045
|
+
if (!sessionRef.current || signatureRef.current !== signature) {
|
|
5046
|
+
sessionRef.current?.dispose();
|
|
5047
|
+
sessionRef.current = api.createCameraFrameCaptureSession(opts);
|
|
5048
|
+
signatureRef.current = signature;
|
|
5049
|
+
}
|
|
5050
|
+
const session = sessionRef.current;
|
|
5051
|
+
inFlightRef.current = true;
|
|
5052
|
+
session.captureAsync(api.resolveCameraCaptureOptions(opts)).then((frame) => {
|
|
5053
|
+
const canvas = canvasRef.current;
|
|
5054
|
+
if (!mountedRef.current || !canvas) return;
|
|
5055
|
+
const ctx = canvas.getContext("2d");
|
|
5056
|
+
if (!ctx) return;
|
|
5057
|
+
if (canvas.width !== frame.width || canvas.height !== frame.height) {
|
|
5058
|
+
canvas.width = frame.width;
|
|
5059
|
+
canvas.height = frame.height;
|
|
5060
|
+
}
|
|
5061
|
+
ctx.drawImage(frame.canvas, 0, 0);
|
|
5062
|
+
}).catch(() => {
|
|
5063
|
+
}).finally(() => {
|
|
5064
|
+
inFlightRef.current = false;
|
|
5065
|
+
});
|
|
5066
|
+
});
|
|
5067
|
+
}
|
|
4851
5068
|
var JOINT_COLORS = {
|
|
4852
5069
|
0: 16711680,
|
|
4853
5070
|
// free - red
|
|
@@ -4867,11 +5084,161 @@ var _contactNormal = new THREE11.Vector3();
|
|
|
4867
5084
|
var MAX_CONTACT_ARROWS = 50;
|
|
4868
5085
|
var CAMERA_DEBUG_LENGTH = 0.12;
|
|
4869
5086
|
var CAMERA_DEBUG_FRUSTUM_DEPTH = 0.08;
|
|
5087
|
+
function toVector32(value, fallback) {
|
|
5088
|
+
if (!value) return fallback.clone();
|
|
5089
|
+
return value instanceof THREE11.Vector3 ? value.clone() : new THREE11.Vector3(value[0], value[1], value[2]);
|
|
5090
|
+
}
|
|
5091
|
+
function createCameraLabel(text, color) {
|
|
5092
|
+
const canvas = document.createElement("canvas");
|
|
5093
|
+
canvas.width = 256;
|
|
5094
|
+
canvas.height = 64;
|
|
5095
|
+
const ctx = canvas.getContext("2d");
|
|
5096
|
+
ctx.fillStyle = new THREE11.Color(color).getStyle();
|
|
5097
|
+
ctx.font = "bold 32px monospace";
|
|
5098
|
+
ctx.textAlign = "center";
|
|
5099
|
+
ctx.fillText(text, 128, 42);
|
|
5100
|
+
const texture = new THREE11.CanvasTexture(canvas);
|
|
5101
|
+
const sprite = new THREE11.Sprite(
|
|
5102
|
+
new THREE11.SpriteMaterial({
|
|
5103
|
+
map: texture,
|
|
5104
|
+
depthTest: false,
|
|
5105
|
+
transparent: true
|
|
5106
|
+
})
|
|
5107
|
+
);
|
|
5108
|
+
sprite.position.set(0, 0.014, 0.01);
|
|
5109
|
+
sprite.scale.set(0.05, 0.012, 1);
|
|
5110
|
+
sprite.renderOrder = 999;
|
|
5111
|
+
return sprite;
|
|
5112
|
+
}
|
|
5113
|
+
function createVirtualCameraDebugObject(camera, index) {
|
|
5114
|
+
const color = camera.color ?? "#ff3d71";
|
|
5115
|
+
const aimColor = camera.aimColor ?? "#38bdf8";
|
|
5116
|
+
const markerScale = camera.markerScale ?? 1;
|
|
5117
|
+
const cameraPosition = toVector32(camera.position, new THREE11.Vector3());
|
|
5118
|
+
const configuredUp = toVector32(camera.up, new THREE11.Vector3(0, 0, 1)).normalize();
|
|
5119
|
+
const cameraQuaternion = new THREE11.Quaternion();
|
|
5120
|
+
const forward = new THREE11.Vector3();
|
|
5121
|
+
if (camera.quaternion) {
|
|
5122
|
+
if (camera.quaternion instanceof THREE11.Quaternion) {
|
|
5123
|
+
cameraQuaternion.copy(camera.quaternion);
|
|
5124
|
+
} else {
|
|
5125
|
+
cameraQuaternion.set(
|
|
5126
|
+
camera.quaternion[0],
|
|
5127
|
+
camera.quaternion[1],
|
|
5128
|
+
camera.quaternion[2],
|
|
5129
|
+
camera.quaternion[3]
|
|
5130
|
+
);
|
|
5131
|
+
}
|
|
5132
|
+
forward.set(0, 0, -1).applyQuaternion(cameraQuaternion).normalize();
|
|
5133
|
+
} else {
|
|
5134
|
+
const target2 = toVector32(
|
|
5135
|
+
camera.lookAt,
|
|
5136
|
+
cameraPosition.clone().add(new THREE11.Vector3(0, 0, -1))
|
|
5137
|
+
);
|
|
5138
|
+
forward.copy(target2).sub(cameraPosition);
|
|
5139
|
+
if (forward.lengthSq() < 1e-8) forward.set(0, 0, -1);
|
|
5140
|
+
forward.normalize();
|
|
5141
|
+
cameraQuaternion.setFromRotationMatrix(
|
|
5142
|
+
new THREE11.Matrix4().lookAt(cameraPosition, target2, configuredUp)
|
|
5143
|
+
);
|
|
5144
|
+
}
|
|
5145
|
+
const target = camera.lookAt ? toVector32(camera.lookAt, cameraPosition.clone().add(forward)) : cameraPosition.clone().addScaledVector(forward, 0.4);
|
|
5146
|
+
const distanceToTarget = Math.max(target.distanceTo(cameraPosition), 1e-3);
|
|
5147
|
+
const depth = camera.frustumDepth ?? Math.min(Math.max(distanceToTarget * 0.42, 0.16), 0.45);
|
|
5148
|
+
const fov = camera.fov ?? 50;
|
|
5149
|
+
const aspect = (camera.width ?? 640) / (camera.height ?? 480);
|
|
5150
|
+
const right = forward.clone().cross(configuredUp);
|
|
5151
|
+
if (right.lengthSq() < 1e-8) right.set(1, 0, 0);
|
|
5152
|
+
right.normalize();
|
|
5153
|
+
const orthogonalUp = right.clone().cross(forward).normalize();
|
|
5154
|
+
const frustumHeight = 2 * Math.tan(THREE11.MathUtils.degToRad(fov) / 2) * depth;
|
|
5155
|
+
const frustumWidth = frustumHeight * aspect;
|
|
5156
|
+
const center = cameraPosition.clone().addScaledVector(forward, depth);
|
|
5157
|
+
const halfRight = right.clone().multiplyScalar(frustumWidth / 2);
|
|
5158
|
+
const halfUp = orthogonalUp.clone().multiplyScalar(frustumHeight / 2);
|
|
5159
|
+
const topLeft = center.clone().sub(halfRight).add(halfUp);
|
|
5160
|
+
const topRight = center.clone().add(halfRight).add(halfUp);
|
|
5161
|
+
const bottomRight = center.clone().add(halfRight).sub(halfUp);
|
|
5162
|
+
const bottomLeft = center.clone().sub(halfRight).sub(halfUp);
|
|
5163
|
+
const frustumPoints = [
|
|
5164
|
+
cameraPosition,
|
|
5165
|
+
topLeft,
|
|
5166
|
+
cameraPosition,
|
|
5167
|
+
topRight,
|
|
5168
|
+
cameraPosition,
|
|
5169
|
+
bottomRight,
|
|
5170
|
+
cameraPosition,
|
|
5171
|
+
bottomLeft,
|
|
5172
|
+
topLeft,
|
|
5173
|
+
topRight,
|
|
5174
|
+
topRight,
|
|
5175
|
+
bottomRight,
|
|
5176
|
+
bottomRight,
|
|
5177
|
+
bottomLeft,
|
|
5178
|
+
bottomLeft,
|
|
5179
|
+
topLeft
|
|
5180
|
+
];
|
|
5181
|
+
const group = new THREE11.Group();
|
|
5182
|
+
group.name = camera.name ?? `virtual-camera-${index}`;
|
|
5183
|
+
group.renderOrder = 999;
|
|
5184
|
+
group.frustumCulled = false;
|
|
5185
|
+
const frustum = new THREE11.LineSegments(
|
|
5186
|
+
new THREE11.BufferGeometry().setFromPoints(frustumPoints),
|
|
5187
|
+
new THREE11.LineBasicMaterial({
|
|
5188
|
+
color,
|
|
5189
|
+
transparent: true,
|
|
5190
|
+
opacity: 0.9,
|
|
5191
|
+
depthTest: false
|
|
5192
|
+
})
|
|
5193
|
+
);
|
|
5194
|
+
frustum.renderOrder = 999;
|
|
5195
|
+
frustum.frustumCulled = false;
|
|
5196
|
+
group.add(frustum);
|
|
5197
|
+
const aim = new THREE11.LineSegments(
|
|
5198
|
+
new THREE11.BufferGeometry().setFromPoints([cameraPosition, target]),
|
|
5199
|
+
new THREE11.LineBasicMaterial({
|
|
5200
|
+
color: aimColor,
|
|
5201
|
+
transparent: true,
|
|
5202
|
+
opacity: 0.95,
|
|
5203
|
+
depthTest: false
|
|
5204
|
+
})
|
|
5205
|
+
);
|
|
5206
|
+
aim.renderOrder = 999;
|
|
5207
|
+
aim.frustumCulled = false;
|
|
5208
|
+
group.add(aim);
|
|
5209
|
+
const markerGroup = new THREE11.Group();
|
|
5210
|
+
markerGroup.position.copy(cameraPosition);
|
|
5211
|
+
markerGroup.quaternion.copy(cameraQuaternion);
|
|
5212
|
+
markerGroup.renderOrder = 999;
|
|
5213
|
+
markerGroup.frustumCulled = false;
|
|
5214
|
+
markerGroup.add(new THREE11.Mesh(
|
|
5215
|
+
new THREE11.BoxGeometry(0.045 * markerScale, 0.028 * markerScale, 0.022 * markerScale),
|
|
5216
|
+
new THREE11.MeshBasicMaterial({ color, depthTest: false })
|
|
5217
|
+
));
|
|
5218
|
+
const lens = new THREE11.Mesh(
|
|
5219
|
+
new THREE11.BoxGeometry(0.025 * markerScale, 0.018 * markerScale, 0.014 * markerScale),
|
|
5220
|
+
new THREE11.MeshBasicMaterial({ color: aimColor, depthTest: false })
|
|
5221
|
+
);
|
|
5222
|
+
lens.position.set(0, 0, -0.021 * markerScale);
|
|
5223
|
+
markerGroup.add(lens);
|
|
5224
|
+
if (camera.name) markerGroup.add(createCameraLabel(camera.name, color));
|
|
5225
|
+
group.add(markerGroup);
|
|
5226
|
+
const targetMarker = new THREE11.Mesh(
|
|
5227
|
+
new THREE11.SphereGeometry(0.018 * markerScale, 16, 10),
|
|
5228
|
+
new THREE11.MeshBasicMaterial({ color: aimColor, depthTest: false })
|
|
5229
|
+
);
|
|
5230
|
+
targetMarker.position.copy(target);
|
|
5231
|
+
targetMarker.renderOrder = 999;
|
|
5232
|
+
targetMarker.frustumCulled = false;
|
|
5233
|
+
group.add(targetMarker);
|
|
5234
|
+
return group;
|
|
5235
|
+
}
|
|
4870
5236
|
function Debug({
|
|
4871
5237
|
showGeoms = false,
|
|
4872
5238
|
showSites = false,
|
|
4873
5239
|
showJoints = false,
|
|
4874
5240
|
showCameras = false,
|
|
5241
|
+
virtualCameras = [],
|
|
4875
5242
|
showContacts = false,
|
|
4876
5243
|
showCOM = false,
|
|
4877
5244
|
showInertia = false,
|
|
@@ -4888,6 +5255,7 @@ function Debug({
|
|
|
4888
5255
|
const sites = [];
|
|
4889
5256
|
const joints = [];
|
|
4890
5257
|
const cameras = [];
|
|
5258
|
+
const virtualCameraObjects = [];
|
|
4891
5259
|
const comMarkers = [];
|
|
4892
5260
|
if (showGeoms) {
|
|
4893
5261
|
for (let i = 0; i < model.ngeom; i++) {
|
|
@@ -5066,6 +5434,9 @@ function Debug({
|
|
|
5066
5434
|
cameras.push(group);
|
|
5067
5435
|
}
|
|
5068
5436
|
}
|
|
5437
|
+
for (let i = 0; i < virtualCameras.length; i += 1) {
|
|
5438
|
+
virtualCameraObjects.push(createVirtualCameraDebugObject(virtualCameras[i], i));
|
|
5439
|
+
}
|
|
5069
5440
|
if (showCOM) {
|
|
5070
5441
|
for (let i = 1; i < model.nbody; i++) {
|
|
5071
5442
|
const geometry = new THREE11.SphereGeometry(5e-3, 6, 6);
|
|
@@ -5075,8 +5446,8 @@ function Debug({
|
|
|
5075
5446
|
comMarkers.push(mesh);
|
|
5076
5447
|
}
|
|
5077
5448
|
}
|
|
5078
|
-
return { geoms, sites, joints, cameras, comMarkers };
|
|
5079
|
-
}, [status, mjModelRef, showGeoms, showSites, showJoints, showCameras, showCOM]);
|
|
5449
|
+
return { geoms, sites, joints, cameras, virtualCameraObjects, comMarkers };
|
|
5450
|
+
}, [status, mjModelRef, showGeoms, showSites, showJoints, showCameras, virtualCameras, showCOM]);
|
|
5080
5451
|
useEffect(() => {
|
|
5081
5452
|
const group = groupRef.current;
|
|
5082
5453
|
if (!group || !debugGeometry) return;
|
|
@@ -5085,6 +5456,7 @@ function Debug({
|
|
|
5085
5456
|
...debugGeometry.sites,
|
|
5086
5457
|
...debugGeometry.joints,
|
|
5087
5458
|
...debugGeometry.cameras,
|
|
5459
|
+
...debugGeometry.virtualCameraObjects,
|
|
5088
5460
|
...debugGeometry.comMarkers
|
|
5089
5461
|
];
|
|
5090
5462
|
for (const obj of allObjects) group.add(obj);
|
|
@@ -6371,6 +6743,73 @@ function useControlWriter(options) {
|
|
|
6371
6743
|
release
|
|
6372
6744
|
}), [canWrite, owner, read, release, write]);
|
|
6373
6745
|
}
|
|
6746
|
+
|
|
6747
|
+
// src/hooks/useControlGroup.ts
|
|
6748
|
+
function controlGroup(names) {
|
|
6749
|
+
return { keys: names };
|
|
6750
|
+
}
|
|
6751
|
+
function useControlGroup(group, options = {}) {
|
|
6752
|
+
const generatedOwner = useId();
|
|
6753
|
+
const owner = options.owner ?? `control-group:${generatedOwner}`;
|
|
6754
|
+
const { mjModelRef, mjDataRef, status } = useMujocoContext();
|
|
6755
|
+
const names = group.keys;
|
|
6756
|
+
const namesRef = useRef(names);
|
|
6757
|
+
namesRef.current = names;
|
|
6758
|
+
const namesKey = names.join("\0");
|
|
6759
|
+
const selector = useMemo(
|
|
6760
|
+
() => ({ actuators: [...names] }),
|
|
6761
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
6762
|
+
[namesKey]
|
|
6763
|
+
);
|
|
6764
|
+
const { canWrite, release } = useControlWriter({ ...options, owner, selector });
|
|
6765
|
+
const indicesRef = useRef([]);
|
|
6766
|
+
useEffect(() => {
|
|
6767
|
+
const model = mjModelRef.current;
|
|
6768
|
+
if (!model || status !== "ready") {
|
|
6769
|
+
indicesRef.current = [];
|
|
6770
|
+
return;
|
|
6771
|
+
}
|
|
6772
|
+
indicesRef.current = namesRef.current.map((name) => {
|
|
6773
|
+
const id = findActuatorByName(model, name);
|
|
6774
|
+
if (id < 0) {
|
|
6775
|
+
console.warn(`[mujoco-react] useControlGroup: actuator "${name}" was not found.`);
|
|
6776
|
+
}
|
|
6777
|
+
return id;
|
|
6778
|
+
});
|
|
6779
|
+
}, [mjModelRef, status, namesKey]);
|
|
6780
|
+
const set = useCallback(
|
|
6781
|
+
(values, setOptions = {}) => {
|
|
6782
|
+
const data = mjDataRef.current;
|
|
6783
|
+
if (!data) return false;
|
|
6784
|
+
if (!setOptions.force && !canWrite()) return false;
|
|
6785
|
+
const groupNames = namesRef.current;
|
|
6786
|
+
const indices = indicesRef.current;
|
|
6787
|
+
for (let i = 0; i < groupNames.length; i += 1) {
|
|
6788
|
+
const value = values[groupNames[i]];
|
|
6789
|
+
if (value === void 0) continue;
|
|
6790
|
+
const adr = indices[i];
|
|
6791
|
+
if (adr >= 0) data.ctrl[adr] = value;
|
|
6792
|
+
}
|
|
6793
|
+
return true;
|
|
6794
|
+
},
|
|
6795
|
+
[canWrite, mjDataRef]
|
|
6796
|
+
);
|
|
6797
|
+
const read = useCallback(() => {
|
|
6798
|
+
const data = mjDataRef.current;
|
|
6799
|
+
const groupNames = namesRef.current;
|
|
6800
|
+
const indices = indicesRef.current;
|
|
6801
|
+
const result = {};
|
|
6802
|
+
for (let i = 0; i < groupNames.length; i += 1) {
|
|
6803
|
+
const adr = indices[i];
|
|
6804
|
+
result[groupNames[i]] = data && adr >= 0 ? data.ctrl[adr] ?? 0 : 0;
|
|
6805
|
+
}
|
|
6806
|
+
return result;
|
|
6807
|
+
}, [mjDataRef]);
|
|
6808
|
+
return useMemo(
|
|
6809
|
+
() => ({ owner, set, read, canWrite, release }),
|
|
6810
|
+
[owner, set, read, canWrite, release]
|
|
6811
|
+
);
|
|
6812
|
+
}
|
|
6374
6813
|
var geomNameCacheByModel2 = /* @__PURE__ */ new WeakMap();
|
|
6375
6814
|
var bodyNameCacheByModel = /* @__PURE__ */ new WeakMap();
|
|
6376
6815
|
function getCachedName(cacheByModel, model, id, address) {
|
|
@@ -7667,6 +8106,130 @@ function usePolicyCameraFramesFromMountedStreams(defaultOptions) {
|
|
|
7667
8106
|
reset
|
|
7668
8107
|
};
|
|
7669
8108
|
}
|
|
8109
|
+
function sessionSignature(stream) {
|
|
8110
|
+
return JSON.stringify({
|
|
8111
|
+
width: stream.width,
|
|
8112
|
+
height: stream.height,
|
|
8113
|
+
channels: stream.channels,
|
|
8114
|
+
renderIsolation: stream.renderIsolation ?? false,
|
|
8115
|
+
cameraName: stream.cameraName,
|
|
8116
|
+
siteName: stream.siteName,
|
|
8117
|
+
bodyName: stream.bodyName
|
|
8118
|
+
});
|
|
8119
|
+
}
|
|
8120
|
+
function addTensorAliases(tensors, stream, tensor, includeObservationImageAliases) {
|
|
8121
|
+
const keys = /* @__PURE__ */ new Set([stream.key, ...stream.aliases ?? []]);
|
|
8122
|
+
if (includeObservationImageAliases) {
|
|
8123
|
+
for (const base of [stream.key, ...stream.aliases ?? []]) {
|
|
8124
|
+
keys.add(`observation.images.${base}`);
|
|
8125
|
+
}
|
|
8126
|
+
}
|
|
8127
|
+
for (const key of keys) tensors[key] = tensor;
|
|
8128
|
+
}
|
|
8129
|
+
function usePolicyCameraTensors(options) {
|
|
8130
|
+
const mujoco = useMujoco();
|
|
8131
|
+
const [status, setStatus] = useState("idle");
|
|
8132
|
+
const [error, setError] = useState(null);
|
|
8133
|
+
const sessionsRef = useRef(/* @__PURE__ */ new Map());
|
|
8134
|
+
const disposeSessions = useCallback(() => {
|
|
8135
|
+
for (const { session } of sessionsRef.current.values()) session.dispose();
|
|
8136
|
+
sessionsRef.current.clear();
|
|
8137
|
+
}, []);
|
|
8138
|
+
useEffect(() => disposeSessions, [disposeSessions]);
|
|
8139
|
+
const reset = useCallback(() => {
|
|
8140
|
+
setStatus("idle");
|
|
8141
|
+
setError(null);
|
|
8142
|
+
}, []);
|
|
8143
|
+
const capture = useCallback(() => {
|
|
8144
|
+
const api = mujoco.api;
|
|
8145
|
+
if (!api) {
|
|
8146
|
+
throw new Error("MuJoCo scene is not ready for policy camera tensor capture.");
|
|
8147
|
+
}
|
|
8148
|
+
setStatus("capturing");
|
|
8149
|
+
setError(null);
|
|
8150
|
+
try {
|
|
8151
|
+
const sessions = sessionsRef.current;
|
|
8152
|
+
const seen = /* @__PURE__ */ new Set();
|
|
8153
|
+
const tensors = {};
|
|
8154
|
+
const sourceParts = [];
|
|
8155
|
+
for (const stream of options.streams) {
|
|
8156
|
+
seen.add(stream.key);
|
|
8157
|
+
const resolved = {
|
|
8158
|
+
...api.resolveCameraCaptureOptions(stream),
|
|
8159
|
+
channels: stream.channels,
|
|
8160
|
+
layout: stream.layout,
|
|
8161
|
+
range: stream.range
|
|
8162
|
+
};
|
|
8163
|
+
const signature = sessionSignature(stream);
|
|
8164
|
+
let entry = sessions.get(stream.key);
|
|
8165
|
+
if (!entry || entry.signature !== signature) {
|
|
8166
|
+
entry?.session.dispose();
|
|
8167
|
+
entry = {
|
|
8168
|
+
session: api.createCameraFrameCaptureSession(resolved),
|
|
8169
|
+
signature
|
|
8170
|
+
};
|
|
8171
|
+
sessions.set(stream.key, entry);
|
|
8172
|
+
}
|
|
8173
|
+
const tensor = entry.session.captureTensor(resolved);
|
|
8174
|
+
addTensorAliases(
|
|
8175
|
+
tensors,
|
|
8176
|
+
stream,
|
|
8177
|
+
tensor,
|
|
8178
|
+
options.includeObservationImageAliases ?? false
|
|
8179
|
+
);
|
|
8180
|
+
sourceParts.push(`${stream.key}:${tensor.source.kind}`);
|
|
8181
|
+
}
|
|
8182
|
+
for (const key of [...sessions.keys()]) {
|
|
8183
|
+
if (!seen.has(key)) {
|
|
8184
|
+
sessions.get(key)?.session.dispose();
|
|
8185
|
+
sessions.delete(key);
|
|
8186
|
+
}
|
|
8187
|
+
}
|
|
8188
|
+
setStatus("captured");
|
|
8189
|
+
return {
|
|
8190
|
+
tensors,
|
|
8191
|
+
sourceSummary: sourceParts.join(" + ") || "not used by policy",
|
|
8192
|
+
capturedAt: Date.now()
|
|
8193
|
+
};
|
|
8194
|
+
} catch (nextError) {
|
|
8195
|
+
const captureError = nextError instanceof Error ? nextError : new Error("Unable to capture policy camera tensors.");
|
|
8196
|
+
setError(captureError);
|
|
8197
|
+
setStatus("error");
|
|
8198
|
+
throw captureError;
|
|
8199
|
+
}
|
|
8200
|
+
}, [mujoco.api, options.includeObservationImageAliases, options.streams]);
|
|
8201
|
+
return {
|
|
8202
|
+
status,
|
|
8203
|
+
error,
|
|
8204
|
+
isCapturing: status === "capturing",
|
|
8205
|
+
capture,
|
|
8206
|
+
reset
|
|
8207
|
+
};
|
|
8208
|
+
}
|
|
8209
|
+
function usePolicyCameraTensorsFromMountedStreams(options) {
|
|
8210
|
+
const mujoco = useMujoco();
|
|
8211
|
+
const tensorOptions = options.tensor;
|
|
8212
|
+
const mountedOptions = useMemo(() => {
|
|
8213
|
+
const api = mujoco.api;
|
|
8214
|
+
if (!api) {
|
|
8215
|
+
return {
|
|
8216
|
+
streams: [],
|
|
8217
|
+
includeObservationImageAliases: options.includeObservationImageAliases ?? false
|
|
8218
|
+
};
|
|
8219
|
+
}
|
|
8220
|
+
const plan = createPolicyCameraFrameCapturePlanFromApi(api, options);
|
|
8221
|
+
return {
|
|
8222
|
+
streams: plan.streams.map(({ key, aliases, ...stream }) => ({
|
|
8223
|
+
...stream,
|
|
8224
|
+
...tensorOptions,
|
|
8225
|
+
key,
|
|
8226
|
+
aliases
|
|
8227
|
+
})),
|
|
8228
|
+
includeObservationImageAliases: plan.includeObservationImageAliases ?? false
|
|
8229
|
+
};
|
|
8230
|
+
}, [mujoco.api, options, tensorOptions]);
|
|
8231
|
+
return usePolicyCameraTensors(mountedOptions);
|
|
8232
|
+
}
|
|
7670
8233
|
function useCameraSequenceRecorder() {
|
|
7671
8234
|
const mujoco = useMujoco();
|
|
7672
8235
|
const [status, setStatus] = useState("idle");
|
|
@@ -7971,6 +8534,19 @@ function useCameraAnimation() {
|
|
|
7971
8534
|
* @license
|
|
7972
8535
|
* SPDX-License-Identifier: Apache-2.0
|
|
7973
8536
|
*/
|
|
8537
|
+
/**
|
|
8538
|
+
* @license
|
|
8539
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
8540
|
+
*
|
|
8541
|
+
* Live on-screen camera viewports for MuJoCo scenes. Each view renders the
|
|
8542
|
+
* shared scene from a named MuJoCo camera/site/body into a `gl.scissor` region
|
|
8543
|
+
* tracking a DOM element — no GPU readback, no PNG encoding.
|
|
8544
|
+
*
|
|
8545
|
+
* While at least one viewport is mounted the canvas switches from R3F's
|
|
8546
|
+
* automatic render to a managed render loop (main scene full-frame, then each
|
|
8547
|
+
* viewport). This is incompatible with `EffectComposer`/postprocessing or other
|
|
8548
|
+
* custom render loops; use the offscreen capture APIs in those setups instead.
|
|
8549
|
+
*/
|
|
7974
8550
|
/**
|
|
7975
8551
|
* @license
|
|
7976
8552
|
* SPDX-License-Identifier: Apache-2.0
|
|
@@ -8019,6 +8595,21 @@ function useCameraAnimation() {
|
|
|
8019
8595
|
* light_type: 0 = directional, 1 = spot (maps to mjLIGHT_DIRECTIONAL/mjLIGHT_SPOT)
|
|
8020
8596
|
* Note: light_directional does NOT exist in WASM — use light_type instead.
|
|
8021
8597
|
*/
|
|
8598
|
+
/**
|
|
8599
|
+
* @license
|
|
8600
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
8601
|
+
*
|
|
8602
|
+
* Stream a live MuJoCo camera into a DOM `<canvas>`. Each frame the scene is
|
|
8603
|
+
* rendered offscreen from the selected camera and blitted into the canvas, so
|
|
8604
|
+
* it composites normally in the DOM (works inside opaque panels) and does NOT
|
|
8605
|
+
* take over the render loop. Prefer this over `useCameraViewport` for camera
|
|
8606
|
+
* tiles embedded in HTML UI; use `useCameraViewport` for transparent overlays
|
|
8607
|
+
* on a full-bleed canvas.
|
|
8608
|
+
*
|
|
8609
|
+
* Uses the async capture path so Gaussian-splat environments render through
|
|
8610
|
+
* their dedicated capture renderer — streaming a splat scene at full rate does
|
|
8611
|
+
* not disturb the main view's splat sort.
|
|
8612
|
+
*/
|
|
8022
8613
|
/**
|
|
8023
8614
|
* @license
|
|
8024
8615
|
* SPDX-License-Identifier: Apache-2.0
|
|
@@ -8104,6 +8695,14 @@ function useCameraAnimation() {
|
|
|
8104
8695
|
*
|
|
8105
8696
|
* Cooperative actuator/control ownership for policies, IK, teleop, and replay.
|
|
8106
8697
|
*/
|
|
8698
|
+
/**
|
|
8699
|
+
* @license
|
|
8700
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
8701
|
+
*
|
|
8702
|
+
* Typed, named control groups. Define the group once (like a zod schema or a
|
|
8703
|
+
* tanstack route), then read/set actuators by name with inferred keys — no
|
|
8704
|
+
* positional arrays, no `as const`, no owner bookkeeping.
|
|
8705
|
+
*/
|
|
8107
8706
|
/**
|
|
8108
8707
|
* @license
|
|
8109
8708
|
* SPDX-License-Identifier: Apache-2.0
|
|
@@ -8176,6 +8775,15 @@ function useCameraAnimation() {
|
|
|
8176
8775
|
*
|
|
8177
8776
|
* React wrapper for capturing policy image payloads from Three/MuJoCo cameras.
|
|
8178
8777
|
*/
|
|
8778
|
+
/**
|
|
8779
|
+
* @license
|
|
8780
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
8781
|
+
*
|
|
8782
|
+
* Capture policy observation tensors directly from Three/MuJoCo cameras,
|
|
8783
|
+
* skipping the data-URL/PNG round-trip. Sessions are created once per camera
|
|
8784
|
+
* and reused every step, so live inference and dataset recording read straight
|
|
8785
|
+
* from the GPU into Float32 tensors.
|
|
8786
|
+
*/
|
|
8179
8787
|
/**
|
|
8180
8788
|
* @license
|
|
8181
8789
|
* SPDX-License-Identifier: Apache-2.0
|
|
@@ -8224,6 +8832,6 @@ function useCameraAnimation() {
|
|
|
8224
8832
|
* useCameraAnimation — composable camera animation hook.
|
|
8225
8833
|
*/
|
|
8226
8834
|
|
|
8227
|
-
export { Body, ContactListener, ContactMarkers, Debug, DragInteraction, FlexRenderer, IkGizmo, InstancedGeomRenderer, MountedCameraFrameSequenceManifestStatus, MountedCameraFrameSequenceReadinessStatus, MountedCameraFrameSourceSuggestionMatch, MujocoCanvas, MujocoPhysics, MujocoProvider, MujocoSimProvider, SceneLights, SplatCollisionProxyPreview, TendonRenderer, TrajectoryPlayer, applyPolicyActionToControls, bodyPositionField, buildObservation, canFetchSplatCollisionProxyXml, captureFrame, captureFrameBlob, capturePolicyCameraFrames, capturePolicyCameraFramesFromMountedStreams, clampPolicyActionValue, createContiguousControlGroup, createController, createControllerHook, createMountedCameraFrameSequenceManifest, createMountedCameraFrameSequencePlan, createMountedCameraFrameSequencePlanFromApi, createMountedCameraFrameSequenceReadiness, createMountedCameraFrameSourceSuggestions, createNamedObservationBuilder, createPolicyCameraFrameCapturePlan, createPolicyCameraFrameCapturePlanFromApi, ctrlField, fetchSplatCollisionProxyXml, findActuatorByName, findBodyByName, findGeomByName, findJointByName, findKeyframeByName, findSensorByName, findSiteByName, findTendonByName, geomPositionField, getActuatedJoints, getCameraFrameCaptureSourceTarget, getControlMap, getMountedCameraFrameCaptureSource, getName, imagePointToNdc, isMountedCameraFrameCaptureSource, loadScene, parseSplatCollisionProxyGeoms, projectImagePointTo3D, qposField, qvelField, readNamedObservation, recordMountedCameraFrameSequence, resolveControlGroup, resolveMountedCameraFrameSource, sitePositionField, useActuators, useAfterPhysicsStep, useBeforePhysicsStep, useBodyMeshes, useBodyPose, useBodyState, useCameraAnimation, useCameraFrameCapture, useCameraSequenceRecorder, useContactEvents, useContactHistory, useContacts, useControlWriter, useCtrl, useCtrlNoise, useFrameCapture, useGamepad, useGeomPose, useGravityCompensation, useIkController, useJointState, useKeyboardIkTarget, useKeyboardTeleop, useMountedCameraSequenceRecorder, useMujoco, useMujocoWasm, useNamedObservation, useObservation, usePolicy, usePolicyCameraFrames, usePolicyCameraFramesFromMountedStreams, useRemotePolicy, useSceneLights, useSelectionHighlight, useSensor, useSensors, useSitePose, useSitePosition, useSplatCollisionProxyGeoms, useTrajectoryPlayer, useTrajectoryRecorder, useVideoRecorder };
|
|
8835
|
+
export { Body, CameraView, ContactListener, ContactMarkers, Debug, DragInteraction, FlexRenderer, GenericIK, IkGizmo, InstancedGeomRenderer, MountedCameraFrameSequenceManifestStatus, MountedCameraFrameSequenceReadinessStatus, MountedCameraFrameSourceSuggestionMatch, MujocoCanvas, MujocoPhysics, MujocoProvider, MujocoSimProvider, SceneLights, SplatCollisionProxyPreview, TendonRenderer, TrajectoryPlayer, applyPolicyActionToControls, bodyPositionField, buildObservation, canFetchSplatCollisionProxyXml, captureFrame, captureFrameBlob, capturePolicyCameraFrames, capturePolicyCameraFramesFromMountedStreams, clampPolicyActionValue, controlGroup, createContiguousControlGroup, createController, createControllerHook, createMountedCameraFrameSequenceManifest, createMountedCameraFrameSequencePlan, createMountedCameraFrameSequencePlanFromApi, createMountedCameraFrameSequenceReadiness, createMountedCameraFrameSourceSuggestions, createNamedObservationBuilder, createPolicyCameraFrameCapturePlan, createPolicyCameraFrameCapturePlanFromApi, ctrlField, fetchSplatCollisionProxyXml, findActuatorByName, findBodyByName, findGeomByName, findJointByName, findKeyframeByName, findSensorByName, findSiteByName, findTendonByName, geomPositionField, getActuatedJoints, getCameraFrameCaptureSourceTarget, getControlMap, getMountedCameraFrameCaptureSource, getName, imagePointToNdc, isMountedCameraFrameCaptureSource, loadScene, parseSplatCollisionProxyGeoms, projectImagePointTo3D, qposField, qvelField, readNamedObservation, recordMountedCameraFrameSequence, resolveControlGroup, resolveMountedCameraFrameSource, sitePositionField, useActuators, useAfterPhysicsStep, useBeforePhysicsStep, useBodyMeshes, useBodyPose, useBodyState, useCameraAnimation, useCameraFrameCapture, useCameraSequenceRecorder, useCameraStream, useCameraViewport, useContactEvents, useContactHistory, useContacts, useControlGroup, useControlWriter, useCtrl, useCtrlNoise, useFrameCapture, useGamepad, useGeomPose, useGravityCompensation, useIkController, useJointState, useKeyboardIkTarget, useKeyboardTeleop, useMountedCameraSequenceRecorder, useMujoco, useMujocoWasm, useNamedObservation, useObservation, usePolicy, usePolicyCameraFrames, usePolicyCameraFramesFromMountedStreams, usePolicyCameraTensors, usePolicyCameraTensorsFromMountedStreams, useRemotePolicy, useSceneLights, useSelectionHighlight, useSensor, useSensors, useSitePose, useSitePosition, useSplatCollisionProxyGeoms, useTrajectoryPlayer, useTrajectoryRecorder, useVideoRecorder };
|
|
8228
8836
|
//# sourceMappingURL=index.js.map
|
|
8229
8837
|
//# sourceMappingURL=index.js.map
|