mujoco-react 10.0.1 → 10.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/dist/index.js CHANGED
@@ -1,11 +1,11 @@
1
- import { withContacts, getContact, captureCameraFrame, captureCameraFrameBlob, createCameraFrameCaptureSession, CAPTURE_EXCLUDE_KEY } from './chunk-QTCAVQS6.js';
2
- export { 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-QTCAVQS6.js';
1
+ import { withContacts, getContact, captureCameraFrame, captureCameraFrameBlob, createCameraFrameCaptureSession, CAMERA_FRAME_CAPTURE_PRE_RENDER_USER_DATA_KEY, CAPTURE_EXCLUDE_KEY } from './chunk-FEKBKHEN.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, createCameraFrameCaptureSession, createPairedSplatEnvironment, createSparkSplatViewerUrl, createSplatEnvironmentUserData, createSplatSceneConfig, createVisualScenarioExecutionContext, getContact, getScenarioBackground, getScenarioCameraPosition, getSplatEnvironmentReadiness, registerModelResources, renderCameraFrameToCanvas, useSplatEnvironment, useSplatSceneConfig, useVisualScenarioEffects, useVisualScenarioExecutionContext, withSplatEnvironment } from './chunk-FEKBKHEN.js';
3
3
  import loadMujoco from '@mujoco/mujoco';
4
4
  import defaultMujocoWasmUrl from '@mujoco/mujoco/mujoco.wasm?url';
5
5
  import { createContext, forwardRef, useEffect, useContext, useState, useRef, useCallback, useMemo, useLayoutEffect } from 'react';
6
6
  import { jsx, jsxs } from 'react/jsx-runtime';
7
7
  import { Canvas, useThree, useFrame } from '@react-three/fiber';
8
- import * as THREE11 from 'three';
8
+ import * as THREE10 from 'three';
9
9
  import { PivotControls } from '@react-three/drei';
10
10
 
11
11
  var MujocoContext = createContext({
@@ -105,16 +105,16 @@ function MujocoProvider({
105
105
  }
106
106
  );
107
107
  }
108
- var CapsuleGeometry = class extends THREE11.BufferGeometry {
108
+ var CapsuleGeometry = class extends THREE10.BufferGeometry {
109
109
  parameters;
110
110
  constructor(radius = 1, length = 1, capSegments = 4, radialSegments = 8) {
111
111
  super();
112
112
  this.type = "CapsuleGeometry";
113
113
  this.parameters = { radius, length, capSegments, radialSegments };
114
- const path = new THREE11.Path();
114
+ const path = new THREE10.Path();
115
115
  path.absarc(0, -length / 2, radius, Math.PI * 1.5, 0, false);
116
116
  path.absarc(0, length / 2, radius, 0, Math.PI * 0.5, false);
117
- const latheGeometry = new THREE11.LatheGeometry(path.getPoints(capSegments), radialSegments);
117
+ const latheGeometry = new THREE10.LatheGeometry(path.getPoints(capSegments), radialSegments);
118
118
  const self = this;
119
119
  self.setIndex(latheGeometry.getIndex());
120
120
  self.setAttribute("position", latheGeometry.getAttribute("position"));
@@ -122,231 +122,6 @@ var CapsuleGeometry = class extends THREE11.BufferGeometry {
122
122
  self.setAttribute("uv", latheGeometry.getAttribute("uv"));
123
123
  }
124
124
  };
125
- var Reflector = class extends THREE11.Mesh {
126
- isReflector = true;
127
- camera;
128
- reflectorPlane = new THREE11.Plane();
129
- normal = new THREE11.Vector3();
130
- reflectorWorldPosition = new THREE11.Vector3();
131
- cameraWorldPosition = new THREE11.Vector3();
132
- rotationMatrix = new THREE11.Matrix4();
133
- lookAtPosition = new THREE11.Vector3(0, 0, -1);
134
- clipPlane = new THREE11.Vector4();
135
- view = new THREE11.Vector3();
136
- target = new THREE11.Vector3();
137
- q = new THREE11.Vector4();
138
- textureMatrix = new THREE11.Matrix4();
139
- virtualCamera;
140
- renderTarget;
141
- constructor(geometry, options = {}) {
142
- super(geometry);
143
- this.type = "Reflector";
144
- this.camera = new THREE11.PerspectiveCamera();
145
- const color = options.color !== void 0 ? new THREE11.Color(options.color) : new THREE11.Color(8355711);
146
- const textureWidth = options.textureWidth || 512;
147
- const textureHeight = options.textureHeight || 512;
148
- const clipBias = options.clipBias || 0;
149
- const multisample = options.multisample !== void 0 ? options.multisample : 4;
150
- const blendTexture = options.texture || void 0;
151
- const mixStrength = options.mixStrength !== void 0 ? options.mixStrength : 0.25;
152
- this.virtualCamera = this.camera;
153
- this.renderTarget = new THREE11.WebGLRenderTarget(textureWidth, textureHeight, {
154
- samples: multisample,
155
- type: THREE11.HalfFloatType
156
- });
157
- this.material = new THREE11.MeshPhysicalMaterial({
158
- map: blendTexture,
159
- color,
160
- roughness: 0.5,
161
- metalness: 0.1
162
- });
163
- this.material.onBeforeCompile = (shader) => {
164
- shader.uniforms.tDiffuse = { value: this.renderTarget.texture };
165
- shader.uniforms.textureMatrix = { value: this.textureMatrix };
166
- shader.uniforms.mixStrength = { value: mixStrength };
167
- const bodyStart = shader.vertexShader.indexOf("void main() {");
168
- shader.vertexShader = "uniform mat4 textureMatrix;\nvarying vec4 vUvReflection;\n" + shader.vertexShader.slice(0, bodyStart) + shader.vertexShader.slice(bodyStart, -1) + " vUvReflection = textureMatrix * vec4( position, 1.0 );\n}";
169
- const fragmentBodyStart = shader.fragmentShader.indexOf("void main() {");
170
- shader.fragmentShader = "uniform sampler2D tDiffuse;\nuniform float mixStrength;\nvarying vec4 vUvReflection;\n" + shader.fragmentShader.slice(0, fragmentBodyStart) + shader.fragmentShader.slice(fragmentBodyStart, -1) + " vec4 reflectionColor = texture2DProj( tDiffuse, vUvReflection );\n gl_FragColor = vec4( mix( gl_FragColor.rgb, reflectionColor.rgb, mixStrength ), gl_FragColor.a );\n}";
171
- };
172
- this.receiveShadow = true;
173
- this.onBeforeRender = (renderer, scene, camera) => {
174
- this.reflectorWorldPosition.setFromMatrixPosition(this.matrixWorld);
175
- this.cameraWorldPosition.setFromMatrixPosition(camera.matrixWorld);
176
- this.rotationMatrix.extractRotation(this.matrixWorld);
177
- this.normal.set(0, 0, 1);
178
- this.normal.applyMatrix4(this.rotationMatrix);
179
- this.view.subVectors(this.reflectorWorldPosition, this.cameraWorldPosition);
180
- if (this.view.dot(this.normal) > 0) return;
181
- this.view.reflect(this.normal).negate();
182
- this.view.add(this.reflectorWorldPosition);
183
- this.rotationMatrix.extractRotation(camera.matrixWorld);
184
- this.lookAtPosition.set(0, 0, -1);
185
- this.lookAtPosition.applyMatrix4(this.rotationMatrix);
186
- this.lookAtPosition.add(this.cameraWorldPosition);
187
- this.target.subVectors(this.reflectorWorldPosition, this.lookAtPosition);
188
- this.target.reflect(this.normal).negate();
189
- this.target.add(this.reflectorWorldPosition);
190
- this.virtualCamera.position.copy(this.view);
191
- this.virtualCamera.up.set(0, 1, 0);
192
- this.virtualCamera.up.applyMatrix4(this.rotationMatrix);
193
- this.virtualCamera.up.reflect(this.normal);
194
- this.virtualCamera.lookAt(this.target);
195
- this.virtualCamera.far = camera.far;
196
- this.virtualCamera.updateMatrixWorld();
197
- this.virtualCamera.projectionMatrix.copy(camera.projectionMatrix);
198
- this.textureMatrix.set(
199
- 0.5,
200
- 0,
201
- 0,
202
- 0.5,
203
- 0,
204
- 0.5,
205
- 0,
206
- 0.5,
207
- 0,
208
- 0,
209
- 0.5,
210
- 0.5,
211
- 0,
212
- 0,
213
- 0,
214
- 1
215
- );
216
- this.textureMatrix.multiply(this.virtualCamera.projectionMatrix);
217
- this.textureMatrix.multiply(this.virtualCamera.matrixWorldInverse);
218
- this.textureMatrix.multiply(this.matrixWorld);
219
- this.reflectorPlane.setFromNormalAndCoplanarPoint(this.normal, this.reflectorWorldPosition);
220
- this.reflectorPlane.applyMatrix4(this.virtualCamera.matrixWorldInverse);
221
- this.clipPlane.set(this.reflectorPlane.normal.x, this.reflectorPlane.normal.y, this.reflectorPlane.normal.z, this.reflectorPlane.constant);
222
- const projectionMatrix = this.virtualCamera.projectionMatrix;
223
- this.q.x = (Math.sign(this.clipPlane.x) + projectionMatrix.elements[8]) / projectionMatrix.elements[0];
224
- this.q.y = (Math.sign(this.clipPlane.y) + projectionMatrix.elements[9]) / projectionMatrix.elements[5];
225
- this.q.z = -1;
226
- this.q.w = (1 + projectionMatrix.elements[10]) / projectionMatrix.elements[14];
227
- this.clipPlane.multiplyScalar(2 / this.clipPlane.dot(this.q));
228
- projectionMatrix.elements[2] = this.clipPlane.x;
229
- projectionMatrix.elements[6] = this.clipPlane.y;
230
- projectionMatrix.elements[10] = this.clipPlane.z + 1 - clipBias;
231
- projectionMatrix.elements[14] = this.clipPlane.w;
232
- this.visible = false;
233
- const currentRenderTarget = renderer.getRenderTarget();
234
- const currentXrEnabled = renderer.xr.enabled;
235
- const currentShadowAutoUpdate = renderer.shadowMap.autoUpdate;
236
- renderer.xr.enabled = false;
237
- renderer.shadowMap.autoUpdate = false;
238
- renderer.setRenderTarget(this.renderTarget);
239
- renderer.state.buffers.depth.setMask(true);
240
- if (renderer.autoClear === false) renderer.clear();
241
- renderer.render(scene, this.virtualCamera);
242
- renderer.xr.enabled = currentXrEnabled;
243
- renderer.shadowMap.autoUpdate = currentShadowAutoUpdate;
244
- renderer.setRenderTarget(currentRenderTarget);
245
- const viewport = camera.viewport;
246
- if (viewport !== void 0) {
247
- renderer.state.viewport(viewport);
248
- }
249
- this.visible = true;
250
- };
251
- }
252
- getRenderTarget() {
253
- return this.renderTarget;
254
- }
255
- dispose() {
256
- this.renderTarget.dispose();
257
- const mesh = this;
258
- if (Array.isArray(mesh.material)) {
259
- mesh.material.forEach((m) => m.dispose());
260
- } else {
261
- mesh.material.dispose();
262
- }
263
- }
264
- };
265
-
266
- // src/rendering/GeomBuilder.ts
267
- var GeomBuilder = class {
268
- mujoco;
269
- constructor(mujoco) {
270
- this.mujoco = mujoco;
271
- }
272
- /**
273
- * Creates a Three.js Object3D (usually a Mesh) for a specific geometry in the MuJoCo model.
274
- * Returns null if the geometry shouldn't be rendered (e.g., invisible collision triggers).
275
- */
276
- create(mjModel, g) {
277
- if (mjModel.geom_group[g] === 3) return null;
278
- const type = mjModel.geom_type[g];
279
- const size = mjModel.geom_size.subarray(g * 3, g * 3 + 3);
280
- const pos = mjModel.geom_pos.subarray(g * 3, g * 3 + 3);
281
- const quat = mjModel.geom_quat.subarray(g * 4, g * 4 + 4);
282
- const matId = mjModel.geom_matid[g];
283
- const color = new THREE11.Color(16777215);
284
- let opacity = 1;
285
- if (matId >= 0) {
286
- const rgba = mjModel.mat_rgba.subarray(matId * 4, matId * 4 + 4);
287
- color.setRGB(rgba[0], rgba[1], rgba[2]);
288
- opacity = rgba[3];
289
- } else {
290
- const rgba = mjModel.geom_rgba.subarray(g * 4, g * 4 + 4);
291
- color.setRGB(rgba[0], rgba[1], rgba[2]);
292
- opacity = rgba[3];
293
- }
294
- const MG = this.mujoco.mjtGeom;
295
- let geo = null;
296
- const getVal = (v) => v?.value ?? v;
297
- if (type === getVal(MG.mjGEOM_PLANE)) {
298
- geo = new THREE11.PlaneGeometry(size[0] * 2 || 5, size[1] * 2 || 5);
299
- } else if (type === getVal(MG.mjGEOM_SPHERE)) {
300
- geo = new THREE11.SphereGeometry(size[0], 24, 24);
301
- } else if (type === getVal(MG.mjGEOM_CAPSULE)) {
302
- geo = new CapsuleGeometry(size[0], size[1] * 2, 24, 12);
303
- geo.rotateX(Math.PI / 2);
304
- } else if (type === getVal(MG.mjGEOM_BOX)) {
305
- geo = new THREE11.BoxGeometry(size[0] * 2, size[1] * 2, size[2] * 2);
306
- } else if (type === getVal(MG.mjGEOM_CYLINDER)) {
307
- geo = new THREE11.CylinderGeometry(size[0], size[0], size[1] * 2, 24);
308
- geo.rotateX(Math.PI / 2);
309
- } else if (type === getVal(MG.mjGEOM_MESH)) {
310
- const mId = mjModel.geom_dataid[g];
311
- const vAdr = mjModel.mesh_vertadr[mId];
312
- const vNum = mjModel.mesh_vertnum[mId];
313
- const fAdr = mjModel.mesh_faceadr[mId];
314
- const fNum = mjModel.mesh_facenum[mId];
315
- geo = new THREE11.BufferGeometry();
316
- geo.setAttribute("position", new THREE11.Float32BufferAttribute(mjModel.mesh_vert.subarray(vAdr * 3, (vAdr + vNum) * 3), 3));
317
- geo.setIndex(Array.from(mjModel.mesh_face.subarray(fAdr * 3, (fAdr + fNum) * 3)));
318
- geo.computeVertexNormals();
319
- }
320
- if (geo) {
321
- let mesh;
322
- if (type === getVal(MG.mjGEOM_PLANE)) {
323
- mesh = new Reflector(geo, {
324
- clipBias: 3e-3,
325
- textureWidth: 1024,
326
- textureHeight: 1024,
327
- color,
328
- mixStrength: 0.25
329
- });
330
- } else {
331
- mesh = new THREE11.Mesh(geo, new THREE11.MeshStandardMaterial({
332
- color,
333
- transparent: opacity < 1,
334
- opacity,
335
- roughness: 0.6,
336
- metalness: 0.2
337
- }));
338
- mesh.castShadow = true;
339
- mesh.receiveShadow = true;
340
- }
341
- mesh.position.set(pos[0], pos[1], pos[2]);
342
- mesh.quaternion.set(quat[1], quat[2], quat[3], quat[0]);
343
- mesh.userData.bodyID = mjModel.geom_bodyid[g];
344
- mesh.userData.geomID = g;
345
- return mesh;
346
- }
347
- return null;
348
- }
349
- };
350
125
 
351
126
  // src/core/SceneLoader.ts
352
127
  var JOINT_TYPE_NAMES = {
@@ -704,8 +479,10 @@ function createContiguousControlGroup(mjModel, count) {
704
479
  }
705
480
  function sceneObjectToXml(obj) {
706
481
  const joint = obj.freejoint ? "<freejoint/>" : "";
482
+ const geomName = obj.geomName ?? `${obj.name}_geom`;
707
483
  const pos = obj.position.map((v) => v.toFixed(3)).join(" ");
708
- const size = obj.size.map((v) => v.toFixed(3)).join(" ");
484
+ const sizeValues = obj.type === "sphere" ? obj.size.slice(0, 1) : obj.type === "cylinder" ? obj.size.slice(0, 2) : obj.size;
485
+ const size = sizeValues.map((v) => v.toFixed(3)).join(" ");
709
486
  const rgba = obj.rgba.join(" ");
710
487
  const mass = obj.mass ? ` mass="${obj.mass}"` : "";
711
488
  const friction = obj.friction ? ` friction="${obj.friction}"` : "";
@@ -713,7 +490,7 @@ function sceneObjectToXml(obj) {
713
490
  const solimp = obj.solimp ? ` solimp="${obj.solimp}"` : "";
714
491
  const condim = obj.condim ? ` condim="${obj.condim}"` : "";
715
492
  const group = obj.group !== void 0 ? ` group="${obj.group}"` : "";
716
- return `<body name="${obj.name}" pos="${pos}">${joint}<geom type="${obj.type}" size="${size}" rgba="${rgba}" contype="1" conaffinity="1"${mass}${friction}${solref}${solimp}${condim}${group}/></body>`;
493
+ return `<body name="${obj.name}" pos="${pos}">${joint}<geom name="${geomName}" type="${obj.type}" size="${size}" rgba="${rgba}" contype="1" conaffinity="1"${mass}${friction}${solref}${solimp}${condim}${group}/></body>`;
717
494
  }
718
495
  function ensureDir(mujoco, fname) {
719
496
  const dirParts = fname.split("/");
@@ -1112,6 +889,137 @@ function collectDependencyPaths(xmlString, currentFile, parser) {
1112
889
  });
1113
890
  return paths;
1114
891
  }
892
+
893
+ // src/rendering/GeomBuilder.ts
894
+ var GeomBuilder = class {
895
+ mujoco;
896
+ textureCache = /* @__PURE__ */ new Map();
897
+ constructor(mujoco) {
898
+ this.mujoco = mujoco;
899
+ }
900
+ getMaterialTexture(mjModel, matId) {
901
+ if (matId < 0 || !mjModel.mat_texid || !mjModel.tex_data) return null;
902
+ const materialCount = Math.max(1, Math.floor(mjModel.mat_rgba.length / 4));
903
+ const textureRoles = Math.max(1, Math.floor(mjModel.mat_texid.length / materialCount));
904
+ let texId = -1;
905
+ for (let role = 0; role < textureRoles; role += 1) {
906
+ const candidate = mjModel.mat_texid[matId * textureRoles + role];
907
+ if (candidate >= 0) {
908
+ texId = candidate;
909
+ break;
910
+ }
911
+ }
912
+ if (texId < 0) return null;
913
+ const cached = this.textureCache.get(texId);
914
+ if (cached) return cached;
915
+ const width = Number(mjModel.tex_width[texId]);
916
+ const height = Number(mjModel.tex_height[texId]);
917
+ const channels = Number(mjModel.tex_nchannel[texId]);
918
+ const offset = Number(mjModel.tex_adr[texId]);
919
+ if (width <= 0 || height <= 0 || channels <= 0 || offset < 0) return null;
920
+ const source = mjModel.tex_data.subarray(offset, offset + width * height * channels);
921
+ const rgba = new Uint8Array(width * height * 4);
922
+ for (let i = 0, j = 0; i < width * height; i += 1, j += channels) {
923
+ const r = source[j] ?? 255;
924
+ const g = channels > 1 ? source[j + 1] : r;
925
+ const b = channels > 2 ? source[j + 2] : r;
926
+ const a = channels > 3 ? source[j + 3] : 255;
927
+ const out = i * 4;
928
+ rgba[out] = r;
929
+ rgba[out + 1] = g;
930
+ rgba[out + 2] = b;
931
+ rgba[out + 3] = a;
932
+ }
933
+ const texture = new THREE10.DataTexture(rgba, width, height, THREE10.RGBAFormat);
934
+ texture.colorSpace = THREE10.LinearSRGBColorSpace;
935
+ texture.wrapS = THREE10.RepeatWrapping;
936
+ texture.wrapT = THREE10.RepeatWrapping;
937
+ texture.flipY = true;
938
+ const repeatOffset = matId * 2;
939
+ const repeatS = mjModel.mat_texrepeat?.[repeatOffset] ?? 1;
940
+ const repeatT = mjModel.mat_texrepeat?.[repeatOffset + 1] ?? 1;
941
+ texture.repeat.set(repeatS || 1, repeatT || 1);
942
+ texture.needsUpdate = true;
943
+ this.textureCache.set(texId, texture);
944
+ return texture;
945
+ }
946
+ /**
947
+ * Creates a Three.js Object3D (usually a Mesh) for a specific geometry in the MuJoCo model.
948
+ * Returns null if the geometry shouldn't be rendered (e.g., invisible collision triggers).
949
+ */
950
+ create(mjModel, g) {
951
+ if (mjModel.geom_group[g] === 3) return null;
952
+ const type = mjModel.geom_type[g];
953
+ const size = mjModel.geom_size.subarray(g * 3, g * 3 + 3);
954
+ const pos = mjModel.geom_pos.subarray(g * 3, g * 3 + 3);
955
+ const quat = mjModel.geom_quat.subarray(g * 4, g * 4 + 4);
956
+ const matId = mjModel.geom_matid[g];
957
+ const color = new THREE10.Color(16777215);
958
+ const map = this.getMaterialTexture(mjModel, matId);
959
+ let opacity = 1;
960
+ if (matId >= 0) {
961
+ const rgba = mjModel.mat_rgba.subarray(matId * 4, matId * 4 + 4);
962
+ color.setRGB(rgba[0], rgba[1], rgba[2]);
963
+ opacity = rgba[3];
964
+ } else {
965
+ const rgba = mjModel.geom_rgba.subarray(g * 4, g * 4 + 4);
966
+ color.setRGB(rgba[0], rgba[1], rgba[2]);
967
+ opacity = rgba[3];
968
+ }
969
+ const MG = this.mujoco.mjtGeom;
970
+ let geo = null;
971
+ const getVal = (v) => v?.value ?? v;
972
+ if (type === getVal(MG.mjGEOM_PLANE)) {
973
+ geo = new THREE10.PlaneGeometry(size[0] * 2 || 5, size[1] * 2 || 5);
974
+ } else if (type === getVal(MG.mjGEOM_SPHERE)) {
975
+ geo = new THREE10.SphereGeometry(size[0], 24, 24);
976
+ } else if (type === getVal(MG.mjGEOM_CAPSULE)) {
977
+ geo = new CapsuleGeometry(size[0], size[1] * 2, 24, 12);
978
+ geo.rotateX(Math.PI / 2);
979
+ } else if (type === getVal(MG.mjGEOM_BOX)) {
980
+ geo = new THREE10.BoxGeometry(size[0] * 2, size[1] * 2, size[2] * 2);
981
+ } else if (type === getVal(MG.mjGEOM_CYLINDER)) {
982
+ geo = new THREE10.CylinderGeometry(size[0], size[0], size[1] * 2, 24);
983
+ geo.rotateX(Math.PI / 2);
984
+ } else if (type === getVal(MG.mjGEOM_MESH)) {
985
+ const mId = mjModel.geom_dataid[g];
986
+ const vAdr = mjModel.mesh_vertadr[mId];
987
+ const vNum = mjModel.mesh_vertnum[mId];
988
+ const fAdr = mjModel.mesh_faceadr[mId];
989
+ const fNum = mjModel.mesh_facenum[mId];
990
+ geo = new THREE10.BufferGeometry();
991
+ geo.setAttribute("position", new THREE10.Float32BufferAttribute(mjModel.mesh_vert.subarray(vAdr * 3, (vAdr + vNum) * 3), 3));
992
+ geo.setIndex(Array.from(mjModel.mesh_face.subarray(fAdr * 3, (fAdr + fNum) * 3)));
993
+ geo.computeVertexNormals();
994
+ }
995
+ if (geo) {
996
+ const isPlane = type === getVal(MG.mjGEOM_PLANE);
997
+ const materialMap = isPlane && map ? map.clone() : map;
998
+ if (isPlane && materialMap) {
999
+ materialMap.repeat.multiplyScalar(2.5);
1000
+ materialMap.needsUpdate = true;
1001
+ }
1002
+ const mesh = new THREE10.Mesh(geo, new THREE10.MeshStandardMaterial({
1003
+ color,
1004
+ map: materialMap,
1005
+ transparent: opacity < 1,
1006
+ opacity,
1007
+ roughness: 0.6,
1008
+ metalness: 0
1009
+ }));
1010
+ mesh.castShadow = type !== getVal(MG.mjGEOM_PLANE);
1011
+ mesh.receiveShadow = true;
1012
+ mesh.position.set(pos[0], pos[1], pos[2]);
1013
+ mesh.quaternion.set(quat[1], quat[2], quat[3], quat[0]);
1014
+ mesh.userData.bodyID = mjModel.geom_bodyid[g];
1015
+ mesh.userData.geomID = g;
1016
+ mesh.userData.geomGroup = mjModel.geom_group[g];
1017
+ mesh.userData.geomName = getName(mjModel, mjModel.name_geomadr[g]);
1018
+ return mesh;
1019
+ }
1020
+ return null;
1021
+ }
1022
+ };
1115
1023
  function SceneRenderer(props) {
1116
1024
  const {
1117
1025
  mjModelRef,
@@ -1142,7 +1050,7 @@ function SceneRenderer(props) {
1142
1050
  }
1143
1051
  const refs = [];
1144
1052
  for (let i = 0; i < model.nbody; i++) {
1145
- const bodyGroup = new THREE11.Group();
1053
+ const bodyGroup = new THREE10.Group();
1146
1054
  bodyGroup.userData.bodyID = i;
1147
1055
  const bodyName = getName(model, model.name_bodyadr[i]);
1148
1056
  if (!hiddenBodiesRef.current.has(bodyName)) {
@@ -1158,7 +1066,7 @@ function SceneRenderer(props) {
1158
1066
  }
1159
1067
  bodyRefs.current = refs;
1160
1068
  }, [status, geomBuilder, mjModelRef]);
1161
- useFrame(() => {
1069
+ const syncBodiesToData = useCallback(() => {
1162
1070
  const data = mjDataRef.current;
1163
1071
  if (!data) return;
1164
1072
  const bodies = bodyRefs.current;
@@ -1171,9 +1079,9 @@ function SceneRenderer(props) {
1171
1079
  const alpha = interpolation.alpha;
1172
1080
  const i3 = i * 3;
1173
1081
  ref.position.set(
1174
- THREE11.MathUtils.lerp(interpolation.previousXpos[i3], interpolation.currentXpos[i3], alpha),
1175
- THREE11.MathUtils.lerp(interpolation.previousXpos[i3 + 1], interpolation.currentXpos[i3 + 1], alpha),
1176
- THREE11.MathUtils.lerp(interpolation.previousXpos[i3 + 2], interpolation.currentXpos[i3 + 2], alpha)
1082
+ THREE10.MathUtils.lerp(interpolation.previousXpos[i3], interpolation.currentXpos[i3], alpha),
1083
+ THREE10.MathUtils.lerp(interpolation.previousXpos[i3 + 1], interpolation.currentXpos[i3 + 1], alpha),
1084
+ THREE10.MathUtils.lerp(interpolation.previousXpos[i3 + 2], interpolation.currentXpos[i3 + 2], alpha)
1177
1085
  );
1178
1086
  const i4 = i * 4;
1179
1087
  _previousQuat.set(
@@ -1203,12 +1111,17 @@ function SceneRenderer(props) {
1203
1111
  );
1204
1112
  }
1205
1113
  }
1206
- });
1114
+ }, [interpolateRef, interpolationStateRef, mjDataRef]);
1115
+ useFrame(syncBodiesToData);
1207
1116
  return /* @__PURE__ */ jsx(
1208
1117
  "group",
1209
1118
  {
1210
1119
  ...props,
1211
1120
  ref: groupRef,
1121
+ userData: {
1122
+ ...props.userData,
1123
+ [CAMERA_FRAME_CAPTURE_PRE_RENDER_USER_DATA_KEY]: syncBodiesToData
1124
+ },
1212
1125
  onDoubleClick: (e) => {
1213
1126
  if (typeof props.onDoubleClick === "function") props.onDoubleClick(e);
1214
1127
  e.stopPropagation();
@@ -1228,8 +1141,8 @@ function SceneRenderer(props) {
1228
1141
  }
1229
1142
  );
1230
1143
  }
1231
- var _previousQuat = new THREE11.Quaternion();
1232
- var _currentQuat = new THREE11.Quaternion();
1144
+ var _previousQuat = new THREE10.Quaternion();
1145
+ var _currentQuat = new THREE10.Quaternion();
1233
1146
  function isTargetRef(target) {
1234
1147
  return Boolean(target && typeof target === "object" && "current" in target);
1235
1148
  }
@@ -1810,8 +1723,8 @@ var _applyPoint = new Float64Array(3);
1810
1723
  var _rayPnt = new Float64Array(3);
1811
1724
  var _rayVec = new Float64Array(3);
1812
1725
  var _rayGeomId = new Int32Array(1);
1813
- var _projRaycaster = new THREE11.Raycaster();
1814
- var _projNdc = new THREE11.Vector2();
1726
+ var _projRaycaster = new THREE10.Raycaster();
1727
+ var _projNdc = new THREE10.Vector2();
1815
1728
  function waitForNextAnimationFrame2() {
1816
1729
  return new Promise((resolve) => {
1817
1730
  requestAnimationFrame(() => resolve());
@@ -1836,7 +1749,7 @@ function quaternionFromMujocoQuat(values, offset) {
1836
1749
  ];
1837
1750
  }
1838
1751
  function quaternionFromXmat(values, offset) {
1839
- const matrix = new THREE11.Matrix4();
1752
+ const matrix = new THREE10.Matrix4();
1840
1753
  matrix.set(
1841
1754
  values[offset],
1842
1755
  values[offset + 1],
@@ -1855,13 +1768,49 @@ function quaternionFromXmat(values, offset) {
1855
1768
  0,
1856
1769
  1
1857
1770
  );
1858
- const quaternion = new THREE11.Quaternion().setFromRotationMatrix(matrix);
1771
+ const quaternion = new THREE10.Quaternion().setFromRotationMatrix(matrix);
1859
1772
  return [quaternion.x, quaternion.y, quaternion.z, quaternion.w];
1860
1773
  }
1861
1774
  function omitResolvedCameraSelectors(options) {
1862
1775
  const { cameraName, siteName, bodyName, ...rest } = options;
1863
1776
  return rest;
1864
1777
  }
1778
+ function vector3FromCaptureValue(value) {
1779
+ return value instanceof THREE10.Vector3 ? value.clone() : new THREE10.Vector3(value[0], value[1], value[2]);
1780
+ }
1781
+ function quaternionFromCaptureValue(value) {
1782
+ return value instanceof THREE10.Quaternion ? value.clone() : new THREE10.Quaternion(value[0], value[1], value[2], value[3]);
1783
+ }
1784
+ function applyMountedCameraPoseOffsets(options, position, quaternion) {
1785
+ const resolvedPosition = new THREE10.Vector3(position[0], position[1], position[2]);
1786
+ const resolvedQuaternion = new THREE10.Quaternion(
1787
+ quaternion[0],
1788
+ quaternion[1],
1789
+ quaternion[2],
1790
+ quaternion[3]
1791
+ );
1792
+ if (options.positionOffset) {
1793
+ resolvedPosition.add(
1794
+ vector3FromCaptureValue(options.positionOffset).applyQuaternion(resolvedQuaternion)
1795
+ );
1796
+ }
1797
+ if (options.quaternionOffset) {
1798
+ resolvedQuaternion.multiply(quaternionFromCaptureValue(options.quaternionOffset)).normalize();
1799
+ }
1800
+ return {
1801
+ position: [
1802
+ resolvedPosition.x,
1803
+ resolvedPosition.y,
1804
+ resolvedPosition.z
1805
+ ],
1806
+ quaternion: [
1807
+ resolvedQuaternion.x,
1808
+ resolvedQuaternion.y,
1809
+ resolvedQuaternion.z,
1810
+ resolvedQuaternion.w
1811
+ ]
1812
+ };
1813
+ }
1865
1814
  function countMountedCameraSelectors(options) {
1866
1815
  return Number(Boolean(options.cameraName)) + Number(Boolean(options.siteName)) + Number(Boolean(options.bodyName));
1867
1816
  }
@@ -2553,10 +2502,10 @@ function MujocoSimProvider({
2553
2502
  `MuJoCo camera "${options.cameraName}" does not expose a capture pose.`
2554
2503
  );
2555
2504
  }
2505
+ const pose = applyMountedCameraPoseOffsets(options, position, quaternion);
2556
2506
  return {
2557
2507
  ...baseOptions,
2558
- position,
2559
- quaternion,
2508
+ ...pose,
2560
2509
  fov: options.fov ?? model.cam_fovy?.[cameraId],
2561
2510
  source: { kind: "mujoco-camera", cameraName: options.cameraName }
2562
2511
  };
@@ -2566,10 +2515,14 @@ function MujocoSimProvider({
2566
2515
  if (siteId < 0) {
2567
2516
  throw new Error(`MuJoCo site "${options.siteName}" was not found.`);
2568
2517
  }
2518
+ const pose = applyMountedCameraPoseOffsets(
2519
+ options,
2520
+ vector3FromArray(data.site_xpos, siteId * 3),
2521
+ quaternionFromXmat(data.site_xmat, siteId * 9)
2522
+ );
2569
2523
  return {
2570
2524
  ...baseOptions,
2571
- position: vector3FromArray(data.site_xpos, siteId * 3),
2572
- quaternion: quaternionFromXmat(data.site_xmat, siteId * 9),
2525
+ ...pose,
2573
2526
  source: { kind: "mujoco-site", siteName: options.siteName }
2574
2527
  };
2575
2528
  }
@@ -2583,10 +2536,14 @@ function MujocoSimProvider({
2583
2536
  `MuJoCo body "${options.bodyName}" does not expose world orientation data.`
2584
2537
  );
2585
2538
  }
2539
+ const pose = applyMountedCameraPoseOffsets(
2540
+ options,
2541
+ vector3FromArray(data.xpos, bodyId * 3),
2542
+ quaternionFromXmat(data.xmat, bodyId * 9)
2543
+ );
2586
2544
  return {
2587
2545
  ...baseOptions,
2588
- position: vector3FromArray(data.xpos, bodyId * 3),
2589
- quaternion: quaternionFromXmat(data.xmat, bodyId * 9),
2546
+ ...pose,
2590
2547
  source: { kind: "mujoco-body", bodyName: options.bodyName }
2591
2548
  };
2592
2549
  }
@@ -2633,7 +2590,7 @@ function MujocoSimProvider({
2633
2590
  const geomId = _rayGeomId[0];
2634
2591
  const bodyId = geomId >= 0 ? model.geom_bodyid[geomId] : -1;
2635
2592
  return {
2636
- point: new THREE11.Vector3(
2593
+ point: new THREE10.Vector3(
2637
2594
  origin.x + dir.x * dist,
2638
2595
  origin.y + dir.y * dist,
2639
2596
  origin.z + dir.z * dist
@@ -3612,7 +3569,7 @@ function solve6x6(A, b, x) {
3612
3569
  }
3613
3570
 
3614
3571
  // src/hooks/useIkController.ts
3615
- var _syncMat4 = new THREE11.Matrix4();
3572
+ var _syncMat4 = new THREE10.Matrix4();
3616
3573
  function syncGizmoToSite(data, siteId, target) {
3617
3574
  if (siteId === -1) return;
3618
3575
  const sitePos = data.site_xpos.subarray(siteId * 3, siteId * 3 + 3);
@@ -3644,7 +3601,7 @@ var useIkController = createControllerHook(
3644
3601
  const { mjModelRef, mjDataRef, mujocoRef, resetCallbacks, status } = useMujocoContext();
3645
3602
  const ikEnabledRef = useRef(false);
3646
3603
  const ikCalculatingRef = useRef(false);
3647
- const ikTargetRef = useRef(new THREE11.Group());
3604
+ const ikTargetRef = useRef(new THREE10.Group());
3648
3605
  const siteIdRef = useRef(-1);
3649
3606
  const controlGroupRef = useRef(null);
3650
3607
  const genericIkRef = useRef(new GenericIK(mujocoRef.current));
@@ -3652,10 +3609,10 @@ var useIkController = createControllerHook(
3652
3609
  const needsInitialSync = useRef(true);
3653
3610
  const gizmoAnimRef = useRef({
3654
3611
  active: false,
3655
- startPos: new THREE11.Vector3(),
3656
- endPos: new THREE11.Vector3(),
3657
- startRot: new THREE11.Quaternion(),
3658
- endRot: new THREE11.Quaternion(),
3612
+ startPos: new THREE10.Vector3(),
3613
+ endPos: new THREE10.Vector3(),
3614
+ startRot: new THREE10.Quaternion(),
3615
+ endRot: new THREE10.Quaternion(),
3659
3616
  startTime: 0,
3660
3617
  duration: 1e3
3661
3618
  });
@@ -3724,8 +3681,8 @@ var useIkController = createControllerHook(
3724
3681
  const ga = gizmoAnimRef.current;
3725
3682
  const target = ikTargetRef.current;
3726
3683
  if (!ga.active || !target) return;
3727
- const now = performance.now();
3728
- const elapsed = now - ga.startTime;
3684
+ const now2 = performance.now();
3685
+ const elapsed = now2 - ga.startTime;
3729
3686
  const t = Math.min(elapsed / ga.duration, 1);
3730
3687
  const ease = 1 - Math.pow(1 - t, 3);
3731
3688
  target.position.lerpVectors(ga.startPos, ga.endPos, ease);
@@ -3807,8 +3764,8 @@ var useIkController = createControllerHook(
3807
3764
  const target = ikTargetRef.current;
3808
3765
  if (!target) return;
3809
3766
  const targetPos = pos.clone();
3810
- const targetRot = new THREE11.Quaternion().setFromEuler(
3811
- new THREE11.Euler(Math.PI, 0, 0)
3767
+ const targetRot = new THREE10.Quaternion().setFromEuler(
3768
+ new THREE10.Euler(Math.PI, 0, 0)
3812
3769
  );
3813
3770
  if (duration > 0) {
3814
3771
  const ga = gizmoAnimRef.current;
@@ -3833,7 +3790,7 @@ var useIkController = createControllerHook(
3833
3790
  if (!ikCalculatingRef.current || !target) return null;
3834
3791
  return {
3835
3792
  pos: target.position.clone(),
3836
- rot: new THREE11.Euler().setFromQuaternion(target.quaternion)
3793
+ rot: new THREE10.Euler().setFromQuaternion(target.quaternion)
3837
3794
  };
3838
3795
  },
3839
3796
  []
@@ -3929,10 +3886,10 @@ function Body({
3929
3886
  if (!hasChildren) return null;
3930
3887
  return /* @__PURE__ */ jsx("group", { ref: groupRef, children });
3931
3888
  }
3932
- var _mat4 = new THREE11.Matrix4();
3933
- var _pos = new THREE11.Vector3();
3934
- var _quat = new THREE11.Quaternion();
3935
- var _scale = new THREE11.Vector3(1, 1, 1);
3889
+ var _mat4 = new THREE10.Matrix4();
3890
+ var _pos = new THREE10.Vector3();
3891
+ var _quat = new THREE10.Quaternion();
3892
+ var _scale = new THREE10.Vector3(1, 1, 1);
3936
3893
  function IkGizmo({ controller, siteName, scale = 0.18, onDrag }) {
3937
3894
  const { mjModelRef, mjDataRef, status } = useMujocoContext();
3938
3895
  const { ikTargetRef, siteIdRef, ikEnabledRef, setIkEnabled } = controller;
@@ -4031,7 +3988,7 @@ function IkGizmo({ controller, siteName, scale = 0.18, onDrag }) {
4031
3988
  }
4032
3989
  );
4033
3990
  }
4034
- var _dummy = new THREE11.Object3D();
3991
+ var _dummy = new THREE10.Object3D();
4035
3992
  function ContactMarkers({
4036
3993
  maxContacts = 100,
4037
3994
  radius = 8e-3,
@@ -4085,11 +4042,11 @@ function ContactMarkers({
4085
4042
  var _force = new Float64Array(3);
4086
4043
  var _torque = new Float64Array(3);
4087
4044
  var _point = new Float64Array(3);
4088
- var _bodyPos = new THREE11.Vector3();
4089
- var _bodyQuat = new THREE11.Quaternion();
4090
- var _worldHit = new THREE11.Vector3();
4091
- var _raycaster = new THREE11.Raycaster();
4092
- var _mouse = new THREE11.Vector2();
4045
+ var _bodyPos = new THREE10.Vector3();
4046
+ var _bodyQuat = new THREE10.Quaternion();
4047
+ var _worldHit = new THREE10.Vector3();
4048
+ var _raycaster = new THREE10.Raycaster();
4049
+ var _mouse = new THREE10.Vector2();
4093
4050
  function DragInteraction({
4094
4051
  stiffness = 250,
4095
4052
  showArrow = true,
@@ -4100,16 +4057,16 @@ function DragInteraction({
4100
4057
  const draggingRef = useRef(false);
4101
4058
  const bodyIdRef = useRef(-1);
4102
4059
  const grabDistanceRef = useRef(0);
4103
- const localHitRef = useRef(new THREE11.Vector3());
4104
- const grabWorldRef = useRef(new THREE11.Vector3());
4105
- const mouseWorldRef = useRef(new THREE11.Vector3());
4060
+ const localHitRef = useRef(new THREE10.Vector3());
4061
+ const grabWorldRef = useRef(new THREE10.Vector3());
4062
+ const mouseWorldRef = useRef(new THREE10.Vector3());
4106
4063
  const arrowRef = useRef(null);
4107
4064
  const groupRef = useRef(null);
4108
4065
  useEffect(() => {
4109
4066
  if (!showArrow || !groupRef.current) return;
4110
- const arrow = new THREE11.ArrowHelper(
4111
- new THREE11.Vector3(0, 1, 0),
4112
- new THREE11.Vector3(),
4067
+ const arrow = new THREE10.ArrowHelper(
4068
+ new THREE10.Vector3(0, 1, 0),
4069
+ new THREE10.Vector3(),
4113
4070
  0.1,
4114
4071
  16729156
4115
4072
  );
@@ -4292,7 +4249,7 @@ function useSceneLights(intensity = 1) {
4292
4249
  const dr = lightDiffuse ? lightDiffuse[3 * i] : 1;
4293
4250
  const dg = lightDiffuse ? lightDiffuse[3 * i + 1] : 1;
4294
4251
  const db = lightDiffuse ? lightDiffuse[3 * i + 2] : 1;
4295
- const color = new THREE11.Color(dr, dg, db);
4252
+ const color = new THREE10.Color(dr, dg, db);
4296
4253
  const px = lightPos[3 * i];
4297
4254
  const py = lightPos[3 * i + 1];
4298
4255
  const pz = lightPos[3 * i + 2];
@@ -4300,7 +4257,7 @@ function useSceneLights(intensity = 1) {
4300
4257
  const dy = lightDir[3 * i + 1];
4301
4258
  const dz = lightDir[3 * i + 2];
4302
4259
  if (isDirectional) {
4303
- const light = new THREE11.DirectionalLight(color, finalIntensity);
4260
+ const light = new THREE10.DirectionalLight(color, finalIntensity);
4304
4261
  light.position.set(px, py, pz);
4305
4262
  light.target.position.set(px + dx, py + dy, pz + dz);
4306
4263
  light.castShadow = castShadow;
@@ -4323,7 +4280,7 @@ function useSceneLights(intensity = 1) {
4323
4280
  const cutoff = lightCutoff ? lightCutoff[i] : 45;
4324
4281
  const exponent = lightExponent ? lightExponent[i] : 10;
4325
4282
  const angle = cutoff * Math.PI / 180;
4326
- const light = new THREE11.SpotLight(color, finalIntensity, 0, angle, exponent / 128);
4283
+ const light = new THREE10.SpotLight(color, finalIntensity, 0, angle, exponent / 128);
4327
4284
  light.position.set(px, py, pz);
4328
4285
  light.target.position.set(px + dx, py + dy, pz + dz);
4329
4286
  light.castShadow = castShadow;
@@ -4606,12 +4563,12 @@ var JOINT_COLORS = {
4606
4563
  3: 16776960
4607
4564
  // hinge - yellow
4608
4565
  };
4609
- var _v3a = new THREE11.Vector3();
4610
- new THREE11.Vector3();
4611
- var _quat2 = new THREE11.Quaternion();
4612
- var _cameraMatrix = new THREE11.Matrix4();
4613
- var _contactPos = new THREE11.Vector3();
4614
- var _contactNormal = new THREE11.Vector3();
4566
+ var _v3a = new THREE10.Vector3();
4567
+ new THREE10.Vector3();
4568
+ var _quat2 = new THREE10.Quaternion();
4569
+ var _cameraMatrix = new THREE10.Matrix4();
4570
+ var _contactPos = new THREE10.Vector3();
4571
+ var _contactNormal = new THREE10.Vector3();
4615
4572
  var MAX_CONTACT_ARROWS = 50;
4616
4573
  var CAMERA_DEBUG_LENGTH = 0.12;
4617
4574
  var CAMERA_DEBUG_FRUSTUM_DEPTH = 0.08;
@@ -4644,21 +4601,21 @@ function Debug({
4644
4601
  let geometry = null;
4645
4602
  switch (type) {
4646
4603
  case 2:
4647
- geometry = new THREE11.SphereGeometry(s[3 * i], 12, 8);
4604
+ geometry = new THREE10.SphereGeometry(s[3 * i], 12, 8);
4648
4605
  break;
4649
4606
  case 3:
4650
- geometry = new THREE11.CapsuleGeometry(s[3 * i], s[3 * i + 1] * 2, 6, 8);
4607
+ geometry = new THREE10.CapsuleGeometry(s[3 * i], s[3 * i + 1] * 2, 6, 8);
4651
4608
  break;
4652
4609
  case 5:
4653
- geometry = new THREE11.CylinderGeometry(s[3 * i], s[3 * i], s[3 * i + 1] * 2, 12);
4610
+ geometry = new THREE10.CylinderGeometry(s[3 * i], s[3 * i], s[3 * i + 1] * 2, 12);
4654
4611
  break;
4655
4612
  case 6:
4656
- geometry = new THREE11.BoxGeometry(s[3 * i] * 2, s[3 * i + 1] * 2, s[3 * i + 2] * 2);
4613
+ geometry = new THREE10.BoxGeometry(s[3 * i] * 2, s[3 * i + 1] * 2, s[3 * i + 2] * 2);
4657
4614
  break;
4658
4615
  }
4659
4616
  if (geometry) {
4660
- const mat = new THREE11.MeshBasicMaterial({ color: 65280, wireframe: true, transparent: true, opacity: 0.3 });
4661
- const mesh = new THREE11.Mesh(geometry, mat);
4617
+ const mat = new THREE10.MeshBasicMaterial({ color: 65280, wireframe: true, transparent: true, opacity: 0.3 });
4618
+ const mesh = new THREE10.Mesh(geometry, mat);
4662
4619
  mesh.userData.geomId = i;
4663
4620
  mesh.userData.bodyId = model.geom_bodyid[i];
4664
4621
  geoms.push(mesh);
@@ -4681,9 +4638,9 @@ function Debug({
4681
4638
  }
4682
4639
  if (maxGeomSize > 0) radius = maxGeomSize * 0.15;
4683
4640
  }
4684
- const geometry = new THREE11.OctahedronGeometry(radius);
4685
- const mat = new THREE11.MeshBasicMaterial({ color: 16711935, depthTest: false });
4686
- const mesh = new THREE11.Mesh(geometry, mat);
4641
+ const geometry = new THREE10.OctahedronGeometry(radius);
4642
+ const mat = new THREE10.MeshBasicMaterial({ color: 16711935, depthTest: false });
4643
+ const mesh = new THREE10.Mesh(geometry, mat);
4687
4644
  mesh.renderOrder = 999;
4688
4645
  mesh.frustumCulled = false;
4689
4646
  mesh.userData.siteId = i;
@@ -4695,9 +4652,9 @@ function Debug({
4695
4652
  ctx.font = "bold 36px monospace";
4696
4653
  ctx.textAlign = "center";
4697
4654
  ctx.fillText(getName(model, model.name_siteadr[i]), 128, 42);
4698
- const tex = new THREE11.CanvasTexture(canvas);
4699
- const spriteMat = new THREE11.SpriteMaterial({ map: tex, depthTest: false, transparent: true });
4700
- const sprite = new THREE11.Sprite(spriteMat);
4655
+ const tex = new THREE10.CanvasTexture(canvas);
4656
+ const spriteMat = new THREE10.SpriteMaterial({ map: tex, depthTest: false, transparent: true });
4657
+ const sprite = new THREE10.Sprite(spriteMat);
4701
4658
  const labelScale = radius * 15;
4702
4659
  sprite.scale.set(labelScale, labelScale * 0.25, 1);
4703
4660
  sprite.position.y = radius * 2;
@@ -4720,9 +4677,9 @@ function Debug({
4720
4677
  }
4721
4678
  }
4722
4679
  const arrowLen = Math.max(maxGeomSize * 0.8, 0.05);
4723
- const arrow = new THREE11.ArrowHelper(
4724
- new THREE11.Vector3(0, 0, 1),
4725
- new THREE11.Vector3(),
4680
+ const arrow = new THREE10.ArrowHelper(
4681
+ new THREE10.Vector3(0, 0, 1),
4682
+ new THREE10.Vector3(),
4726
4683
  arrowLen,
4727
4684
  color,
4728
4685
  arrowLen * 0.25,
@@ -4730,7 +4687,7 @@ function Debug({
4730
4687
  );
4731
4688
  arrow.renderOrder = 999;
4732
4689
  arrow.frustumCulled = false;
4733
- arrow.line.material = new THREE11.LineBasicMaterial({ color, depthTest: false });
4690
+ arrow.line.material = new THREE10.LineBasicMaterial({ color, depthTest: false });
4734
4691
  arrow.cone.material.depthTest = false;
4735
4692
  arrow.line.renderOrder = 999;
4736
4693
  arrow.line.frustumCulled = false;
@@ -4745,20 +4702,20 @@ function Debug({
4745
4702
  }
4746
4703
  if (showCameras && model.ncam && model.name_camadr) {
4747
4704
  for (let i = 0; i < model.ncam; i++) {
4748
- const group = new THREE11.Group();
4705
+ const group = new THREE10.Group();
4749
4706
  group.userData.cameraId = i;
4750
4707
  group.renderOrder = 999;
4751
4708
  group.frustumCulled = false;
4752
- const marker = new THREE11.Mesh(
4753
- new THREE11.BoxGeometry(0.014, 9e-3, 6e-3),
4754
- new THREE11.MeshBasicMaterial({ color: 3718648, depthTest: false })
4709
+ const marker = new THREE10.Mesh(
4710
+ new THREE10.BoxGeometry(0.014, 9e-3, 6e-3),
4711
+ new THREE10.MeshBasicMaterial({ color: 3718648, depthTest: false })
4755
4712
  );
4756
4713
  marker.renderOrder = 999;
4757
4714
  marker.frustumCulled = false;
4758
4715
  group.add(marker);
4759
- const forward = new THREE11.ArrowHelper(
4760
- new THREE11.Vector3(0, 0, -1),
4761
- new THREE11.Vector3(),
4716
+ const forward = new THREE10.ArrowHelper(
4717
+ new THREE10.Vector3(0, 0, -1),
4718
+ new THREE10.Vector3(),
4762
4719
  CAMERA_DEBUG_LENGTH,
4763
4720
  3718648,
4764
4721
  CAMERA_DEBUG_LENGTH * 0.24,
@@ -4766,20 +4723,20 @@ function Debug({
4766
4723
  );
4767
4724
  forward.renderOrder = 999;
4768
4725
  forward.frustumCulled = false;
4769
- forward.line.material = new THREE11.LineBasicMaterial({
4726
+ forward.line.material = new THREE10.LineBasicMaterial({
4770
4727
  color: 3718648,
4771
4728
  depthTest: false
4772
4729
  });
4773
4730
  forward.cone.material.depthTest = false;
4774
4731
  group.add(forward);
4775
- const frustumGeometry = new THREE11.BufferGeometry();
4732
+ const frustumGeometry = new THREE10.BufferGeometry();
4776
4733
  frustumGeometry.setAttribute(
4777
4734
  "position",
4778
- new THREE11.Float32BufferAttribute(new Float32Array(8 * 2 * 3), 3)
4735
+ new THREE10.Float32BufferAttribute(new Float32Array(8 * 2 * 3), 3)
4779
4736
  );
4780
- const frustum = new THREE11.LineSegments(
4737
+ const frustum = new THREE10.LineSegments(
4781
4738
  frustumGeometry,
4782
- new THREE11.LineBasicMaterial({
4739
+ new THREE10.LineBasicMaterial({
4783
4740
  color: 3718648,
4784
4741
  transparent: true,
4785
4742
  opacity: 0.8,
@@ -4798,9 +4755,9 @@ function Debug({
4798
4755
  ctx.font = "bold 32px monospace";
4799
4756
  ctx.textAlign = "center";
4800
4757
  ctx.fillText(getName(model, model.name_camadr[i]), 128, 42);
4801
- const texture = new THREE11.CanvasTexture(canvas);
4802
- const sprite = new THREE11.Sprite(
4803
- new THREE11.SpriteMaterial({
4758
+ const texture = new THREE10.CanvasTexture(canvas);
4759
+ const sprite = new THREE10.Sprite(
4760
+ new THREE10.SpriteMaterial({
4804
4761
  map: texture,
4805
4762
  depthTest: false,
4806
4763
  transparent: true
@@ -4816,9 +4773,9 @@ function Debug({
4816
4773
  }
4817
4774
  if (showCOM) {
4818
4775
  for (let i = 1; i < model.nbody; i++) {
4819
- const geometry = new THREE11.SphereGeometry(5e-3, 6, 6);
4820
- const mat = new THREE11.MeshBasicMaterial({ color: 16711680 });
4821
- const mesh = new THREE11.Mesh(geometry, mat);
4776
+ const geometry = new THREE10.SphereGeometry(5e-3, 6, 6);
4777
+ const mat = new THREE10.MeshBasicMaterial({ color: 16711680 });
4778
+ const mesh = new THREE10.Mesh(geometry, mat);
4822
4779
  mesh.userData.bodyId = i;
4823
4780
  comMarkers.push(mesh);
4824
4781
  }
@@ -4927,7 +4884,7 @@ function Debug({
4927
4884
  );
4928
4885
  group.quaternion.setFromRotationMatrix(_cameraMatrix);
4929
4886
  const fovy = model.cam_fovy?.[cameraId] ?? 45;
4930
- const halfHeight = Math.tan(THREE11.MathUtils.degToRad(fovy) / 2) * CAMERA_DEBUG_FRUSTUM_DEPTH;
4887
+ const halfHeight = Math.tan(THREE10.MathUtils.degToRad(fovy) / 2) * CAMERA_DEBUG_FRUSTUM_DEPTH;
4931
4888
  const halfWidth = halfHeight * 4 / 3;
4932
4889
  const positions = group.userData.frustum.geometry.attributes.position;
4933
4890
  const array = positions.array;
@@ -4972,9 +4929,9 @@ function Debug({
4972
4929
  contactPoolInitRef.current = true;
4973
4930
  const pool = [];
4974
4931
  for (let i = 0; i < MAX_CONTACT_ARROWS; i++) {
4975
- const arrow = new THREE11.ArrowHelper(
4976
- new THREE11.Vector3(0, 1, 0),
4977
- new THREE11.Vector3(),
4932
+ const arrow = new THREE10.ArrowHelper(
4933
+ new THREE10.Vector3(0, 1, 0),
4934
+ new THREE10.Vector3(),
4978
4935
  0.1,
4979
4936
  16729156,
4980
4937
  0.03,
@@ -5039,9 +4996,9 @@ function Debug({
5039
4996
  }
5040
4997
  );
5041
4998
  }
5042
- var DEFAULT_TENDON_COLOR = new THREE11.Color(0.3, 0.3, 0.8);
4999
+ var DEFAULT_TENDON_COLOR = new THREE10.Color(0.3, 0.3, 0.8);
5043
5000
  var DEFAULT_TENDON_WIDTH = 2e-3;
5044
- new THREE11.Vector3();
5001
+ new THREE10.Vector3();
5045
5002
  function TendonRenderer(props) {
5046
5003
  const { mjModelRef, mjDataRef, status } = useMujocoContext();
5047
5004
  const groupRef = useRef(null);
@@ -5055,7 +5012,7 @@ function TendonRenderer(props) {
5055
5012
  if (!model || !data || !group) return;
5056
5013
  const ntendon = model.ntendon ?? 0;
5057
5014
  if (ntendon === 0) return;
5058
- const material = new THREE11.MeshStandardMaterial({
5015
+ const material = new THREE10.MeshStandardMaterial({
5059
5016
  color: DEFAULT_TENDON_COLOR,
5060
5017
  roughness: 0.6,
5061
5018
  metalness: 0.1
@@ -5070,11 +5027,11 @@ function TendonRenderer(props) {
5070
5027
  curves.push(null);
5071
5028
  continue;
5072
5029
  }
5073
- const points = Array.from({ length: wrapNum }, () => new THREE11.Vector3());
5074
- const curve = new THREE11.CatmullRomCurve3(points, false);
5030
+ const points = Array.from({ length: wrapNum }, () => new THREE10.Vector3());
5031
+ const curve = new THREE10.CatmullRomCurve3(points, false);
5075
5032
  const segments = Math.max(wrapNum * 2, 4);
5076
- const geometry = new THREE11.TubeGeometry(curve, segments, DEFAULT_TENDON_WIDTH, 6, false);
5077
- const mesh = new THREE11.Mesh(geometry, material);
5033
+ const geometry = new THREE10.TubeGeometry(curve, segments, DEFAULT_TENDON_WIDTH, 6, false);
5034
+ const mesh = new THREE10.Mesh(geometry, material);
5078
5035
  mesh.frustumCulled = false;
5079
5036
  group.add(mesh);
5080
5037
  meshes.push(mesh);
@@ -5129,11 +5086,11 @@ function TendonRenderer(props) {
5129
5086
  if (curve.points.length !== validCount) {
5130
5087
  curve.points.length = validCount;
5131
5088
  while (curve.points.length < validCount) {
5132
- curve.points.push(new THREE11.Vector3());
5089
+ curve.points.push(new THREE10.Vector3());
5133
5090
  }
5134
5091
  }
5135
5092
  mesh.geometry.dispose();
5136
- mesh.geometry = new THREE11.TubeGeometry(
5093
+ mesh.geometry = new THREE10.TubeGeometry(
5137
5094
  curve,
5138
5095
  Math.max(validCount * 2, 4),
5139
5096
  DEFAULT_TENDON_WIDTH,
@@ -5160,24 +5117,24 @@ function FlexRenderer(props) {
5160
5117
  const vertAdr = model.flex_vertadr[f];
5161
5118
  const vertNum = model.flex_vertnum[f];
5162
5119
  if (vertNum === 0) continue;
5163
- const geometry = new THREE11.BufferGeometry();
5120
+ const geometry = new THREE10.BufferGeometry();
5164
5121
  const positions = new Float32Array(vertNum * 3);
5165
- geometry.setAttribute("position", new THREE11.BufferAttribute(positions, 3));
5122
+ geometry.setAttribute("position", new THREE10.BufferAttribute(positions, 3));
5166
5123
  geometry.computeVertexNormals();
5167
- let color = new THREE11.Color(0.5, 0.5, 0.5);
5124
+ let color = new THREE10.Color(0.5, 0.5, 0.5);
5168
5125
  if (model.flex_rgba) {
5169
- color = new THREE11.Color(
5126
+ color = new THREE10.Color(
5170
5127
  model.flex_rgba[4 * f],
5171
5128
  model.flex_rgba[4 * f + 1],
5172
5129
  model.flex_rgba[4 * f + 2]
5173
5130
  );
5174
5131
  }
5175
- const material = new THREE11.MeshStandardMaterial({
5132
+ const material = new THREE10.MeshStandardMaterial({
5176
5133
  color,
5177
5134
  roughness: 0.7,
5178
- side: THREE11.DoubleSide
5135
+ side: THREE10.DoubleSide
5179
5136
  });
5180
- const mesh = new THREE11.Mesh(geometry, material);
5137
+ const mesh = new THREE10.Mesh(geometry, material);
5181
5138
  mesh.userData.flexId = f;
5182
5139
  mesh.userData.vertAdr = vertAdr;
5183
5140
  mesh.userData.vertNum = vertNum;
@@ -5213,7 +5170,7 @@ function FlexRenderer(props) {
5213
5170
  return /* @__PURE__ */ jsx("group", { ...props, ref: groupRef });
5214
5171
  }
5215
5172
  var GEOM_TYPE_NAMES2 = ["plane", "hfield", "sphere", "capsule", "ellipsoid", "cylinder", "box", "mesh"];
5216
- var _matrix = new THREE11.Matrix4();
5173
+ var _matrix = new THREE10.Matrix4();
5217
5174
  function getGeomInfo(model, geomId) {
5218
5175
  const size = model.geom_size.subarray(geomId * 3, geomId * 3 + 3);
5219
5176
  const type = model.geom_type[geomId];
@@ -5235,10 +5192,10 @@ function geomSignature(model, geomId) {
5235
5192
  return [type, size, mat, data, rgba].join("|");
5236
5193
  }
5237
5194
  function firstMesh(object) {
5238
- if (object instanceof THREE11.Mesh) return object;
5195
+ if (object instanceof THREE10.Mesh) return object;
5239
5196
  let mesh = null;
5240
5197
  object.traverse((child) => {
5241
- if (!mesh && child instanceof THREE11.Mesh) mesh = child;
5198
+ if (!mesh && child instanceof THREE10.Mesh) mesh = child;
5242
5199
  });
5243
5200
  return mesh;
5244
5201
  }
@@ -5532,12 +5489,12 @@ function useTrajectoryPlayer(trajectory, options = {}) {
5532
5489
  if ((optionsRef.current.mode ?? "kinematic") !== "kinematic") return;
5533
5490
  const traj = trajectoryRef.current;
5534
5491
  if (traj.length === 0) return;
5535
- const now = performance.now();
5492
+ const now2 = performance.now();
5536
5493
  const fps = optionsRef.current.fps ?? 30;
5537
5494
  const frameInterval = 1e3 / (fps * speedRef.current);
5538
- const elapsed = now - lastFrameTimeRef.current;
5495
+ const elapsed = now2 - lastFrameTimeRef.current;
5539
5496
  if (elapsed < frameInterval) return;
5540
- lastFrameTimeRef.current = now;
5497
+ lastFrameTimeRef.current = now2;
5541
5498
  const model = mjModelRef.current;
5542
5499
  const data = mjDataRef.current;
5543
5500
  if (!model || !data) return;
@@ -5667,12 +5624,12 @@ function useActuators() {
5667
5624
  return actuators;
5668
5625
  }, [status, mjModelRef]);
5669
5626
  }
5670
- var _mat42 = new THREE11.Matrix4();
5627
+ var _mat42 = new THREE10.Matrix4();
5671
5628
  function useSitePosition(siteName) {
5672
5629
  const { mjModelRef, mjDataRef, status } = useMujocoContext();
5673
5630
  const siteIdRef = useRef(-1);
5674
- const positionRef = useRef(new THREE11.Vector3());
5675
- const quaternionRef = useRef(new THREE11.Quaternion());
5631
+ const positionRef = useRef(new THREE10.Vector3());
5632
+ const quaternionRef = useRef(new THREE10.Quaternion());
5676
5633
  useEffect(() => {
5677
5634
  const model = mjModelRef.current;
5678
5635
  if (!model || status !== "ready") {
@@ -5865,10 +5822,10 @@ function useJointState(name, options = {}) {
5865
5822
  function useBodyState(name) {
5866
5823
  const { mjModelRef, status } = useMujocoContext();
5867
5824
  const bodyIdRef = useRef(-1);
5868
- const position = useRef(new THREE11.Vector3());
5869
- const quaternion = useRef(new THREE11.Quaternion());
5870
- const linearVelocity = useRef(new THREE11.Vector3());
5871
- const angularVelocity = useRef(new THREE11.Vector3());
5825
+ const position = useRef(new THREE10.Vector3());
5826
+ const quaternion = useRef(new THREE10.Quaternion());
5827
+ const linearVelocity = useRef(new THREE10.Vector3());
5828
+ const angularVelocity = useRef(new THREE10.Vector3());
5872
5829
  useEffect(() => {
5873
5830
  const model = mjModelRef.current;
5874
5831
  if (!model || status !== "ready") return;
@@ -5894,6 +5851,93 @@ function useBodyState(name) {
5894
5851
  });
5895
5852
  return { position, quaternion, linearVelocity, angularVelocity };
5896
5853
  }
5854
+ var _matrix2 = new THREE10.Matrix4();
5855
+ function quaternionFromMatrixArray(target, values, offset) {
5856
+ _matrix2.set(
5857
+ values[offset],
5858
+ values[offset + 1],
5859
+ values[offset + 2],
5860
+ 0,
5861
+ values[offset + 3],
5862
+ values[offset + 4],
5863
+ values[offset + 5],
5864
+ 0,
5865
+ values[offset + 6],
5866
+ values[offset + 7],
5867
+ values[offset + 8],
5868
+ 0,
5869
+ 0,
5870
+ 0,
5871
+ 0,
5872
+ 1
5873
+ );
5874
+ target.setFromRotationMatrix(_matrix2);
5875
+ }
5876
+ function quaternionFromMujocoQuat2(target, values, offset) {
5877
+ target.set(
5878
+ values[offset + 1] ?? 0,
5879
+ values[offset + 2] ?? 0,
5880
+ values[offset + 3] ?? 0,
5881
+ values[offset] ?? 1
5882
+ );
5883
+ }
5884
+ function useNamedPose(kind, name) {
5885
+ const { mjModelRef, status } = useMujocoContext();
5886
+ const idRef = useRef(-1);
5887
+ const foundRef = useRef(false);
5888
+ const positionRef = useRef(new THREE10.Vector3());
5889
+ const quaternionRef = useRef(new THREE10.Quaternion());
5890
+ useEffect(() => {
5891
+ const model = mjModelRef.current;
5892
+ if (!model || status !== "ready") {
5893
+ idRef.current = -1;
5894
+ foundRef.current = false;
5895
+ return;
5896
+ }
5897
+ if (kind === "body") idRef.current = findBodyByName(model, name);
5898
+ else if (kind === "geom") idRef.current = findGeomByName(model, name);
5899
+ else idRef.current = findSiteByName(model, name);
5900
+ foundRef.current = idRef.current >= 0;
5901
+ }, [kind, name, status, mjModelRef]);
5902
+ useAfterPhysicsStep(({ data }) => {
5903
+ const id = idRef.current;
5904
+ if (id < 0) return;
5905
+ if (kind === "body") {
5906
+ const p2 = id * 3;
5907
+ positionRef.current.set(data.xpos[p2], data.xpos[p2 + 1], data.xpos[p2 + 2]);
5908
+ if (data.xmat) {
5909
+ quaternionFromMatrixArray(quaternionRef.current, data.xmat, id * 9);
5910
+ } else {
5911
+ quaternionFromMujocoQuat2(quaternionRef.current, data.xquat, id * 4);
5912
+ }
5913
+ return;
5914
+ }
5915
+ if (kind === "geom") {
5916
+ const p2 = id * 3;
5917
+ positionRef.current.set(data.geom_xpos[p2], data.geom_xpos[p2 + 1], data.geom_xpos[p2 + 2]);
5918
+ quaternionFromMatrixArray(quaternionRef.current, data.geom_xmat, id * 9);
5919
+ return;
5920
+ }
5921
+ const p = id * 3;
5922
+ positionRef.current.set(data.site_xpos[p], data.site_xpos[p + 1], data.site_xpos[p + 2]);
5923
+ quaternionFromMatrixArray(quaternionRef.current, data.site_xmat, id * 9);
5924
+ });
5925
+ return {
5926
+ id: idRef,
5927
+ found: foundRef,
5928
+ position: positionRef,
5929
+ quaternion: quaternionRef
5930
+ };
5931
+ }
5932
+ function useBodyPose(name) {
5933
+ return useNamedPose("body", name);
5934
+ }
5935
+ function useGeomPose(name) {
5936
+ return useNamedPose("geom", name);
5937
+ }
5938
+ function useSitePose(name) {
5939
+ return useNamedPose("site", name);
5940
+ }
5897
5941
  function useCtrl(name) {
5898
5942
  const { mjModelRef, mjDataRef, status } = useMujocoContext();
5899
5943
  const actuatorIdRef = useRef(-1);
@@ -5927,47 +5971,240 @@ function useCtrl(name) {
5927
5971
  }
5928
5972
  }), [name, mjDataRef]);
5929
5973
  }
5930
- function useKeyboardTeleop(config) {
5931
- const { mjModelRef, mjDataRef, status } = useMujocoContext();
5932
- const pressedRef = useRef(/* @__PURE__ */ new Set());
5933
- const toggleStateRef = useRef(/* @__PURE__ */ new Map());
5934
- const enabledRef = useRef(config.enabled ?? true);
5935
- enabledRef.current = config.enabled ?? true;
5936
- const bindingsRef = useRef(config.bindings);
5937
- bindingsRef.current = config.bindings;
5938
- const actuatorCacheRef = useRef(/* @__PURE__ */ new Map());
5974
+ var claimsByModel = /* @__PURE__ */ new WeakMap();
5975
+ function getClaims(model) {
5976
+ let claims = claimsByModel.get(model);
5977
+ if (!claims) {
5978
+ claims = /* @__PURE__ */ new Map();
5979
+ claimsByModel.set(model, claims);
5980
+ }
5981
+ return claims;
5982
+ }
5983
+ function releaseClaims(model, token) {
5984
+ if (!model) return;
5985
+ const claims = claimsByModel.get(model);
5986
+ if (!claims) return;
5987
+ for (const [actuatorIndex, claim] of claims) {
5988
+ if (claim.token === token) claims.delete(actuatorIndex);
5989
+ }
5990
+ }
5991
+ function useControlWriter(options) {
5992
+ const {
5993
+ owner,
5994
+ selector,
5995
+ enabled = true,
5996
+ warnOnConflict = true,
5997
+ allowSameOwner = true,
5998
+ onConflict
5999
+ } = options;
6000
+ const { api, mjModelRef, mjDataRef, status } = useMujocoContext();
6001
+ const tokenRef = useRef(Symbol(owner));
6002
+ const claimedModelRef = useRef(null);
6003
+ const groupRef = useRef(null);
6004
+ const conflictsRef = useRef([]);
6005
+ const onConflictRef = useRef(onConflict);
6006
+ onConflictRef.current = onConflict;
6007
+ const release = useCallback(() => {
6008
+ releaseClaims(claimedModelRef.current, tokenRef.current);
6009
+ claimedModelRef.current = null;
6010
+ conflictsRef.current = [];
6011
+ }, []);
5939
6012
  useEffect(() => {
6013
+ release();
6014
+ if (!enabled || status !== "ready") {
6015
+ groupRef.current = null;
6016
+ return;
6017
+ }
5940
6018
  const model = mjModelRef.current;
5941
- if (!model || status !== "ready") return;
5942
- const cache = /* @__PURE__ */ new Map();
5943
- for (const binding of Object.values(config.bindings)) {
5944
- if (!cache.has(binding.actuator)) {
5945
- cache.set(binding.actuator, findActuatorByName(model, binding.actuator));
6019
+ if (!model) {
6020
+ groupRef.current = null;
6021
+ return;
6022
+ }
6023
+ const group = selector ? api.resolveControlGroup(selector) : api.getControlMap();
6024
+ groupRef.current = group;
6025
+ if (!group) return;
6026
+ const claims = getClaims(model);
6027
+ const conflicts = [];
6028
+ for (const actuatorIndex of group.ctrlAdr) {
6029
+ const existing = claims.get(actuatorIndex);
6030
+ if (existing && existing.token !== tokenRef.current && (!allowSameOwner || existing.owner !== owner)) {
6031
+ conflicts.push({
6032
+ actuatorIndex,
6033
+ owner: existing.owner,
6034
+ requestedBy: owner
6035
+ });
5946
6036
  }
5947
6037
  }
5948
- actuatorCacheRef.current = cache;
5949
- }, [config.bindings, status, mjModelRef]);
5950
- useEffect(() => {
5951
- const onKeyDown = (e) => {
5952
- if (!enabledRef.current) return;
5953
- const key = e.key.toLowerCase();
5954
- if (bindingsRef.current[key]) {
5955
- pressedRef.current.add(key);
5956
- const binding = bindingsRef.current[key];
5957
- if (binding.toggle) {
5958
- const current = toggleStateRef.current.get(key) ?? false;
5959
- toggleStateRef.current.set(key, !current);
5960
- }
6038
+ conflictsRef.current = conflicts;
6039
+ if (conflicts.length > 0) {
6040
+ onConflictRef.current?.(conflicts);
6041
+ if (warnOnConflict) {
6042
+ console.warn(
6043
+ `[mujoco-react] Control writer "${owner}" conflicts with existing writer(s): ${conflicts.map((conflict) => `${conflict.actuatorIndex}:${conflict.owner}`).join(", ")}`
6044
+ );
5961
6045
  }
5962
- };
5963
- const onKeyUp = (e) => {
5964
- pressedRef.current.delete(e.key.toLowerCase());
5965
- };
5966
- window.addEventListener("keydown", onKeyDown);
5967
- window.addEventListener("keyup", onKeyUp);
5968
- return () => {
5969
- window.removeEventListener("keydown", onKeyDown);
5970
- window.removeEventListener("keyup", onKeyUp);
6046
+ return;
6047
+ }
6048
+ for (const actuatorIndex of group.ctrlAdr) {
6049
+ claims.set(actuatorIndex, { owner, token: tokenRef.current });
6050
+ }
6051
+ claimedModelRef.current = model;
6052
+ return release;
6053
+ }, [allowSameOwner, api, enabled, mjModelRef, owner, release, selector, status, warnOnConflict]);
6054
+ const canWrite = useCallback(() => enabled && groupRef.current !== null && conflictsRef.current.length === 0, [enabled]);
6055
+ const read = useCallback(() => {
6056
+ const data = mjDataRef.current;
6057
+ const group = groupRef.current;
6058
+ if (!data || !group) return new Float64Array(0);
6059
+ return group.readCtrl(data);
6060
+ }, [mjDataRef]);
6061
+ const write = useCallback((values, writeOptions = {}) => {
6062
+ const data = mjDataRef.current;
6063
+ const group = groupRef.current;
6064
+ if (!data || !group) return false;
6065
+ if (!writeOptions.force && !canWrite()) return false;
6066
+ group.writeCtrl(data, values);
6067
+ return true;
6068
+ }, [canWrite, mjDataRef]);
6069
+ return useMemo(() => ({
6070
+ owner,
6071
+ group: groupRef,
6072
+ conflicts: conflictsRef,
6073
+ canWrite,
6074
+ read,
6075
+ write,
6076
+ release
6077
+ }), [canWrite, owner, read, release, write]);
6078
+ }
6079
+ var geomNameCacheByModel2 = /* @__PURE__ */ new WeakMap();
6080
+ var bodyNameCacheByModel = /* @__PURE__ */ new WeakMap();
6081
+ function getCachedName(cacheByModel, model, id, address) {
6082
+ if (id < 0) return "";
6083
+ let cache = cacheByModel.get(model);
6084
+ if (!cache) {
6085
+ cache = /* @__PURE__ */ new Map();
6086
+ cacheByModel.set(model, cache);
6087
+ }
6088
+ let name = cache.get(id);
6089
+ if (name === void 0) {
6090
+ name = getName(model, address);
6091
+ cache.set(id, name);
6092
+ }
6093
+ return name;
6094
+ }
6095
+ function matchesFilter(entry, bodyNames, geomNames, includeWorldBody) {
6096
+ if (!includeWorldBody && (entry.body1 === 0 || entry.body2 === 0)) return false;
6097
+ if (bodyNames && !bodyNames.includes(entry.body1Name) && !bodyNames.includes(entry.body2Name)) {
6098
+ return false;
6099
+ }
6100
+ if (geomNames && !geomNames.includes(entry.geom1Name) && !geomNames.includes(entry.geom2Name)) {
6101
+ return false;
6102
+ }
6103
+ return true;
6104
+ }
6105
+ function useContactHistory(options = {}) {
6106
+ const entriesRef = useRef([]);
6107
+ const optionsRef = useRef(options);
6108
+ optionsRef.current = options;
6109
+ const clear = useCallback(() => {
6110
+ entriesRef.current = [];
6111
+ }, []);
6112
+ const countPair = useCallback((nameA, nameB) => {
6113
+ let count = 0;
6114
+ for (const entry of entriesRef.current) {
6115
+ const matchesBodies = entry.body1Name === nameA && entry.body2Name === nameB || entry.body1Name === nameB && entry.body2Name === nameA;
6116
+ const matchesGeoms = entry.geom1Name === nameA && entry.geom2Name === nameB || entry.geom1Name === nameB && entry.geom2Name === nameA;
6117
+ if (matchesBodies || matchesGeoms) count += 1;
6118
+ }
6119
+ return count;
6120
+ }, []);
6121
+ useAfterPhysicsStep(({ model, data }) => {
6122
+ if ((data.ncon ?? 0) <= 0) return;
6123
+ const {
6124
+ maxLength = 2e3,
6125
+ bodyNames,
6126
+ geomNames,
6127
+ includeWorldBody = false
6128
+ } = optionsRef.current;
6129
+ if (maxLength <= 0) return;
6130
+ const nextEntries = [];
6131
+ withContacts(data, (contacts) => {
6132
+ for (let index = 0; index < data.ncon; index += 1) {
6133
+ const contact = getContact(contacts, index);
6134
+ if (!contact) break;
6135
+ const body1 = model.geom_bodyid[contact.geom1] ?? -1;
6136
+ const body2 = model.geom_bodyid[contact.geom2] ?? -1;
6137
+ const entry = {
6138
+ geom1: contact.geom1,
6139
+ geom2: contact.geom2,
6140
+ geom1Name: getCachedName(geomNameCacheByModel2, model, contact.geom1, model.name_geomadr[contact.geom1]),
6141
+ geom2Name: getCachedName(geomNameCacheByModel2, model, contact.geom2, model.name_geomadr[contact.geom2]),
6142
+ body1,
6143
+ body2,
6144
+ body1Name: body1 >= 0 ? getCachedName(bodyNameCacheByModel, model, body1, model.name_bodyadr[body1]) : "",
6145
+ body2Name: body2 >= 0 ? getCachedName(bodyNameCacheByModel, model, body2, model.name_bodyadr[body2]) : "",
6146
+ pos: [contact.pos[0], contact.pos[1], contact.pos[2]],
6147
+ depth: contact.dist,
6148
+ time: data.time
6149
+ };
6150
+ if (matchesFilter(entry, bodyNames, geomNames, includeWorldBody)) {
6151
+ nextEntries.push(entry);
6152
+ }
6153
+ }
6154
+ });
6155
+ if (nextEntries.length === 0) return;
6156
+ entriesRef.current.push(...nextEntries);
6157
+ if (entriesRef.current.length > maxLength) {
6158
+ entriesRef.current.splice(0, entriesRef.current.length - maxLength);
6159
+ }
6160
+ });
6161
+ return {
6162
+ entries: entriesRef,
6163
+ clear,
6164
+ countPair
6165
+ };
6166
+ }
6167
+ function useKeyboardTeleop(config) {
6168
+ const { mjModelRef, mjDataRef, status } = useMujocoContext();
6169
+ const pressedRef = useRef(/* @__PURE__ */ new Set());
6170
+ const toggleStateRef = useRef(/* @__PURE__ */ new Map());
6171
+ const enabledRef = useRef(config.enabled ?? true);
6172
+ enabledRef.current = config.enabled ?? true;
6173
+ const bindingsRef = useRef(config.bindings);
6174
+ bindingsRef.current = config.bindings;
6175
+ const actuatorCacheRef = useRef(/* @__PURE__ */ new Map());
6176
+ useEffect(() => {
6177
+ const model = mjModelRef.current;
6178
+ if (!model || status !== "ready") return;
6179
+ const cache = /* @__PURE__ */ new Map();
6180
+ for (const binding of Object.values(config.bindings)) {
6181
+ if (!cache.has(binding.actuator)) {
6182
+ cache.set(binding.actuator, findActuatorByName(model, binding.actuator));
6183
+ }
6184
+ }
6185
+ actuatorCacheRef.current = cache;
6186
+ }, [config.bindings, status, mjModelRef]);
6187
+ useEffect(() => {
6188
+ const onKeyDown = (e) => {
6189
+ if (!enabledRef.current) return;
6190
+ const key = e.key.toLowerCase();
6191
+ if (bindingsRef.current[key]) {
6192
+ pressedRef.current.add(key);
6193
+ const binding = bindingsRef.current[key];
6194
+ if (binding.toggle) {
6195
+ const current = toggleStateRef.current.get(key) ?? false;
6196
+ toggleStateRef.current.set(key, !current);
6197
+ }
6198
+ }
6199
+ };
6200
+ const onKeyUp = (e) => {
6201
+ pressedRef.current.delete(e.key.toLowerCase());
6202
+ };
6203
+ window.addEventListener("keydown", onKeyDown);
6204
+ window.addEventListener("keyup", onKeyUp);
6205
+ return () => {
6206
+ window.removeEventListener("keydown", onKeyDown);
6207
+ window.removeEventListener("keyup", onKeyUp);
5971
6208
  };
5972
6209
  }, []);
5973
6210
  useBeforePhysicsStep(({ data }) => {
@@ -5992,9 +6229,9 @@ function useKeyboardTeleop(config) {
5992
6229
  }
5993
6230
  var DEFAULT_TRANSLATE_SPEED = 0.25;
5994
6231
  var DEFAULT_ROTATE_SPEED = 1;
5995
- var _translation = new THREE11.Vector3();
5996
- var _axis = new THREE11.Vector3();
5997
- var _quat3 = new THREE11.Quaternion();
6232
+ var _translation = new THREE10.Vector3();
6233
+ var _axis = new THREE10.Vector3();
6234
+ var _quat3 = new THREE10.Quaternion();
5998
6235
  function actionSign(action) {
5999
6236
  return action.endsWith("+") ? 1 : -1;
6000
6237
  }
@@ -6119,29 +6356,114 @@ function useKeyboardIkTarget(config) {
6119
6356
  }
6120
6357
  });
6121
6358
  }
6359
+ function isPromiseLike(value) {
6360
+ return typeof value === "object" && value !== null && "then" in value && typeof value.then === "function";
6361
+ }
6362
+ function isPolicyActionChunk(value) {
6363
+ return Array.isArray(value) && value.length > 0 && (Array.isArray(value[0]) || ArrayBuffer.isView(value[0]));
6364
+ }
6365
+ function toPolicyActions(output) {
6366
+ return isPolicyActionChunk(output) ? [...output] : [output];
6367
+ }
6368
+ function enqueuePolicyActions(queue, actions, observation, strategy) {
6369
+ if (strategy === "replace") {
6370
+ queue.splice(0, queue.length);
6371
+ }
6372
+ queue.push(...actions.map((action) => ({ action, observation })));
6373
+ }
6122
6374
  function usePolicy(config) {
6123
6375
  const lastActionTimeRef = useRef(0);
6124
6376
  const lastObservationRef = useRef(null);
6125
6377
  const lastActionRef = useRef(null);
6378
+ const actionQueueRef = useRef([]);
6379
+ const inFlightRef = useRef(false);
6380
+ const lastErrorRef = useRef(null);
6381
+ const epochRef = useRef(0);
6126
6382
  const isRunningRef = useRef(config.enabled ?? true);
6127
6383
  const configRef = useRef(config);
6128
6384
  configRef.current = config;
6129
6385
  isRunningRef.current = config.enabled ?? isRunningRef.current;
6386
+ const clearQueue = useCallback(() => {
6387
+ epochRef.current += 1;
6388
+ actionQueueRef.current.splice(0, actionQueueRef.current.length);
6389
+ inFlightRef.current = false;
6390
+ lastErrorRef.current = null;
6391
+ }, []);
6392
+ const reset = useCallback(() => {
6393
+ clearQueue();
6394
+ lastActionTimeRef.current = 0;
6395
+ lastObservationRef.current = null;
6396
+ lastActionRef.current = null;
6397
+ }, [clearQueue]);
6130
6398
  useBeforePhysicsStep(({ model, data }) => {
6131
6399
  if (!isRunningRef.current) return;
6132
6400
  const cfg = configRef.current;
6133
6401
  model.opt?.timestep ?? 2e-3;
6134
6402
  const interval = 1 / cfg.frequency;
6135
6403
  if (data.time - lastActionTimeRef.current >= interval) {
6404
+ const queuedAction = actionQueueRef.current.shift();
6405
+ if (queuedAction) {
6406
+ cfg.onAction({
6407
+ action: queuedAction.action,
6408
+ observation: queuedAction.observation,
6409
+ model,
6410
+ data
6411
+ });
6412
+ lastActionTimeRef.current = data.time;
6413
+ lastActionRef.current = queuedAction.action;
6414
+ }
6415
+ const prefetchThreshold = cfg.prefetchThreshold ?? 0;
6416
+ const shouldInfer = !inFlightRef.current && (!queuedAction || actionQueueRef.current.length <= prefetchThreshold);
6417
+ if (!shouldInfer) return;
6136
6418
  const observation = cfg.onObservation({ model, data });
6137
- const action = cfg.infer ? cfg.infer({ observation, model, data }) : observation;
6138
- cfg.onAction({ action, observation, model, data });
6419
+ const result = cfg.infer ? cfg.infer({ observation, model, data }) : observation;
6420
+ if (isPromiseLike(result)) {
6421
+ const epoch = epochRef.current;
6422
+ inFlightRef.current = true;
6423
+ result.then((output) => {
6424
+ if (epoch !== epochRef.current) return;
6425
+ enqueuePolicyActions(
6426
+ actionQueueRef.current,
6427
+ toPolicyActions(output),
6428
+ observation,
6429
+ cfg.queueStrategy ?? "append"
6430
+ );
6431
+ lastErrorRef.current = null;
6432
+ }).catch((error) => {
6433
+ if (epoch !== epochRef.current) return;
6434
+ lastErrorRef.current = error;
6435
+ cfg.onError?.(error);
6436
+ }).finally(() => {
6437
+ if (epoch !== epochRef.current) return;
6438
+ inFlightRef.current = false;
6439
+ });
6440
+ } else {
6441
+ const actions = toPolicyActions(result);
6442
+ if (queuedAction) {
6443
+ enqueuePolicyActions(
6444
+ actionQueueRef.current,
6445
+ actions,
6446
+ observation,
6447
+ cfg.queueStrategy ?? "append"
6448
+ );
6449
+ } else {
6450
+ const [action, ...queuedActions] = actions;
6451
+ if (!action) return;
6452
+ enqueuePolicyActions(
6453
+ actionQueueRef.current,
6454
+ queuedActions,
6455
+ observation,
6456
+ cfg.queueStrategy ?? "append"
6457
+ );
6458
+ cfg.onAction({ action, observation, model, data });
6459
+ lastActionRef.current = action;
6460
+ }
6461
+ }
6139
6462
  lastActionTimeRef.current = data.time;
6140
6463
  lastObservationRef.current = observation;
6141
- lastActionRef.current = action;
6142
6464
  }
6143
6465
  });
6144
- return {
6466
+ return useMemo(() => ({
6145
6467
  get isRunning() {
6146
6468
  return isRunningRef.current;
6147
6469
  },
@@ -6150,15 +6472,304 @@ function usePolicy(config) {
6150
6472
  },
6151
6473
  stop: () => {
6152
6474
  isRunningRef.current = false;
6475
+ if (configRef.current.clearQueueOnStop) reset();
6476
+ },
6477
+ clearQueue,
6478
+ reset,
6479
+ get inFlight() {
6480
+ return inFlightRef.current;
6481
+ },
6482
+ get queuedActions() {
6483
+ return actionQueueRef.current.length;
6153
6484
  },
6154
6485
  get lastObservation() {
6155
6486
  return lastObservationRef.current;
6156
6487
  },
6157
6488
  get lastAction() {
6158
6489
  return lastActionRef.current;
6490
+ },
6491
+ get lastError() {
6492
+ return lastErrorRef.current;
6159
6493
  }
6494
+ }), [clearQueue, reset]);
6495
+ }
6496
+ function now() {
6497
+ return typeof performance !== "undefined" ? performance.now() : Date.now();
6498
+ }
6499
+ function isAbortError(error) {
6500
+ return typeof DOMException !== "undefined" && error instanceof DOMException && error.name === "AbortError" || error instanceof Error && error.name === "AbortError";
6501
+ }
6502
+ function createAbortError(message) {
6503
+ if (typeof DOMException !== "undefined") {
6504
+ return new DOMException(message, "AbortError");
6505
+ }
6506
+ const error = new Error(message);
6507
+ error.name = "AbortError";
6508
+ return error;
6509
+ }
6510
+ function abortController(controller, reason) {
6511
+ if (!controller || controller.signal.aborted) return;
6512
+ if (reason !== void 0) {
6513
+ controller.abort(reason);
6514
+ } else {
6515
+ controller.abort();
6516
+ }
6517
+ }
6518
+ function createMergedAbortSignal(localSignal, externalSignal) {
6519
+ if (!externalSignal) return localSignal;
6520
+ if (externalSignal.aborted) {
6521
+ const controller2 = new AbortController();
6522
+ abortController(controller2, externalSignal.reason);
6523
+ return controller2.signal;
6524
+ }
6525
+ if (typeof AbortSignal !== "undefined" && typeof AbortSignal.any === "function") {
6526
+ return AbortSignal.any([localSignal, externalSignal]);
6527
+ }
6528
+ const controller = new AbortController();
6529
+ const abortFromLocal = () => abortController(controller, localSignal.reason);
6530
+ const abortFromExternal = () => abortController(controller, externalSignal.reason);
6531
+ localSignal.addEventListener("abort", abortFromLocal, { once: true });
6532
+ externalSignal.addEventListener("abort", abortFromExternal, { once: true });
6533
+ return controller.signal;
6534
+ }
6535
+ function vectorToArray(vector) {
6536
+ return Array.from(vector, (value) => Number(value));
6537
+ }
6538
+ function isPolicyVectorArray(value) {
6539
+ return Array.isArray(value) && value.every((entry) => Array.isArray(entry) || ArrayBuffer.isView(entry));
6540
+ }
6541
+ function isPolicyVector(value) {
6542
+ return Array.isArray(value) || ArrayBuffer.isView(value);
6543
+ }
6544
+ function defaultBuildRemotePolicyRequest(input) {
6545
+ const observation = vectorToArray(input.observation);
6546
+ return {
6547
+ observation,
6548
+ state: observation,
6549
+ time: input.data.time,
6550
+ reset: input.reset
6160
6551
  };
6161
6552
  }
6553
+ async function defaultReadRemotePolicyResponse(response) {
6554
+ const text = await response.text();
6555
+ if (text.length === 0) return null;
6556
+ return JSON.parse(text);
6557
+ }
6558
+ function defaultParseRemotePolicyResponse(responseBody) {
6559
+ if (responseBody && typeof responseBody === "object") {
6560
+ const body = responseBody;
6561
+ if (typeof body.error === "string" && body.error.length > 0) {
6562
+ throw new Error(body.error);
6563
+ }
6564
+ if (isPolicyVectorArray(body.actions) && body.actions.length > 0) {
6565
+ return body.actions;
6566
+ }
6567
+ if (isPolicyVector(body.action)) {
6568
+ return body.action;
6569
+ }
6570
+ }
6571
+ if (isPolicyVectorArray(responseBody) && responseBody.length > 0) {
6572
+ return responseBody;
6573
+ }
6574
+ if (isPolicyVector(responseBody)) {
6575
+ return responseBody;
6576
+ }
6577
+ throw new Error("Remote policy response must include `action` or `actions`.");
6578
+ }
6579
+ function createHttpError(response, responseBody) {
6580
+ const suffix = responseBody && typeof responseBody === "object" && "error" in responseBody ? `: ${String(responseBody.error)}` : "";
6581
+ return new Error(`Remote policy request failed with HTTP ${response.status}${suffix}`);
6582
+ }
6583
+ function useRemotePolicy(config) {
6584
+ const configRef = useRef(config);
6585
+ configRef.current = config;
6586
+ const requestCountRef = useRef(0);
6587
+ const responseCountRef = useRef(0);
6588
+ const remoteStatusRef = useRef("idle");
6589
+ const lastRequestBodyRef = useRef(null);
6590
+ const lastResponseBodyRef = useRef(null);
6591
+ const lastHttpStatusRef = useRef(null);
6592
+ const lastRequestMsRef = useRef(null);
6593
+ const abortControllerRef = useRef(null);
6594
+ const remoteEpochRef = useRef(0);
6595
+ const policy = usePolicy({
6596
+ ...config,
6597
+ infer: async ({ observation, model, data }) => {
6598
+ const cfg = configRef.current;
6599
+ abortController(abortControllerRef.current, createAbortError("Remote policy request was superseded."));
6600
+ const controller = new AbortController();
6601
+ abortControllerRef.current = controller;
6602
+ const signal = createMergedAbortSignal(controller.signal, cfg.signal);
6603
+ const remoteEpoch = remoteEpochRef.current;
6604
+ const requestIndex = requestCountRef.current;
6605
+ const requestInput = {
6606
+ observation,
6607
+ model,
6608
+ data,
6609
+ reset: requestIndex === 0,
6610
+ requestIndex,
6611
+ signal
6612
+ };
6613
+ requestCountRef.current += 1;
6614
+ const requestStartedAt = now();
6615
+ const body = await (cfg.buildRequest?.(requestInput) ?? defaultBuildRemotePolicyRequest(requestInput));
6616
+ signal.throwIfAborted();
6617
+ if (remoteEpoch !== remoteEpochRef.current) {
6618
+ throw createAbortError("Remote policy request was reset.");
6619
+ }
6620
+ lastRequestBodyRef.current = body;
6621
+ remoteStatusRef.current = "requesting";
6622
+ cfg.onRequest?.({
6623
+ ...requestInput,
6624
+ body,
6625
+ requestStartedAt
6626
+ });
6627
+ let response = null;
6628
+ let responseBody = null;
6629
+ try {
6630
+ const headers = new Headers(cfg.headers);
6631
+ if (!headers.has("content-type")) {
6632
+ headers.set("content-type", "application/json");
6633
+ }
6634
+ const fetcher = cfg.fetcher ?? fetch;
6635
+ response = await fetcher(String(cfg.endpoint), {
6636
+ ...cfg.requestInit,
6637
+ method: cfg.method ?? "POST",
6638
+ credentials: cfg.credentials,
6639
+ headers,
6640
+ signal,
6641
+ body: typeof body === "string" ? body : JSON.stringify(body)
6642
+ });
6643
+ if (remoteEpoch === remoteEpochRef.current) {
6644
+ lastHttpStatusRef.current = response.status;
6645
+ }
6646
+ responseBody = await (cfg.readResponse?.(response) ?? defaultReadRemotePolicyResponse(response));
6647
+ signal.throwIfAborted();
6648
+ if (remoteEpoch !== remoteEpochRef.current) {
6649
+ throw createAbortError("Remote policy request was reset.");
6650
+ }
6651
+ lastResponseBodyRef.current = responseBody;
6652
+ if (!response.ok) {
6653
+ throw createHttpError(response, responseBody);
6654
+ }
6655
+ const responseFinishedAt = now();
6656
+ const info = {
6657
+ ...requestInput,
6658
+ body,
6659
+ requestStartedAt,
6660
+ response,
6661
+ responseBody,
6662
+ responseFinishedAt,
6663
+ requestMs: responseFinishedAt - requestStartedAt
6664
+ };
6665
+ if (remoteEpoch === remoteEpochRef.current) {
6666
+ lastRequestMsRef.current = info.requestMs;
6667
+ responseCountRef.current += 1;
6668
+ }
6669
+ cfg.onResponse?.(info);
6670
+ const output = await (cfg.parseResponse?.(responseBody, info) ?? defaultParseRemotePolicyResponse(responseBody));
6671
+ if (remoteEpoch === remoteEpochRef.current) {
6672
+ remoteStatusRef.current = "ready";
6673
+ }
6674
+ return output;
6675
+ } catch (error) {
6676
+ if (response && remoteEpoch === remoteEpochRef.current) {
6677
+ lastHttpStatusRef.current = response.status;
6678
+ }
6679
+ if (isAbortError(error) || signal.aborted) {
6680
+ if (remoteEpoch === remoteEpochRef.current) {
6681
+ remoteStatusRef.current = "aborted";
6682
+ }
6683
+ throw error;
6684
+ }
6685
+ if (remoteEpoch === remoteEpochRef.current) {
6686
+ remoteStatusRef.current = "error";
6687
+ }
6688
+ throw error;
6689
+ } finally {
6690
+ if (abortControllerRef.current === controller) {
6691
+ abortControllerRef.current = null;
6692
+ }
6693
+ }
6694
+ }
6695
+ });
6696
+ return useMemo(() => {
6697
+ const abort = (reason) => {
6698
+ abortController(abortControllerRef.current, reason);
6699
+ if (abortControllerRef.current) {
6700
+ remoteStatusRef.current = "aborted";
6701
+ }
6702
+ };
6703
+ const resetRemoteState = () => {
6704
+ remoteEpochRef.current += 1;
6705
+ abort(createAbortError("Remote policy request was reset."));
6706
+ requestCountRef.current = 0;
6707
+ responseCountRef.current = 0;
6708
+ remoteStatusRef.current = "idle";
6709
+ lastRequestBodyRef.current = null;
6710
+ lastResponseBodyRef.current = null;
6711
+ lastHttpStatusRef.current = null;
6712
+ lastRequestMsRef.current = null;
6713
+ };
6714
+ return {
6715
+ get isRunning() {
6716
+ return policy.isRunning;
6717
+ },
6718
+ start: policy.start,
6719
+ stop: () => {
6720
+ if (configRef.current.abortOnStop ?? true) {
6721
+ abort(createAbortError("Remote policy request was stopped."));
6722
+ }
6723
+ policy.stop();
6724
+ if (configRef.current.clearQueueOnStop) {
6725
+ resetRemoteState();
6726
+ }
6727
+ },
6728
+ clearQueue: policy.clearQueue,
6729
+ abort,
6730
+ reset: () => {
6731
+ resetRemoteState();
6732
+ policy.reset();
6733
+ },
6734
+ get inFlight() {
6735
+ return policy.inFlight;
6736
+ },
6737
+ get queuedActions() {
6738
+ return policy.queuedActions;
6739
+ },
6740
+ get lastObservation() {
6741
+ return policy.lastObservation;
6742
+ },
6743
+ get lastAction() {
6744
+ return policy.lastAction;
6745
+ },
6746
+ get lastError() {
6747
+ return policy.lastError;
6748
+ },
6749
+ get remoteStatus() {
6750
+ return remoteStatusRef.current;
6751
+ },
6752
+ get requestCount() {
6753
+ return requestCountRef.current;
6754
+ },
6755
+ get responseCount() {
6756
+ return responseCountRef.current;
6757
+ },
6758
+ get lastRequestBody() {
6759
+ return lastRequestBodyRef.current;
6760
+ },
6761
+ get lastResponseBody() {
6762
+ return lastResponseBodyRef.current;
6763
+ },
6764
+ get lastHttpStatus() {
6765
+ return lastHttpStatusRef.current;
6766
+ },
6767
+ get lastRequestMs() {
6768
+ return lastRequestMsRef.current;
6769
+ }
6770
+ };
6771
+ }, [policy]);
6772
+ }
6162
6773
  var EMPTY_OBSERVATION = {
6163
6774
  values: new Float32Array(0),
6164
6775
  layout: []
@@ -6179,6 +6790,134 @@ function useObservation(config) {
6179
6790
  }
6180
6791
  }), [mjDataRef, mjModelRef]);
6181
6792
  }
6793
+
6794
+ // src/policyObservation.ts
6795
+ function pushValues(target, value, size) {
6796
+ if (typeof value === "number") {
6797
+ target.push(value);
6798
+ for (let index = 1; index < size; index += 1) target.push(0);
6799
+ return;
6800
+ }
6801
+ for (let index = 0; index < size; index += 1) {
6802
+ target.push(Number(value[index] ?? 0));
6803
+ }
6804
+ }
6805
+ function readNamedObservation(model, data, options) {
6806
+ const values = [];
6807
+ const layout = [];
6808
+ const missing = options.missing ?? "skip";
6809
+ for (const field of options.fields) {
6810
+ const start = values.length;
6811
+ const value = field.read({ model, data });
6812
+ if (value === null || value === void 0) {
6813
+ if (missing === "skip") continue;
6814
+ if (missing === "throw") {
6815
+ throw new Error(`Unable to read named observation field "${field.name}".`);
6816
+ }
6817
+ for (let index = 0; index < field.size; index += 1) values.push(0);
6818
+ } else {
6819
+ pushValues(values, value, field.size);
6820
+ }
6821
+ layout.push({
6822
+ name: field.name,
6823
+ start,
6824
+ size: field.size,
6825
+ units: field.units
6826
+ });
6827
+ }
6828
+ return {
6829
+ values: options.output === "float64" ? new Float64Array(values) : new Float32Array(values),
6830
+ layout
6831
+ };
6832
+ }
6833
+ function createNamedObservationBuilder(options) {
6834
+ return (model, data) => readNamedObservation(model, data, options);
6835
+ }
6836
+ function qposField(name, index, units = "qpos") {
6837
+ return {
6838
+ name,
6839
+ size: 1,
6840
+ units,
6841
+ read: ({ data }) => data.qpos[index]
6842
+ };
6843
+ }
6844
+ function qvelField(name, index, units = "qvel") {
6845
+ return {
6846
+ name,
6847
+ size: 1,
6848
+ units,
6849
+ read: ({ data }) => data.qvel[index]
6850
+ };
6851
+ }
6852
+ function ctrlField(name, index, units = "ctrl") {
6853
+ return {
6854
+ name,
6855
+ size: 1,
6856
+ units,
6857
+ read: ({ data }) => data.ctrl[index]
6858
+ };
6859
+ }
6860
+ function bodyPositionField(name, units = "world_position") {
6861
+ return {
6862
+ name: `body:${name}:xpos`,
6863
+ size: 3,
6864
+ units,
6865
+ read: ({ model, data }) => {
6866
+ const bodyId = findBodyByName(model, name);
6867
+ if (bodyId < 0) return null;
6868
+ const offset = bodyId * 3;
6869
+ return data.xpos.subarray(offset, offset + 3);
6870
+ }
6871
+ };
6872
+ }
6873
+ function geomPositionField(name, units = "world_position") {
6874
+ return {
6875
+ name: `geom:${name}:xpos`,
6876
+ size: 3,
6877
+ units,
6878
+ read: ({ model, data }) => {
6879
+ const geomId = findGeomByName(model, name);
6880
+ if (geomId < 0) return null;
6881
+ const offset = geomId * 3;
6882
+ return data.geom_xpos.subarray(offset, offset + 3);
6883
+ }
6884
+ };
6885
+ }
6886
+ function sitePositionField(name, units = "world_position") {
6887
+ return {
6888
+ name: `site:${name}:xpos`,
6889
+ size: 3,
6890
+ units,
6891
+ read: ({ model, data }) => {
6892
+ const siteId = findSiteByName(model, name);
6893
+ if (siteId < 0) return null;
6894
+ const offset = siteId * 3;
6895
+ return data.site_xpos.subarray(offset, offset + 3);
6896
+ }
6897
+ };
6898
+ }
6899
+
6900
+ // src/hooks/useNamedObservation.ts
6901
+ var EMPTY_NAMED_OBSERVATION = {
6902
+ values: new Float32Array(0),
6903
+ layout: []
6904
+ };
6905
+ function useNamedObservation(options) {
6906
+ const { mjModelRef, mjDataRef } = useMujocoContext();
6907
+ const optionsRef = useRef(options);
6908
+ optionsRef.current = options;
6909
+ return useMemo(() => ({
6910
+ read() {
6911
+ const model = mjModelRef.current;
6912
+ const data = mjDataRef.current;
6913
+ if (!model || !data) return EMPTY_NAMED_OBSERVATION;
6914
+ return readNamedObservation(model, data, optionsRef.current);
6915
+ },
6916
+ readValues() {
6917
+ return this.read().values;
6918
+ }
6919
+ }), [mjDataRef, mjModelRef]);
6920
+ }
6182
6921
  function useTrajectoryRecorder(options = {}) {
6183
6922
  const { mjModelRef } = useMujocoContext();
6184
6923
  const recordingRef = useRef(false);
@@ -6414,6 +7153,223 @@ function useCameraFrameCapture(defaultOptions = {}) {
6414
7153
  reset
6415
7154
  };
6416
7155
  }
7156
+
7157
+ // src/policyCameraFrames.ts
7158
+ function addPolicyImageAliases(images, stream, frame, includeObservationImageAliases) {
7159
+ const keys = /* @__PURE__ */ new Set();
7160
+ keys.add(stream.key);
7161
+ for (const alias of stream.aliases ?? []) keys.add(alias);
7162
+ if (includeObservationImageAliases) {
7163
+ keys.add(`observation.images.${stream.key}`);
7164
+ for (const alias of stream.aliases ?? []) {
7165
+ keys.add(`observation.images.${alias}`);
7166
+ }
7167
+ }
7168
+ for (const key of keys) {
7169
+ images[key] = frame.dataUrl;
7170
+ }
7171
+ }
7172
+ function describeFrameSource(key, frame) {
7173
+ return `${key}:${frame.source.kind}`;
7174
+ }
7175
+ function hasExplicitPolicyCameraSource(options) {
7176
+ return Boolean(
7177
+ options?.camera || options?.position || options?.quaternion || options?.source
7178
+ );
7179
+ }
7180
+ function createPolicyCameraFrameCapturePlan(options) {
7181
+ const {
7182
+ cameraKeys,
7183
+ defaults,
7184
+ streamOptions,
7185
+ includeObservationImageAliases,
7186
+ requireAll,
7187
+ ...sourceOptions
7188
+ } = options;
7189
+ const mountedPlan = createMountedCameraFrameSequencePlan(cameraKeys, {
7190
+ ...sourceOptions,
7191
+ defaults,
7192
+ cameraOptions: streamOptions
7193
+ });
7194
+ const streams = [];
7195
+ const missingKeys = new Set(mountedPlan.missingKeys);
7196
+ for (const key of cameraKeys) {
7197
+ const perStreamOptions = streamOptions?.[key];
7198
+ if (hasExplicitPolicyCameraSource(perStreamOptions)) {
7199
+ missingKeys.delete(key);
7200
+ streams.push({
7201
+ ...defaults,
7202
+ ...perStreamOptions,
7203
+ key,
7204
+ aliases: perStreamOptions?.aliases
7205
+ });
7206
+ continue;
7207
+ }
7208
+ const mountedCamera = mountedPlan.cameras.find((camera) => camera.key === key);
7209
+ if (!mountedCamera) continue;
7210
+ const { key: _mountedKey, ...captureOptions } = mountedCamera;
7211
+ streams.push({
7212
+ ...captureOptions,
7213
+ key,
7214
+ aliases: perStreamOptions?.aliases
7215
+ });
7216
+ }
7217
+ const result = {
7218
+ cameraKeys: [...cameraKeys],
7219
+ streams,
7220
+ includeObservationImageAliases,
7221
+ mountedPlan,
7222
+ missingKeys: [...missingKeys]
7223
+ };
7224
+ if (requireAll && result.missingKeys.length > 0) {
7225
+ throw new Error(
7226
+ `Unable to resolve policy camera stream${result.missingKeys.length === 1 ? "" : "s"} for ${result.missingKeys.join(", ")}.`
7227
+ );
7228
+ }
7229
+ return result;
7230
+ }
7231
+ function createPolicyCameraFrameCapturePlanFromApi(api, options) {
7232
+ return createPolicyCameraFrameCapturePlan({
7233
+ ...options,
7234
+ cameras: api.getCameras(),
7235
+ sites: api.getSites(),
7236
+ bodies: api.getBodies()
7237
+ });
7238
+ }
7239
+ async function capturePolicyCameraFrames(target, options) {
7240
+ const includeObservationImageAliases = options.includeObservationImageAliases ?? true;
7241
+ const entries = await Promise.all(
7242
+ options.streams.map(async ({ key, aliases, ...captureOptions }) => {
7243
+ const frame = await target.captureCameraFrame(captureOptions);
7244
+ return [key, { frame, aliases }];
7245
+ })
7246
+ );
7247
+ const frames = {};
7248
+ const images = {};
7249
+ const sourceParts = [];
7250
+ for (const [key, { frame, aliases }] of entries) {
7251
+ const stream = { key, aliases };
7252
+ frames[key] = frame;
7253
+ addPolicyImageAliases(
7254
+ images,
7255
+ stream,
7256
+ frame,
7257
+ includeObservationImageAliases
7258
+ );
7259
+ sourceParts.push(describeFrameSource(key, frame));
7260
+ }
7261
+ return {
7262
+ frames,
7263
+ images,
7264
+ sourceSummary: sourceParts.length > 0 ? sourceParts.join(" + ") : "not used by policy",
7265
+ capturedAt: Date.now()
7266
+ };
7267
+ }
7268
+ async function capturePolicyCameraFramesFromMountedStreams(target, options) {
7269
+ const plan = createPolicyCameraFrameCapturePlanFromApi(target, options);
7270
+ const result = await capturePolicyCameraFrames(target, plan);
7271
+ return { ...result, plan };
7272
+ }
7273
+
7274
+ // src/hooks/usePolicyCameraFrames.ts
7275
+ function mergePolicyCameraFrameCaptureOptions(defaultOptions, options) {
7276
+ return {
7277
+ ...defaultOptions,
7278
+ ...options,
7279
+ cameraKeys: options.cameraKeys ?? defaultOptions.cameraKeys,
7280
+ aliases: {
7281
+ ...defaultOptions.aliases,
7282
+ ...options.aliases
7283
+ },
7284
+ defaults: {
7285
+ ...defaultOptions.defaults,
7286
+ ...options.defaults
7287
+ },
7288
+ streamOptions: {
7289
+ ...defaultOptions.streamOptions,
7290
+ ...options.streamOptions
7291
+ }
7292
+ };
7293
+ }
7294
+ function usePolicyCameraFrames(defaultOptions) {
7295
+ const mujoco = useMujoco();
7296
+ const [status, setStatus] = useState("idle");
7297
+ const [error, setError] = useState(null);
7298
+ const reset = useCallback(() => {
7299
+ setStatus("idle");
7300
+ setError(null);
7301
+ }, []);
7302
+ const capture = useCallback(
7303
+ async (options = {}) => {
7304
+ if (!mujoco.api) {
7305
+ throw new Error("MuJoCo scene is not ready for policy camera capture.");
7306
+ }
7307
+ setStatus("capturing");
7308
+ setError(null);
7309
+ try {
7310
+ const result = await capturePolicyCameraFrames(mujoco.api, {
7311
+ ...defaultOptions,
7312
+ ...options,
7313
+ streams: options.streams ?? defaultOptions.streams
7314
+ });
7315
+ setStatus("captured");
7316
+ return result;
7317
+ } catch (nextError) {
7318
+ const error2 = nextError instanceof Error ? nextError : new Error("Unable to capture policy camera frames.");
7319
+ setError(error2);
7320
+ setStatus("error");
7321
+ throw error2;
7322
+ }
7323
+ },
7324
+ [defaultOptions, mujoco.api]
7325
+ );
7326
+ return {
7327
+ status,
7328
+ error,
7329
+ isCapturing: status === "capturing",
7330
+ capture,
7331
+ reset
7332
+ };
7333
+ }
7334
+ function usePolicyCameraFramesFromMountedStreams(defaultOptions) {
7335
+ const mujoco = useMujoco();
7336
+ const [status, setStatus] = useState("idle");
7337
+ const [error, setError] = useState(null);
7338
+ const reset = useCallback(() => {
7339
+ setStatus("idle");
7340
+ setError(null);
7341
+ }, []);
7342
+ const capture = useCallback(
7343
+ async (options = {}) => {
7344
+ if (!mujoco.api) {
7345
+ throw new Error("MuJoCo scene is not ready for mounted policy camera capture.");
7346
+ }
7347
+ setStatus("capturing");
7348
+ setError(null);
7349
+ try {
7350
+ const result = await capturePolicyCameraFramesFromMountedStreams(
7351
+ mujoco.api,
7352
+ mergePolicyCameraFrameCaptureOptions(defaultOptions, options)
7353
+ );
7354
+ setStatus("captured");
7355
+ return result;
7356
+ } catch (nextError) {
7357
+ const error2 = nextError instanceof Error ? nextError : new Error("Unable to capture mounted policy camera frames.");
7358
+ setError(error2);
7359
+ setStatus("error");
7360
+ throw error2;
7361
+ }
7362
+ },
7363
+ [defaultOptions, mujoco.api]
7364
+ );
7365
+ return {
7366
+ status,
7367
+ error,
7368
+ isCapturing: status === "capturing",
7369
+ capture,
7370
+ reset
7371
+ };
7372
+ }
6417
7373
  function useCameraSequenceRecorder() {
6418
7374
  const mujoco = useMujoco();
6419
7375
  const [status, setStatus] = useState("idle");
@@ -6534,6 +7490,38 @@ function useMountedCameraSequenceRecorder(defaultOptions = {}) {
6534
7490
  reset
6535
7491
  };
6536
7492
  }
7493
+
7494
+ // src/policyControls.ts
7495
+ function clampPolicyActionValue(model, actuatorIndex, value) {
7496
+ const ranges = model.actuator_ctrlrange;
7497
+ const min = ranges?.[actuatorIndex * 2] ?? -Infinity;
7498
+ const max = ranges?.[actuatorIndex * 2 + 1] ?? Infinity;
7499
+ return Math.max(min, Math.min(max, value));
7500
+ }
7501
+ function applyPolicyActionToControls(model, data, action, options = {}) {
7502
+ const actuatorOffset = options.actuatorOffset ?? 0;
7503
+ const actionSize = options.actionSize ?? action.length;
7504
+ const shouldClamp = options.clamp ?? true;
7505
+ const shouldSkipInvalid = options.skipInvalid ?? true;
7506
+ const count = Math.max(
7507
+ 0,
7508
+ Math.min(actionSize, action.length, data.ctrl.length - actuatorOffset, model.nu - actuatorOffset)
7509
+ );
7510
+ const applied = [];
7511
+ const skipped = [];
7512
+ for (let index = 0; index < count; index += 1) {
7513
+ const actuatorIndex = actuatorOffset + index;
7514
+ const value = Number(action[index]);
7515
+ if (shouldSkipInvalid && !Number.isFinite(value)) {
7516
+ skipped.push(actuatorIndex);
7517
+ continue;
7518
+ }
7519
+ const nextValue = shouldClamp ? clampPolicyActionValue(model, actuatorIndex, value) : value;
7520
+ data.ctrl[actuatorIndex] = nextValue;
7521
+ applied.push(nextValue);
7522
+ }
7523
+ return { applied, skipped, actuatorOffset };
7524
+ }
6537
7525
  function useCtrlNoise(config = {}) {
6538
7526
  const { mjModelRef } = useMujocoContext();
6539
7527
  const configRef = useRef(config);
@@ -6585,7 +7573,7 @@ function useSelectionHighlight(bodyId, options = {}) {
6585
7573
  }
6586
7574
  }
6587
7575
  prevRef.current = [];
6588
- const highlightColor = new THREE11.Color(color);
7576
+ const highlightColor = new THREE10.Color(color);
6589
7577
  for (const mesh of meshes) {
6590
7578
  const mat = mesh.material;
6591
7579
  if (mat.emissive) {
@@ -6612,15 +7600,15 @@ function useSelectionHighlight(bodyId, options = {}) {
6612
7600
  }
6613
7601
  function useCameraAnimation() {
6614
7602
  const { camera } = useThree();
6615
- const orbitTargetRef = useRef(new THREE11.Vector3(0, 0, 0));
7603
+ const orbitTargetRef = useRef(new THREE10.Vector3(0, 0, 0));
6616
7604
  const cameraAnimRef = useRef({
6617
7605
  active: false,
6618
- startPos: new THREE11.Vector3(),
6619
- endPos: new THREE11.Vector3(),
6620
- startRot: new THREE11.Quaternion(),
6621
- endRot: new THREE11.Quaternion(),
6622
- startTarget: new THREE11.Vector3(),
6623
- endTarget: new THREE11.Vector3(),
7606
+ startPos: new THREE10.Vector3(),
7607
+ endPos: new THREE10.Vector3(),
7608
+ startRot: new THREE10.Quaternion(),
7609
+ endRot: new THREE10.Quaternion(),
7610
+ startTarget: new THREE10.Vector3(),
7611
+ endTarget: new THREE10.Vector3(),
6624
7612
  startTime: 0,
6625
7613
  duration: 0,
6626
7614
  resolve: null
@@ -6628,8 +7616,8 @@ function useCameraAnimation() {
6628
7616
  useFrame((state) => {
6629
7617
  const ca = cameraAnimRef.current;
6630
7618
  if (!ca.active) return;
6631
- const now = performance.now();
6632
- const progress = Math.min((now - ca.startTime) / ca.duration, 1);
7619
+ const now2 = performance.now();
7620
+ const progress = Math.min((now2 - ca.startTime) / ca.duration, 1);
6633
7621
  const ease = progress < 0.5 ? 4 * progress * progress * progress : 1 - Math.pow(-2 * progress + 2, 3) / 2;
6634
7622
  camera.position.lerpVectors(ca.startPos, ca.endPos, ease);
6635
7623
  camera.quaternion.slerpQuaternions(ca.startRot, ca.endRot, ease);
@@ -6795,12 +7783,30 @@ function useCameraAnimation() {
6795
7783
  *
6796
7784
  * useBodyState — per-body position/velocity tracking (spec 2.2)
6797
7785
  */
7786
+ /**
7787
+ * @license
7788
+ * SPDX-License-Identifier: Apache-2.0
7789
+ *
7790
+ * Ref-based world pose hooks for named MuJoCo bodies, geoms, and sites.
7791
+ */
6798
7792
  /**
6799
7793
  * @license
6800
7794
  * SPDX-License-Identifier: Apache-2.0
6801
7795
  *
6802
7796
  * useCtrl — handle-based read/write access to a named actuator's ctrl value (spec 3.1)
6803
7797
  */
7798
+ /**
7799
+ * @license
7800
+ * SPDX-License-Identifier: Apache-2.0
7801
+ *
7802
+ * Cooperative actuator/control ownership for policies, IK, teleop, and replay.
7803
+ */
7804
+ /**
7805
+ * @license
7806
+ * SPDX-License-Identifier: Apache-2.0
7807
+ *
7808
+ * Bounded contact history for rollout verification and debugging.
7809
+ */
6804
7810
  /**
6805
7811
  * @license
6806
7812
  * SPDX-License-Identifier: Apache-2.0
@@ -6813,6 +7819,24 @@ function useCameraAnimation() {
6813
7819
  *
6814
7820
  * usePolicy — policy decimation loop hook (spec 10.1)
6815
7821
  */
7822
+ /**
7823
+ * @license
7824
+ * SPDX-License-Identifier: Apache-2.0
7825
+ *
7826
+ * useRemotePolicy — HTTP JSON inference wrapper around usePolicy.
7827
+ */
7828
+ /**
7829
+ * @license
7830
+ * SPDX-License-Identifier: Apache-2.0
7831
+ *
7832
+ * Named policy observation builders with layout and units metadata.
7833
+ */
7834
+ /**
7835
+ * @license
7836
+ * SPDX-License-Identifier: Apache-2.0
7837
+ *
7838
+ * Stable React handle for named policy observations.
7839
+ */
6816
7840
  /**
6817
7841
  * @license
6818
7842
  * SPDX-License-Identifier: Apache-2.0
@@ -6837,6 +7861,18 @@ function useCameraAnimation() {
6837
7861
  *
6838
7862
  * React state wrapper around MuJoCo/R3F offscreen camera-frame capture.
6839
7863
  */
7864
+ /**
7865
+ * @license
7866
+ * SPDX-License-Identifier: Apache-2.0
7867
+ *
7868
+ * Helpers for turning Three/MuJoCo camera captures into policy image payloads.
7869
+ */
7870
+ /**
7871
+ * @license
7872
+ * SPDX-License-Identifier: Apache-2.0
7873
+ *
7874
+ * React wrapper for capturing policy image payloads from Three/MuJoCo cameras.
7875
+ */
6840
7876
  /**
6841
7877
  * @license
6842
7878
  * SPDX-License-Identifier: Apache-2.0
@@ -6849,6 +7885,12 @@ function useCameraAnimation() {
6849
7885
  *
6850
7886
  * React state wrapper for named MuJoCo camera/site/body sequence recording.
6851
7887
  */
7888
+ /**
7889
+ * @license
7890
+ * SPDX-License-Identifier: Apache-2.0
7891
+ *
7892
+ * Helpers for applying policy action vectors to MuJoCo controls.
7893
+ */
6852
7894
  /**
6853
7895
  * @license
6854
7896
  * SPDX-License-Identifier: Apache-2.0
@@ -6879,6 +7921,6 @@ function useCameraAnimation() {
6879
7921
  * useCameraAnimation — composable camera animation hook.
6880
7922
  */
6881
7923
 
6882
- export { Body, ContactListener, ContactMarkers, Debug, DragInteraction, FlexRenderer, IkGizmo, InstancedGeomRenderer, MountedCameraFrameSequenceManifestStatus, MountedCameraFrameSequenceReadinessStatus, MountedCameraFrameSourceSuggestionMatch, MujocoCanvas, MujocoPhysics, MujocoProvider, MujocoSimProvider, SceneLights, SplatCollisionProxyPreview, TendonRenderer, TrajectoryPlayer, buildObservation, canFetchSplatCollisionProxyXml, captureFrame, captureFrameBlob, createContiguousControlGroup, createController, createControllerHook, createMountedCameraFrameSequenceManifest, createMountedCameraFrameSequencePlan, createMountedCameraFrameSequencePlanFromApi, createMountedCameraFrameSequenceReadiness, createMountedCameraFrameSourceSuggestions, fetchSplatCollisionProxyXml, findActuatorByName, findBodyByName, findGeomByName, findJointByName, findKeyframeByName, findSensorByName, findSiteByName, findTendonByName, getActuatedJoints, getCameraFrameCaptureSourceTarget, getControlMap, getMountedCameraFrameCaptureSource, getName, isMountedCameraFrameCaptureSource, loadScene, parseSplatCollisionProxyGeoms, recordMountedCameraFrameSequence, resolveControlGroup, resolveMountedCameraFrameSource, useActuators, useAfterPhysicsStep, useBeforePhysicsStep, useBodyMeshes, useBodyState, useCameraAnimation, useCameraFrameCapture, useCameraSequenceRecorder, useContactEvents, useContacts, useCtrl, useCtrlNoise, useFrameCapture, useGamepad, useGravityCompensation, useIkController, useJointState, useKeyboardIkTarget, useKeyboardTeleop, useMountedCameraSequenceRecorder, useMujoco, useMujocoWasm, useObservation, usePolicy, useSceneLights, useSelectionHighlight, useSensor, useSensors, useSitePosition, useSplatCollisionProxyGeoms, useTrajectoryPlayer, useTrajectoryRecorder, useVideoRecorder };
7924
+ 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, isMountedCameraFrameCaptureSource, loadScene, parseSplatCollisionProxyGeoms, 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 };
6883
7925
  //# sourceMappingURL=index.js.map
6884
7926
  //# sourceMappingURL=index.js.map