mujoco-react 10.4.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 +73 -136
- package/dist/{chunk-FBXXXPLQ.js → chunk-KHZ5U36J.js} +157 -16
- package/dist/chunk-KHZ5U36J.js.map +1 -0
- package/dist/index.d.ts +179 -48
- package/dist/index.js +470 -17
- 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-CdFZCYmy.d.ts → types-CViUme8D.d.ts} +141 -1
- package/package.json +14 -3
- package/src/components/CameraView.tsx +245 -0
- 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 +44 -0
- package/src/onnx.ts +126 -0
- package/src/policyImageTensors.ts +150 -0
- package/src/rendering/cameraFrameCapture.ts +112 -15
- package/src/types.ts +28 -0
- package/dist/chunk-FBXXXPLQ.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
|
|
@@ -6526,6 +6743,73 @@ function useControlWriter(options) {
|
|
|
6526
6743
|
release
|
|
6527
6744
|
}), [canWrite, owner, read, release, write]);
|
|
6528
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
|
+
}
|
|
6529
6813
|
var geomNameCacheByModel2 = /* @__PURE__ */ new WeakMap();
|
|
6530
6814
|
var bodyNameCacheByModel = /* @__PURE__ */ new WeakMap();
|
|
6531
6815
|
function getCachedName(cacheByModel, model, id, address) {
|
|
@@ -7822,6 +8106,130 @@ function usePolicyCameraFramesFromMountedStreams(defaultOptions) {
|
|
|
7822
8106
|
reset
|
|
7823
8107
|
};
|
|
7824
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
|
+
}
|
|
7825
8233
|
function useCameraSequenceRecorder() {
|
|
7826
8234
|
const mujoco = useMujoco();
|
|
7827
8235
|
const [status, setStatus] = useState("idle");
|
|
@@ -8126,6 +8534,19 @@ function useCameraAnimation() {
|
|
|
8126
8534
|
* @license
|
|
8127
8535
|
* SPDX-License-Identifier: Apache-2.0
|
|
8128
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
|
+
*/
|
|
8129
8550
|
/**
|
|
8130
8551
|
* @license
|
|
8131
8552
|
* SPDX-License-Identifier: Apache-2.0
|
|
@@ -8174,6 +8595,21 @@ function useCameraAnimation() {
|
|
|
8174
8595
|
* light_type: 0 = directional, 1 = spot (maps to mjLIGHT_DIRECTIONAL/mjLIGHT_SPOT)
|
|
8175
8596
|
* Note: light_directional does NOT exist in WASM — use light_type instead.
|
|
8176
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
|
+
*/
|
|
8177
8613
|
/**
|
|
8178
8614
|
* @license
|
|
8179
8615
|
* SPDX-License-Identifier: Apache-2.0
|
|
@@ -8259,6 +8695,14 @@ function useCameraAnimation() {
|
|
|
8259
8695
|
*
|
|
8260
8696
|
* Cooperative actuator/control ownership for policies, IK, teleop, and replay.
|
|
8261
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
|
+
*/
|
|
8262
8706
|
/**
|
|
8263
8707
|
* @license
|
|
8264
8708
|
* SPDX-License-Identifier: Apache-2.0
|
|
@@ -8331,6 +8775,15 @@ function useCameraAnimation() {
|
|
|
8331
8775
|
*
|
|
8332
8776
|
* React wrapper for capturing policy image payloads from Three/MuJoCo cameras.
|
|
8333
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
|
+
*/
|
|
8334
8787
|
/**
|
|
8335
8788
|
* @license
|
|
8336
8789
|
* SPDX-License-Identifier: Apache-2.0
|
|
@@ -8379,6 +8832,6 @@ function useCameraAnimation() {
|
|
|
8379
8832
|
* useCameraAnimation — composable camera animation hook.
|
|
8380
8833
|
*/
|
|
8381
8834
|
|
|
8382
|
-
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 };
|
|
8383
8836
|
//# sourceMappingURL=index.js.map
|
|
8384
8837
|
//# sourceMappingURL=index.js.map
|