mujoco-react 9.0.0 → 9.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@ import defaultMujocoWasmUrl from '@mujoco/mujoco/mujoco.wasm?url';
4
4
  import { createContext, forwardRef, useEffect, useContext, useState, useRef, useCallback, useMemo, useLayoutEffect } from 'react';
5
5
  import { jsx, jsxs } from 'react/jsx-runtime';
6
6
  import { Canvas, useThree, useFrame } from '@react-three/fiber';
7
- import * as THREE11 from 'three';
7
+ import * as THREE12 from 'three';
8
8
  import { PivotControls } from '@react-three/drei';
9
9
 
10
10
  var MujocoContext = createContext({
@@ -178,16 +178,16 @@ function withContacts(data, read) {
178
178
  contacts.delete?.();
179
179
  }
180
180
  }
181
- var CapsuleGeometry = class extends THREE11.BufferGeometry {
181
+ var CapsuleGeometry = class extends THREE12.BufferGeometry {
182
182
  parameters;
183
183
  constructor(radius = 1, length = 1, capSegments = 4, radialSegments = 8) {
184
184
  super();
185
185
  this.type = "CapsuleGeometry";
186
186
  this.parameters = { radius, length, capSegments, radialSegments };
187
- const path = new THREE11.Path();
187
+ const path = new THREE12.Path();
188
188
  path.absarc(0, -length / 2, radius, Math.PI * 1.5, 0, false);
189
189
  path.absarc(0, length / 2, radius, 0, Math.PI * 0.5, false);
190
- const latheGeometry = new THREE11.LatheGeometry(path.getPoints(capSegments), radialSegments);
190
+ const latheGeometry = new THREE12.LatheGeometry(path.getPoints(capSegments), radialSegments);
191
191
  const self = this;
192
192
  self.setIndex(latheGeometry.getIndex());
193
193
  self.setAttribute("position", latheGeometry.getAttribute("position"));
@@ -195,27 +195,27 @@ var CapsuleGeometry = class extends THREE11.BufferGeometry {
195
195
  self.setAttribute("uv", latheGeometry.getAttribute("uv"));
196
196
  }
197
197
  };
198
- var Reflector = class extends THREE11.Mesh {
198
+ var Reflector = class extends THREE12.Mesh {
199
199
  isReflector = true;
200
200
  camera;
201
- reflectorPlane = new THREE11.Plane();
202
- normal = new THREE11.Vector3();
203
- reflectorWorldPosition = new THREE11.Vector3();
204
- cameraWorldPosition = new THREE11.Vector3();
205
- rotationMatrix = new THREE11.Matrix4();
206
- lookAtPosition = new THREE11.Vector3(0, 0, -1);
207
- clipPlane = new THREE11.Vector4();
208
- view = new THREE11.Vector3();
209
- target = new THREE11.Vector3();
210
- q = new THREE11.Vector4();
211
- textureMatrix = new THREE11.Matrix4();
201
+ reflectorPlane = new THREE12.Plane();
202
+ normal = new THREE12.Vector3();
203
+ reflectorWorldPosition = new THREE12.Vector3();
204
+ cameraWorldPosition = new THREE12.Vector3();
205
+ rotationMatrix = new THREE12.Matrix4();
206
+ lookAtPosition = new THREE12.Vector3(0, 0, -1);
207
+ clipPlane = new THREE12.Vector4();
208
+ view = new THREE12.Vector3();
209
+ target = new THREE12.Vector3();
210
+ q = new THREE12.Vector4();
211
+ textureMatrix = new THREE12.Matrix4();
212
212
  virtualCamera;
213
213
  renderTarget;
214
214
  constructor(geometry, options = {}) {
215
215
  super(geometry);
216
216
  this.type = "Reflector";
217
- this.camera = new THREE11.PerspectiveCamera();
218
- const color = options.color !== void 0 ? new THREE11.Color(options.color) : new THREE11.Color(8355711);
217
+ this.camera = new THREE12.PerspectiveCamera();
218
+ const color = options.color !== void 0 ? new THREE12.Color(options.color) : new THREE12.Color(8355711);
219
219
  const textureWidth = options.textureWidth || 512;
220
220
  const textureHeight = options.textureHeight || 512;
221
221
  const clipBias = options.clipBias || 0;
@@ -223,11 +223,11 @@ var Reflector = class extends THREE11.Mesh {
223
223
  const blendTexture = options.texture || void 0;
224
224
  const mixStrength = options.mixStrength !== void 0 ? options.mixStrength : 0.25;
225
225
  this.virtualCamera = this.camera;
226
- this.renderTarget = new THREE11.WebGLRenderTarget(textureWidth, textureHeight, {
226
+ this.renderTarget = new THREE12.WebGLRenderTarget(textureWidth, textureHeight, {
227
227
  samples: multisample,
228
- type: THREE11.HalfFloatType
228
+ type: THREE12.HalfFloatType
229
229
  });
230
- this.material = new THREE11.MeshPhysicalMaterial({
230
+ this.material = new THREE12.MeshPhysicalMaterial({
231
231
  map: blendTexture,
232
232
  color,
233
233
  roughness: 0.5,
@@ -353,7 +353,7 @@ var GeomBuilder = class {
353
353
  const pos = mjModel.geom_pos.subarray(g * 3, g * 3 + 3);
354
354
  const quat = mjModel.geom_quat.subarray(g * 4, g * 4 + 4);
355
355
  const matId = mjModel.geom_matid[g];
356
- const color = new THREE11.Color(16777215);
356
+ const color = new THREE12.Color(16777215);
357
357
  let opacity = 1;
358
358
  if (matId >= 0) {
359
359
  const rgba = mjModel.mat_rgba.subarray(matId * 4, matId * 4 + 4);
@@ -368,16 +368,16 @@ var GeomBuilder = class {
368
368
  let geo = null;
369
369
  const getVal = (v) => v?.value ?? v;
370
370
  if (type === getVal(MG.mjGEOM_PLANE)) {
371
- geo = new THREE11.PlaneGeometry(size[0] * 2 || 5, size[1] * 2 || 5);
371
+ geo = new THREE12.PlaneGeometry(size[0] * 2 || 5, size[1] * 2 || 5);
372
372
  } else if (type === getVal(MG.mjGEOM_SPHERE)) {
373
- geo = new THREE11.SphereGeometry(size[0], 24, 24);
373
+ geo = new THREE12.SphereGeometry(size[0], 24, 24);
374
374
  } else if (type === getVal(MG.mjGEOM_CAPSULE)) {
375
375
  geo = new CapsuleGeometry(size[0], size[1] * 2, 24, 12);
376
376
  geo.rotateX(Math.PI / 2);
377
377
  } else if (type === getVal(MG.mjGEOM_BOX)) {
378
- geo = new THREE11.BoxGeometry(size[0] * 2, size[1] * 2, size[2] * 2);
378
+ geo = new THREE12.BoxGeometry(size[0] * 2, size[1] * 2, size[2] * 2);
379
379
  } else if (type === getVal(MG.mjGEOM_CYLINDER)) {
380
- geo = new THREE11.CylinderGeometry(size[0], size[0], size[1] * 2, 24);
380
+ geo = new THREE12.CylinderGeometry(size[0], size[0], size[1] * 2, 24);
381
381
  geo.rotateX(Math.PI / 2);
382
382
  } else if (type === getVal(MG.mjGEOM_MESH)) {
383
383
  const mId = mjModel.geom_dataid[g];
@@ -385,8 +385,8 @@ var GeomBuilder = class {
385
385
  const vNum = mjModel.mesh_vertnum[mId];
386
386
  const fAdr = mjModel.mesh_faceadr[mId];
387
387
  const fNum = mjModel.mesh_facenum[mId];
388
- geo = new THREE11.BufferGeometry();
389
- geo.setAttribute("position", new THREE11.Float32BufferAttribute(mjModel.mesh_vert.subarray(vAdr * 3, (vAdr + vNum) * 3), 3));
388
+ geo = new THREE12.BufferGeometry();
389
+ geo.setAttribute("position", new THREE12.Float32BufferAttribute(mjModel.mesh_vert.subarray(vAdr * 3, (vAdr + vNum) * 3), 3));
390
390
  geo.setIndex(Array.from(mjModel.mesh_face.subarray(fAdr * 3, (fAdr + fNum) * 3)));
391
391
  geo.computeVertexNormals();
392
392
  }
@@ -401,7 +401,7 @@ var GeomBuilder = class {
401
401
  mixStrength: 0.25
402
402
  });
403
403
  } else {
404
- mesh = new THREE11.Mesh(geo, new THREE11.MeshStandardMaterial({
404
+ mesh = new THREE12.Mesh(geo, new THREE12.MeshStandardMaterial({
405
405
  color,
406
406
  transparent: opacity < 1,
407
407
  opacity,
@@ -1206,7 +1206,7 @@ function SceneRenderer(props) {
1206
1206
  }
1207
1207
  const refs = [];
1208
1208
  for (let i = 0; i < model.nbody; i++) {
1209
- const bodyGroup = new THREE11.Group();
1209
+ const bodyGroup = new THREE12.Group();
1210
1210
  bodyGroup.userData.bodyID = i;
1211
1211
  const bodyName = getName(model, model.name_bodyadr[i]);
1212
1212
  if (!hiddenBodiesRef.current.has(bodyName)) {
@@ -1235,9 +1235,9 @@ function SceneRenderer(props) {
1235
1235
  const alpha = interpolation.alpha;
1236
1236
  const i3 = i * 3;
1237
1237
  ref.position.set(
1238
- THREE11.MathUtils.lerp(interpolation.previousXpos[i3], interpolation.currentXpos[i3], alpha),
1239
- THREE11.MathUtils.lerp(interpolation.previousXpos[i3 + 1], interpolation.currentXpos[i3 + 1], alpha),
1240
- THREE11.MathUtils.lerp(interpolation.previousXpos[i3 + 2], interpolation.currentXpos[i3 + 2], alpha)
1238
+ THREE12.MathUtils.lerp(interpolation.previousXpos[i3], interpolation.currentXpos[i3], alpha),
1239
+ THREE12.MathUtils.lerp(interpolation.previousXpos[i3 + 1], interpolation.currentXpos[i3 + 1], alpha),
1240
+ THREE12.MathUtils.lerp(interpolation.previousXpos[i3 + 2], interpolation.currentXpos[i3 + 2], alpha)
1241
1241
  );
1242
1242
  const i4 = i * 4;
1243
1243
  _previousQuat.set(
@@ -1292,8 +1292,234 @@ function SceneRenderer(props) {
1292
1292
  }
1293
1293
  );
1294
1294
  }
1295
- var _previousQuat = new THREE11.Quaternion();
1296
- var _currentQuat = new THREE11.Quaternion();
1295
+ var _previousQuat = new THREE12.Quaternion();
1296
+ var _currentQuat = new THREE12.Quaternion();
1297
+ function isTargetRef(target) {
1298
+ return Boolean(target && typeof target === "object" && "current" in target);
1299
+ }
1300
+ function resolveCanvasTarget(target) {
1301
+ const resolvedTarget = isTargetRef(target) ? target.current : target;
1302
+ if (!resolvedTarget) {
1303
+ throw new Error("No frame capture target is available.");
1304
+ }
1305
+ if (resolvedTarget instanceof HTMLCanvasElement) {
1306
+ return resolvedTarget;
1307
+ }
1308
+ const canvas = resolvedTarget.querySelector("canvas");
1309
+ if (!canvas) {
1310
+ throw new Error("Frame capture target does not contain a canvas.");
1311
+ }
1312
+ return canvas;
1313
+ }
1314
+ function waitForNextAnimationFrame() {
1315
+ return new Promise((resolve) => {
1316
+ requestAnimationFrame(() => resolve());
1317
+ });
1318
+ }
1319
+ async function captureFrame(options) {
1320
+ const type = options.type ?? "image/png";
1321
+ const canvas = resolveCanvasTarget(options.target);
1322
+ if (options.waitForAnimationFrame ?? true) {
1323
+ await waitForNextAnimationFrame();
1324
+ }
1325
+ return {
1326
+ canvas,
1327
+ dataUrl: canvas.toDataURL(type, options.quality),
1328
+ type
1329
+ };
1330
+ }
1331
+ async function captureFrameBlob(options) {
1332
+ const type = options.type ?? "image/png";
1333
+ const canvas = resolveCanvasTarget(options.target);
1334
+ if (options.waitForAnimationFrame ?? true) {
1335
+ await waitForNextAnimationFrame();
1336
+ }
1337
+ const blob = await new Promise((resolve, reject) => {
1338
+ canvas.toBlob(
1339
+ (nextBlob) => {
1340
+ if (nextBlob) {
1341
+ resolve(nextBlob);
1342
+ } else {
1343
+ reject(new Error("Canvas frame capture did not produce a Blob."));
1344
+ }
1345
+ },
1346
+ type,
1347
+ options.quality
1348
+ );
1349
+ });
1350
+ return { canvas, blob, type };
1351
+ }
1352
+ function useFrameCapture(defaultOptions = {}) {
1353
+ const [status, setStatus] = useState("idle");
1354
+ const [error, setError] = useState(null);
1355
+ const reset = useCallback(() => {
1356
+ setStatus("idle");
1357
+ setError(null);
1358
+ }, []);
1359
+ const capture = useCallback(
1360
+ async (options = {}) => {
1361
+ setStatus("capturing");
1362
+ setError(null);
1363
+ try {
1364
+ const result = await captureFrame({ ...defaultOptions, ...options });
1365
+ setStatus("captured");
1366
+ return result;
1367
+ } catch (nextError) {
1368
+ const error2 = nextError instanceof Error ? nextError : new Error("Unable to capture the current canvas frame.");
1369
+ setError(error2);
1370
+ setStatus("error");
1371
+ throw error2;
1372
+ }
1373
+ },
1374
+ [defaultOptions]
1375
+ );
1376
+ const captureBlob = useCallback(
1377
+ async (options = {}) => {
1378
+ setStatus("capturing");
1379
+ setError(null);
1380
+ try {
1381
+ const result = await captureFrameBlob({
1382
+ ...defaultOptions,
1383
+ ...options
1384
+ });
1385
+ setStatus("captured");
1386
+ return result;
1387
+ } catch (nextError) {
1388
+ const error2 = nextError instanceof Error ? nextError : new Error("Unable to capture the current canvas frame.");
1389
+ setError(error2);
1390
+ setStatus("error");
1391
+ throw error2;
1392
+ }
1393
+ },
1394
+ [defaultOptions]
1395
+ );
1396
+ return {
1397
+ status,
1398
+ error,
1399
+ isCapturing: status === "capturing",
1400
+ capture,
1401
+ captureBlob,
1402
+ reset
1403
+ };
1404
+ }
1405
+ function toVector3(value, fallback) {
1406
+ if (!value) return fallback.clone();
1407
+ return value instanceof THREE12.Vector3 ? value.clone() : new THREE12.Vector3(value[0], value[1], value[2]);
1408
+ }
1409
+ function applyCameraPose(camera, options, fallbackCamera) {
1410
+ camera.position.copy(toVector3(options.position, fallbackCamera.position));
1411
+ camera.up.copy(toVector3(options.up, fallbackCamera.up));
1412
+ if (options.quaternion) {
1413
+ if (options.quaternion instanceof THREE12.Quaternion) {
1414
+ camera.quaternion.copy(options.quaternion);
1415
+ } else {
1416
+ camera.quaternion.set(
1417
+ options.quaternion[0],
1418
+ options.quaternion[1],
1419
+ options.quaternion[2],
1420
+ options.quaternion[3]
1421
+ );
1422
+ }
1423
+ } else if (options.lookAt) {
1424
+ camera.lookAt(toVector3(options.lookAt, new THREE12.Vector3()));
1425
+ } else {
1426
+ camera.quaternion.copy(fallbackCamera.quaternion);
1427
+ }
1428
+ camera.updateMatrixWorld();
1429
+ }
1430
+ function createCaptureCamera(options, fallbackCamera, width, height) {
1431
+ const camera = options.camera ? options.camera.clone() : fallbackCamera instanceof THREE12.PerspectiveCamera ? fallbackCamera.clone() : new THREE12.PerspectiveCamera(45, width / height, 0.01, 100);
1432
+ if (camera instanceof THREE12.PerspectiveCamera) {
1433
+ camera.aspect = width / height;
1434
+ camera.fov = options.fov ?? camera.fov;
1435
+ camera.near = options.near ?? camera.near;
1436
+ camera.far = options.far ?? camera.far;
1437
+ camera.updateProjectionMatrix();
1438
+ }
1439
+ applyCameraPose(camera, options, fallbackCamera);
1440
+ return camera;
1441
+ }
1442
+ function readRenderTargetToCanvas(renderer, target, width, height) {
1443
+ const pixels = new Uint8Array(width * height * 4);
1444
+ renderer.readRenderTargetPixels(target, 0, 0, width, height, pixels);
1445
+ const canvas = document.createElement("canvas");
1446
+ canvas.width = width;
1447
+ canvas.height = height;
1448
+ const context = canvas.getContext("2d");
1449
+ if (!context) {
1450
+ throw new Error("Unable to create a 2D canvas for camera frame capture.");
1451
+ }
1452
+ const imageData = context.createImageData(width, height);
1453
+ const rowBytes = width * 4;
1454
+ for (let y = 0; y < height; y += 1) {
1455
+ const sourceStart = (height - y - 1) * rowBytes;
1456
+ const targetStart = y * rowBytes;
1457
+ imageData.data.set(
1458
+ pixels.subarray(sourceStart, sourceStart + rowBytes),
1459
+ targetStart
1460
+ );
1461
+ }
1462
+ context.putImageData(imageData, 0, 0);
1463
+ return canvas;
1464
+ }
1465
+ function renderCameraFrameToCanvas(renderer, scene, fallbackCamera, options = {}) {
1466
+ const width = Math.max(1, Math.floor(options.width ?? renderer.domElement.width));
1467
+ const height = Math.max(1, Math.floor(options.height ?? renderer.domElement.height));
1468
+ const camera = createCaptureCamera(options, fallbackCamera, width, height);
1469
+ const target = new THREE12.WebGLRenderTarget(width, height, {
1470
+ format: THREE12.RGBAFormat,
1471
+ type: THREE12.UnsignedByteType
1472
+ });
1473
+ const previousTarget = renderer.getRenderTarget();
1474
+ const previousXrEnabled = renderer.xr.enabled;
1475
+ scene.updateMatrixWorld(true);
1476
+ try {
1477
+ renderer.xr.enabled = false;
1478
+ renderer.setRenderTarget(target);
1479
+ renderer.clear();
1480
+ renderer.render(scene, camera);
1481
+ const canvas = readRenderTargetToCanvas(renderer, target, width, height);
1482
+ return { canvas, camera, width, height };
1483
+ } finally {
1484
+ renderer.setRenderTarget(previousTarget);
1485
+ renderer.xr.enabled = previousXrEnabled;
1486
+ target.dispose();
1487
+ }
1488
+ }
1489
+ async function captureCameraFrame(renderer, scene, fallbackCamera, options = {}) {
1490
+ const type = options.type ?? "image/png";
1491
+ const result = renderCameraFrameToCanvas(
1492
+ renderer,
1493
+ scene,
1494
+ fallbackCamera,
1495
+ options
1496
+ );
1497
+ return {
1498
+ ...result,
1499
+ dataUrl: result.canvas.toDataURL(type, options.quality),
1500
+ type
1501
+ };
1502
+ }
1503
+ async function captureCameraFrameBlob(renderer, scene, fallbackCamera, options = {}) {
1504
+ const type = options.type ?? "image/png";
1505
+ const result = renderCameraFrameToCanvas(
1506
+ renderer,
1507
+ scene,
1508
+ fallbackCamera,
1509
+ options
1510
+ );
1511
+ const blob = await new Promise((resolve, reject) => {
1512
+ result.canvas.toBlob(
1513
+ (nextBlob) => {
1514
+ if (nextBlob) resolve(nextBlob);
1515
+ else reject(new Error("Camera frame capture did not produce a Blob."));
1516
+ },
1517
+ type,
1518
+ options.quality
1519
+ );
1520
+ });
1521
+ return { ...result, blob, type };
1522
+ }
1297
1523
  var JOINT_TYPE_NAMES2 = ["free", "ball", "slide", "hinge"];
1298
1524
  var GEOM_TYPE_NAMES = ["plane", "hfield", "sphere", "capsule", "ellipsoid", "cylinder", "box", "mesh"];
1299
1525
  var SENSOR_TYPE_NAMES = {
@@ -1369,8 +1595,13 @@ var _applyPoint = new Float64Array(3);
1369
1595
  var _rayPnt = new Float64Array(3);
1370
1596
  var _rayVec = new Float64Array(3);
1371
1597
  var _rayGeomId = new Int32Array(1);
1372
- var _projRaycaster = new THREE11.Raycaster();
1373
- var _projNdc = new THREE11.Vector2();
1598
+ var _projRaycaster = new THREE12.Raycaster();
1599
+ var _projNdc = new THREE12.Vector2();
1600
+ function waitForNextAnimationFrame2() {
1601
+ return new Promise((resolve) => {
1602
+ requestAnimationFrame(() => resolve());
1603
+ });
1604
+ }
1374
1605
  var MujocoSimContext = createContext(null);
1375
1606
  function useMujocoContext() {
1376
1607
  const ctx = useContext(MujocoSimContext);
@@ -1455,7 +1686,7 @@ function MujocoSimProvider({
1455
1686
  interpolate,
1456
1687
  children
1457
1688
  }) {
1458
- const { gl, camera } = useThree();
1689
+ const { gl, camera, scene } = useThree();
1459
1690
  const [status, setStatus] = useState("loading");
1460
1691
  const mjModelRef = useRef(null);
1461
1692
  const mjDataRef = useRef(null);
@@ -1704,6 +1935,27 @@ function MujocoSimProvider({
1704
1935
  const step = useCallback((n = 1) => {
1705
1936
  stepsToRunRef.current = n;
1706
1937
  }, []);
1938
+ const stepImmediately = useCallback((steps = 1) => {
1939
+ const model = mjModelRef.current;
1940
+ const data = mjDataRef.current;
1941
+ if (!model || !data) return false;
1942
+ for (let stepIndex = 0; stepIndex < steps; stepIndex += 1) {
1943
+ for (let i = 0; i < model.nv; i += 1) {
1944
+ data.qfrc_applied[i] = 0;
1945
+ }
1946
+ for (const cb of beforeStepCallbacks.current) {
1947
+ cb({ model, data });
1948
+ }
1949
+ mujoco.mj_step(model, data);
1950
+ for (const cb of afterStepCallbacks.current) {
1951
+ cb({ model, data });
1952
+ }
1953
+ onStepRef.current?.({ time: data.time, model, data });
1954
+ }
1955
+ physicsAccumulatorRef.current = 0;
1956
+ interpolationStateRef.current.valid = false;
1957
+ return true;
1958
+ }, [mujoco]);
1707
1959
  const getTime = useCallback(() => {
1708
1960
  return mjDataRef.current?.time ?? 0;
1709
1961
  }, []);
@@ -2014,7 +2266,7 @@ function MujocoSimProvider({
2014
2266
  const geomId = _rayGeomId[0];
2015
2267
  const bodyId = geomId >= 0 ? model.geom_bodyid[geomId] : -1;
2016
2268
  return {
2017
- point: new THREE11.Vector3(
2269
+ point: new THREE12.Vector3(
2018
2270
  origin.x + dir.x * dist,
2019
2271
  origin.y + dir.y * dist,
2020
2272
  origin.z + dir.z * dist
@@ -2145,6 +2397,85 @@ function MujocoSimProvider({
2145
2397
  },
2146
2398
  [gl]
2147
2399
  );
2400
+ const getCanvas = useCallback(() => {
2401
+ return gl.domElement ?? null;
2402
+ }, [gl]);
2403
+ const captureFrameApi = useCallback(
2404
+ (options = {}) => {
2405
+ return captureFrame({ ...options, target: gl.domElement });
2406
+ },
2407
+ [gl]
2408
+ );
2409
+ const captureFrameBlobApi = useCallback(
2410
+ (options = {}) => {
2411
+ return captureFrameBlob({ ...options, target: gl.domElement });
2412
+ },
2413
+ [gl]
2414
+ );
2415
+ const captureCameraFrameApi = useCallback(
2416
+ (options = {}) => {
2417
+ return captureCameraFrame(gl, scene, camera, options);
2418
+ },
2419
+ [camera, gl, scene]
2420
+ );
2421
+ const captureCameraFrameBlobApi = useCallback(
2422
+ (options = {}) => {
2423
+ return captureCameraFrameBlob(gl, scene, camera, options);
2424
+ },
2425
+ [camera, gl, scene]
2426
+ );
2427
+ const recordCameraSequenceApi = useCallback(
2428
+ async (options) => {
2429
+ const frameCount = Math.max(0, Math.floor(options.frames));
2430
+ const stepsPerFrame = Math.max(1, Math.floor(options.stepsPerFrame ?? 1));
2431
+ const cameras = options.cameras;
2432
+ const frames = [];
2433
+ const wasPaused = pausedRef.current;
2434
+ if (frameCount === 0 || cameras.length === 0) {
2435
+ return {
2436
+ frames,
2437
+ cameraKeys: cameras.map((sequenceCamera) => sequenceCamera.key),
2438
+ frameCount: 0
2439
+ };
2440
+ }
2441
+ try {
2442
+ pausedRef.current = true;
2443
+ stepsToRunRef.current = 0;
2444
+ if (options.reset) reset();
2445
+ for (let frameIndex = 0; frameIndex < frameCount; frameIndex += 1) {
2446
+ if (frameIndex > 0 || options.captureInitialFrame === false) {
2447
+ stepImmediately(stepsPerFrame);
2448
+ }
2449
+ await waitForNextAnimationFrame2();
2450
+ const cameraFrames = {};
2451
+ for (const sequenceCamera of cameras) {
2452
+ const { key, ...captureOptions } = sequenceCamera;
2453
+ cameraFrames[key] = await captureCameraFrame(
2454
+ gl,
2455
+ scene,
2456
+ camera,
2457
+ captureOptions
2458
+ );
2459
+ }
2460
+ const frame = {
2461
+ frameIndex,
2462
+ time: getTime(),
2463
+ cameras: cameraFrames
2464
+ };
2465
+ frames.push(frame);
2466
+ await options.onFrame?.(frame);
2467
+ }
2468
+ } finally {
2469
+ pausedRef.current = wasPaused;
2470
+ }
2471
+ return {
2472
+ frames,
2473
+ cameraKeys: cameras.map((sequenceCamera) => sequenceCamera.key),
2474
+ frameCount: frames.length
2475
+ };
2476
+ },
2477
+ [camera, getTime, gl, reset, scene, stepImmediately]
2478
+ );
2148
2479
  const project2DTo3D = useCallback(
2149
2480
  (x, y, cameraPos, lookAt) => {
2150
2481
  const virtCam = camera.clone();
@@ -2155,9 +2486,9 @@ function MujocoSimProvider({
2155
2486
  _projNdc.set(x * 2 - 1, -(y * 2 - 1));
2156
2487
  _projRaycaster.setFromCamera(_projNdc, virtCam);
2157
2488
  const objects = [];
2158
- const scene = camera.parent;
2159
- if (scene) {
2160
- scene.traverse((c) => {
2489
+ const scene2 = camera.parent;
2490
+ if (scene2) {
2491
+ scene2.traverse((c) => {
2161
2492
  if (c.isMesh) objects.push(c);
2162
2493
  });
2163
2494
  }
@@ -2251,7 +2582,13 @@ function MujocoSimProvider({
2251
2582
  addBody: addBodyApi,
2252
2583
  removeBody: removeBodyApi,
2253
2584
  recompile: recompileApi,
2585
+ getCanvas,
2254
2586
  getCanvasSnapshot,
2587
+ captureFrame: captureFrameApi,
2588
+ captureFrameBlob: captureFrameBlobApi,
2589
+ captureCameraFrame: captureCameraFrameApi,
2590
+ captureCameraFrameBlob: captureCameraFrameBlobApi,
2591
+ recordCameraSequence: recordCameraSequenceApi,
2255
2592
  project2DTo3D,
2256
2593
  setBodyMass,
2257
2594
  setGeomFriction,
@@ -2303,7 +2640,13 @@ function MujocoSimProvider({
2303
2640
  addBodyApi,
2304
2641
  removeBodyApi,
2305
2642
  recompileApi,
2643
+ getCanvas,
2306
2644
  getCanvasSnapshot,
2645
+ captureFrameApi,
2646
+ captureFrameBlobApi,
2647
+ captureCameraFrameApi,
2648
+ captureCameraFrameBlobApi,
2649
+ recordCameraSequenceApi,
2307
2650
  project2DTo3D,
2308
2651
  setBodyMass,
2309
2652
  setGeomFriction,
@@ -2770,7 +3113,7 @@ function solve6x6(A, b, x) {
2770
3113
  }
2771
3114
 
2772
3115
  // src/hooks/useIkController.ts
2773
- var _syncMat4 = new THREE11.Matrix4();
3116
+ var _syncMat4 = new THREE12.Matrix4();
2774
3117
  function syncGizmoToSite(data, siteId, target) {
2775
3118
  if (siteId === -1) return;
2776
3119
  const sitePos = data.site_xpos.subarray(siteId * 3, siteId * 3 + 3);
@@ -2802,7 +3145,7 @@ var useIkController = createControllerHook(
2802
3145
  const { mjModelRef, mjDataRef, mujocoRef, resetCallbacks, status } = useMujocoContext();
2803
3146
  const ikEnabledRef = useRef(false);
2804
3147
  const ikCalculatingRef = useRef(false);
2805
- const ikTargetRef = useRef(new THREE11.Group());
3148
+ const ikTargetRef = useRef(new THREE12.Group());
2806
3149
  const siteIdRef = useRef(-1);
2807
3150
  const controlGroupRef = useRef(null);
2808
3151
  const genericIkRef = useRef(new GenericIK(mujocoRef.current));
@@ -2810,10 +3153,10 @@ var useIkController = createControllerHook(
2810
3153
  const needsInitialSync = useRef(true);
2811
3154
  const gizmoAnimRef = useRef({
2812
3155
  active: false,
2813
- startPos: new THREE11.Vector3(),
2814
- endPos: new THREE11.Vector3(),
2815
- startRot: new THREE11.Quaternion(),
2816
- endRot: new THREE11.Quaternion(),
3156
+ startPos: new THREE12.Vector3(),
3157
+ endPos: new THREE12.Vector3(),
3158
+ startRot: new THREE12.Quaternion(),
3159
+ endRot: new THREE12.Quaternion(),
2817
3160
  startTime: 0,
2818
3161
  duration: 1e3
2819
3162
  });
@@ -2958,8 +3301,8 @@ var useIkController = createControllerHook(
2958
3301
  const target = ikTargetRef.current;
2959
3302
  if (!target) return;
2960
3303
  const targetPos = pos.clone();
2961
- const targetRot = new THREE11.Quaternion().setFromEuler(
2962
- new THREE11.Euler(Math.PI, 0, 0)
3304
+ const targetRot = new THREE12.Quaternion().setFromEuler(
3305
+ new THREE12.Euler(Math.PI, 0, 0)
2963
3306
  );
2964
3307
  if (duration > 0) {
2965
3308
  const ga = gizmoAnimRef.current;
@@ -2984,7 +3327,7 @@ var useIkController = createControllerHook(
2984
3327
  if (!ikCalculatingRef.current || !target) return null;
2985
3328
  return {
2986
3329
  pos: target.position.clone(),
2987
- rot: new THREE11.Euler().setFromQuaternion(target.quaternion)
3330
+ rot: new THREE12.Euler().setFromQuaternion(target.quaternion)
2988
3331
  };
2989
3332
  },
2990
3333
  []
@@ -3080,10 +3423,10 @@ function Body({
3080
3423
  if (!hasChildren) return null;
3081
3424
  return /* @__PURE__ */ jsx("group", { ref: groupRef, children });
3082
3425
  }
3083
- var _mat4 = new THREE11.Matrix4();
3084
- var _pos = new THREE11.Vector3();
3085
- var _quat = new THREE11.Quaternion();
3086
- var _scale = new THREE11.Vector3(1, 1, 1);
3426
+ var _mat4 = new THREE12.Matrix4();
3427
+ var _pos = new THREE12.Vector3();
3428
+ var _quat = new THREE12.Quaternion();
3429
+ var _scale = new THREE12.Vector3(1, 1, 1);
3087
3430
  function IkGizmo({ controller, siteName, scale = 0.18, onDrag }) {
3088
3431
  const { mjModelRef, mjDataRef, status } = useMujocoContext();
3089
3432
  const { ikTargetRef, siteIdRef, ikEnabledRef, setIkEnabled } = controller;
@@ -3175,7 +3518,7 @@ function IkGizmo({ controller, siteName, scale = 0.18, onDrag }) {
3175
3518
  }
3176
3519
  ) });
3177
3520
  }
3178
- var _dummy = new THREE11.Object3D();
3521
+ var _dummy = new THREE12.Object3D();
3179
3522
  function ContactMarkers({
3180
3523
  maxContacts = 100,
3181
3524
  radius = 8e-3,
@@ -3219,11 +3562,11 @@ function ContactMarkers({
3219
3562
  var _force = new Float64Array(3);
3220
3563
  var _torque = new Float64Array(3);
3221
3564
  var _point = new Float64Array(3);
3222
- var _bodyPos = new THREE11.Vector3();
3223
- var _bodyQuat = new THREE11.Quaternion();
3224
- var _worldHit = new THREE11.Vector3();
3225
- var _raycaster = new THREE11.Raycaster();
3226
- var _mouse = new THREE11.Vector2();
3565
+ var _bodyPos = new THREE12.Vector3();
3566
+ var _bodyQuat = new THREE12.Quaternion();
3567
+ var _worldHit = new THREE12.Vector3();
3568
+ var _raycaster = new THREE12.Raycaster();
3569
+ var _mouse = new THREE12.Vector2();
3227
3570
  function DragInteraction({
3228
3571
  stiffness = 250,
3229
3572
  showArrow = true,
@@ -3234,16 +3577,16 @@ function DragInteraction({
3234
3577
  const draggingRef = useRef(false);
3235
3578
  const bodyIdRef = useRef(-1);
3236
3579
  const grabDistanceRef = useRef(0);
3237
- const localHitRef = useRef(new THREE11.Vector3());
3238
- const grabWorldRef = useRef(new THREE11.Vector3());
3239
- const mouseWorldRef = useRef(new THREE11.Vector3());
3580
+ const localHitRef = useRef(new THREE12.Vector3());
3581
+ const grabWorldRef = useRef(new THREE12.Vector3());
3582
+ const mouseWorldRef = useRef(new THREE12.Vector3());
3240
3583
  const arrowRef = useRef(null);
3241
3584
  const groupRef = useRef(null);
3242
3585
  useEffect(() => {
3243
3586
  if (!showArrow || !groupRef.current) return;
3244
- const arrow = new THREE11.ArrowHelper(
3245
- new THREE11.Vector3(0, 1, 0),
3246
- new THREE11.Vector3(),
3587
+ const arrow = new THREE12.ArrowHelper(
3588
+ new THREE12.Vector3(0, 1, 0),
3589
+ new THREE12.Vector3(),
3247
3590
  0.1,
3248
3591
  16729156
3249
3592
  );
@@ -3425,7 +3768,7 @@ function useSceneLights(intensity = 1) {
3425
3768
  const dr = lightDiffuse ? lightDiffuse[3 * i] : 1;
3426
3769
  const dg = lightDiffuse ? lightDiffuse[3 * i + 1] : 1;
3427
3770
  const db = lightDiffuse ? lightDiffuse[3 * i + 2] : 1;
3428
- const color = new THREE11.Color(dr, dg, db);
3771
+ const color = new THREE12.Color(dr, dg, db);
3429
3772
  const px = lightPos[3 * i];
3430
3773
  const py = lightPos[3 * i + 1];
3431
3774
  const pz = lightPos[3 * i + 2];
@@ -3433,7 +3776,7 @@ function useSceneLights(intensity = 1) {
3433
3776
  const dy = lightDir[3 * i + 1];
3434
3777
  const dz = lightDir[3 * i + 2];
3435
3778
  if (isDirectional) {
3436
- const light = new THREE11.DirectionalLight(color, finalIntensity);
3779
+ const light = new THREE12.DirectionalLight(color, finalIntensity);
3437
3780
  light.position.set(px, py, pz);
3438
3781
  light.target.position.set(px + dx, py + dy, pz + dz);
3439
3782
  light.castShadow = castShadow;
@@ -3456,7 +3799,7 @@ function useSceneLights(intensity = 1) {
3456
3799
  const cutoff = lightCutoff ? lightCutoff[i] : 45;
3457
3800
  const exponent = lightExponent ? lightExponent[i] : 10;
3458
3801
  const angle = cutoff * Math.PI / 180;
3459
- const light = new THREE11.SpotLight(color, finalIntensity, 0, angle, exponent / 128);
3802
+ const light = new THREE12.SpotLight(color, finalIntensity, 0, angle, exponent / 128);
3460
3803
  light.position.set(px, py, pz);
3461
3804
  light.target.position.set(px + dx, py + dy, pz + dz);
3462
3805
  light.castShadow = castShadow;
@@ -3514,11 +3857,11 @@ var JOINT_COLORS = {
3514
3857
  3: 16776960
3515
3858
  // hinge - yellow
3516
3859
  };
3517
- var _v3a = new THREE11.Vector3();
3518
- new THREE11.Vector3();
3519
- var _quat2 = new THREE11.Quaternion();
3520
- var _contactPos = new THREE11.Vector3();
3521
- var _contactNormal = new THREE11.Vector3();
3860
+ var _v3a = new THREE12.Vector3();
3861
+ new THREE12.Vector3();
3862
+ var _quat2 = new THREE12.Quaternion();
3863
+ var _contactPos = new THREE12.Vector3();
3864
+ var _contactNormal = new THREE12.Vector3();
3522
3865
  var MAX_CONTACT_ARROWS = 50;
3523
3866
  function Debug({
3524
3867
  showGeoms = false,
@@ -3547,21 +3890,21 @@ function Debug({
3547
3890
  let geometry = null;
3548
3891
  switch (type) {
3549
3892
  case 2:
3550
- geometry = new THREE11.SphereGeometry(s[3 * i], 12, 8);
3893
+ geometry = new THREE12.SphereGeometry(s[3 * i], 12, 8);
3551
3894
  break;
3552
3895
  case 3:
3553
- geometry = new THREE11.CapsuleGeometry(s[3 * i], s[3 * i + 1] * 2, 6, 8);
3896
+ geometry = new THREE12.CapsuleGeometry(s[3 * i], s[3 * i + 1] * 2, 6, 8);
3554
3897
  break;
3555
3898
  case 5:
3556
- geometry = new THREE11.CylinderGeometry(s[3 * i], s[3 * i], s[3 * i + 1] * 2, 12);
3899
+ geometry = new THREE12.CylinderGeometry(s[3 * i], s[3 * i], s[3 * i + 1] * 2, 12);
3557
3900
  break;
3558
3901
  case 6:
3559
- geometry = new THREE11.BoxGeometry(s[3 * i] * 2, s[3 * i + 1] * 2, s[3 * i + 2] * 2);
3902
+ geometry = new THREE12.BoxGeometry(s[3 * i] * 2, s[3 * i + 1] * 2, s[3 * i + 2] * 2);
3560
3903
  break;
3561
3904
  }
3562
3905
  if (geometry) {
3563
- const mat = new THREE11.MeshBasicMaterial({ color: 65280, wireframe: true, transparent: true, opacity: 0.3 });
3564
- const mesh = new THREE11.Mesh(geometry, mat);
3906
+ const mat = new THREE12.MeshBasicMaterial({ color: 65280, wireframe: true, transparent: true, opacity: 0.3 });
3907
+ const mesh = new THREE12.Mesh(geometry, mat);
3565
3908
  mesh.userData.geomId = i;
3566
3909
  mesh.userData.bodyId = model.geom_bodyid[i];
3567
3910
  geoms.push(mesh);
@@ -3584,9 +3927,9 @@ function Debug({
3584
3927
  }
3585
3928
  if (maxGeomSize > 0) radius = maxGeomSize * 0.15;
3586
3929
  }
3587
- const geometry = new THREE11.OctahedronGeometry(radius);
3588
- const mat = new THREE11.MeshBasicMaterial({ color: 16711935, depthTest: false });
3589
- const mesh = new THREE11.Mesh(geometry, mat);
3930
+ const geometry = new THREE12.OctahedronGeometry(radius);
3931
+ const mat = new THREE12.MeshBasicMaterial({ color: 16711935, depthTest: false });
3932
+ const mesh = new THREE12.Mesh(geometry, mat);
3590
3933
  mesh.renderOrder = 999;
3591
3934
  mesh.frustumCulled = false;
3592
3935
  mesh.userData.siteId = i;
@@ -3598,9 +3941,9 @@ function Debug({
3598
3941
  ctx.font = "bold 36px monospace";
3599
3942
  ctx.textAlign = "center";
3600
3943
  ctx.fillText(getName(model, model.name_siteadr[i]), 128, 42);
3601
- const tex = new THREE11.CanvasTexture(canvas);
3602
- const spriteMat = new THREE11.SpriteMaterial({ map: tex, depthTest: false, transparent: true });
3603
- const sprite = new THREE11.Sprite(spriteMat);
3944
+ const tex = new THREE12.CanvasTexture(canvas);
3945
+ const spriteMat = new THREE12.SpriteMaterial({ map: tex, depthTest: false, transparent: true });
3946
+ const sprite = new THREE12.Sprite(spriteMat);
3604
3947
  const labelScale = radius * 15;
3605
3948
  sprite.scale.set(labelScale, labelScale * 0.25, 1);
3606
3949
  sprite.position.y = radius * 2;
@@ -3623,9 +3966,9 @@ function Debug({
3623
3966
  }
3624
3967
  }
3625
3968
  const arrowLen = Math.max(maxGeomSize * 0.8, 0.05);
3626
- const arrow = new THREE11.ArrowHelper(
3627
- new THREE11.Vector3(0, 0, 1),
3628
- new THREE11.Vector3(),
3969
+ const arrow = new THREE12.ArrowHelper(
3970
+ new THREE12.Vector3(0, 0, 1),
3971
+ new THREE12.Vector3(),
3629
3972
  arrowLen,
3630
3973
  color,
3631
3974
  arrowLen * 0.25,
@@ -3633,7 +3976,7 @@ function Debug({
3633
3976
  );
3634
3977
  arrow.renderOrder = 999;
3635
3978
  arrow.frustumCulled = false;
3636
- arrow.line.material = new THREE11.LineBasicMaterial({ color, depthTest: false });
3979
+ arrow.line.material = new THREE12.LineBasicMaterial({ color, depthTest: false });
3637
3980
  arrow.cone.material.depthTest = false;
3638
3981
  arrow.line.renderOrder = 999;
3639
3982
  arrow.line.frustumCulled = false;
@@ -3648,9 +3991,9 @@ function Debug({
3648
3991
  }
3649
3992
  if (showCOM) {
3650
3993
  for (let i = 1; i < model.nbody; i++) {
3651
- const geometry = new THREE11.SphereGeometry(5e-3, 6, 6);
3652
- const mat = new THREE11.MeshBasicMaterial({ color: 16711680 });
3653
- const mesh = new THREE11.Mesh(geometry, mat);
3994
+ const geometry = new THREE12.SphereGeometry(5e-3, 6, 6);
3995
+ const mat = new THREE12.MeshBasicMaterial({ color: 16711680 });
3996
+ const mesh = new THREE12.Mesh(geometry, mat);
3654
3997
  mesh.userData.bodyId = i;
3655
3998
  comMarkers.push(mesh);
3656
3999
  }
@@ -3741,9 +4084,9 @@ function Debug({
3741
4084
  contactPoolInitRef.current = true;
3742
4085
  const pool = [];
3743
4086
  for (let i = 0; i < MAX_CONTACT_ARROWS; i++) {
3744
- const arrow = new THREE11.ArrowHelper(
3745
- new THREE11.Vector3(0, 1, 0),
3746
- new THREE11.Vector3(),
4087
+ const arrow = new THREE12.ArrowHelper(
4088
+ new THREE12.Vector3(0, 1, 0),
4089
+ new THREE12.Vector3(),
3747
4090
  0.1,
3748
4091
  16729156,
3749
4092
  0.03,
@@ -3798,9 +4141,9 @@ function Debug({
3798
4141
  showContacts && /* @__PURE__ */ jsx("group", { ref: contactGroupRef })
3799
4142
  ] });
3800
4143
  }
3801
- var DEFAULT_TENDON_COLOR = new THREE11.Color(0.3, 0.3, 0.8);
4144
+ var DEFAULT_TENDON_COLOR = new THREE12.Color(0.3, 0.3, 0.8);
3802
4145
  var DEFAULT_TENDON_WIDTH = 2e-3;
3803
- new THREE11.Vector3();
4146
+ new THREE12.Vector3();
3804
4147
  function TendonRenderer(props) {
3805
4148
  const { mjModelRef, mjDataRef, status } = useMujocoContext();
3806
4149
  const groupRef = useRef(null);
@@ -3814,7 +4157,7 @@ function TendonRenderer(props) {
3814
4157
  if (!model || !data || !group) return;
3815
4158
  const ntendon = model.ntendon ?? 0;
3816
4159
  if (ntendon === 0) return;
3817
- const material = new THREE11.MeshStandardMaterial({
4160
+ const material = new THREE12.MeshStandardMaterial({
3818
4161
  color: DEFAULT_TENDON_COLOR,
3819
4162
  roughness: 0.6,
3820
4163
  metalness: 0.1
@@ -3829,11 +4172,11 @@ function TendonRenderer(props) {
3829
4172
  curves.push(null);
3830
4173
  continue;
3831
4174
  }
3832
- const points = Array.from({ length: wrapNum }, () => new THREE11.Vector3());
3833
- const curve = new THREE11.CatmullRomCurve3(points, false);
4175
+ const points = Array.from({ length: wrapNum }, () => new THREE12.Vector3());
4176
+ const curve = new THREE12.CatmullRomCurve3(points, false);
3834
4177
  const segments = Math.max(wrapNum * 2, 4);
3835
- const geometry = new THREE11.TubeGeometry(curve, segments, DEFAULT_TENDON_WIDTH, 6, false);
3836
- const mesh = new THREE11.Mesh(geometry, material);
4178
+ const geometry = new THREE12.TubeGeometry(curve, segments, DEFAULT_TENDON_WIDTH, 6, false);
4179
+ const mesh = new THREE12.Mesh(geometry, material);
3837
4180
  mesh.frustumCulled = false;
3838
4181
  group.add(mesh);
3839
4182
  meshes.push(mesh);
@@ -3888,11 +4231,11 @@ function TendonRenderer(props) {
3888
4231
  if (curve.points.length !== validCount) {
3889
4232
  curve.points.length = validCount;
3890
4233
  while (curve.points.length < validCount) {
3891
- curve.points.push(new THREE11.Vector3());
4234
+ curve.points.push(new THREE12.Vector3());
3892
4235
  }
3893
4236
  }
3894
4237
  mesh.geometry.dispose();
3895
- mesh.geometry = new THREE11.TubeGeometry(
4238
+ mesh.geometry = new THREE12.TubeGeometry(
3896
4239
  curve,
3897
4240
  Math.max(validCount * 2, 4),
3898
4241
  DEFAULT_TENDON_WIDTH,
@@ -3919,24 +4262,24 @@ function FlexRenderer(props) {
3919
4262
  const vertAdr = model.flex_vertadr[f];
3920
4263
  const vertNum = model.flex_vertnum[f];
3921
4264
  if (vertNum === 0) continue;
3922
- const geometry = new THREE11.BufferGeometry();
4265
+ const geometry = new THREE12.BufferGeometry();
3923
4266
  const positions = new Float32Array(vertNum * 3);
3924
- geometry.setAttribute("position", new THREE11.BufferAttribute(positions, 3));
4267
+ geometry.setAttribute("position", new THREE12.BufferAttribute(positions, 3));
3925
4268
  geometry.computeVertexNormals();
3926
- let color = new THREE11.Color(0.5, 0.5, 0.5);
4269
+ let color = new THREE12.Color(0.5, 0.5, 0.5);
3927
4270
  if (model.flex_rgba) {
3928
- color = new THREE11.Color(
4271
+ color = new THREE12.Color(
3929
4272
  model.flex_rgba[4 * f],
3930
4273
  model.flex_rgba[4 * f + 1],
3931
4274
  model.flex_rgba[4 * f + 2]
3932
4275
  );
3933
4276
  }
3934
- const material = new THREE11.MeshStandardMaterial({
4277
+ const material = new THREE12.MeshStandardMaterial({
3935
4278
  color,
3936
4279
  roughness: 0.7,
3937
- side: THREE11.DoubleSide
4280
+ side: THREE12.DoubleSide
3938
4281
  });
3939
- const mesh = new THREE11.Mesh(geometry, material);
4282
+ const mesh = new THREE12.Mesh(geometry, material);
3940
4283
  mesh.userData.flexId = f;
3941
4284
  mesh.userData.vertAdr = vertAdr;
3942
4285
  mesh.userData.vertNum = vertNum;
@@ -3972,7 +4315,7 @@ function FlexRenderer(props) {
3972
4315
  return /* @__PURE__ */ jsx("group", { ...props, ref: groupRef });
3973
4316
  }
3974
4317
  var GEOM_TYPE_NAMES2 = ["plane", "hfield", "sphere", "capsule", "ellipsoid", "cylinder", "box", "mesh"];
3975
- var _matrix = new THREE11.Matrix4();
4318
+ var _matrix = new THREE12.Matrix4();
3976
4319
  function getGeomInfo(model, geomId) {
3977
4320
  const size = model.geom_size.subarray(geomId * 3, geomId * 3 + 3);
3978
4321
  const type = model.geom_type[geomId];
@@ -3994,10 +4337,10 @@ function geomSignature(model, geomId) {
3994
4337
  return [type, size, mat, data, rgba].join("|");
3995
4338
  }
3996
4339
  function firstMesh(object) {
3997
- if (object instanceof THREE11.Mesh) return object;
4340
+ if (object instanceof THREE12.Mesh) return object;
3998
4341
  let mesh = null;
3999
4342
  object.traverse((child) => {
4000
- if (!mesh && child instanceof THREE11.Mesh) mesh = child;
4343
+ if (!mesh && child instanceof THREE12.Mesh) mesh = child;
4001
4344
  });
4002
4345
  return mesh;
4003
4346
  }
@@ -4426,12 +4769,12 @@ function useActuators() {
4426
4769
  return actuators;
4427
4770
  }, [status, mjModelRef]);
4428
4771
  }
4429
- var _mat42 = new THREE11.Matrix4();
4772
+ var _mat42 = new THREE12.Matrix4();
4430
4773
  function useSitePosition(siteName) {
4431
4774
  const { mjModelRef, mjDataRef, status } = useMujocoContext();
4432
4775
  const siteIdRef = useRef(-1);
4433
- const positionRef = useRef(new THREE11.Vector3());
4434
- const quaternionRef = useRef(new THREE11.Quaternion());
4776
+ const positionRef = useRef(new THREE12.Vector3());
4777
+ const quaternionRef = useRef(new THREE12.Quaternion());
4435
4778
  useEffect(() => {
4436
4779
  const model = mjModelRef.current;
4437
4780
  if (!model || status !== "ready") {
@@ -4622,10 +4965,10 @@ function useJointState(name) {
4622
4965
  function useBodyState(name) {
4623
4966
  const { mjModelRef, status } = useMujocoContext();
4624
4967
  const bodyIdRef = useRef(-1);
4625
- const position = useRef(new THREE11.Vector3());
4626
- const quaternion = useRef(new THREE11.Quaternion());
4627
- const linearVelocity = useRef(new THREE11.Vector3());
4628
- const angularVelocity = useRef(new THREE11.Vector3());
4968
+ const position = useRef(new THREE12.Vector3());
4969
+ const quaternion = useRef(new THREE12.Quaternion());
4970
+ const linearVelocity = useRef(new THREE12.Vector3());
4971
+ const angularVelocity = useRef(new THREE12.Vector3());
4629
4972
  useEffect(() => {
4630
4973
  const model = mjModelRef.current;
4631
4974
  if (!model || status !== "ready") return;
@@ -4979,62 +5322,8 @@ function useVideoRecorder(options = {}) {
4979
5322
  }
4980
5323
  };
4981
5324
  }
4982
- function isTargetRef(target) {
4983
- return Boolean(target && typeof target === "object" && "current" in target);
4984
- }
4985
- function resolveCanvasTarget(target) {
4986
- const resolvedTarget = isTargetRef(target) ? target.current : target;
4987
- if (!resolvedTarget) {
4988
- throw new Error("No frame capture target is available.");
4989
- }
4990
- if (resolvedTarget instanceof HTMLCanvasElement) {
4991
- return resolvedTarget;
4992
- }
4993
- const canvas = resolvedTarget.querySelector("canvas");
4994
- if (!canvas) {
4995
- throw new Error("Frame capture target does not contain a canvas.");
4996
- }
4997
- return canvas;
4998
- }
4999
- function waitForNextAnimationFrame() {
5000
- return new Promise((resolve) => {
5001
- requestAnimationFrame(() => resolve());
5002
- });
5003
- }
5004
- async function captureFrame(options) {
5005
- const type = options.type ?? "image/png";
5006
- const canvas = resolveCanvasTarget(options.target);
5007
- if (options.waitForAnimationFrame ?? true) {
5008
- await waitForNextAnimationFrame();
5009
- }
5010
- return {
5011
- canvas,
5012
- dataUrl: canvas.toDataURL(type, options.quality),
5013
- type
5014
- };
5015
- }
5016
- async function captureFrameBlob(options) {
5017
- const type = options.type ?? "image/png";
5018
- const canvas = resolveCanvasTarget(options.target);
5019
- if (options.waitForAnimationFrame ?? true) {
5020
- await waitForNextAnimationFrame();
5021
- }
5022
- const blob = await new Promise((resolve, reject) => {
5023
- canvas.toBlob(
5024
- (nextBlob) => {
5025
- if (nextBlob) {
5026
- resolve(nextBlob);
5027
- } else {
5028
- reject(new Error("Canvas frame capture did not produce a Blob."));
5029
- }
5030
- },
5031
- type,
5032
- options.quality
5033
- );
5034
- });
5035
- return { canvas, blob, type };
5036
- }
5037
- function useFrameCapture(defaultOptions = {}) {
5325
+ function useCameraFrameCapture(defaultOptions = {}) {
5326
+ const mujoco = useMujoco();
5038
5327
  const [status, setStatus] = useState("idle");
5039
5328
  const [error, setError] = useState(null);
5040
5329
  const reset = useCallback(() => {
@@ -5043,40 +5332,49 @@ function useFrameCapture(defaultOptions = {}) {
5043
5332
  }, []);
5044
5333
  const capture = useCallback(
5045
5334
  async (options = {}) => {
5335
+ if (!mujoco.api) {
5336
+ throw new Error("MuJoCo scene is not ready for camera frame capture.");
5337
+ }
5046
5338
  setStatus("capturing");
5047
5339
  setError(null);
5048
5340
  try {
5049
- const result = await captureFrame({ ...defaultOptions, ...options });
5341
+ const result = await mujoco.api.captureCameraFrame({
5342
+ ...defaultOptions,
5343
+ ...options
5344
+ });
5050
5345
  setStatus("captured");
5051
5346
  return result;
5052
5347
  } catch (nextError) {
5053
- const error2 = nextError instanceof Error ? nextError : new Error("Unable to capture the current canvas frame.");
5348
+ const error2 = nextError instanceof Error ? nextError : new Error("Unable to capture the requested camera frame.");
5054
5349
  setError(error2);
5055
5350
  setStatus("error");
5056
5351
  throw error2;
5057
5352
  }
5058
5353
  },
5059
- [defaultOptions]
5354
+ [defaultOptions, mujoco.api]
5060
5355
  );
5061
5356
  const captureBlob = useCallback(
5062
5357
  async (options = {}) => {
5358
+ if (!mujoco.api) {
5359
+ throw new Error("MuJoCo scene is not ready for camera frame capture.");
5360
+ }
5063
5361
  setStatus("capturing");
5064
5362
  setError(null);
5065
5363
  try {
5066
- const result = await captureFrameBlob({
5364
+ const result = await mujoco.api.captureCameraFrameBlob({
5067
5365
  ...defaultOptions,
5068
5366
  ...options
5069
5367
  });
5070
5368
  setStatus("captured");
5071
5369
  return result;
5072
5370
  } catch (nextError) {
5073
- const error2 = nextError instanceof Error ? nextError : new Error("Unable to capture the current canvas frame.");
5371
+ const error2 = nextError instanceof Error ? nextError : new Error("Unable to capture the requested camera frame.");
5074
5372
  setError(error2);
5075
5373
  setStatus("error");
5076
5374
  throw error2;
5077
5375
  }
5078
5376
  },
5079
- [defaultOptions]
5377
+ [defaultOptions, mujoco.api]
5080
5378
  );
5081
5379
  return {
5082
5380
  status,
@@ -5087,6 +5385,42 @@ function useFrameCapture(defaultOptions = {}) {
5087
5385
  reset
5088
5386
  };
5089
5387
  }
5388
+ function useCameraSequenceRecorder() {
5389
+ const mujoco = useMujoco();
5390
+ const [status, setStatus] = useState("idle");
5391
+ const [error, setError] = useState(null);
5392
+ const reset = useCallback(() => {
5393
+ setStatus("idle");
5394
+ setError(null);
5395
+ }, []);
5396
+ const record = useCallback(
5397
+ async (options) => {
5398
+ if (!mujoco.api) {
5399
+ throw new Error("MuJoCo scene is not ready for camera sequence recording.");
5400
+ }
5401
+ setStatus("capturing");
5402
+ setError(null);
5403
+ try {
5404
+ const result = await mujoco.api.recordCameraSequence(options);
5405
+ setStatus("captured");
5406
+ return result;
5407
+ } catch (nextError) {
5408
+ const error2 = nextError instanceof Error ? nextError : new Error("Unable to record the requested camera sequence.");
5409
+ setError(error2);
5410
+ setStatus("error");
5411
+ throw error2;
5412
+ }
5413
+ },
5414
+ [mujoco.api]
5415
+ );
5416
+ return {
5417
+ status,
5418
+ error,
5419
+ isRecording: status === "capturing",
5420
+ record,
5421
+ reset
5422
+ };
5423
+ }
5090
5424
  function useCtrlNoise(config = {}) {
5091
5425
  const { mjModelRef } = useMujocoContext();
5092
5426
  const configRef = useRef(config);
@@ -5138,7 +5472,7 @@ function useSelectionHighlight(bodyId, options = {}) {
5138
5472
  }
5139
5473
  }
5140
5474
  prevRef.current = [];
5141
- const highlightColor = new THREE11.Color(color);
5475
+ const highlightColor = new THREE12.Color(color);
5142
5476
  for (const mesh of meshes) {
5143
5477
  const mat = mesh.material;
5144
5478
  if (mat.emissive) {
@@ -5165,15 +5499,15 @@ function useSelectionHighlight(bodyId, options = {}) {
5165
5499
  }
5166
5500
  function useCameraAnimation() {
5167
5501
  const { camera } = useThree();
5168
- const orbitTargetRef = useRef(new THREE11.Vector3(0, 0, 0));
5502
+ const orbitTargetRef = useRef(new THREE12.Vector3(0, 0, 0));
5169
5503
  const cameraAnimRef = useRef({
5170
5504
  active: false,
5171
- startPos: new THREE11.Vector3(),
5172
- endPos: new THREE11.Vector3(),
5173
- startRot: new THREE11.Quaternion(),
5174
- endRot: new THREE11.Quaternion(),
5175
- startTarget: new THREE11.Vector3(),
5176
- endTarget: new THREE11.Vector3(),
5505
+ startPos: new THREE12.Vector3(),
5506
+ endPos: new THREE12.Vector3(),
5507
+ startRot: new THREE12.Quaternion(),
5508
+ endRot: new THREE12.Quaternion(),
5509
+ startTarget: new THREE12.Vector3(),
5510
+ endTarget: new THREE12.Vector3(),
5177
5511
  startTime: 0,
5178
5512
  duration: 0,
5179
5513
  resolve: null
@@ -5239,6 +5573,18 @@ function useCameraAnimation() {
5239
5573
  * @license
5240
5574
  * SPDX-License-Identifier: Apache-2.0
5241
5575
  */
5576
+ /**
5577
+ * @license
5578
+ * SPDX-License-Identifier: Apache-2.0
5579
+ *
5580
+ * useFrameCapture — still-frame capture for canvas-backed MuJoCo/R3F scenes.
5581
+ */
5582
+ /**
5583
+ * @license
5584
+ * SPDX-License-Identifier: Apache-2.0
5585
+ *
5586
+ * Offscreen camera-frame capture for R3F/MuJoCo scenes.
5587
+ */
5242
5588
  /**
5243
5589
  * @license
5244
5590
  * SPDX-License-Identifier: Apache-2.0
@@ -5376,7 +5722,13 @@ function useCameraAnimation() {
5376
5722
  * @license
5377
5723
  * SPDX-License-Identifier: Apache-2.0
5378
5724
  *
5379
- * useFrameCapture still-frame capture for canvas-backed MuJoCo/R3F scenes.
5725
+ * React state wrapper around MuJoCo/R3F offscreen camera-frame capture.
5726
+ */
5727
+ /**
5728
+ * @license
5729
+ * SPDX-License-Identifier: Apache-2.0
5730
+ *
5731
+ * React state wrapper around fixed-camera simulation sequence recording.
5380
5732
  */
5381
5733
  /**
5382
5734
  * @license
@@ -5408,6 +5760,6 @@ function useCameraAnimation() {
5408
5760
  * useCameraAnimation — composable camera animation hook.
5409
5761
  */
5410
5762
 
5411
- export { Body, ContactListener, ContactMarkers, Debug, DragInteraction, FlexRenderer, IkGizmo, InstancedGeomRenderer, MujocoCanvas, MujocoPhysics, MujocoProvider, MujocoSimProvider, RobotActuators, RobotBodies, RobotGeoms, RobotJoints, RobotKeyframes, RobotResources, RobotSensors, RobotSites, SceneLights, TendonRenderer, TrajectoryPlayer, buildObservation, captureFrame, captureFrameBlob, createContiguousControlGroup, createController, createControllerHook, findActuatorByName, findBodyByName, findGeomByName, findJointByName, findKeyframeByName, findSensorByName, findSiteByName, findTendonByName, getActuatedJoints, getContact, getControlMap, getName, loadScene, registerRobotResources, resolveControlGroup, useActuators, useAfterPhysicsStep, useBeforePhysicsStep, useBodyMeshes, useBodyState, useCameraAnimation, useContactEvents, useContacts, useCtrl, useCtrlNoise, useFrameCapture, useGamepad, useGravityCompensation, useIkController, useJointState, useKeyboardTeleop, useMujoco, useMujocoWasm, useObservation, usePolicy, useSceneLights, useSelectionHighlight, useSensor, useSensors, useSitePosition, useTrajectoryPlayer, useTrajectoryRecorder, useVideoRecorder };
5763
+ export { Body, ContactListener, ContactMarkers, Debug, DragInteraction, FlexRenderer, IkGizmo, InstancedGeomRenderer, MujocoCanvas, MujocoPhysics, MujocoProvider, MujocoSimProvider, RobotActuators, RobotBodies, RobotGeoms, RobotJoints, RobotKeyframes, RobotResources, RobotSensors, RobotSites, SceneLights, TendonRenderer, TrajectoryPlayer, buildObservation, captureCameraFrame, captureCameraFrameBlob, captureFrame, captureFrameBlob, createContiguousControlGroup, createController, createControllerHook, findActuatorByName, findBodyByName, findGeomByName, findJointByName, findKeyframeByName, findSensorByName, findSiteByName, findTendonByName, getActuatedJoints, getContact, getControlMap, getName, loadScene, registerRobotResources, renderCameraFrameToCanvas, resolveControlGroup, useActuators, useAfterPhysicsStep, useBeforePhysicsStep, useBodyMeshes, useBodyState, useCameraAnimation, useCameraFrameCapture, useCameraSequenceRecorder, useContactEvents, useContacts, useCtrl, useCtrlNoise, useFrameCapture, useGamepad, useGravityCompensation, useIkController, useJointState, useKeyboardTeleop, useMujoco, useMujocoWasm, useObservation, usePolicy, useSceneLights, useSelectionHighlight, useSensor, useSensors, useSitePosition, useTrajectoryPlayer, useTrajectoryRecorder, useVideoRecorder };
5412
5764
  //# sourceMappingURL=index.js.map
5413
5765
  //# sourceMappingURL=index.js.map