mujoco-react 9.3.0 → 9.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +155 -15
- package/dist/{chunk-T3GVZJ4F.js → chunk-6MOK6ZWB.js} +501 -33
- package/dist/chunk-6MOK6ZWB.js.map +1 -0
- package/dist/index.d.ts +129 -7
- package/dist/index.js +846 -437
- package/dist/index.js.map +1 -1
- package/dist/spark.d.ts +27 -3
- package/dist/spark.js +156 -3
- package/dist/spark.js.map +1 -1
- package/dist/{types-oxbxOkAx.d.ts → types-BDB9QT6Z.d.ts} +42 -3
- package/dist/vite.js +8 -4
- package/dist/vite.js.map +1 -1
- package/package.json +1 -1
- package/src/components/ContactMarkers.tsx +8 -1
- package/src/components/Debug.tsx +154 -3
- package/src/components/DragInteraction.tsx +2 -0
- package/src/components/IkGizmo.tsx +5 -1
- package/src/components/SplatCollisionProxyPreview.tsx +350 -0
- package/src/components/VisualScenario.tsx +140 -41
- package/src/core/MujocoSimProvider.tsx +6 -6
- package/src/hooks/useMountedCameraSequenceRecorder.ts +54 -6
- package/src/index.ts +32 -0
- package/src/rendering/cameraFrameCapture.ts +259 -28
- package/src/rendering/cameraFrameSource.ts +382 -2
- package/src/spark.tsx +241 -1
- package/src/types.ts +45 -2
- package/src/vite.ts +9 -4
- package/dist/chunk-T3GVZJ4F.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { withContacts, getContact } from './chunk-
|
|
2
|
-
export { RobotActuators, RobotBodies, RobotCameras, RobotGeoms, RobotJoints, RobotKeyframes, RobotResources, RobotSensors, RobotSites, ScenarioLighting, SplatEnvironment, SplatEnvironmentReadinessStatus, VisualScenarioEffects, createPairedSplatEnvironment, createSparkSplatViewerUrl, createSplatEnvironmentUserData, getContact, getScenarioBackground, getScenarioCameraPosition, getSplatEnvironmentReadiness, registerRobotResources, useSplatEnvironment, useSplatSceneConfig, useVisualScenarioEffects, withSplatEnvironment } from './chunk-
|
|
1
|
+
import { withContacts, getContact, captureCameraFrame, captureCameraFrameBlob, createCameraFrameCaptureSession, CAPTURE_EXCLUDE_KEY } from './chunk-6MOK6ZWB.js';
|
|
2
|
+
export { CAPTURE_EXCLUDE_KEY, RobotActuators, RobotBodies, RobotCameras, RobotGeoms, RobotJoints, RobotKeyframes, RobotResources, RobotSensors, RobotSites, ScenarioLighting, SplatEnvironment, SplatEnvironmentReadinessStatus, VisualScenarioEffects, captureCameraFrame, captureCameraFrameBlob, createCameraFrameCaptureSession, createPairedSplatEnvironment, createSparkSplatViewerUrl, createSplatEnvironmentUserData, createSplatSceneConfig, createVisualScenarioExecutionContext, getContact, getScenarioBackground, getScenarioCameraPosition, getSplatEnvironmentReadiness, registerRobotResources, renderCameraFrameToCanvas, useSplatEnvironment, useSplatSceneConfig, useVisualScenarioEffects, useVisualScenarioExecutionContext, withSplatEnvironment } from './chunk-6MOK6ZWB.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
|
|
8
|
+
import * as THREE11 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
|
|
108
|
+
var CapsuleGeometry = class extends THREE11.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
|
|
114
|
+
const path = new THREE11.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
|
|
117
|
+
const latheGeometry = new THREE11.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,27 +122,27 @@ var CapsuleGeometry = class extends THREE12.BufferGeometry {
|
|
|
122
122
|
self.setAttribute("uv", latheGeometry.getAttribute("uv"));
|
|
123
123
|
}
|
|
124
124
|
};
|
|
125
|
-
var Reflector = class extends
|
|
125
|
+
var Reflector = class extends THREE11.Mesh {
|
|
126
126
|
isReflector = true;
|
|
127
127
|
camera;
|
|
128
|
-
reflectorPlane = new
|
|
129
|
-
normal = new
|
|
130
|
-
reflectorWorldPosition = new
|
|
131
|
-
cameraWorldPosition = new
|
|
132
|
-
rotationMatrix = new
|
|
133
|
-
lookAtPosition = new
|
|
134
|
-
clipPlane = new
|
|
135
|
-
view = new
|
|
136
|
-
target = new
|
|
137
|
-
q = new
|
|
138
|
-
textureMatrix = new
|
|
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
139
|
virtualCamera;
|
|
140
140
|
renderTarget;
|
|
141
141
|
constructor(geometry, options = {}) {
|
|
142
142
|
super(geometry);
|
|
143
143
|
this.type = "Reflector";
|
|
144
|
-
this.camera = new
|
|
145
|
-
const color = options.color !== void 0 ? new
|
|
144
|
+
this.camera = new THREE11.PerspectiveCamera();
|
|
145
|
+
const color = options.color !== void 0 ? new THREE11.Color(options.color) : new THREE11.Color(8355711);
|
|
146
146
|
const textureWidth = options.textureWidth || 512;
|
|
147
147
|
const textureHeight = options.textureHeight || 512;
|
|
148
148
|
const clipBias = options.clipBias || 0;
|
|
@@ -150,11 +150,11 @@ var Reflector = class extends THREE12.Mesh {
|
|
|
150
150
|
const blendTexture = options.texture || void 0;
|
|
151
151
|
const mixStrength = options.mixStrength !== void 0 ? options.mixStrength : 0.25;
|
|
152
152
|
this.virtualCamera = this.camera;
|
|
153
|
-
this.renderTarget = new
|
|
153
|
+
this.renderTarget = new THREE11.WebGLRenderTarget(textureWidth, textureHeight, {
|
|
154
154
|
samples: multisample,
|
|
155
|
-
type:
|
|
155
|
+
type: THREE11.HalfFloatType
|
|
156
156
|
});
|
|
157
|
-
this.material = new
|
|
157
|
+
this.material = new THREE11.MeshPhysicalMaterial({
|
|
158
158
|
map: blendTexture,
|
|
159
159
|
color,
|
|
160
160
|
roughness: 0.5,
|
|
@@ -280,7 +280,7 @@ var GeomBuilder = class {
|
|
|
280
280
|
const pos = mjModel.geom_pos.subarray(g * 3, g * 3 + 3);
|
|
281
281
|
const quat = mjModel.geom_quat.subarray(g * 4, g * 4 + 4);
|
|
282
282
|
const matId = mjModel.geom_matid[g];
|
|
283
|
-
const color = new
|
|
283
|
+
const color = new THREE11.Color(16777215);
|
|
284
284
|
let opacity = 1;
|
|
285
285
|
if (matId >= 0) {
|
|
286
286
|
const rgba = mjModel.mat_rgba.subarray(matId * 4, matId * 4 + 4);
|
|
@@ -295,16 +295,16 @@ var GeomBuilder = class {
|
|
|
295
295
|
let geo = null;
|
|
296
296
|
const getVal = (v) => v?.value ?? v;
|
|
297
297
|
if (type === getVal(MG.mjGEOM_PLANE)) {
|
|
298
|
-
geo = new
|
|
298
|
+
geo = new THREE11.PlaneGeometry(size[0] * 2 || 5, size[1] * 2 || 5);
|
|
299
299
|
} else if (type === getVal(MG.mjGEOM_SPHERE)) {
|
|
300
|
-
geo = new
|
|
300
|
+
geo = new THREE11.SphereGeometry(size[0], 24, 24);
|
|
301
301
|
} else if (type === getVal(MG.mjGEOM_CAPSULE)) {
|
|
302
302
|
geo = new CapsuleGeometry(size[0], size[1] * 2, 24, 12);
|
|
303
303
|
geo.rotateX(Math.PI / 2);
|
|
304
304
|
} else if (type === getVal(MG.mjGEOM_BOX)) {
|
|
305
|
-
geo = new
|
|
305
|
+
geo = new THREE11.BoxGeometry(size[0] * 2, size[1] * 2, size[2] * 2);
|
|
306
306
|
} else if (type === getVal(MG.mjGEOM_CYLINDER)) {
|
|
307
|
-
geo = new
|
|
307
|
+
geo = new THREE11.CylinderGeometry(size[0], size[0], size[1] * 2, 24);
|
|
308
308
|
geo.rotateX(Math.PI / 2);
|
|
309
309
|
} else if (type === getVal(MG.mjGEOM_MESH)) {
|
|
310
310
|
const mId = mjModel.geom_dataid[g];
|
|
@@ -312,8 +312,8 @@ var GeomBuilder = class {
|
|
|
312
312
|
const vNum = mjModel.mesh_vertnum[mId];
|
|
313
313
|
const fAdr = mjModel.mesh_faceadr[mId];
|
|
314
314
|
const fNum = mjModel.mesh_facenum[mId];
|
|
315
|
-
geo = new
|
|
316
|
-
geo.setAttribute("position", new
|
|
315
|
+
geo = new THREE11.BufferGeometry();
|
|
316
|
+
geo.setAttribute("position", new THREE11.Float32BufferAttribute(mjModel.mesh_vert.subarray(vAdr * 3, (vAdr + vNum) * 3), 3));
|
|
317
317
|
geo.setIndex(Array.from(mjModel.mesh_face.subarray(fAdr * 3, (fAdr + fNum) * 3)));
|
|
318
318
|
geo.computeVertexNormals();
|
|
319
319
|
}
|
|
@@ -328,7 +328,7 @@ var GeomBuilder = class {
|
|
|
328
328
|
mixStrength: 0.25
|
|
329
329
|
});
|
|
330
330
|
} else {
|
|
331
|
-
mesh = new
|
|
331
|
+
mesh = new THREE11.Mesh(geo, new THREE11.MeshStandardMaterial({
|
|
332
332
|
color,
|
|
333
333
|
transparent: opacity < 1,
|
|
334
334
|
opacity,
|
|
@@ -1142,7 +1142,7 @@ function SceneRenderer(props) {
|
|
|
1142
1142
|
}
|
|
1143
1143
|
const refs = [];
|
|
1144
1144
|
for (let i = 0; i < model.nbody; i++) {
|
|
1145
|
-
const bodyGroup = new
|
|
1145
|
+
const bodyGroup = new THREE11.Group();
|
|
1146
1146
|
bodyGroup.userData.bodyID = i;
|
|
1147
1147
|
const bodyName = getName(model, model.name_bodyadr[i]);
|
|
1148
1148
|
if (!hiddenBodiesRef.current.has(bodyName)) {
|
|
@@ -1171,9 +1171,9 @@ function SceneRenderer(props) {
|
|
|
1171
1171
|
const alpha = interpolation.alpha;
|
|
1172
1172
|
const i3 = i * 3;
|
|
1173
1173
|
ref.position.set(
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
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)
|
|
1177
1177
|
);
|
|
1178
1178
|
const i4 = i * 4;
|
|
1179
1179
|
_previousQuat.set(
|
|
@@ -1228,8 +1228,8 @@ function SceneRenderer(props) {
|
|
|
1228
1228
|
}
|
|
1229
1229
|
);
|
|
1230
1230
|
}
|
|
1231
|
-
var _previousQuat = new
|
|
1232
|
-
var _currentQuat = new
|
|
1231
|
+
var _previousQuat = new THREE11.Quaternion();
|
|
1232
|
+
var _currentQuat = new THREE11.Quaternion();
|
|
1233
1233
|
function isTargetRef(target) {
|
|
1234
1234
|
return Boolean(target && typeof target === "object" && "current" in target);
|
|
1235
1235
|
}
|
|
@@ -1338,251 +1338,26 @@ function useFrameCapture(defaultOptions = {}) {
|
|
|
1338
1338
|
reset
|
|
1339
1339
|
};
|
|
1340
1340
|
}
|
|
1341
|
-
function toVector3(value, fallback) {
|
|
1342
|
-
if (!value) return fallback.clone();
|
|
1343
|
-
return value instanceof THREE12.Vector3 ? value.clone() : new THREE12.Vector3(value[0], value[1], value[2]);
|
|
1344
|
-
}
|
|
1345
|
-
function applyCameraPose(camera, options, fallbackCamera) {
|
|
1346
|
-
camera.position.copy(toVector3(options.position, fallbackCamera.position));
|
|
1347
|
-
camera.up.copy(toVector3(options.up, fallbackCamera.up));
|
|
1348
|
-
if (options.quaternion) {
|
|
1349
|
-
if (options.quaternion instanceof THREE12.Quaternion) {
|
|
1350
|
-
camera.quaternion.copy(options.quaternion);
|
|
1351
|
-
} else {
|
|
1352
|
-
camera.quaternion.set(
|
|
1353
|
-
options.quaternion[0],
|
|
1354
|
-
options.quaternion[1],
|
|
1355
|
-
options.quaternion[2],
|
|
1356
|
-
options.quaternion[3]
|
|
1357
|
-
);
|
|
1358
|
-
}
|
|
1359
|
-
} else if (options.lookAt) {
|
|
1360
|
-
camera.lookAt(toVector3(options.lookAt, new THREE12.Vector3()));
|
|
1361
|
-
} else {
|
|
1362
|
-
camera.quaternion.copy(fallbackCamera.quaternion);
|
|
1363
|
-
}
|
|
1364
|
-
camera.updateMatrixWorld();
|
|
1365
|
-
}
|
|
1366
|
-
function createCaptureCamera(options, fallbackCamera, width, height) {
|
|
1367
|
-
const camera = options.camera ? options.camera.clone() : fallbackCamera instanceof THREE12.PerspectiveCamera ? fallbackCamera.clone() : new THREE12.PerspectiveCamera(45, width / height, 0.01, 100);
|
|
1368
|
-
if (camera instanceof THREE12.PerspectiveCamera) {
|
|
1369
|
-
camera.aspect = width / height;
|
|
1370
|
-
camera.fov = options.fov ?? camera.fov;
|
|
1371
|
-
camera.near = options.near ?? camera.near;
|
|
1372
|
-
camera.far = options.far ?? camera.far;
|
|
1373
|
-
camera.updateProjectionMatrix();
|
|
1374
|
-
}
|
|
1375
|
-
applyCameraPose(camera, options, fallbackCamera);
|
|
1376
|
-
return camera;
|
|
1377
|
-
}
|
|
1378
|
-
function getCaptureDimensions(renderer, options) {
|
|
1379
|
-
const width = Math.max(
|
|
1380
|
-
1,
|
|
1381
|
-
Math.floor(options.width ?? renderer.domElement.width)
|
|
1382
|
-
);
|
|
1383
|
-
const height = Math.max(
|
|
1384
|
-
1,
|
|
1385
|
-
Math.floor(options.height ?? renderer.domElement.height)
|
|
1386
|
-
);
|
|
1387
|
-
return { width, height };
|
|
1388
|
-
}
|
|
1389
|
-
function prepareCaptureCamera(camera, options, fallbackCamera, width, height) {
|
|
1390
|
-
if (options.camera) {
|
|
1391
|
-
camera.copy(options.camera);
|
|
1392
|
-
}
|
|
1393
|
-
if (camera instanceof THREE12.PerspectiveCamera) {
|
|
1394
|
-
camera.aspect = width / height;
|
|
1395
|
-
camera.fov = options.fov ?? camera.fov;
|
|
1396
|
-
camera.near = options.near ?? camera.near;
|
|
1397
|
-
camera.far = options.far ?? camera.far;
|
|
1398
|
-
camera.updateProjectionMatrix();
|
|
1399
|
-
}
|
|
1400
|
-
applyCameraPose(camera, options, fallbackCamera);
|
|
1401
|
-
}
|
|
1402
|
-
function readRenderTargetToCanvas(renderer, target, canvas, context, pixels, imageData, width, height) {
|
|
1403
|
-
renderer.readRenderTargetPixels(target, 0, 0, width, height, pixels);
|
|
1404
|
-
const rowBytes = width * 4;
|
|
1405
|
-
for (let y = 0; y < height; y += 1) {
|
|
1406
|
-
const sourceStart = (height - y - 1) * rowBytes;
|
|
1407
|
-
const targetStart = y * rowBytes;
|
|
1408
|
-
imageData.data.set(
|
|
1409
|
-
pixels.subarray(sourceStart, sourceStart + rowBytes),
|
|
1410
|
-
targetStart
|
|
1411
|
-
);
|
|
1412
|
-
}
|
|
1413
|
-
context.putImageData(imageData, 0, 0);
|
|
1414
|
-
return canvas;
|
|
1415
|
-
}
|
|
1416
|
-
function getCameraFrameCaptureSource(options) {
|
|
1417
|
-
if (options.source) return options.source;
|
|
1418
|
-
if (options.cameraName) {
|
|
1419
|
-
return { kind: "mujoco-camera", cameraName: options.cameraName };
|
|
1420
|
-
}
|
|
1421
|
-
if (options.siteName) {
|
|
1422
|
-
return { kind: "mujoco-site", siteName: options.siteName };
|
|
1423
|
-
}
|
|
1424
|
-
if (options.bodyName) {
|
|
1425
|
-
return { kind: "mujoco-body", bodyName: options.bodyName };
|
|
1426
|
-
}
|
|
1427
|
-
if (options.camera) return { kind: "custom-camera" };
|
|
1428
|
-
if (options.position || options.lookAt || options.quaternion) {
|
|
1429
|
-
return { kind: "explicit-pose" };
|
|
1430
|
-
}
|
|
1431
|
-
return { kind: "fallback-camera" };
|
|
1432
|
-
}
|
|
1433
|
-
function createCameraFrameCaptureSession(renderer, scene, fallbackCamera, options = {}) {
|
|
1434
|
-
const { width, height } = getCaptureDimensions(renderer, options);
|
|
1435
|
-
const camera = createCaptureCamera(options, fallbackCamera, width, height);
|
|
1436
|
-
const target = new THREE12.WebGLRenderTarget(width, height, {
|
|
1437
|
-
format: THREE12.RGBAFormat,
|
|
1438
|
-
type: THREE12.UnsignedByteType
|
|
1439
|
-
});
|
|
1440
|
-
const canvas = document.createElement("canvas");
|
|
1441
|
-
canvas.width = width;
|
|
1442
|
-
canvas.height = height;
|
|
1443
|
-
const context = canvas.getContext("2d");
|
|
1444
|
-
if (!context) {
|
|
1445
|
-
target.dispose();
|
|
1446
|
-
throw new Error("Unable to create a 2D canvas for camera frame capture.");
|
|
1447
|
-
}
|
|
1448
|
-
const drawContext = context;
|
|
1449
|
-
const pixels = new Uint8Array(width * height * 4);
|
|
1450
|
-
const imageData = drawContext.createImageData(width, height);
|
|
1451
|
-
function capture(nextOptions = {}) {
|
|
1452
|
-
const captureOptions = { ...options, ...nextOptions };
|
|
1453
|
-
const nextDimensions = getCaptureDimensions(renderer, captureOptions);
|
|
1454
|
-
if (nextDimensions.width !== width || nextDimensions.height !== height) {
|
|
1455
|
-
throw new Error(
|
|
1456
|
-
"Camera frame capture sessions require stable width and height."
|
|
1457
|
-
);
|
|
1458
|
-
}
|
|
1459
|
-
prepareCaptureCamera(
|
|
1460
|
-
camera,
|
|
1461
|
-
captureOptions,
|
|
1462
|
-
fallbackCamera,
|
|
1463
|
-
width,
|
|
1464
|
-
height
|
|
1465
|
-
);
|
|
1466
|
-
const previousTarget = renderer.getRenderTarget();
|
|
1467
|
-
const previousXrEnabled = renderer.xr.enabled;
|
|
1468
|
-
scene.updateMatrixWorld(true);
|
|
1469
|
-
try {
|
|
1470
|
-
renderer.xr.enabled = false;
|
|
1471
|
-
renderer.setRenderTarget(target);
|
|
1472
|
-
renderer.clear();
|
|
1473
|
-
renderer.render(scene, camera);
|
|
1474
|
-
readRenderTargetToCanvas(
|
|
1475
|
-
renderer,
|
|
1476
|
-
target,
|
|
1477
|
-
canvas,
|
|
1478
|
-
drawContext,
|
|
1479
|
-
pixels,
|
|
1480
|
-
imageData,
|
|
1481
|
-
width,
|
|
1482
|
-
height
|
|
1483
|
-
);
|
|
1484
|
-
return {
|
|
1485
|
-
canvas,
|
|
1486
|
-
camera,
|
|
1487
|
-
width,
|
|
1488
|
-
height,
|
|
1489
|
-
source: getCameraFrameCaptureSource(captureOptions)
|
|
1490
|
-
};
|
|
1491
|
-
} finally {
|
|
1492
|
-
renderer.setRenderTarget(previousTarget);
|
|
1493
|
-
renderer.xr.enabled = previousXrEnabled;
|
|
1494
|
-
}
|
|
1495
|
-
}
|
|
1496
|
-
return {
|
|
1497
|
-
width,
|
|
1498
|
-
height,
|
|
1499
|
-
capture,
|
|
1500
|
-
captureDataUrl(nextOptions = {}) {
|
|
1501
|
-
const type = nextOptions.type ?? options.type ?? "image/png";
|
|
1502
|
-
const result = capture(nextOptions);
|
|
1503
|
-
return {
|
|
1504
|
-
...result,
|
|
1505
|
-
dataUrl: result.canvas.toDataURL(
|
|
1506
|
-
type,
|
|
1507
|
-
nextOptions.quality ?? options.quality
|
|
1508
|
-
),
|
|
1509
|
-
type
|
|
1510
|
-
};
|
|
1511
|
-
},
|
|
1512
|
-
async captureBlob(nextOptions = {}) {
|
|
1513
|
-
const type = nextOptions.type ?? options.type ?? "image/png";
|
|
1514
|
-
const result = capture(nextOptions);
|
|
1515
|
-
const blob = await new Promise((resolve, reject) => {
|
|
1516
|
-
result.canvas.toBlob(
|
|
1517
|
-
(nextBlob) => {
|
|
1518
|
-
if (nextBlob) resolve(nextBlob);
|
|
1519
|
-
else reject(new Error("Camera frame capture did not produce a Blob."));
|
|
1520
|
-
},
|
|
1521
|
-
type,
|
|
1522
|
-
nextOptions.quality ?? options.quality
|
|
1523
|
-
);
|
|
1524
|
-
});
|
|
1525
|
-
return { ...result, blob, type };
|
|
1526
|
-
},
|
|
1527
|
-
dispose() {
|
|
1528
|
-
target.dispose();
|
|
1529
|
-
}
|
|
1530
|
-
};
|
|
1531
|
-
}
|
|
1532
|
-
function renderCameraFrameToCanvas(renderer, scene, fallbackCamera, options = {}) {
|
|
1533
|
-
const session = createCameraFrameCaptureSession(
|
|
1534
|
-
renderer,
|
|
1535
|
-
scene,
|
|
1536
|
-
fallbackCamera,
|
|
1537
|
-
options
|
|
1538
|
-
);
|
|
1539
|
-
try {
|
|
1540
|
-
return session.capture();
|
|
1541
|
-
} finally {
|
|
1542
|
-
session.dispose();
|
|
1543
|
-
}
|
|
1544
|
-
}
|
|
1545
|
-
async function captureCameraFrame(renderer, scene, fallbackCamera, options = {}) {
|
|
1546
|
-
const type = options.type ?? "image/png";
|
|
1547
|
-
const result = renderCameraFrameToCanvas(
|
|
1548
|
-
renderer,
|
|
1549
|
-
scene,
|
|
1550
|
-
fallbackCamera,
|
|
1551
|
-
options
|
|
1552
|
-
);
|
|
1553
|
-
return {
|
|
1554
|
-
...result,
|
|
1555
|
-
dataUrl: result.canvas.toDataURL(type, options.quality),
|
|
1556
|
-
type
|
|
1557
|
-
};
|
|
1558
|
-
}
|
|
1559
|
-
async function captureCameraFrameBlob(renderer, scene, fallbackCamera, options = {}) {
|
|
1560
|
-
const type = options.type ?? "image/png";
|
|
1561
|
-
const result = renderCameraFrameToCanvas(
|
|
1562
|
-
renderer,
|
|
1563
|
-
scene,
|
|
1564
|
-
fallbackCamera,
|
|
1565
|
-
options
|
|
1566
|
-
);
|
|
1567
|
-
const blob = await new Promise((resolve, reject) => {
|
|
1568
|
-
result.canvas.toBlob(
|
|
1569
|
-
(nextBlob) => {
|
|
1570
|
-
if (nextBlob) resolve(nextBlob);
|
|
1571
|
-
else reject(new Error("Camera frame capture did not produce a Blob."));
|
|
1572
|
-
},
|
|
1573
|
-
type,
|
|
1574
|
-
options.quality
|
|
1575
|
-
);
|
|
1576
|
-
});
|
|
1577
|
-
return { ...result, blob, type };
|
|
1578
|
-
}
|
|
1579
1341
|
|
|
1580
1342
|
// src/rendering/cameraFrameSource.ts
|
|
1343
|
+
var MountedCameraFrameSourceSuggestionMatch = {
|
|
1344
|
+
Direct: "direct",
|
|
1345
|
+
Alias: "alias",
|
|
1346
|
+
Normalized: "normalized",
|
|
1347
|
+
Prefix: "prefix",
|
|
1348
|
+
Suffix: "suffix",
|
|
1349
|
+
Contains: "contains"
|
|
1350
|
+
};
|
|
1581
1351
|
var MountedCameraFrameSequenceReadinessStatus = {
|
|
1582
1352
|
Ready: "ready",
|
|
1583
1353
|
Partial: "partial",
|
|
1584
1354
|
Missing: "missing"
|
|
1585
1355
|
};
|
|
1356
|
+
var MountedCameraFrameSequenceManifestStatus = {
|
|
1357
|
+
Complete: "complete",
|
|
1358
|
+
Partial: "partial",
|
|
1359
|
+
Missing: "missing"
|
|
1360
|
+
};
|
|
1586
1361
|
function getResourceName(resource) {
|
|
1587
1362
|
if (!resource) return null;
|
|
1588
1363
|
return typeof resource === "string" ? resource : resource.name ?? null;
|
|
@@ -1592,6 +1367,9 @@ function createNameSet(resources) {
|
|
|
1592
1367
|
(resources ?? []).map((resource) => getResourceName(resource)).filter((name) => Boolean(name))
|
|
1593
1368
|
);
|
|
1594
1369
|
}
|
|
1370
|
+
function createResourceNames(resources) {
|
|
1371
|
+
return (resources ?? []).map((resource) => getResourceName(resource)).filter((name) => Boolean(name));
|
|
1372
|
+
}
|
|
1595
1373
|
function normalizeAliasCandidates(value) {
|
|
1596
1374
|
if (!value) return [];
|
|
1597
1375
|
return Array.isArray(value) ? value : [value];
|
|
@@ -1599,6 +1377,24 @@ function normalizeAliasCandidates(value) {
|
|
|
1599
1377
|
function countMountedSelectors(selector) {
|
|
1600
1378
|
return Number(Boolean(selector.cameraName)) + Number(Boolean(selector.siteName)) + Number(Boolean(selector.bodyName));
|
|
1601
1379
|
}
|
|
1380
|
+
function normalizeCameraSourceName(value) {
|
|
1381
|
+
return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
1382
|
+
}
|
|
1383
|
+
function createCameraSourceKeyVariants(key) {
|
|
1384
|
+
const candidates = [
|
|
1385
|
+
key,
|
|
1386
|
+
key.startsWith("observation.images.") ? key.slice("observation.images.".length) : "",
|
|
1387
|
+
key.includes(".") ? key.split(".").at(-1) ?? "" : "",
|
|
1388
|
+
key.includes("/") ? key.split("/").at(-1) ?? "" : ""
|
|
1389
|
+
];
|
|
1390
|
+
return candidates.map((candidate) => candidate.trim()).filter((candidate, index, items) => candidate && items.indexOf(candidate) === index);
|
|
1391
|
+
}
|
|
1392
|
+
function getSelectorKey(selector) {
|
|
1393
|
+
if (selector.cameraName) return `camera:${selector.cameraName}`;
|
|
1394
|
+
if (selector.siteName) return `site:${selector.siteName}`;
|
|
1395
|
+
if (selector.bodyName) return `body:${selector.bodyName}`;
|
|
1396
|
+
return null;
|
|
1397
|
+
}
|
|
1602
1398
|
function getMountedCameraFrameCaptureSource(selector) {
|
|
1603
1399
|
if (countMountedSelectors(selector) !== 1) return null;
|
|
1604
1400
|
if (selector.cameraName) {
|
|
@@ -1623,10 +1419,119 @@ function getCameraFrameCaptureSourceTarget(source) {
|
|
|
1623
1419
|
if (source.kind === "explicit-pose") return "explicit pose";
|
|
1624
1420
|
return "fallback camera";
|
|
1625
1421
|
}
|
|
1422
|
+
function createMountedCameraFrameSourceSuggestion(key, selector, resourceName, match) {
|
|
1423
|
+
const source = getMountedCameraFrameCaptureSource(selector);
|
|
1424
|
+
if (!source) return null;
|
|
1425
|
+
return {
|
|
1426
|
+
key,
|
|
1427
|
+
selector,
|
|
1428
|
+
source,
|
|
1429
|
+
resourceName,
|
|
1430
|
+
resourceKind: source.kind,
|
|
1431
|
+
match
|
|
1432
|
+
};
|
|
1433
|
+
}
|
|
1434
|
+
function addMountedCameraFrameSourceSuggestion(suggestions, seen, suggestion) {
|
|
1435
|
+
if (!suggestion) return;
|
|
1436
|
+
const selectorKey = getSelectorKey(suggestion.selector);
|
|
1437
|
+
if (!selectorKey || seen.has(selectorKey)) return;
|
|
1438
|
+
seen.add(selectorKey);
|
|
1439
|
+
suggestions.push(suggestion);
|
|
1440
|
+
}
|
|
1441
|
+
function getCameraFrameResourceMatch(key, resourceName) {
|
|
1442
|
+
if (resourceName === key) return MountedCameraFrameSourceSuggestionMatch.Direct;
|
|
1443
|
+
const normalizedResource = normalizeCameraSourceName(resourceName);
|
|
1444
|
+
if (!normalizedResource) return null;
|
|
1445
|
+
for (const variant of createCameraSourceKeyVariants(key)) {
|
|
1446
|
+
if (resourceName === variant) return MountedCameraFrameSourceSuggestionMatch.Direct;
|
|
1447
|
+
const normalizedKey = normalizeCameraSourceName(variant);
|
|
1448
|
+
if (!normalizedKey) continue;
|
|
1449
|
+
if (normalizedResource === normalizedKey) {
|
|
1450
|
+
return MountedCameraFrameSourceSuggestionMatch.Normalized;
|
|
1451
|
+
}
|
|
1452
|
+
if (normalizedResource.startsWith(`${normalizedKey}_`)) {
|
|
1453
|
+
return MountedCameraFrameSourceSuggestionMatch.Prefix;
|
|
1454
|
+
}
|
|
1455
|
+
if (normalizedResource.endsWith(`_${normalizedKey}`)) {
|
|
1456
|
+
return MountedCameraFrameSourceSuggestionMatch.Suffix;
|
|
1457
|
+
}
|
|
1458
|
+
if (normalizedResource.includes(`_${normalizedKey}_`)) {
|
|
1459
|
+
return MountedCameraFrameSourceSuggestionMatch.Contains;
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
return null;
|
|
1463
|
+
}
|
|
1626
1464
|
function isSelectorMounted(selector, cameraNames, siteNames, bodyNames) {
|
|
1627
1465
|
if (countMountedSelectors(selector) !== 1) return false;
|
|
1628
1466
|
return (selector.cameraName ? cameraNames.has(selector.cameraName) : false) || (selector.siteName ? siteNames.has(selector.siteName) : false) || (selector.bodyName ? bodyNames.has(selector.bodyName) : false);
|
|
1629
1467
|
}
|
|
1468
|
+
function createMountedCameraFrameSourceSuggestions(key, options) {
|
|
1469
|
+
const cameraNames = createNameSet(options.cameras);
|
|
1470
|
+
const siteNames = createNameSet(options.sites);
|
|
1471
|
+
const bodyNames = createNameSet(options.bodies);
|
|
1472
|
+
const suggestions = [];
|
|
1473
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1474
|
+
for (const selector of normalizeAliasCandidates(options.aliases?.[key])) {
|
|
1475
|
+
if (!isSelectorMounted(selector, cameraNames, siteNames, bodyNames)) {
|
|
1476
|
+
continue;
|
|
1477
|
+
}
|
|
1478
|
+
const source = getMountedCameraFrameCaptureSource(selector);
|
|
1479
|
+
if (!source) continue;
|
|
1480
|
+
addMountedCameraFrameSourceSuggestion(
|
|
1481
|
+
suggestions,
|
|
1482
|
+
seen,
|
|
1483
|
+
createMountedCameraFrameSourceSuggestion(
|
|
1484
|
+
key,
|
|
1485
|
+
selector,
|
|
1486
|
+
getCameraFrameCaptureSourceTarget(source),
|
|
1487
|
+
MountedCameraFrameSourceSuggestionMatch.Alias
|
|
1488
|
+
)
|
|
1489
|
+
);
|
|
1490
|
+
}
|
|
1491
|
+
for (const cameraName of createResourceNames(options.cameras)) {
|
|
1492
|
+
const match = getCameraFrameResourceMatch(key, cameraName);
|
|
1493
|
+
if (!match) continue;
|
|
1494
|
+
addMountedCameraFrameSourceSuggestion(
|
|
1495
|
+
suggestions,
|
|
1496
|
+
seen,
|
|
1497
|
+
createMountedCameraFrameSourceSuggestion(
|
|
1498
|
+
key,
|
|
1499
|
+
{ cameraName },
|
|
1500
|
+
cameraName,
|
|
1501
|
+
match
|
|
1502
|
+
)
|
|
1503
|
+
);
|
|
1504
|
+
}
|
|
1505
|
+
for (const siteName of createResourceNames(options.sites)) {
|
|
1506
|
+
const match = getCameraFrameResourceMatch(key, siteName);
|
|
1507
|
+
if (!match) continue;
|
|
1508
|
+
addMountedCameraFrameSourceSuggestion(
|
|
1509
|
+
suggestions,
|
|
1510
|
+
seen,
|
|
1511
|
+
createMountedCameraFrameSourceSuggestion(
|
|
1512
|
+
key,
|
|
1513
|
+
{ siteName },
|
|
1514
|
+
siteName,
|
|
1515
|
+
match
|
|
1516
|
+
)
|
|
1517
|
+
);
|
|
1518
|
+
}
|
|
1519
|
+
for (const bodyName of createResourceNames(options.bodies)) {
|
|
1520
|
+
const match = getCameraFrameResourceMatch(key, bodyName);
|
|
1521
|
+
if (!match) continue;
|
|
1522
|
+
addMountedCameraFrameSourceSuggestion(
|
|
1523
|
+
suggestions,
|
|
1524
|
+
seen,
|
|
1525
|
+
createMountedCameraFrameSourceSuggestion(
|
|
1526
|
+
key,
|
|
1527
|
+
{ bodyName },
|
|
1528
|
+
bodyName,
|
|
1529
|
+
match
|
|
1530
|
+
)
|
|
1531
|
+
);
|
|
1532
|
+
}
|
|
1533
|
+
return suggestions;
|
|
1534
|
+
}
|
|
1630
1535
|
function resolveMountedCameraFrameSource(key, options) {
|
|
1631
1536
|
const cameraNames = createNameSet(options.cameras);
|
|
1632
1537
|
const siteNames = createNameSet(options.sites);
|
|
@@ -1637,8 +1542,7 @@ function resolveMountedCameraFrameSource(key, options) {
|
|
|
1637
1542
|
{ bodyName: key }
|
|
1638
1543
|
];
|
|
1639
1544
|
const aliasCandidates = normalizeAliasCandidates(options.aliases?.[key]);
|
|
1640
|
-
const
|
|
1641
|
-
for (const selector of candidates) {
|
|
1545
|
+
for (const selector of aliasCandidates) {
|
|
1642
1546
|
if (!isSelectorMounted(selector, cameraNames, siteNames, bodyNames)) {
|
|
1643
1547
|
continue;
|
|
1644
1548
|
}
|
|
@@ -1646,6 +1550,14 @@ function resolveMountedCameraFrameSource(key, options) {
|
|
|
1646
1550
|
if (!source) continue;
|
|
1647
1551
|
return { key, selector, source };
|
|
1648
1552
|
}
|
|
1553
|
+
const [suggestion] = createMountedCameraFrameSourceSuggestions(key, options);
|
|
1554
|
+
if (suggestion) {
|
|
1555
|
+
return {
|
|
1556
|
+
key,
|
|
1557
|
+
selector: suggestion.selector,
|
|
1558
|
+
source: suggestion.source
|
|
1559
|
+
};
|
|
1560
|
+
}
|
|
1649
1561
|
if (options.allowAliasFallback) {
|
|
1650
1562
|
for (const selector of aliasCandidates) {
|
|
1651
1563
|
const source = getMountedCameraFrameCaptureSource(selector);
|
|
@@ -1653,6 +1565,14 @@ function resolveMountedCameraFrameSource(key, options) {
|
|
|
1653
1565
|
return { key, selector, source };
|
|
1654
1566
|
}
|
|
1655
1567
|
}
|
|
1568
|
+
for (const selector of directCandidates) {
|
|
1569
|
+
if (!isSelectorMounted(selector, cameraNames, siteNames, bodyNames)) {
|
|
1570
|
+
continue;
|
|
1571
|
+
}
|
|
1572
|
+
const source = getMountedCameraFrameCaptureSource(selector);
|
|
1573
|
+
if (!source) continue;
|
|
1574
|
+
return { key, selector, source };
|
|
1575
|
+
}
|
|
1656
1576
|
return null;
|
|
1657
1577
|
}
|
|
1658
1578
|
function createMountedCameraFrameSequencePlan(cameraKeys, options) {
|
|
@@ -1716,6 +1636,77 @@ function createMountedCameraFrameSequenceReadiness(plan) {
|
|
|
1716
1636
|
message: ready ? `All ${plan.cameraKeys.length} requested camera stream${plan.cameraKeys.length === 1 ? "" : "s"} resolve to mounted MuJoCo sources.` : `Missing mounted MuJoCo source${missingKeys.length === 1 ? "" : "s"} for ${missingKeys.join(", ")}.`
|
|
1717
1637
|
};
|
|
1718
1638
|
}
|
|
1639
|
+
function normalizeFrameCount(frameCount) {
|
|
1640
|
+
return Number.isFinite(frameCount) && frameCount !== void 0 ? Math.max(0, Math.floor(frameCount)) : 0;
|
|
1641
|
+
}
|
|
1642
|
+
function createMountedCameraFrameSequenceManifest(result, options = {}) {
|
|
1643
|
+
const cameraKeys = [
|
|
1644
|
+
...options.cameraKeys ?? result.readiness.cameraKeys ?? result.plan.cameraKeys ?? result.cameraKeys
|
|
1645
|
+
];
|
|
1646
|
+
const expectedFrameCount = normalizeFrameCount(
|
|
1647
|
+
options.expectedFrameCount ?? result.frameCount
|
|
1648
|
+
);
|
|
1649
|
+
const recordedFrameCount = normalizeFrameCount(result.frameCount);
|
|
1650
|
+
const streamSummaries = {};
|
|
1651
|
+
const streams = [];
|
|
1652
|
+
let missingFrameCount = 0;
|
|
1653
|
+
let completeStreamCount = 0;
|
|
1654
|
+
let resolvedOrRecordedStreamCount = 0;
|
|
1655
|
+
for (const key of cameraKeys) {
|
|
1656
|
+
const summary = result.cameraSummaries[key];
|
|
1657
|
+
const readiness = result.readiness.cameras[key];
|
|
1658
|
+
const source = summary?.source ?? readiness?.source;
|
|
1659
|
+
const ready = readiness?.ready ?? Boolean(summary);
|
|
1660
|
+
const recorded = normalizeFrameCount(summary?.frameCount);
|
|
1661
|
+
const missing = Math.max(expectedFrameCount - recorded, 0);
|
|
1662
|
+
const complete2 = ready && missing === 0;
|
|
1663
|
+
const status2 = complete2 ? MountedCameraFrameSequenceManifestStatus.Complete : ready || recorded > 0 ? MountedCameraFrameSequenceManifestStatus.Partial : MountedCameraFrameSequenceManifestStatus.Missing;
|
|
1664
|
+
const target = source ? getCameraFrameCaptureSourceTarget(source) : readiness?.message ? void 0 : "missing MuJoCo camera";
|
|
1665
|
+
const message = complete2 ? `Camera stream "${key}" recorded ${recorded} of ${expectedFrameCount} frame${expectedFrameCount === 1 ? "" : "s"}.` : ready || recorded > 0 ? `Camera stream "${key}" recorded ${recorded} of ${expectedFrameCount} frame${expectedFrameCount === 1 ? "" : "s"}.` : readiness?.message ?? `Camera stream "${key}" did not record any frames.`;
|
|
1666
|
+
const stream = {
|
|
1667
|
+
key,
|
|
1668
|
+
ready,
|
|
1669
|
+
complete: complete2,
|
|
1670
|
+
status: status2,
|
|
1671
|
+
source,
|
|
1672
|
+
selector: readiness?.selector,
|
|
1673
|
+
target,
|
|
1674
|
+
width: summary?.width,
|
|
1675
|
+
height: summary?.height,
|
|
1676
|
+
expectedFrameCount,
|
|
1677
|
+
recordedFrameCount: recorded,
|
|
1678
|
+
missingFrameCount: missing,
|
|
1679
|
+
firstFrameIndex: summary?.firstFrameIndex ?? null,
|
|
1680
|
+
lastFrameIndex: summary?.lastFrameIndex ?? null,
|
|
1681
|
+
firstTimestamp: summary?.firstTimestamp ?? null,
|
|
1682
|
+
lastTimestamp: summary?.lastTimestamp ?? null,
|
|
1683
|
+
message
|
|
1684
|
+
};
|
|
1685
|
+
streamSummaries[key] = stream;
|
|
1686
|
+
streams.push(stream);
|
|
1687
|
+
missingFrameCount += missing;
|
|
1688
|
+
if (complete2) completeStreamCount += 1;
|
|
1689
|
+
if (ready || recorded > 0) resolvedOrRecordedStreamCount += 1;
|
|
1690
|
+
}
|
|
1691
|
+
const complete = result.readiness.ready && streams.length === completeStreamCount && missingFrameCount === 0;
|
|
1692
|
+
const status = complete ? MountedCameraFrameSequenceManifestStatus.Complete : resolvedOrRecordedStreamCount > 0 ? MountedCameraFrameSequenceManifestStatus.Partial : MountedCameraFrameSequenceManifestStatus.Missing;
|
|
1693
|
+
return {
|
|
1694
|
+
schema: "mujoco-react/mounted-camera-frame-sequence-manifest@1",
|
|
1695
|
+
ready: result.readiness.ready,
|
|
1696
|
+
complete,
|
|
1697
|
+
status,
|
|
1698
|
+
cameraKeys,
|
|
1699
|
+
resolvedKeys: [...result.readiness.resolvedKeys],
|
|
1700
|
+
missingKeys: [...result.readiness.missingKeys],
|
|
1701
|
+
expectedFrameCount,
|
|
1702
|
+
recordedFrameCount,
|
|
1703
|
+
missingFrameCount,
|
|
1704
|
+
streamSummaries,
|
|
1705
|
+
streams,
|
|
1706
|
+
readiness: result.readiness,
|
|
1707
|
+
message: complete ? `All ${cameraKeys.length} camera stream${cameraKeys.length === 1 ? "" : "s"} recorded ${expectedFrameCount} frame${expectedFrameCount === 1 ? "" : "s"}.` : `Mounted camera sequence coverage is ${status}.`
|
|
1708
|
+
};
|
|
1709
|
+
}
|
|
1719
1710
|
function createMountedCameraFrameSequencePlanFromApi(api, cameraKeys, options = {}) {
|
|
1720
1711
|
return createMountedCameraFrameSequencePlan(cameraKeys, {
|
|
1721
1712
|
...options,
|
|
@@ -1819,8 +1810,8 @@ var _applyPoint = new Float64Array(3);
|
|
|
1819
1810
|
var _rayPnt = new Float64Array(3);
|
|
1820
1811
|
var _rayVec = new Float64Array(3);
|
|
1821
1812
|
var _rayGeomId = new Int32Array(1);
|
|
1822
|
-
var _projRaycaster = new
|
|
1823
|
-
var _projNdc = new
|
|
1813
|
+
var _projRaycaster = new THREE11.Raycaster();
|
|
1814
|
+
var _projNdc = new THREE11.Vector2();
|
|
1824
1815
|
function waitForNextAnimationFrame2() {
|
|
1825
1816
|
return new Promise((resolve) => {
|
|
1826
1817
|
requestAnimationFrame(() => resolve());
|
|
@@ -1836,16 +1827,16 @@ function throwIfCameraSequenceAborted(signal) {
|
|
|
1836
1827
|
function vector3FromArray(values, offset) {
|
|
1837
1828
|
return [values[offset], values[offset + 1], values[offset + 2]];
|
|
1838
1829
|
}
|
|
1839
|
-
function
|
|
1830
|
+
function quaternionFromMujocoQuat(values, offset) {
|
|
1840
1831
|
return [
|
|
1841
|
-
values[offset],
|
|
1842
1832
|
values[offset + 1],
|
|
1843
1833
|
values[offset + 2],
|
|
1844
|
-
values[offset + 3]
|
|
1834
|
+
values[offset + 3],
|
|
1835
|
+
values[offset]
|
|
1845
1836
|
];
|
|
1846
1837
|
}
|
|
1847
1838
|
function quaternionFromXmat(values, offset) {
|
|
1848
|
-
const matrix = new
|
|
1839
|
+
const matrix = new THREE11.Matrix4();
|
|
1849
1840
|
matrix.set(
|
|
1850
1841
|
values[offset],
|
|
1851
1842
|
values[offset + 1],
|
|
@@ -1864,7 +1855,7 @@ function quaternionFromXmat(values, offset) {
|
|
|
1864
1855
|
0,
|
|
1865
1856
|
1
|
|
1866
1857
|
);
|
|
1867
|
-
const quaternion = new
|
|
1858
|
+
const quaternion = new THREE11.Quaternion().setFromRotationMatrix(matrix);
|
|
1868
1859
|
return [quaternion.x, quaternion.y, quaternion.z, quaternion.w];
|
|
1869
1860
|
}
|
|
1870
1861
|
function omitResolvedCameraSelectors(options) {
|
|
@@ -2537,7 +2528,7 @@ function MujocoSimProvider({
|
|
|
2537
2528
|
bodyId: model.cam_bodyid?.[i] ?? -1,
|
|
2538
2529
|
fov: model.cam_fovy?.[i] ?? null,
|
|
2539
2530
|
position: model.cam_pos ? vector3FromArray(model.cam_pos, posOffset) : null,
|
|
2540
|
-
quaternion: model.cam_quat ?
|
|
2531
|
+
quaternion: model.cam_quat ? quaternionFromMujocoQuat(model.cam_quat, quatOffset) : null
|
|
2541
2532
|
});
|
|
2542
2533
|
}
|
|
2543
2534
|
return result;
|
|
@@ -2556,7 +2547,7 @@ function MujocoSimProvider({
|
|
|
2556
2547
|
throw new Error(`MuJoCo camera "${options.cameraName}" was not found.`);
|
|
2557
2548
|
}
|
|
2558
2549
|
const position = data.cam_xpos ? vector3FromArray(data.cam_xpos, cameraId * 3) : model.cam_pos ? vector3FromArray(model.cam_pos, cameraId * 3) : void 0;
|
|
2559
|
-
const quaternion = data.cam_xmat ? quaternionFromXmat(data.cam_xmat, cameraId * 9) : model.cam_quat ?
|
|
2550
|
+
const quaternion = data.cam_xmat ? quaternionFromXmat(data.cam_xmat, cameraId * 9) : model.cam_quat ? quaternionFromMujocoQuat(model.cam_quat, cameraId * 4) : void 0;
|
|
2560
2551
|
if (!position || !quaternion) {
|
|
2561
2552
|
throw new Error(
|
|
2562
2553
|
`MuJoCo camera "${options.cameraName}" does not expose a capture pose.`
|
|
@@ -2642,7 +2633,7 @@ function MujocoSimProvider({
|
|
|
2642
2633
|
const geomId = _rayGeomId[0];
|
|
2643
2634
|
const bodyId = geomId >= 0 ? model.geom_bodyid[geomId] : -1;
|
|
2644
2635
|
return {
|
|
2645
|
-
point: new
|
|
2636
|
+
point: new THREE11.Vector3(
|
|
2646
2637
|
origin.x + dir.x * dist,
|
|
2647
2638
|
origin.y + dir.y * dist,
|
|
2648
2639
|
origin.z + dir.z * dist
|
|
@@ -2922,7 +2913,7 @@ function MujocoSimProvider({
|
|
|
2922
2913
|
const cameraFrames = {};
|
|
2923
2914
|
for (const { key, captureOptions, mountedSource, session } of captureSessions) {
|
|
2924
2915
|
const resolvedCaptureOptions = resolveCameraCaptureOptions(captureOptions);
|
|
2925
|
-
const cameraFrame = session.
|
|
2916
|
+
const cameraFrame = await session.captureDataUrlAsync({
|
|
2926
2917
|
...resolvedCaptureOptions,
|
|
2927
2918
|
source: mountedSource ?? resolvedCaptureOptions.source
|
|
2928
2919
|
});
|
|
@@ -2948,7 +2939,7 @@ function MujocoSimProvider({
|
|
|
2948
2939
|
}
|
|
2949
2940
|
const frame = {
|
|
2950
2941
|
frameIndex,
|
|
2951
|
-
time:
|
|
2942
|
+
time: data.time,
|
|
2952
2943
|
cameras: cameraFrames
|
|
2953
2944
|
};
|
|
2954
2945
|
if (retainFrames) {
|
|
@@ -3611,7 +3602,7 @@ function solve6x6(A, b, x) {
|
|
|
3611
3602
|
}
|
|
3612
3603
|
|
|
3613
3604
|
// src/hooks/useIkController.ts
|
|
3614
|
-
var _syncMat4 = new
|
|
3605
|
+
var _syncMat4 = new THREE11.Matrix4();
|
|
3615
3606
|
function syncGizmoToSite(data, siteId, target) {
|
|
3616
3607
|
if (siteId === -1) return;
|
|
3617
3608
|
const sitePos = data.site_xpos.subarray(siteId * 3, siteId * 3 + 3);
|
|
@@ -3643,7 +3634,7 @@ var useIkController = createControllerHook(
|
|
|
3643
3634
|
const { mjModelRef, mjDataRef, mujocoRef, resetCallbacks, status } = useMujocoContext();
|
|
3644
3635
|
const ikEnabledRef = useRef(false);
|
|
3645
3636
|
const ikCalculatingRef = useRef(false);
|
|
3646
|
-
const ikTargetRef = useRef(new
|
|
3637
|
+
const ikTargetRef = useRef(new THREE11.Group());
|
|
3647
3638
|
const siteIdRef = useRef(-1);
|
|
3648
3639
|
const controlGroupRef = useRef(null);
|
|
3649
3640
|
const genericIkRef = useRef(new GenericIK(mujocoRef.current));
|
|
@@ -3651,10 +3642,10 @@ var useIkController = createControllerHook(
|
|
|
3651
3642
|
const needsInitialSync = useRef(true);
|
|
3652
3643
|
const gizmoAnimRef = useRef({
|
|
3653
3644
|
active: false,
|
|
3654
|
-
startPos: new
|
|
3655
|
-
endPos: new
|
|
3656
|
-
startRot: new
|
|
3657
|
-
endRot: new
|
|
3645
|
+
startPos: new THREE11.Vector3(),
|
|
3646
|
+
endPos: new THREE11.Vector3(),
|
|
3647
|
+
startRot: new THREE11.Quaternion(),
|
|
3648
|
+
endRot: new THREE11.Quaternion(),
|
|
3658
3649
|
startTime: 0,
|
|
3659
3650
|
duration: 1e3
|
|
3660
3651
|
});
|
|
@@ -3799,8 +3790,8 @@ var useIkController = createControllerHook(
|
|
|
3799
3790
|
const target = ikTargetRef.current;
|
|
3800
3791
|
if (!target) return;
|
|
3801
3792
|
const targetPos = pos.clone();
|
|
3802
|
-
const targetRot = new
|
|
3803
|
-
new
|
|
3793
|
+
const targetRot = new THREE11.Quaternion().setFromEuler(
|
|
3794
|
+
new THREE11.Euler(Math.PI, 0, 0)
|
|
3804
3795
|
);
|
|
3805
3796
|
if (duration > 0) {
|
|
3806
3797
|
const ga = gizmoAnimRef.current;
|
|
@@ -3825,7 +3816,7 @@ var useIkController = createControllerHook(
|
|
|
3825
3816
|
if (!ikCalculatingRef.current || !target) return null;
|
|
3826
3817
|
return {
|
|
3827
3818
|
pos: target.position.clone(),
|
|
3828
|
-
rot: new
|
|
3819
|
+
rot: new THREE11.Euler().setFromQuaternion(target.quaternion)
|
|
3829
3820
|
};
|
|
3830
3821
|
},
|
|
3831
3822
|
[]
|
|
@@ -3921,10 +3912,10 @@ function Body({
|
|
|
3921
3912
|
if (!hasChildren) return null;
|
|
3922
3913
|
return /* @__PURE__ */ jsx("group", { ref: groupRef, children });
|
|
3923
3914
|
}
|
|
3924
|
-
var _mat4 = new
|
|
3925
|
-
var _pos = new
|
|
3926
|
-
var _quat = new
|
|
3927
|
-
var _scale = new
|
|
3915
|
+
var _mat4 = new THREE11.Matrix4();
|
|
3916
|
+
var _pos = new THREE11.Vector3();
|
|
3917
|
+
var _quat = new THREE11.Quaternion();
|
|
3918
|
+
var _scale = new THREE11.Vector3(1, 1, 1);
|
|
3928
3919
|
function IkGizmo({ controller, siteName, scale = 0.18, onDrag }) {
|
|
3929
3920
|
const { mjModelRef, mjDataRef, status } = useMujocoContext();
|
|
3930
3921
|
const { ikTargetRef, siteIdRef, ikEnabledRef, setIkEnabled } = controller;
|
|
@@ -3976,47 +3967,54 @@ function IkGizmo({ controller, siteName, scale = 0.18, onDrag }) {
|
|
|
3976
3967
|
}
|
|
3977
3968
|
});
|
|
3978
3969
|
if (status !== "ready") return null;
|
|
3979
|
-
return /* @__PURE__ */ jsx(
|
|
3980
|
-
|
|
3970
|
+
return /* @__PURE__ */ jsx(
|
|
3971
|
+
"group",
|
|
3981
3972
|
{
|
|
3982
|
-
ref:
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
|
|
4005
|
-
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
|
|
4012
|
-
|
|
3973
|
+
ref: wrapperRef,
|
|
3974
|
+
userData: { [CAPTURE_EXCLUDE_KEY]: true },
|
|
3975
|
+
children: /* @__PURE__ */ jsx(
|
|
3976
|
+
PivotControls,
|
|
3977
|
+
{
|
|
3978
|
+
ref: pivotRef,
|
|
3979
|
+
autoTransform: true,
|
|
3980
|
+
scale,
|
|
3981
|
+
fixed: false,
|
|
3982
|
+
depthTest: false,
|
|
3983
|
+
disableScaling: true,
|
|
3984
|
+
onDragStart: () => {
|
|
3985
|
+
draggingRef.current = true;
|
|
3986
|
+
if (!onDrag) {
|
|
3987
|
+
if (!ikEnabledRef.current) setIkEnabled(true);
|
|
3988
|
+
}
|
|
3989
|
+
if (controls) controls.enabled = false;
|
|
3990
|
+
},
|
|
3991
|
+
onDragEnd: () => {
|
|
3992
|
+
draggingRef.current = false;
|
|
3993
|
+
if (pivotRef.current) {
|
|
3994
|
+
pivotRef.current.matrix.identity();
|
|
3995
|
+
pivotRef.current.matrixWorldNeedsUpdate = true;
|
|
3996
|
+
}
|
|
3997
|
+
if (controls) controls.enabled = true;
|
|
3998
|
+
},
|
|
3999
|
+
onDrag: (_l, _dl, world) => {
|
|
4000
|
+
world.decompose(_pos, _quat, _scale);
|
|
4001
|
+
if (onDrag) {
|
|
4002
|
+
onDrag({ position: _pos.clone(), quaternion: _quat.clone() });
|
|
4003
|
+
} else {
|
|
4004
|
+
const target = ikTargetRef.current;
|
|
4005
|
+
if (target) {
|
|
4006
|
+
target.position.copy(_pos);
|
|
4007
|
+
target.quaternion.copy(_quat);
|
|
4008
|
+
}
|
|
4009
|
+
}
|
|
4010
|
+
},
|
|
4011
|
+
children: /* @__PURE__ */ jsx("mesh", { visible: false, children: /* @__PURE__ */ jsx("sphereGeometry", { args: [1e-3] }) })
|
|
4013
4012
|
}
|
|
4014
|
-
|
|
4015
|
-
children: /* @__PURE__ */ jsx("mesh", { visible: false, children: /* @__PURE__ */ jsx("sphereGeometry", { args: [1e-3] }) })
|
|
4013
|
+
)
|
|
4016
4014
|
}
|
|
4017
|
-
)
|
|
4015
|
+
);
|
|
4018
4016
|
}
|
|
4019
|
-
var _dummy = new
|
|
4017
|
+
var _dummy = new THREE11.Object3D();
|
|
4020
4018
|
function ContactMarkers({
|
|
4021
4019
|
maxContacts = 100,
|
|
4022
4020
|
radius = 8e-3,
|
|
@@ -4052,19 +4050,29 @@ function ContactMarkers({
|
|
|
4052
4050
|
mesh.instanceMatrix.needsUpdate = true;
|
|
4053
4051
|
});
|
|
4054
4052
|
if (status !== "ready") return null;
|
|
4055
|
-
return /* @__PURE__ */ jsx(
|
|
4056
|
-
|
|
4057
|
-
|
|
4058
|
-
|
|
4053
|
+
return /* @__PURE__ */ jsx(
|
|
4054
|
+
"group",
|
|
4055
|
+
{
|
|
4056
|
+
...groupProps,
|
|
4057
|
+
userData: {
|
|
4058
|
+
...groupProps.userData,
|
|
4059
|
+
[CAPTURE_EXCLUDE_KEY]: true
|
|
4060
|
+
},
|
|
4061
|
+
children: /* @__PURE__ */ jsxs("instancedMesh", { ref: meshRef, args: [void 0, void 0, maxContacts], frustumCulled: false, renderOrder: 999, children: [
|
|
4062
|
+
/* @__PURE__ */ jsx("sphereGeometry", { args: [radius, 8, 8] }),
|
|
4063
|
+
/* @__PURE__ */ jsx("meshBasicMaterial", { color, depthTest: false })
|
|
4064
|
+
] })
|
|
4065
|
+
}
|
|
4066
|
+
);
|
|
4059
4067
|
}
|
|
4060
4068
|
var _force = new Float64Array(3);
|
|
4061
4069
|
var _torque = new Float64Array(3);
|
|
4062
4070
|
var _point = new Float64Array(3);
|
|
4063
|
-
var _bodyPos = new
|
|
4064
|
-
var _bodyQuat = new
|
|
4065
|
-
var _worldHit = new
|
|
4066
|
-
var _raycaster = new
|
|
4067
|
-
var _mouse = new
|
|
4071
|
+
var _bodyPos = new THREE11.Vector3();
|
|
4072
|
+
var _bodyQuat = new THREE11.Quaternion();
|
|
4073
|
+
var _worldHit = new THREE11.Vector3();
|
|
4074
|
+
var _raycaster = new THREE11.Raycaster();
|
|
4075
|
+
var _mouse = new THREE11.Vector2();
|
|
4068
4076
|
function DragInteraction({
|
|
4069
4077
|
stiffness = 250,
|
|
4070
4078
|
showArrow = true,
|
|
@@ -4075,19 +4083,20 @@ function DragInteraction({
|
|
|
4075
4083
|
const draggingRef = useRef(false);
|
|
4076
4084
|
const bodyIdRef = useRef(-1);
|
|
4077
4085
|
const grabDistanceRef = useRef(0);
|
|
4078
|
-
const localHitRef = useRef(new
|
|
4079
|
-
const grabWorldRef = useRef(new
|
|
4080
|
-
const mouseWorldRef = useRef(new
|
|
4086
|
+
const localHitRef = useRef(new THREE11.Vector3());
|
|
4087
|
+
const grabWorldRef = useRef(new THREE11.Vector3());
|
|
4088
|
+
const mouseWorldRef = useRef(new THREE11.Vector3());
|
|
4081
4089
|
const arrowRef = useRef(null);
|
|
4082
4090
|
const groupRef = useRef(null);
|
|
4083
4091
|
useEffect(() => {
|
|
4084
4092
|
if (!showArrow || !groupRef.current) return;
|
|
4085
|
-
const arrow = new
|
|
4086
|
-
new
|
|
4087
|
-
new
|
|
4093
|
+
const arrow = new THREE11.ArrowHelper(
|
|
4094
|
+
new THREE11.Vector3(0, 1, 0),
|
|
4095
|
+
new THREE11.Vector3(),
|
|
4088
4096
|
0.1,
|
|
4089
4097
|
16729156
|
|
4090
4098
|
);
|
|
4099
|
+
arrow.userData[CAPTURE_EXCLUDE_KEY] = true;
|
|
4091
4100
|
arrow.visible = false;
|
|
4092
4101
|
arrow.line.material.transparent = true;
|
|
4093
4102
|
arrow.line.material.opacity = 0.6;
|
|
@@ -4266,7 +4275,7 @@ function useSceneLights(intensity = 1) {
|
|
|
4266
4275
|
const dr = lightDiffuse ? lightDiffuse[3 * i] : 1;
|
|
4267
4276
|
const dg = lightDiffuse ? lightDiffuse[3 * i + 1] : 1;
|
|
4268
4277
|
const db = lightDiffuse ? lightDiffuse[3 * i + 2] : 1;
|
|
4269
|
-
const color = new
|
|
4278
|
+
const color = new THREE11.Color(dr, dg, db);
|
|
4270
4279
|
const px = lightPos[3 * i];
|
|
4271
4280
|
const py = lightPos[3 * i + 1];
|
|
4272
4281
|
const pz = lightPos[3 * i + 2];
|
|
@@ -4274,7 +4283,7 @@ function useSceneLights(intensity = 1) {
|
|
|
4274
4283
|
const dy = lightDir[3 * i + 1];
|
|
4275
4284
|
const dz = lightDir[3 * i + 2];
|
|
4276
4285
|
if (isDirectional) {
|
|
4277
|
-
const light = new
|
|
4286
|
+
const light = new THREE11.DirectionalLight(color, finalIntensity);
|
|
4278
4287
|
light.position.set(px, py, pz);
|
|
4279
4288
|
light.target.position.set(px + dx, py + dy, pz + dz);
|
|
4280
4289
|
light.castShadow = castShadow;
|
|
@@ -4297,7 +4306,7 @@ function useSceneLights(intensity = 1) {
|
|
|
4297
4306
|
const cutoff = lightCutoff ? lightCutoff[i] : 45;
|
|
4298
4307
|
const exponent = lightExponent ? lightExponent[i] : 10;
|
|
4299
4308
|
const angle = cutoff * Math.PI / 180;
|
|
4300
|
-
const light = new
|
|
4309
|
+
const light = new THREE11.SpotLight(color, finalIntensity, 0, angle, exponent / 128);
|
|
4301
4310
|
light.position.set(px, py, pz);
|
|
4302
4311
|
light.target.position.set(px + dx, py + dy, pz + dz);
|
|
4303
4312
|
light.castShadow = castShadow;
|
|
@@ -4345,6 +4354,231 @@ function SceneLights({ intensity = 1 }) {
|
|
|
4345
4354
|
useSceneLights(intensity);
|
|
4346
4355
|
return null;
|
|
4347
4356
|
}
|
|
4357
|
+
function SplatCollisionProxyPreview({
|
|
4358
|
+
collisionProxy,
|
|
4359
|
+
xmlText,
|
|
4360
|
+
fetchXml = fetchSplatCollisionProxyXml,
|
|
4361
|
+
color = "#60a5fa",
|
|
4362
|
+
opacity = 0.12,
|
|
4363
|
+
planeColor = "#94a3b8",
|
|
4364
|
+
planeOpacity = 0.08,
|
|
4365
|
+
children,
|
|
4366
|
+
...groupProps
|
|
4367
|
+
}) {
|
|
4368
|
+
const { geoms } = useSplatCollisionProxyGeoms({
|
|
4369
|
+
collisionProxy,
|
|
4370
|
+
xmlText,
|
|
4371
|
+
fetchXml
|
|
4372
|
+
});
|
|
4373
|
+
if (geoms.length === 0 && !children) return null;
|
|
4374
|
+
return /* @__PURE__ */ jsxs(
|
|
4375
|
+
"group",
|
|
4376
|
+
{
|
|
4377
|
+
...groupProps,
|
|
4378
|
+
userData: {
|
|
4379
|
+
kind: "splat-collision-proxy-preview",
|
|
4380
|
+
...groupProps.userData
|
|
4381
|
+
},
|
|
4382
|
+
children: [
|
|
4383
|
+
geoms.map((geom) => /* @__PURE__ */ jsx(
|
|
4384
|
+
SplatCollisionProxyGeom,
|
|
4385
|
+
{
|
|
4386
|
+
geom,
|
|
4387
|
+
color,
|
|
4388
|
+
opacity,
|
|
4389
|
+
planeColor,
|
|
4390
|
+
planeOpacity
|
|
4391
|
+
},
|
|
4392
|
+
geom.id
|
|
4393
|
+
)),
|
|
4394
|
+
children
|
|
4395
|
+
]
|
|
4396
|
+
}
|
|
4397
|
+
);
|
|
4398
|
+
}
|
|
4399
|
+
function useSplatCollisionProxyGeoms({
|
|
4400
|
+
collisionProxy,
|
|
4401
|
+
xmlText,
|
|
4402
|
+
fetchXml = fetchSplatCollisionProxyXml,
|
|
4403
|
+
enabled = true
|
|
4404
|
+
}) {
|
|
4405
|
+
const [loadedXmlText, setLoadedXmlText] = useState(null);
|
|
4406
|
+
const [status, setStatus] = useState("idle");
|
|
4407
|
+
const [error, setError] = useState(null);
|
|
4408
|
+
const xmlPath = collisionProxy?.xmlPath;
|
|
4409
|
+
useEffect(() => {
|
|
4410
|
+
let cancelled = false;
|
|
4411
|
+
if (!enabled) {
|
|
4412
|
+
setLoadedXmlText(null);
|
|
4413
|
+
setStatus("idle");
|
|
4414
|
+
setError(null);
|
|
4415
|
+
return void 0;
|
|
4416
|
+
}
|
|
4417
|
+
if (xmlText) {
|
|
4418
|
+
setLoadedXmlText(xmlText);
|
|
4419
|
+
setStatus("ready");
|
|
4420
|
+
setError(null);
|
|
4421
|
+
return void 0;
|
|
4422
|
+
}
|
|
4423
|
+
if (!xmlPath || !canFetchSplatCollisionProxyXml(xmlPath)) {
|
|
4424
|
+
setLoadedXmlText(null);
|
|
4425
|
+
setStatus("idle");
|
|
4426
|
+
setError(null);
|
|
4427
|
+
return void 0;
|
|
4428
|
+
}
|
|
4429
|
+
const fetchPath = xmlPath;
|
|
4430
|
+
async function loadProxyXml() {
|
|
4431
|
+
setStatus("loading");
|
|
4432
|
+
setError(null);
|
|
4433
|
+
try {
|
|
4434
|
+
const nextXmlText = await fetchXml(fetchPath);
|
|
4435
|
+
if (!cancelled) {
|
|
4436
|
+
setLoadedXmlText(nextXmlText);
|
|
4437
|
+
setStatus("ready");
|
|
4438
|
+
}
|
|
4439
|
+
} catch (nextError) {
|
|
4440
|
+
if (!cancelled) {
|
|
4441
|
+
setLoadedXmlText(null);
|
|
4442
|
+
setStatus("error");
|
|
4443
|
+
setError(
|
|
4444
|
+
nextError instanceof Error ? nextError : new Error("Unable to load collision proxy XML.")
|
|
4445
|
+
);
|
|
4446
|
+
}
|
|
4447
|
+
}
|
|
4448
|
+
}
|
|
4449
|
+
void loadProxyXml();
|
|
4450
|
+
return () => {
|
|
4451
|
+
cancelled = true;
|
|
4452
|
+
};
|
|
4453
|
+
}, [enabled, fetchXml, xmlPath, xmlText]);
|
|
4454
|
+
const geoms = useMemo(
|
|
4455
|
+
() => loadedXmlText ? parseSplatCollisionProxyGeoms(loadedXmlText) : [],
|
|
4456
|
+
[loadedXmlText]
|
|
4457
|
+
);
|
|
4458
|
+
return useMemo(
|
|
4459
|
+
() => ({
|
|
4460
|
+
geoms,
|
|
4461
|
+
status,
|
|
4462
|
+
error,
|
|
4463
|
+
xmlPath
|
|
4464
|
+
}),
|
|
4465
|
+
[error, geoms, status, xmlPath]
|
|
4466
|
+
);
|
|
4467
|
+
}
|
|
4468
|
+
async function fetchSplatCollisionProxyXml(xmlPath) {
|
|
4469
|
+
const response = await fetch(xmlPath);
|
|
4470
|
+
if (!response.ok) {
|
|
4471
|
+
throw new Error(`Unable to load collision proxy XML (${response.status}).`);
|
|
4472
|
+
}
|
|
4473
|
+
return response.text();
|
|
4474
|
+
}
|
|
4475
|
+
function canFetchSplatCollisionProxyXml(xmlPath) {
|
|
4476
|
+
return xmlPath.startsWith("/") || xmlPath.startsWith("http://") || xmlPath.startsWith("https://");
|
|
4477
|
+
}
|
|
4478
|
+
function parseSplatCollisionProxyGeoms(xmlText) {
|
|
4479
|
+
const parser = typeof DOMParser === "undefined" ? null : new DOMParser();
|
|
4480
|
+
if (!parser) return [];
|
|
4481
|
+
const document2 = parser.parseFromString(xmlText, "application/xml");
|
|
4482
|
+
if (document2.querySelector("parsererror")) return [];
|
|
4483
|
+
const bodyPositions = /* @__PURE__ */ new Map();
|
|
4484
|
+
for (const body of Array.from(document2.querySelectorAll("body"))) {
|
|
4485
|
+
const parentBody = body.parentElement?.closest("body");
|
|
4486
|
+
const parentPosition = parentBody ? bodyPositions.get(parentBody) ?? [0, 0, 0] : [0, 0, 0];
|
|
4487
|
+
bodyPositions.set(
|
|
4488
|
+
body,
|
|
4489
|
+
addProxyVectors(parentPosition, parseProxyVector(body.getAttribute("pos")))
|
|
4490
|
+
);
|
|
4491
|
+
}
|
|
4492
|
+
return Array.from(document2.querySelectorAll("geom")).map((geom, index) => {
|
|
4493
|
+
const type = getCollisionProxyGeomType(geom);
|
|
4494
|
+
if (!type) return null;
|
|
4495
|
+
const parentBody = geom.closest("body");
|
|
4496
|
+
const bodyPosition = parentBody ? bodyPositions.get(parentBody) ?? [0, 0, 0] : [0, 0, 0];
|
|
4497
|
+
const position = addProxyVectors(
|
|
4498
|
+
bodyPosition,
|
|
4499
|
+
parseProxyVector(geom.getAttribute("pos"))
|
|
4500
|
+
);
|
|
4501
|
+
const size = parseNumberList(geom.getAttribute("size"));
|
|
4502
|
+
return {
|
|
4503
|
+
id: geom.getAttribute("name") ?? `${type}-${index}`,
|
|
4504
|
+
type,
|
|
4505
|
+
position,
|
|
4506
|
+
size
|
|
4507
|
+
};
|
|
4508
|
+
}).filter((geom) => Boolean(geom));
|
|
4509
|
+
}
|
|
4510
|
+
function SplatCollisionProxyGeom({
|
|
4511
|
+
geom,
|
|
4512
|
+
color,
|
|
4513
|
+
opacity,
|
|
4514
|
+
planeColor,
|
|
4515
|
+
planeOpacity
|
|
4516
|
+
}) {
|
|
4517
|
+
if (geom.type === "sphere") {
|
|
4518
|
+
return /* @__PURE__ */ jsxs("mesh", { position: geom.position, children: [
|
|
4519
|
+
/* @__PURE__ */ jsx("sphereGeometry", { args: [geom.size[0] ?? 0.1, 16, 8] }),
|
|
4520
|
+
/* @__PURE__ */ jsx(SplatCollisionProxyMaterial, { color, opacity })
|
|
4521
|
+
] });
|
|
4522
|
+
}
|
|
4523
|
+
if (geom.type === "plane") {
|
|
4524
|
+
const width = geom.size[0] && geom.size[0] > 0 ? geom.size[0] * 2 : 4;
|
|
4525
|
+
const height = geom.size[1] && geom.size[1] > 0 ? geom.size[1] * 2 : 4;
|
|
4526
|
+
return /* @__PURE__ */ jsxs("mesh", { position: geom.position, children: [
|
|
4527
|
+
/* @__PURE__ */ jsx("boxGeometry", { args: [width, height, 0.02] }),
|
|
4528
|
+
/* @__PURE__ */ jsx(SplatCollisionProxyMaterial, { color: planeColor, opacity: planeOpacity })
|
|
4529
|
+
] });
|
|
4530
|
+
}
|
|
4531
|
+
const size = getCollisionProxyBoxSize(geom);
|
|
4532
|
+
return /* @__PURE__ */ jsxs("mesh", { position: geom.position, children: [
|
|
4533
|
+
/* @__PURE__ */ jsx("boxGeometry", { args: size }),
|
|
4534
|
+
/* @__PURE__ */ jsx(SplatCollisionProxyMaterial, { color, opacity })
|
|
4535
|
+
] });
|
|
4536
|
+
}
|
|
4537
|
+
function SplatCollisionProxyMaterial({
|
|
4538
|
+
color,
|
|
4539
|
+
opacity
|
|
4540
|
+
}) {
|
|
4541
|
+
return /* @__PURE__ */ jsx(
|
|
4542
|
+
"meshBasicMaterial",
|
|
4543
|
+
{
|
|
4544
|
+
color,
|
|
4545
|
+
transparent: true,
|
|
4546
|
+
opacity,
|
|
4547
|
+
wireframe: true
|
|
4548
|
+
}
|
|
4549
|
+
);
|
|
4550
|
+
}
|
|
4551
|
+
function getCollisionProxyGeomType(geom) {
|
|
4552
|
+
const type = geom.getAttribute("type") ?? "sphere";
|
|
4553
|
+
if (type === "box" || type === "plane" || type === "sphere" || type === "capsule" || type === "mesh") {
|
|
4554
|
+
return type;
|
|
4555
|
+
}
|
|
4556
|
+
return null;
|
|
4557
|
+
}
|
|
4558
|
+
function getCollisionProxyBoxSize(geom) {
|
|
4559
|
+
if (geom.type === "capsule") {
|
|
4560
|
+
const radius = geom.size[0] ?? 0.05;
|
|
4561
|
+
const halfLength = geom.size[1] ?? radius;
|
|
4562
|
+
return [radius * 2, radius * 2, Math.max(radius * 2, halfLength * 2)];
|
|
4563
|
+
}
|
|
4564
|
+
if (geom.type === "mesh") return [0.2, 0.2, 0.2];
|
|
4565
|
+
return [
|
|
4566
|
+
(geom.size[0] ?? 0.1) * 2,
|
|
4567
|
+
(geom.size[1] ?? geom.size[0] ?? 0.1) * 2,
|
|
4568
|
+
(geom.size[2] ?? geom.size[0] ?? 0.1) * 2
|
|
4569
|
+
];
|
|
4570
|
+
}
|
|
4571
|
+
function parseProxyVector(value) {
|
|
4572
|
+
const values = parseNumberList(value);
|
|
4573
|
+
return [values[0] ?? 0, values[1] ?? 0, values[2] ?? 0];
|
|
4574
|
+
}
|
|
4575
|
+
function parseNumberList(value) {
|
|
4576
|
+
if (!value) return [];
|
|
4577
|
+
return value.trim().split(/\s+/).map((part) => Number(part)).filter((part) => Number.isFinite(part));
|
|
4578
|
+
}
|
|
4579
|
+
function addProxyVectors(a, b) {
|
|
4580
|
+
return [a[0] + b[0], a[1] + b[1], a[2] + b[2]];
|
|
4581
|
+
}
|
|
4348
4582
|
var JOINT_COLORS = {
|
|
4349
4583
|
0: 16711680,
|
|
4350
4584
|
// free - red
|
|
@@ -4355,16 +4589,20 @@ var JOINT_COLORS = {
|
|
|
4355
4589
|
3: 16776960
|
|
4356
4590
|
// hinge - yellow
|
|
4357
4591
|
};
|
|
4358
|
-
var _v3a = new
|
|
4359
|
-
new
|
|
4360
|
-
var _quat2 = new
|
|
4361
|
-
var
|
|
4362
|
-
var
|
|
4592
|
+
var _v3a = new THREE11.Vector3();
|
|
4593
|
+
new THREE11.Vector3();
|
|
4594
|
+
var _quat2 = new THREE11.Quaternion();
|
|
4595
|
+
var _cameraMatrix = new THREE11.Matrix4();
|
|
4596
|
+
var _contactPos = new THREE11.Vector3();
|
|
4597
|
+
var _contactNormal = new THREE11.Vector3();
|
|
4363
4598
|
var MAX_CONTACT_ARROWS = 50;
|
|
4599
|
+
var CAMERA_DEBUG_LENGTH = 0.12;
|
|
4600
|
+
var CAMERA_DEBUG_FRUSTUM_DEPTH = 0.08;
|
|
4364
4601
|
function Debug({
|
|
4365
4602
|
showGeoms = false,
|
|
4366
4603
|
showSites = false,
|
|
4367
4604
|
showJoints = false,
|
|
4605
|
+
showCameras = false,
|
|
4368
4606
|
showContacts = false,
|
|
4369
4607
|
showCOM = false,
|
|
4370
4608
|
showInertia = false,
|
|
@@ -4380,6 +4618,7 @@ function Debug({
|
|
|
4380
4618
|
const geoms = [];
|
|
4381
4619
|
const sites = [];
|
|
4382
4620
|
const joints = [];
|
|
4621
|
+
const cameras = [];
|
|
4383
4622
|
const comMarkers = [];
|
|
4384
4623
|
if (showGeoms) {
|
|
4385
4624
|
for (let i = 0; i < model.ngeom; i++) {
|
|
@@ -4388,21 +4627,21 @@ function Debug({
|
|
|
4388
4627
|
let geometry = null;
|
|
4389
4628
|
switch (type) {
|
|
4390
4629
|
case 2:
|
|
4391
|
-
geometry = new
|
|
4630
|
+
geometry = new THREE11.SphereGeometry(s[3 * i], 12, 8);
|
|
4392
4631
|
break;
|
|
4393
4632
|
case 3:
|
|
4394
|
-
geometry = new
|
|
4633
|
+
geometry = new THREE11.CapsuleGeometry(s[3 * i], s[3 * i + 1] * 2, 6, 8);
|
|
4395
4634
|
break;
|
|
4396
4635
|
case 5:
|
|
4397
|
-
geometry = new
|
|
4636
|
+
geometry = new THREE11.CylinderGeometry(s[3 * i], s[3 * i], s[3 * i + 1] * 2, 12);
|
|
4398
4637
|
break;
|
|
4399
4638
|
case 6:
|
|
4400
|
-
geometry = new
|
|
4639
|
+
geometry = new THREE11.BoxGeometry(s[3 * i] * 2, s[3 * i + 1] * 2, s[3 * i + 2] * 2);
|
|
4401
4640
|
break;
|
|
4402
4641
|
}
|
|
4403
4642
|
if (geometry) {
|
|
4404
|
-
const mat = new
|
|
4405
|
-
const mesh = new
|
|
4643
|
+
const mat = new THREE11.MeshBasicMaterial({ color: 65280, wireframe: true, transparent: true, opacity: 0.3 });
|
|
4644
|
+
const mesh = new THREE11.Mesh(geometry, mat);
|
|
4406
4645
|
mesh.userData.geomId = i;
|
|
4407
4646
|
mesh.userData.bodyId = model.geom_bodyid[i];
|
|
4408
4647
|
geoms.push(mesh);
|
|
@@ -4425,9 +4664,9 @@ function Debug({
|
|
|
4425
4664
|
}
|
|
4426
4665
|
if (maxGeomSize > 0) radius = maxGeomSize * 0.15;
|
|
4427
4666
|
}
|
|
4428
|
-
const geometry = new
|
|
4429
|
-
const mat = new
|
|
4430
|
-
const mesh = new
|
|
4667
|
+
const geometry = new THREE11.OctahedronGeometry(radius);
|
|
4668
|
+
const mat = new THREE11.MeshBasicMaterial({ color: 16711935, depthTest: false });
|
|
4669
|
+
const mesh = new THREE11.Mesh(geometry, mat);
|
|
4431
4670
|
mesh.renderOrder = 999;
|
|
4432
4671
|
mesh.frustumCulled = false;
|
|
4433
4672
|
mesh.userData.siteId = i;
|
|
@@ -4439,9 +4678,9 @@ function Debug({
|
|
|
4439
4678
|
ctx.font = "bold 36px monospace";
|
|
4440
4679
|
ctx.textAlign = "center";
|
|
4441
4680
|
ctx.fillText(getName(model, model.name_siteadr[i]), 128, 42);
|
|
4442
|
-
const tex = new
|
|
4443
|
-
const spriteMat = new
|
|
4444
|
-
const sprite = new
|
|
4681
|
+
const tex = new THREE11.CanvasTexture(canvas);
|
|
4682
|
+
const spriteMat = new THREE11.SpriteMaterial({ map: tex, depthTest: false, transparent: true });
|
|
4683
|
+
const sprite = new THREE11.Sprite(spriteMat);
|
|
4445
4684
|
const labelScale = radius * 15;
|
|
4446
4685
|
sprite.scale.set(labelScale, labelScale * 0.25, 1);
|
|
4447
4686
|
sprite.position.y = radius * 2;
|
|
@@ -4464,9 +4703,9 @@ function Debug({
|
|
|
4464
4703
|
}
|
|
4465
4704
|
}
|
|
4466
4705
|
const arrowLen = Math.max(maxGeomSize * 0.8, 0.05);
|
|
4467
|
-
const arrow = new
|
|
4468
|
-
new
|
|
4469
|
-
new
|
|
4706
|
+
const arrow = new THREE11.ArrowHelper(
|
|
4707
|
+
new THREE11.Vector3(0, 0, 1),
|
|
4708
|
+
new THREE11.Vector3(),
|
|
4470
4709
|
arrowLen,
|
|
4471
4710
|
color,
|
|
4472
4711
|
arrowLen * 0.25,
|
|
@@ -4474,7 +4713,7 @@ function Debug({
|
|
|
4474
4713
|
);
|
|
4475
4714
|
arrow.renderOrder = 999;
|
|
4476
4715
|
arrow.frustumCulled = false;
|
|
4477
|
-
arrow.line.material = new
|
|
4716
|
+
arrow.line.material = new THREE11.LineBasicMaterial({ color, depthTest: false });
|
|
4478
4717
|
arrow.cone.material.depthTest = false;
|
|
4479
4718
|
arrow.line.renderOrder = 999;
|
|
4480
4719
|
arrow.line.frustumCulled = false;
|
|
@@ -4487,17 +4726,88 @@ function Debug({
|
|
|
4487
4726
|
joints.push(arrow);
|
|
4488
4727
|
}
|
|
4489
4728
|
}
|
|
4729
|
+
if (showCameras && model.ncam && model.name_camadr) {
|
|
4730
|
+
for (let i = 0; i < model.ncam; i++) {
|
|
4731
|
+
const group = new THREE11.Group();
|
|
4732
|
+
group.userData.cameraId = i;
|
|
4733
|
+
group.renderOrder = 999;
|
|
4734
|
+
group.frustumCulled = false;
|
|
4735
|
+
const marker = new THREE11.Mesh(
|
|
4736
|
+
new THREE11.BoxGeometry(0.014, 9e-3, 6e-3),
|
|
4737
|
+
new THREE11.MeshBasicMaterial({ color: 3718648, depthTest: false })
|
|
4738
|
+
);
|
|
4739
|
+
marker.renderOrder = 999;
|
|
4740
|
+
marker.frustumCulled = false;
|
|
4741
|
+
group.add(marker);
|
|
4742
|
+
const forward = new THREE11.ArrowHelper(
|
|
4743
|
+
new THREE11.Vector3(0, 0, -1),
|
|
4744
|
+
new THREE11.Vector3(),
|
|
4745
|
+
CAMERA_DEBUG_LENGTH,
|
|
4746
|
+
3718648,
|
|
4747
|
+
CAMERA_DEBUG_LENGTH * 0.24,
|
|
4748
|
+
CAMERA_DEBUG_LENGTH * 0.11
|
|
4749
|
+
);
|
|
4750
|
+
forward.renderOrder = 999;
|
|
4751
|
+
forward.frustumCulled = false;
|
|
4752
|
+
forward.line.material = new THREE11.LineBasicMaterial({
|
|
4753
|
+
color: 3718648,
|
|
4754
|
+
depthTest: false
|
|
4755
|
+
});
|
|
4756
|
+
forward.cone.material.depthTest = false;
|
|
4757
|
+
group.add(forward);
|
|
4758
|
+
const frustumGeometry = new THREE11.BufferGeometry();
|
|
4759
|
+
frustumGeometry.setAttribute(
|
|
4760
|
+
"position",
|
|
4761
|
+
new THREE11.Float32BufferAttribute(new Float32Array(8 * 2 * 3), 3)
|
|
4762
|
+
);
|
|
4763
|
+
const frustum = new THREE11.LineSegments(
|
|
4764
|
+
frustumGeometry,
|
|
4765
|
+
new THREE11.LineBasicMaterial({
|
|
4766
|
+
color: 3718648,
|
|
4767
|
+
transparent: true,
|
|
4768
|
+
opacity: 0.8,
|
|
4769
|
+
depthTest: false
|
|
4770
|
+
})
|
|
4771
|
+
);
|
|
4772
|
+
frustum.renderOrder = 999;
|
|
4773
|
+
frustum.frustumCulled = false;
|
|
4774
|
+
group.userData.frustum = frustum;
|
|
4775
|
+
group.add(frustum);
|
|
4776
|
+
const canvas = document.createElement("canvas");
|
|
4777
|
+
canvas.width = 256;
|
|
4778
|
+
canvas.height = 64;
|
|
4779
|
+
const ctx = canvas.getContext("2d");
|
|
4780
|
+
ctx.fillStyle = "#38bdf8";
|
|
4781
|
+
ctx.font = "bold 32px monospace";
|
|
4782
|
+
ctx.textAlign = "center";
|
|
4783
|
+
ctx.fillText(getName(model, model.name_camadr[i]), 128, 42);
|
|
4784
|
+
const texture = new THREE11.CanvasTexture(canvas);
|
|
4785
|
+
const sprite = new THREE11.Sprite(
|
|
4786
|
+
new THREE11.SpriteMaterial({
|
|
4787
|
+
map: texture,
|
|
4788
|
+
depthTest: false,
|
|
4789
|
+
transparent: true
|
|
4790
|
+
})
|
|
4791
|
+
);
|
|
4792
|
+
sprite.position.set(0, 0.014, 0.01);
|
|
4793
|
+
sprite.scale.set(0.04, 0.01, 1);
|
|
4794
|
+
sprite.renderOrder = 999;
|
|
4795
|
+
group.userData.label = sprite;
|
|
4796
|
+
group.add(sprite);
|
|
4797
|
+
cameras.push(group);
|
|
4798
|
+
}
|
|
4799
|
+
}
|
|
4490
4800
|
if (showCOM) {
|
|
4491
4801
|
for (let i = 1; i < model.nbody; i++) {
|
|
4492
|
-
const geometry = new
|
|
4493
|
-
const mat = new
|
|
4494
|
-
const mesh = new
|
|
4802
|
+
const geometry = new THREE11.SphereGeometry(5e-3, 6, 6);
|
|
4803
|
+
const mat = new THREE11.MeshBasicMaterial({ color: 16711680 });
|
|
4804
|
+
const mesh = new THREE11.Mesh(geometry, mat);
|
|
4495
4805
|
mesh.userData.bodyId = i;
|
|
4496
4806
|
comMarkers.push(mesh);
|
|
4497
4807
|
}
|
|
4498
4808
|
}
|
|
4499
|
-
return { geoms, sites, joints, comMarkers };
|
|
4500
|
-
}, [status, mjModelRef, showGeoms, showSites, showJoints, showCOM]);
|
|
4809
|
+
return { geoms, sites, joints, cameras, comMarkers };
|
|
4810
|
+
}, [status, mjModelRef, showGeoms, showSites, showJoints, showCameras, showCOM]);
|
|
4501
4811
|
useEffect(() => {
|
|
4502
4812
|
const group = groupRef.current;
|
|
4503
4813
|
if (!group || !debugGeometry) return;
|
|
@@ -4505,6 +4815,7 @@ function Debug({
|
|
|
4505
4815
|
...debugGeometry.geoms,
|
|
4506
4816
|
...debugGeometry.sites,
|
|
4507
4817
|
...debugGeometry.joints,
|
|
4818
|
+
...debugGeometry.cameras,
|
|
4508
4819
|
...debugGeometry.comMarkers
|
|
4509
4820
|
];
|
|
4510
4821
|
for (const obj of allObjects) group.add(obj);
|
|
@@ -4567,6 +4878,68 @@ function Debug({
|
|
|
4567
4878
|
arrow.setDirection(_v3a);
|
|
4568
4879
|
}
|
|
4569
4880
|
}
|
|
4881
|
+
const camXpos = data.cam_xpos;
|
|
4882
|
+
const camXmat = data.cam_xmat;
|
|
4883
|
+
if (camXpos && camXmat) {
|
|
4884
|
+
for (const group of debugGeometry.cameras) {
|
|
4885
|
+
const cameraId = group.userData.cameraId;
|
|
4886
|
+
const i3 = cameraId * 3;
|
|
4887
|
+
const i9 = cameraId * 9;
|
|
4888
|
+
group.position.set(
|
|
4889
|
+
camXpos[i3],
|
|
4890
|
+
camXpos[i3 + 1],
|
|
4891
|
+
camXpos[i3 + 2]
|
|
4892
|
+
);
|
|
4893
|
+
_cameraMatrix.set(
|
|
4894
|
+
camXmat[i9],
|
|
4895
|
+
camXmat[i9 + 1],
|
|
4896
|
+
camXmat[i9 + 2],
|
|
4897
|
+
0,
|
|
4898
|
+
camXmat[i9 + 3],
|
|
4899
|
+
camXmat[i9 + 4],
|
|
4900
|
+
camXmat[i9 + 5],
|
|
4901
|
+
0,
|
|
4902
|
+
camXmat[i9 + 6],
|
|
4903
|
+
camXmat[i9 + 7],
|
|
4904
|
+
camXmat[i9 + 8],
|
|
4905
|
+
0,
|
|
4906
|
+
0,
|
|
4907
|
+
0,
|
|
4908
|
+
0,
|
|
4909
|
+
1
|
|
4910
|
+
);
|
|
4911
|
+
group.quaternion.setFromRotationMatrix(_cameraMatrix);
|
|
4912
|
+
const fovy = model.cam_fovy?.[cameraId] ?? 45;
|
|
4913
|
+
const halfHeight = Math.tan(THREE11.MathUtils.degToRad(fovy) / 2) * CAMERA_DEBUG_FRUSTUM_DEPTH;
|
|
4914
|
+
const halfWidth = halfHeight * 4 / 3;
|
|
4915
|
+
const positions = group.userData.frustum.geometry.attributes.position;
|
|
4916
|
+
const array = positions.array;
|
|
4917
|
+
const points = [
|
|
4918
|
+
[0, 0, 0],
|
|
4919
|
+
[-halfWidth, halfHeight, -CAMERA_DEBUG_FRUSTUM_DEPTH],
|
|
4920
|
+
[0, 0, 0],
|
|
4921
|
+
[halfWidth, halfHeight, -CAMERA_DEBUG_FRUSTUM_DEPTH],
|
|
4922
|
+
[0, 0, 0],
|
|
4923
|
+
[halfWidth, -halfHeight, -CAMERA_DEBUG_FRUSTUM_DEPTH],
|
|
4924
|
+
[0, 0, 0],
|
|
4925
|
+
[-halfWidth, -halfHeight, -CAMERA_DEBUG_FRUSTUM_DEPTH],
|
|
4926
|
+
[-halfWidth, halfHeight, -CAMERA_DEBUG_FRUSTUM_DEPTH],
|
|
4927
|
+
[halfWidth, halfHeight, -CAMERA_DEBUG_FRUSTUM_DEPTH],
|
|
4928
|
+
[halfWidth, halfHeight, -CAMERA_DEBUG_FRUSTUM_DEPTH],
|
|
4929
|
+
[halfWidth, -halfHeight, -CAMERA_DEBUG_FRUSTUM_DEPTH],
|
|
4930
|
+
[halfWidth, -halfHeight, -CAMERA_DEBUG_FRUSTUM_DEPTH],
|
|
4931
|
+
[-halfWidth, -halfHeight, -CAMERA_DEBUG_FRUSTUM_DEPTH],
|
|
4932
|
+
[-halfWidth, -halfHeight, -CAMERA_DEBUG_FRUSTUM_DEPTH],
|
|
4933
|
+
[-halfWidth, halfHeight, -CAMERA_DEBUG_FRUSTUM_DEPTH]
|
|
4934
|
+
];
|
|
4935
|
+
for (let i = 0; i < points.length; i += 1) {
|
|
4936
|
+
array[i * 3] = points[i][0];
|
|
4937
|
+
array[i * 3 + 1] = points[i][1];
|
|
4938
|
+
array[i * 3 + 2] = points[i][2];
|
|
4939
|
+
}
|
|
4940
|
+
positions.needsUpdate = true;
|
|
4941
|
+
}
|
|
4942
|
+
}
|
|
4570
4943
|
for (const mesh of debugGeometry.comMarkers) {
|
|
4571
4944
|
const bid = mesh.userData.bodyId;
|
|
4572
4945
|
const i3 = bid * 3;
|
|
@@ -4582,9 +4955,9 @@ function Debug({
|
|
|
4582
4955
|
contactPoolInitRef.current = true;
|
|
4583
4956
|
const pool = [];
|
|
4584
4957
|
for (let i = 0; i < MAX_CONTACT_ARROWS; i++) {
|
|
4585
|
-
const arrow = new
|
|
4586
|
-
new
|
|
4587
|
-
new
|
|
4958
|
+
const arrow = new THREE11.ArrowHelper(
|
|
4959
|
+
new THREE11.Vector3(0, 1, 0),
|
|
4960
|
+
new THREE11.Vector3(),
|
|
4588
4961
|
0.1,
|
|
4589
4962
|
16729156,
|
|
4590
4963
|
0.03,
|
|
@@ -4634,14 +5007,24 @@ function Debug({
|
|
|
4634
5007
|
}
|
|
4635
5008
|
});
|
|
4636
5009
|
if (status !== "ready") return null;
|
|
4637
|
-
return /* @__PURE__ */ jsxs(
|
|
4638
|
-
|
|
4639
|
-
|
|
4640
|
-
|
|
5010
|
+
return /* @__PURE__ */ jsxs(
|
|
5011
|
+
"group",
|
|
5012
|
+
{
|
|
5013
|
+
...groupProps,
|
|
5014
|
+
userData: {
|
|
5015
|
+
...groupProps.userData,
|
|
5016
|
+
[CAPTURE_EXCLUDE_KEY]: true
|
|
5017
|
+
},
|
|
5018
|
+
children: [
|
|
5019
|
+
/* @__PURE__ */ jsx("group", { ref: groupRef }),
|
|
5020
|
+
showContacts && /* @__PURE__ */ jsx("group", { ref: contactGroupRef })
|
|
5021
|
+
]
|
|
5022
|
+
}
|
|
5023
|
+
);
|
|
4641
5024
|
}
|
|
4642
|
-
var DEFAULT_TENDON_COLOR = new
|
|
5025
|
+
var DEFAULT_TENDON_COLOR = new THREE11.Color(0.3, 0.3, 0.8);
|
|
4643
5026
|
var DEFAULT_TENDON_WIDTH = 2e-3;
|
|
4644
|
-
new
|
|
5027
|
+
new THREE11.Vector3();
|
|
4645
5028
|
function TendonRenderer(props) {
|
|
4646
5029
|
const { mjModelRef, mjDataRef, status } = useMujocoContext();
|
|
4647
5030
|
const groupRef = useRef(null);
|
|
@@ -4655,7 +5038,7 @@ function TendonRenderer(props) {
|
|
|
4655
5038
|
if (!model || !data || !group) return;
|
|
4656
5039
|
const ntendon = model.ntendon ?? 0;
|
|
4657
5040
|
if (ntendon === 0) return;
|
|
4658
|
-
const material = new
|
|
5041
|
+
const material = new THREE11.MeshStandardMaterial({
|
|
4659
5042
|
color: DEFAULT_TENDON_COLOR,
|
|
4660
5043
|
roughness: 0.6,
|
|
4661
5044
|
metalness: 0.1
|
|
@@ -4670,11 +5053,11 @@ function TendonRenderer(props) {
|
|
|
4670
5053
|
curves.push(null);
|
|
4671
5054
|
continue;
|
|
4672
5055
|
}
|
|
4673
|
-
const points = Array.from({ length: wrapNum }, () => new
|
|
4674
|
-
const curve = new
|
|
5056
|
+
const points = Array.from({ length: wrapNum }, () => new THREE11.Vector3());
|
|
5057
|
+
const curve = new THREE11.CatmullRomCurve3(points, false);
|
|
4675
5058
|
const segments = Math.max(wrapNum * 2, 4);
|
|
4676
|
-
const geometry = new
|
|
4677
|
-
const mesh = new
|
|
5059
|
+
const geometry = new THREE11.TubeGeometry(curve, segments, DEFAULT_TENDON_WIDTH, 6, false);
|
|
5060
|
+
const mesh = new THREE11.Mesh(geometry, material);
|
|
4678
5061
|
mesh.frustumCulled = false;
|
|
4679
5062
|
group.add(mesh);
|
|
4680
5063
|
meshes.push(mesh);
|
|
@@ -4729,11 +5112,11 @@ function TendonRenderer(props) {
|
|
|
4729
5112
|
if (curve.points.length !== validCount) {
|
|
4730
5113
|
curve.points.length = validCount;
|
|
4731
5114
|
while (curve.points.length < validCount) {
|
|
4732
|
-
curve.points.push(new
|
|
5115
|
+
curve.points.push(new THREE11.Vector3());
|
|
4733
5116
|
}
|
|
4734
5117
|
}
|
|
4735
5118
|
mesh.geometry.dispose();
|
|
4736
|
-
mesh.geometry = new
|
|
5119
|
+
mesh.geometry = new THREE11.TubeGeometry(
|
|
4737
5120
|
curve,
|
|
4738
5121
|
Math.max(validCount * 2, 4),
|
|
4739
5122
|
DEFAULT_TENDON_WIDTH,
|
|
@@ -4760,24 +5143,24 @@ function FlexRenderer(props) {
|
|
|
4760
5143
|
const vertAdr = model.flex_vertadr[f];
|
|
4761
5144
|
const vertNum = model.flex_vertnum[f];
|
|
4762
5145
|
if (vertNum === 0) continue;
|
|
4763
|
-
const geometry = new
|
|
5146
|
+
const geometry = new THREE11.BufferGeometry();
|
|
4764
5147
|
const positions = new Float32Array(vertNum * 3);
|
|
4765
|
-
geometry.setAttribute("position", new
|
|
5148
|
+
geometry.setAttribute("position", new THREE11.BufferAttribute(positions, 3));
|
|
4766
5149
|
geometry.computeVertexNormals();
|
|
4767
|
-
let color = new
|
|
5150
|
+
let color = new THREE11.Color(0.5, 0.5, 0.5);
|
|
4768
5151
|
if (model.flex_rgba) {
|
|
4769
|
-
color = new
|
|
5152
|
+
color = new THREE11.Color(
|
|
4770
5153
|
model.flex_rgba[4 * f],
|
|
4771
5154
|
model.flex_rgba[4 * f + 1],
|
|
4772
5155
|
model.flex_rgba[4 * f + 2]
|
|
4773
5156
|
);
|
|
4774
5157
|
}
|
|
4775
|
-
const material = new
|
|
5158
|
+
const material = new THREE11.MeshStandardMaterial({
|
|
4776
5159
|
color,
|
|
4777
5160
|
roughness: 0.7,
|
|
4778
|
-
side:
|
|
5161
|
+
side: THREE11.DoubleSide
|
|
4779
5162
|
});
|
|
4780
|
-
const mesh = new
|
|
5163
|
+
const mesh = new THREE11.Mesh(geometry, material);
|
|
4781
5164
|
mesh.userData.flexId = f;
|
|
4782
5165
|
mesh.userData.vertAdr = vertAdr;
|
|
4783
5166
|
mesh.userData.vertNum = vertNum;
|
|
@@ -4813,7 +5196,7 @@ function FlexRenderer(props) {
|
|
|
4813
5196
|
return /* @__PURE__ */ jsx("group", { ...props, ref: groupRef });
|
|
4814
5197
|
}
|
|
4815
5198
|
var GEOM_TYPE_NAMES2 = ["plane", "hfield", "sphere", "capsule", "ellipsoid", "cylinder", "box", "mesh"];
|
|
4816
|
-
var _matrix = new
|
|
5199
|
+
var _matrix = new THREE11.Matrix4();
|
|
4817
5200
|
function getGeomInfo(model, geomId) {
|
|
4818
5201
|
const size = model.geom_size.subarray(geomId * 3, geomId * 3 + 3);
|
|
4819
5202
|
const type = model.geom_type[geomId];
|
|
@@ -4835,10 +5218,10 @@ function geomSignature(model, geomId) {
|
|
|
4835
5218
|
return [type, size, mat, data, rgba].join("|");
|
|
4836
5219
|
}
|
|
4837
5220
|
function firstMesh(object) {
|
|
4838
|
-
if (object instanceof
|
|
5221
|
+
if (object instanceof THREE11.Mesh) return object;
|
|
4839
5222
|
let mesh = null;
|
|
4840
5223
|
object.traverse((child) => {
|
|
4841
|
-
if (!mesh && child instanceof
|
|
5224
|
+
if (!mesh && child instanceof THREE11.Mesh) mesh = child;
|
|
4842
5225
|
});
|
|
4843
5226
|
return mesh;
|
|
4844
5227
|
}
|
|
@@ -5267,12 +5650,12 @@ function useActuators() {
|
|
|
5267
5650
|
return actuators;
|
|
5268
5651
|
}, [status, mjModelRef]);
|
|
5269
5652
|
}
|
|
5270
|
-
var _mat42 = new
|
|
5653
|
+
var _mat42 = new THREE11.Matrix4();
|
|
5271
5654
|
function useSitePosition(siteName) {
|
|
5272
5655
|
const { mjModelRef, mjDataRef, status } = useMujocoContext();
|
|
5273
5656
|
const siteIdRef = useRef(-1);
|
|
5274
|
-
const positionRef = useRef(new
|
|
5275
|
-
const quaternionRef = useRef(new
|
|
5657
|
+
const positionRef = useRef(new THREE11.Vector3());
|
|
5658
|
+
const quaternionRef = useRef(new THREE11.Quaternion());
|
|
5276
5659
|
useEffect(() => {
|
|
5277
5660
|
const model = mjModelRef.current;
|
|
5278
5661
|
if (!model || status !== "ready") {
|
|
@@ -5463,10 +5846,10 @@ function useJointState(name) {
|
|
|
5463
5846
|
function useBodyState(name) {
|
|
5464
5847
|
const { mjModelRef, status } = useMujocoContext();
|
|
5465
5848
|
const bodyIdRef = useRef(-1);
|
|
5466
|
-
const position = useRef(new
|
|
5467
|
-
const quaternion = useRef(new
|
|
5468
|
-
const linearVelocity = useRef(new
|
|
5469
|
-
const angularVelocity = useRef(new
|
|
5849
|
+
const position = useRef(new THREE11.Vector3());
|
|
5850
|
+
const quaternion = useRef(new THREE11.Quaternion());
|
|
5851
|
+
const linearVelocity = useRef(new THREE11.Vector3());
|
|
5852
|
+
const angularVelocity = useRef(new THREE11.Vector3());
|
|
5470
5853
|
useEffect(() => {
|
|
5471
5854
|
const model = mjModelRef.current;
|
|
5472
5855
|
if (!model || status !== "ready") return;
|
|
@@ -5923,22 +6306,46 @@ function useMountedCameraSequenceRecorder(defaultOptions = {}) {
|
|
|
5923
6306
|
const mujoco = useMujoco();
|
|
5924
6307
|
const [status, setStatus] = useState("idle");
|
|
5925
6308
|
const [error, setError] = useState(null);
|
|
6309
|
+
const [plan, setPlan] = useState(null);
|
|
6310
|
+
const [readiness, setReadiness] = useState(null);
|
|
6311
|
+
const [result, setResult] = useState(
|
|
6312
|
+
null
|
|
6313
|
+
);
|
|
5926
6314
|
const reset = useCallback(() => {
|
|
5927
6315
|
setStatus("idle");
|
|
5928
6316
|
setError(null);
|
|
6317
|
+
setPlan(null);
|
|
6318
|
+
setReadiness(null);
|
|
6319
|
+
setResult(null);
|
|
5929
6320
|
}, []);
|
|
5930
6321
|
const createPlan = useCallback(
|
|
5931
6322
|
(cameraKeys, options = {}) => {
|
|
5932
6323
|
if (!mujoco.api) {
|
|
5933
6324
|
throw new Error("MuJoCo scene is not ready for mounted camera sequence planning.");
|
|
5934
6325
|
}
|
|
5935
|
-
|
|
5936
|
-
|
|
5937
|
-
|
|
5938
|
-
|
|
6326
|
+
const nextPlan = createMountedCameraFrameSequencePlanFromApi(
|
|
6327
|
+
mujoco.api,
|
|
6328
|
+
cameraKeys,
|
|
6329
|
+
{
|
|
6330
|
+
...defaultOptions,
|
|
6331
|
+
...options
|
|
6332
|
+
}
|
|
6333
|
+
);
|
|
6334
|
+
setPlan(nextPlan);
|
|
6335
|
+
setReadiness(null);
|
|
6336
|
+
return nextPlan;
|
|
5939
6337
|
},
|
|
5940
6338
|
[defaultOptions, mujoco.api]
|
|
5941
6339
|
);
|
|
6340
|
+
const checkReadiness = useCallback(
|
|
6341
|
+
(cameraKeys, options = {}) => {
|
|
6342
|
+
const nextPlan = createPlan(cameraKeys, options);
|
|
6343
|
+
const nextReadiness = createMountedCameraFrameSequenceReadiness(nextPlan);
|
|
6344
|
+
setReadiness(nextReadiness);
|
|
6345
|
+
return nextReadiness;
|
|
6346
|
+
},
|
|
6347
|
+
[createPlan]
|
|
6348
|
+
);
|
|
5942
6349
|
const record = useCallback(
|
|
5943
6350
|
async (options) => {
|
|
5944
6351
|
if (!mujoco.api) {
|
|
@@ -5946,13 +6353,17 @@ function useMountedCameraSequenceRecorder(defaultOptions = {}) {
|
|
|
5946
6353
|
}
|
|
5947
6354
|
setStatus("capturing");
|
|
5948
6355
|
setError(null);
|
|
6356
|
+
setResult(null);
|
|
5949
6357
|
try {
|
|
5950
|
-
const
|
|
6358
|
+
const nextResult = await recordMountedCameraFrameSequence(mujoco.api, {
|
|
5951
6359
|
...defaultOptions,
|
|
5952
6360
|
...options
|
|
5953
6361
|
});
|
|
6362
|
+
setPlan(nextResult.plan);
|
|
6363
|
+
setReadiness(nextResult.readiness);
|
|
6364
|
+
setResult(nextResult);
|
|
5954
6365
|
setStatus("captured");
|
|
5955
|
-
return
|
|
6366
|
+
return nextResult;
|
|
5956
6367
|
} catch (nextError) {
|
|
5957
6368
|
const error2 = nextError instanceof Error ? nextError : new Error("Unable to record the requested mounted camera sequence.");
|
|
5958
6369
|
setError(error2);
|
|
@@ -5965,8 +6376,12 @@ function useMountedCameraSequenceRecorder(defaultOptions = {}) {
|
|
|
5965
6376
|
return {
|
|
5966
6377
|
status,
|
|
5967
6378
|
error,
|
|
6379
|
+
plan,
|
|
6380
|
+
readiness,
|
|
6381
|
+
result,
|
|
5968
6382
|
isRecording: status === "capturing",
|
|
5969
6383
|
createPlan,
|
|
6384
|
+
checkReadiness,
|
|
5970
6385
|
record,
|
|
5971
6386
|
reset
|
|
5972
6387
|
};
|
|
@@ -6022,7 +6437,7 @@ function useSelectionHighlight(bodyId, options = {}) {
|
|
|
6022
6437
|
}
|
|
6023
6438
|
}
|
|
6024
6439
|
prevRef.current = [];
|
|
6025
|
-
const highlightColor = new
|
|
6440
|
+
const highlightColor = new THREE11.Color(color);
|
|
6026
6441
|
for (const mesh of meshes) {
|
|
6027
6442
|
const mat = mesh.material;
|
|
6028
6443
|
if (mat.emissive) {
|
|
@@ -6049,15 +6464,15 @@ function useSelectionHighlight(bodyId, options = {}) {
|
|
|
6049
6464
|
}
|
|
6050
6465
|
function useCameraAnimation() {
|
|
6051
6466
|
const { camera } = useThree();
|
|
6052
|
-
const orbitTargetRef = useRef(new
|
|
6467
|
+
const orbitTargetRef = useRef(new THREE11.Vector3(0, 0, 0));
|
|
6053
6468
|
const cameraAnimRef = useRef({
|
|
6054
6469
|
active: false,
|
|
6055
|
-
startPos: new
|
|
6056
|
-
endPos: new
|
|
6057
|
-
startRot: new
|
|
6058
|
-
endRot: new
|
|
6059
|
-
startTarget: new
|
|
6060
|
-
endTarget: new
|
|
6470
|
+
startPos: new THREE11.Vector3(),
|
|
6471
|
+
endPos: new THREE11.Vector3(),
|
|
6472
|
+
startRot: new THREE11.Quaternion(),
|
|
6473
|
+
endRot: new THREE11.Quaternion(),
|
|
6474
|
+
startTarget: new THREE11.Vector3(),
|
|
6475
|
+
endTarget: new THREE11.Vector3(),
|
|
6061
6476
|
startTime: 0,
|
|
6062
6477
|
duration: 0,
|
|
6063
6478
|
resolve: null
|
|
@@ -6129,12 +6544,6 @@ function useCameraAnimation() {
|
|
|
6129
6544
|
*
|
|
6130
6545
|
* useFrameCapture — still-frame capture for canvas-backed MuJoCo/R3F scenes.
|
|
6131
6546
|
*/
|
|
6132
|
-
/**
|
|
6133
|
-
* @license
|
|
6134
|
-
* SPDX-License-Identifier: Apache-2.0
|
|
6135
|
-
*
|
|
6136
|
-
* Offscreen camera-frame capture for R3F/MuJoCo scenes.
|
|
6137
|
-
*/
|
|
6138
6547
|
/**
|
|
6139
6548
|
* @license
|
|
6140
6549
|
* SPDX-License-Identifier: Apache-2.0
|
|
@@ -6322,6 +6731,6 @@ function useCameraAnimation() {
|
|
|
6322
6731
|
* useCameraAnimation — composable camera animation hook.
|
|
6323
6732
|
*/
|
|
6324
6733
|
|
|
6325
|
-
export { Body, ContactListener, ContactMarkers, Debug, DragInteraction, FlexRenderer, IkGizmo, InstancedGeomRenderer, MountedCameraFrameSequenceReadinessStatus, MujocoCanvas, MujocoPhysics, MujocoProvider, MujocoSimProvider, SceneLights, TendonRenderer, TrajectoryPlayer, buildObservation,
|
|
6734
|
+
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, useKeyboardTeleop, useMountedCameraSequenceRecorder, useMujoco, useMujocoWasm, useObservation, usePolicy, useSceneLights, useSelectionHighlight, useSensor, useSensors, useSitePosition, useSplatCollisionProxyGeoms, useTrajectoryPlayer, useTrajectoryRecorder, useVideoRecorder };
|
|
6326
6735
|
//# sourceMappingURL=index.js.map
|
|
6327
6736
|
//# sourceMappingURL=index.js.map
|