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