mujoco-react 10.0.1 → 10.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-QTCAVQS6.js → chunk-3BMNRSS2.js} +56 -5
- package/dist/chunk-3BMNRSS2.js.map +1 -0
- package/dist/index.d.ts +281 -19
- package/dist/index.js +1451 -258
- 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-CLD5K3JD.d.ts} +163 -2
- package/package.json +1 -1
- package/src/components/SceneRenderer.tsx +11 -4
- package/src/core/MujocoSimProvider.tsx +97 -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 +88 -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/rendering/imageProjection.ts +186 -0
- package/src/types.ts +188 -1
- package/dist/chunk-QTCAVQS6.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
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-3BMNRSS2.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-3BMNRSS2.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';
|
|
@@ -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 THREE11.DataTexture(rgba, width, height, THREE11.RGBAFormat);
|
|
934
|
+
texture.colorSpace = THREE11.LinearSRGBColorSpace;
|
|
935
|
+
texture.wrapS = THREE11.RepeatWrapping;
|
|
936
|
+
texture.wrapT = THREE11.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 THREE11.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 THREE11.PlaneGeometry(size[0] * 2 || 5, size[1] * 2 || 5);
|
|
974
|
+
} else if (type === getVal(MG.mjGEOM_SPHERE)) {
|
|
975
|
+
geo = new THREE11.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 THREE11.BoxGeometry(size[0] * 2, size[1] * 2, size[2] * 2);
|
|
981
|
+
} else if (type === getVal(MG.mjGEOM_CYLINDER)) {
|
|
982
|
+
geo = new THREE11.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 THREE11.BufferGeometry();
|
|
991
|
+
geo.setAttribute("position", new THREE11.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 THREE11.Mesh(geo, new THREE11.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,
|
|
@@ -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;
|
|
@@ -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();
|
|
@@ -1735,6 +1648,125 @@ async function recordMountedCameraFrameSequence(api, options) {
|
|
|
1735
1648
|
readiness
|
|
1736
1649
|
};
|
|
1737
1650
|
}
|
|
1651
|
+
var _raycaster = new THREE11.Raycaster();
|
|
1652
|
+
var _ndc = new THREE11.Vector2();
|
|
1653
|
+
function toVector3(value, fallback) {
|
|
1654
|
+
if (!value) return fallback.clone();
|
|
1655
|
+
return value instanceof THREE11.Vector3 ? value.clone() : new THREE11.Vector3(value[0], value[1], value[2]);
|
|
1656
|
+
}
|
|
1657
|
+
function applyCameraPose(camera, options, fallbackCamera) {
|
|
1658
|
+
camera.position.copy(toVector3(options.position, fallbackCamera.position));
|
|
1659
|
+
camera.up.copy(toVector3(options.up, fallbackCamera.up));
|
|
1660
|
+
if (options.quaternion) {
|
|
1661
|
+
if (options.quaternion instanceof THREE11.Quaternion) {
|
|
1662
|
+
camera.quaternion.copy(options.quaternion);
|
|
1663
|
+
} else {
|
|
1664
|
+
camera.quaternion.set(
|
|
1665
|
+
options.quaternion[0],
|
|
1666
|
+
options.quaternion[1],
|
|
1667
|
+
options.quaternion[2],
|
|
1668
|
+
options.quaternion[3]
|
|
1669
|
+
);
|
|
1670
|
+
}
|
|
1671
|
+
} else if (options.lookAt) {
|
|
1672
|
+
camera.lookAt(toVector3(options.lookAt, new THREE11.Vector3()));
|
|
1673
|
+
} else {
|
|
1674
|
+
camera.quaternion.copy(fallbackCamera.quaternion);
|
|
1675
|
+
}
|
|
1676
|
+
camera.updateMatrixWorld();
|
|
1677
|
+
}
|
|
1678
|
+
function createProjectionCamera(fallbackCamera, options, width, height) {
|
|
1679
|
+
const camera = options.camera ? options.camera.clone() : fallbackCamera instanceof THREE11.PerspectiveCamera ? fallbackCamera.clone() : new THREE11.PerspectiveCamera(45, width / height, 0.01, 100);
|
|
1680
|
+
if (camera instanceof THREE11.PerspectiveCamera) {
|
|
1681
|
+
camera.aspect = width / height;
|
|
1682
|
+
camera.fov = options.fov ?? camera.fov;
|
|
1683
|
+
camera.near = options.near ?? camera.near;
|
|
1684
|
+
camera.far = options.far ?? camera.far;
|
|
1685
|
+
camera.updateProjectionMatrix();
|
|
1686
|
+
}
|
|
1687
|
+
applyCameraPose(camera, options, fallbackCamera);
|
|
1688
|
+
return camera;
|
|
1689
|
+
}
|
|
1690
|
+
function getProjectionSource(options) {
|
|
1691
|
+
if (options.source) return options.source;
|
|
1692
|
+
if (options.cameraName) return { kind: "mujoco-camera", cameraName: options.cameraName };
|
|
1693
|
+
if (options.siteName) return { kind: "mujoco-site", siteName: options.siteName };
|
|
1694
|
+
if (options.bodyName) return { kind: "mujoco-body", bodyName: options.bodyName };
|
|
1695
|
+
if (options.camera) return { kind: "custom-camera" };
|
|
1696
|
+
if (options.position || options.lookAt || options.quaternion) return { kind: "explicit-pose" };
|
|
1697
|
+
return { kind: "fallback-camera" };
|
|
1698
|
+
}
|
|
1699
|
+
function imageSize(renderer, options) {
|
|
1700
|
+
return [
|
|
1701
|
+
Math.max(1, Math.floor(options.imageWidth ?? options.width ?? renderer.domElement.width)),
|
|
1702
|
+
Math.max(1, Math.floor(options.imageHeight ?? options.height ?? renderer.domElement.height))
|
|
1703
|
+
];
|
|
1704
|
+
}
|
|
1705
|
+
function imagePointToNdc(x, y, coordinateSpace = "normalized", width = 1, height = 1) {
|
|
1706
|
+
if (coordinateSpace === "ndc") return [x, y];
|
|
1707
|
+
if (coordinateSpace === "normalized-1000") {
|
|
1708
|
+
return [x / 1e3 * 2 - 1, 1 - y / 1e3 * 2];
|
|
1709
|
+
}
|
|
1710
|
+
if (coordinateSpace === "pixel") {
|
|
1711
|
+
return [x / width * 2 - 1, 1 - y / height * 2];
|
|
1712
|
+
}
|
|
1713
|
+
return [x * 2 - 1, 1 - y * 2];
|
|
1714
|
+
}
|
|
1715
|
+
function isProjectionCandidate(object, options) {
|
|
1716
|
+
if (!object.visible) return false;
|
|
1717
|
+
if (object.userData[CAPTURE_EXCLUDE_KEY]) return false;
|
|
1718
|
+
const geomGroup = object.userData.geomGroup;
|
|
1719
|
+
const geomName = object.userData.geomName;
|
|
1720
|
+
if (options.hiddenGeomNames && typeof geomName === "string" && options.hiddenGeomNames.includes(geomName)) {
|
|
1721
|
+
return false;
|
|
1722
|
+
}
|
|
1723
|
+
if (options.hiddenGeomGroups && typeof geomGroup === "number" && options.hiddenGeomGroups.includes(geomGroup)) {
|
|
1724
|
+
return false;
|
|
1725
|
+
}
|
|
1726
|
+
if (options.visibleGeomGroups && typeof geomGroup === "number" && !options.visibleGeomGroups.includes(geomGroup)) {
|
|
1727
|
+
return false;
|
|
1728
|
+
}
|
|
1729
|
+
return true;
|
|
1730
|
+
}
|
|
1731
|
+
function findBodyId(object) {
|
|
1732
|
+
let current = object;
|
|
1733
|
+
while (current && current.userData.bodyID === void 0 && current.parent) {
|
|
1734
|
+
current = current.parent;
|
|
1735
|
+
}
|
|
1736
|
+
return typeof current?.userData.bodyID === "number" ? current.userData.bodyID : -1;
|
|
1737
|
+
}
|
|
1738
|
+
function projectImagePointTo3D(renderer, scene, fallbackCamera, options) {
|
|
1739
|
+
const [width, height] = imageSize(renderer, options);
|
|
1740
|
+
const [ndcX, ndcY] = imagePointToNdc(
|
|
1741
|
+
options.x,
|
|
1742
|
+
options.y,
|
|
1743
|
+
options.coordinateSpace,
|
|
1744
|
+
width,
|
|
1745
|
+
height
|
|
1746
|
+
);
|
|
1747
|
+
const projectionCamera = createProjectionCamera(fallbackCamera, options, width, height);
|
|
1748
|
+
scene.updateMatrixWorld(true);
|
|
1749
|
+
_ndc.set(ndcX, ndcY);
|
|
1750
|
+
_raycaster.setFromCamera(_ndc, projectionCamera);
|
|
1751
|
+
_raycaster.far = options.maxDistance ?? Infinity;
|
|
1752
|
+
const objects = [];
|
|
1753
|
+
scene.traverse((object) => {
|
|
1754
|
+
if (object.isMesh && isProjectionCandidate(object, options)) {
|
|
1755
|
+
objects.push(object);
|
|
1756
|
+
}
|
|
1757
|
+
});
|
|
1758
|
+
const [hit] = _raycaster.intersectObjects(objects, true);
|
|
1759
|
+
if (!hit) return null;
|
|
1760
|
+
return {
|
|
1761
|
+
point: hit.point.clone(),
|
|
1762
|
+
bodyId: findBodyId(hit.object),
|
|
1763
|
+
geomId: typeof hit.object.userData.geomID === "number" ? hit.object.userData.geomID : -1,
|
|
1764
|
+
distance: hit.distance,
|
|
1765
|
+
ndc: [ndcX, ndcY],
|
|
1766
|
+
imageSize: [width, height],
|
|
1767
|
+
source: getProjectionSource(options)
|
|
1768
|
+
};
|
|
1769
|
+
}
|
|
1738
1770
|
var JOINT_TYPE_NAMES2 = ["free", "ball", "slide", "hinge"];
|
|
1739
1771
|
var GEOM_TYPE_NAMES = ["plane", "hfield", "sphere", "capsule", "ellipsoid", "cylinder", "box", "mesh"];
|
|
1740
1772
|
var SENSOR_TYPE_NAMES = {
|
|
@@ -1862,6 +1894,42 @@ function omitResolvedCameraSelectors(options) {
|
|
|
1862
1894
|
const { cameraName, siteName, bodyName, ...rest } = options;
|
|
1863
1895
|
return rest;
|
|
1864
1896
|
}
|
|
1897
|
+
function vector3FromCaptureValue(value) {
|
|
1898
|
+
return value instanceof THREE11.Vector3 ? value.clone() : new THREE11.Vector3(value[0], value[1], value[2]);
|
|
1899
|
+
}
|
|
1900
|
+
function quaternionFromCaptureValue(value) {
|
|
1901
|
+
return value instanceof THREE11.Quaternion ? value.clone() : new THREE11.Quaternion(value[0], value[1], value[2], value[3]);
|
|
1902
|
+
}
|
|
1903
|
+
function applyMountedCameraPoseOffsets(options, position, quaternion) {
|
|
1904
|
+
const resolvedPosition = new THREE11.Vector3(position[0], position[1], position[2]);
|
|
1905
|
+
const resolvedQuaternion = new THREE11.Quaternion(
|
|
1906
|
+
quaternion[0],
|
|
1907
|
+
quaternion[1],
|
|
1908
|
+
quaternion[2],
|
|
1909
|
+
quaternion[3]
|
|
1910
|
+
);
|
|
1911
|
+
if (options.positionOffset) {
|
|
1912
|
+
resolvedPosition.add(
|
|
1913
|
+
vector3FromCaptureValue(options.positionOffset).applyQuaternion(resolvedQuaternion)
|
|
1914
|
+
);
|
|
1915
|
+
}
|
|
1916
|
+
if (options.quaternionOffset) {
|
|
1917
|
+
resolvedQuaternion.multiply(quaternionFromCaptureValue(options.quaternionOffset)).normalize();
|
|
1918
|
+
}
|
|
1919
|
+
return {
|
|
1920
|
+
position: [
|
|
1921
|
+
resolvedPosition.x,
|
|
1922
|
+
resolvedPosition.y,
|
|
1923
|
+
resolvedPosition.z
|
|
1924
|
+
],
|
|
1925
|
+
quaternion: [
|
|
1926
|
+
resolvedQuaternion.x,
|
|
1927
|
+
resolvedQuaternion.y,
|
|
1928
|
+
resolvedQuaternion.z,
|
|
1929
|
+
resolvedQuaternion.w
|
|
1930
|
+
]
|
|
1931
|
+
};
|
|
1932
|
+
}
|
|
1865
1933
|
function countMountedCameraSelectors(options) {
|
|
1866
1934
|
return Number(Boolean(options.cameraName)) + Number(Boolean(options.siteName)) + Number(Boolean(options.bodyName));
|
|
1867
1935
|
}
|
|
@@ -2553,10 +2621,10 @@ function MujocoSimProvider({
|
|
|
2553
2621
|
`MuJoCo camera "${options.cameraName}" does not expose a capture pose.`
|
|
2554
2622
|
);
|
|
2555
2623
|
}
|
|
2624
|
+
const pose = applyMountedCameraPoseOffsets(options, position, quaternion);
|
|
2556
2625
|
return {
|
|
2557
2626
|
...baseOptions,
|
|
2558
|
-
|
|
2559
|
-
quaternion,
|
|
2627
|
+
...pose,
|
|
2560
2628
|
fov: options.fov ?? model.cam_fovy?.[cameraId],
|
|
2561
2629
|
source: { kind: "mujoco-camera", cameraName: options.cameraName }
|
|
2562
2630
|
};
|
|
@@ -2566,10 +2634,14 @@ function MujocoSimProvider({
|
|
|
2566
2634
|
if (siteId < 0) {
|
|
2567
2635
|
throw new Error(`MuJoCo site "${options.siteName}" was not found.`);
|
|
2568
2636
|
}
|
|
2637
|
+
const pose = applyMountedCameraPoseOffsets(
|
|
2638
|
+
options,
|
|
2639
|
+
vector3FromArray(data.site_xpos, siteId * 3),
|
|
2640
|
+
quaternionFromXmat(data.site_xmat, siteId * 9)
|
|
2641
|
+
);
|
|
2569
2642
|
return {
|
|
2570
2643
|
...baseOptions,
|
|
2571
|
-
|
|
2572
|
-
quaternion: quaternionFromXmat(data.site_xmat, siteId * 9),
|
|
2644
|
+
...pose,
|
|
2573
2645
|
source: { kind: "mujoco-site", siteName: options.siteName }
|
|
2574
2646
|
};
|
|
2575
2647
|
}
|
|
@@ -2583,10 +2655,14 @@ function MujocoSimProvider({
|
|
|
2583
2655
|
`MuJoCo body "${options.bodyName}" does not expose world orientation data.`
|
|
2584
2656
|
);
|
|
2585
2657
|
}
|
|
2658
|
+
const pose = applyMountedCameraPoseOffsets(
|
|
2659
|
+
options,
|
|
2660
|
+
vector3FromArray(data.xpos, bodyId * 3),
|
|
2661
|
+
quaternionFromXmat(data.xmat, bodyId * 9)
|
|
2662
|
+
);
|
|
2586
2663
|
return {
|
|
2587
2664
|
...baseOptions,
|
|
2588
|
-
|
|
2589
|
-
quaternion: quaternionFromXmat(data.xmat, bodyId * 9),
|
|
2665
|
+
...pose,
|
|
2590
2666
|
source: { kind: "mujoco-body", bodyName: options.bodyName }
|
|
2591
2667
|
};
|
|
2592
2668
|
}
|
|
@@ -2994,6 +3070,30 @@ function MujocoSimProvider({
|
|
|
2994
3070
|
},
|
|
2995
3071
|
[camera, gl]
|
|
2996
3072
|
);
|
|
3073
|
+
const projectImagePointTo3D2 = useCallback(
|
|
3074
|
+
(options) => {
|
|
3075
|
+
const {
|
|
3076
|
+
x,
|
|
3077
|
+
y,
|
|
3078
|
+
coordinateSpace,
|
|
3079
|
+
imageWidth,
|
|
3080
|
+
imageHeight,
|
|
3081
|
+
maxDistance,
|
|
3082
|
+
...captureOptions
|
|
3083
|
+
} = options;
|
|
3084
|
+
const resolvedCaptureOptions = resolveCameraCaptureOptions(captureOptions);
|
|
3085
|
+
return projectImagePointTo3D(gl, scene, camera, {
|
|
3086
|
+
...resolvedCaptureOptions,
|
|
3087
|
+
x,
|
|
3088
|
+
y,
|
|
3089
|
+
coordinateSpace,
|
|
3090
|
+
imageWidth,
|
|
3091
|
+
imageHeight,
|
|
3092
|
+
maxDistance
|
|
3093
|
+
});
|
|
3094
|
+
},
|
|
3095
|
+
[camera, gl, resolveCameraCaptureOptions, scene]
|
|
3096
|
+
);
|
|
2997
3097
|
const setBodyMass = useCallback((name, mass) => {
|
|
2998
3098
|
const model = mjModelRef.current;
|
|
2999
3099
|
if (!model) return;
|
|
@@ -3078,6 +3178,7 @@ function MujocoSimProvider({
|
|
|
3078
3178
|
captureCameraFrameBlob: captureCameraFrameBlobApi,
|
|
3079
3179
|
recordCameraSequence: recordCameraSequenceApi,
|
|
3080
3180
|
project2DTo3D,
|
|
3181
|
+
projectImagePointTo3D: projectImagePointTo3D2,
|
|
3081
3182
|
setBodyMass,
|
|
3082
3183
|
setGeomFriction,
|
|
3083
3184
|
setGeomSize,
|
|
@@ -3137,6 +3238,7 @@ function MujocoSimProvider({
|
|
|
3137
3238
|
captureCameraFrameBlobApi,
|
|
3138
3239
|
recordCameraSequenceApi,
|
|
3139
3240
|
project2DTo3D,
|
|
3241
|
+
projectImagePointTo3D2,
|
|
3140
3242
|
setBodyMass,
|
|
3141
3243
|
setGeomFriction,
|
|
3142
3244
|
setGeomSize
|
|
@@ -3724,8 +3826,8 @@ var useIkController = createControllerHook(
|
|
|
3724
3826
|
const ga = gizmoAnimRef.current;
|
|
3725
3827
|
const target = ikTargetRef.current;
|
|
3726
3828
|
if (!ga.active || !target) return;
|
|
3727
|
-
const
|
|
3728
|
-
const elapsed =
|
|
3829
|
+
const now2 = performance.now();
|
|
3830
|
+
const elapsed = now2 - ga.startTime;
|
|
3729
3831
|
const t = Math.min(elapsed / ga.duration, 1);
|
|
3730
3832
|
const ease = 1 - Math.pow(1 - t, 3);
|
|
3731
3833
|
target.position.lerpVectors(ga.startPos, ga.endPos, ease);
|
|
@@ -4088,7 +4190,7 @@ var _point = new Float64Array(3);
|
|
|
4088
4190
|
var _bodyPos = new THREE11.Vector3();
|
|
4089
4191
|
var _bodyQuat = new THREE11.Quaternion();
|
|
4090
4192
|
var _worldHit = new THREE11.Vector3();
|
|
4091
|
-
var
|
|
4193
|
+
var _raycaster2 = new THREE11.Raycaster();
|
|
4092
4194
|
var _mouse = new THREE11.Vector2();
|
|
4093
4195
|
function DragInteraction({
|
|
4094
4196
|
stiffness = 250,
|
|
@@ -4137,8 +4239,8 @@ function DragInteraction({
|
|
|
4137
4239
|
(evt.clientX - rect.left) / rect.width * 2 - 1,
|
|
4138
4240
|
-((evt.clientY - rect.top) / rect.height) * 2 + 1
|
|
4139
4241
|
);
|
|
4140
|
-
|
|
4141
|
-
const hits =
|
|
4242
|
+
_raycaster2.setFromCamera(_mouse, camera);
|
|
4243
|
+
const hits = _raycaster2.intersectObjects(scene.children, true);
|
|
4142
4244
|
for (const hit of hits) {
|
|
4143
4245
|
let obj = hit.object;
|
|
4144
4246
|
while (obj && obj.userData.bodyID === void 0 && obj.parent) {
|
|
@@ -4183,8 +4285,8 @@ function DragInteraction({
|
|
|
4183
4285
|
(evt.clientX - rect.left) / rect.width * 2 - 1,
|
|
4184
4286
|
-((evt.clientY - rect.top) / rect.height) * 2 + 1
|
|
4185
4287
|
);
|
|
4186
|
-
|
|
4187
|
-
mouseWorldRef.current.copy(
|
|
4288
|
+
_raycaster2.setFromCamera(_mouse, camera);
|
|
4289
|
+
mouseWorldRef.current.copy(_raycaster2.ray.origin).addScaledVector(_raycaster2.ray.direction, grabDistanceRef.current);
|
|
4188
4290
|
};
|
|
4189
4291
|
const onPointerUp = () => {
|
|
4190
4292
|
if (!draggingRef.current) return;
|
|
@@ -5532,12 +5634,12 @@ function useTrajectoryPlayer(trajectory, options = {}) {
|
|
|
5532
5634
|
if ((optionsRef.current.mode ?? "kinematic") !== "kinematic") return;
|
|
5533
5635
|
const traj = trajectoryRef.current;
|
|
5534
5636
|
if (traj.length === 0) return;
|
|
5535
|
-
const
|
|
5637
|
+
const now2 = performance.now();
|
|
5536
5638
|
const fps = optionsRef.current.fps ?? 30;
|
|
5537
5639
|
const frameInterval = 1e3 / (fps * speedRef.current);
|
|
5538
|
-
const elapsed =
|
|
5640
|
+
const elapsed = now2 - lastFrameTimeRef.current;
|
|
5539
5641
|
if (elapsed < frameInterval) return;
|
|
5540
|
-
lastFrameTimeRef.current =
|
|
5642
|
+
lastFrameTimeRef.current = now2;
|
|
5541
5643
|
const model = mjModelRef.current;
|
|
5542
5644
|
const data = mjDataRef.current;
|
|
5543
5645
|
if (!model || !data) return;
|
|
@@ -5894,6 +5996,93 @@ function useBodyState(name) {
|
|
|
5894
5996
|
});
|
|
5895
5997
|
return { position, quaternion, linearVelocity, angularVelocity };
|
|
5896
5998
|
}
|
|
5999
|
+
var _matrix2 = new THREE11.Matrix4();
|
|
6000
|
+
function quaternionFromMatrixArray(target, values, offset) {
|
|
6001
|
+
_matrix2.set(
|
|
6002
|
+
values[offset],
|
|
6003
|
+
values[offset + 1],
|
|
6004
|
+
values[offset + 2],
|
|
6005
|
+
0,
|
|
6006
|
+
values[offset + 3],
|
|
6007
|
+
values[offset + 4],
|
|
6008
|
+
values[offset + 5],
|
|
6009
|
+
0,
|
|
6010
|
+
values[offset + 6],
|
|
6011
|
+
values[offset + 7],
|
|
6012
|
+
values[offset + 8],
|
|
6013
|
+
0,
|
|
6014
|
+
0,
|
|
6015
|
+
0,
|
|
6016
|
+
0,
|
|
6017
|
+
1
|
|
6018
|
+
);
|
|
6019
|
+
target.setFromRotationMatrix(_matrix2);
|
|
6020
|
+
}
|
|
6021
|
+
function quaternionFromMujocoQuat2(target, values, offset) {
|
|
6022
|
+
target.set(
|
|
6023
|
+
values[offset + 1] ?? 0,
|
|
6024
|
+
values[offset + 2] ?? 0,
|
|
6025
|
+
values[offset + 3] ?? 0,
|
|
6026
|
+
values[offset] ?? 1
|
|
6027
|
+
);
|
|
6028
|
+
}
|
|
6029
|
+
function useNamedPose(kind, name) {
|
|
6030
|
+
const { mjModelRef, status } = useMujocoContext();
|
|
6031
|
+
const idRef = useRef(-1);
|
|
6032
|
+
const foundRef = useRef(false);
|
|
6033
|
+
const positionRef = useRef(new THREE11.Vector3());
|
|
6034
|
+
const quaternionRef = useRef(new THREE11.Quaternion());
|
|
6035
|
+
useEffect(() => {
|
|
6036
|
+
const model = mjModelRef.current;
|
|
6037
|
+
if (!model || status !== "ready") {
|
|
6038
|
+
idRef.current = -1;
|
|
6039
|
+
foundRef.current = false;
|
|
6040
|
+
return;
|
|
6041
|
+
}
|
|
6042
|
+
if (kind === "body") idRef.current = findBodyByName(model, name);
|
|
6043
|
+
else if (kind === "geom") idRef.current = findGeomByName(model, name);
|
|
6044
|
+
else idRef.current = findSiteByName(model, name);
|
|
6045
|
+
foundRef.current = idRef.current >= 0;
|
|
6046
|
+
}, [kind, name, status, mjModelRef]);
|
|
6047
|
+
useAfterPhysicsStep(({ data }) => {
|
|
6048
|
+
const id = idRef.current;
|
|
6049
|
+
if (id < 0) return;
|
|
6050
|
+
if (kind === "body") {
|
|
6051
|
+
const p2 = id * 3;
|
|
6052
|
+
positionRef.current.set(data.xpos[p2], data.xpos[p2 + 1], data.xpos[p2 + 2]);
|
|
6053
|
+
if (data.xmat) {
|
|
6054
|
+
quaternionFromMatrixArray(quaternionRef.current, data.xmat, id * 9);
|
|
6055
|
+
} else {
|
|
6056
|
+
quaternionFromMujocoQuat2(quaternionRef.current, data.xquat, id * 4);
|
|
6057
|
+
}
|
|
6058
|
+
return;
|
|
6059
|
+
}
|
|
6060
|
+
if (kind === "geom") {
|
|
6061
|
+
const p2 = id * 3;
|
|
6062
|
+
positionRef.current.set(data.geom_xpos[p2], data.geom_xpos[p2 + 1], data.geom_xpos[p2 + 2]);
|
|
6063
|
+
quaternionFromMatrixArray(quaternionRef.current, data.geom_xmat, id * 9);
|
|
6064
|
+
return;
|
|
6065
|
+
}
|
|
6066
|
+
const p = id * 3;
|
|
6067
|
+
positionRef.current.set(data.site_xpos[p], data.site_xpos[p + 1], data.site_xpos[p + 2]);
|
|
6068
|
+
quaternionFromMatrixArray(quaternionRef.current, data.site_xmat, id * 9);
|
|
6069
|
+
});
|
|
6070
|
+
return {
|
|
6071
|
+
id: idRef,
|
|
6072
|
+
found: foundRef,
|
|
6073
|
+
position: positionRef,
|
|
6074
|
+
quaternion: quaternionRef
|
|
6075
|
+
};
|
|
6076
|
+
}
|
|
6077
|
+
function useBodyPose(name) {
|
|
6078
|
+
return useNamedPose("body", name);
|
|
6079
|
+
}
|
|
6080
|
+
function useGeomPose(name) {
|
|
6081
|
+
return useNamedPose("geom", name);
|
|
6082
|
+
}
|
|
6083
|
+
function useSitePose(name) {
|
|
6084
|
+
return useNamedPose("site", name);
|
|
6085
|
+
}
|
|
5897
6086
|
function useCtrl(name) {
|
|
5898
6087
|
const { mjModelRef, mjDataRef, status } = useMujocoContext();
|
|
5899
6088
|
const actuatorIdRef = useRef(-1);
|
|
@@ -5927,10 +6116,203 @@ function useCtrl(name) {
|
|
|
5927
6116
|
}
|
|
5928
6117
|
}), [name, mjDataRef]);
|
|
5929
6118
|
}
|
|
5930
|
-
|
|
5931
|
-
|
|
5932
|
-
|
|
5933
|
-
|
|
6119
|
+
var claimsByModel = /* @__PURE__ */ new WeakMap();
|
|
6120
|
+
function getClaims(model) {
|
|
6121
|
+
let claims = claimsByModel.get(model);
|
|
6122
|
+
if (!claims) {
|
|
6123
|
+
claims = /* @__PURE__ */ new Map();
|
|
6124
|
+
claimsByModel.set(model, claims);
|
|
6125
|
+
}
|
|
6126
|
+
return claims;
|
|
6127
|
+
}
|
|
6128
|
+
function releaseClaims(model, token) {
|
|
6129
|
+
if (!model) return;
|
|
6130
|
+
const claims = claimsByModel.get(model);
|
|
6131
|
+
if (!claims) return;
|
|
6132
|
+
for (const [actuatorIndex, claim] of claims) {
|
|
6133
|
+
if (claim.token === token) claims.delete(actuatorIndex);
|
|
6134
|
+
}
|
|
6135
|
+
}
|
|
6136
|
+
function useControlWriter(options) {
|
|
6137
|
+
const {
|
|
6138
|
+
owner,
|
|
6139
|
+
selector,
|
|
6140
|
+
enabled = true,
|
|
6141
|
+
warnOnConflict = true,
|
|
6142
|
+
allowSameOwner = true,
|
|
6143
|
+
onConflict
|
|
6144
|
+
} = options;
|
|
6145
|
+
const { api, mjModelRef, mjDataRef, status } = useMujocoContext();
|
|
6146
|
+
const tokenRef = useRef(Symbol(owner));
|
|
6147
|
+
const claimedModelRef = useRef(null);
|
|
6148
|
+
const groupRef = useRef(null);
|
|
6149
|
+
const conflictsRef = useRef([]);
|
|
6150
|
+
const onConflictRef = useRef(onConflict);
|
|
6151
|
+
onConflictRef.current = onConflict;
|
|
6152
|
+
const release = useCallback(() => {
|
|
6153
|
+
releaseClaims(claimedModelRef.current, tokenRef.current);
|
|
6154
|
+
claimedModelRef.current = null;
|
|
6155
|
+
conflictsRef.current = [];
|
|
6156
|
+
}, []);
|
|
6157
|
+
useEffect(() => {
|
|
6158
|
+
release();
|
|
6159
|
+
if (!enabled || status !== "ready") {
|
|
6160
|
+
groupRef.current = null;
|
|
6161
|
+
return;
|
|
6162
|
+
}
|
|
6163
|
+
const model = mjModelRef.current;
|
|
6164
|
+
if (!model) {
|
|
6165
|
+
groupRef.current = null;
|
|
6166
|
+
return;
|
|
6167
|
+
}
|
|
6168
|
+
const group = selector ? api.resolveControlGroup(selector) : api.getControlMap();
|
|
6169
|
+
groupRef.current = group;
|
|
6170
|
+
if (!group) return;
|
|
6171
|
+
const claims = getClaims(model);
|
|
6172
|
+
const conflicts = [];
|
|
6173
|
+
for (const actuatorIndex of group.ctrlAdr) {
|
|
6174
|
+
const existing = claims.get(actuatorIndex);
|
|
6175
|
+
if (existing && existing.token !== tokenRef.current && (!allowSameOwner || existing.owner !== owner)) {
|
|
6176
|
+
conflicts.push({
|
|
6177
|
+
actuatorIndex,
|
|
6178
|
+
owner: existing.owner,
|
|
6179
|
+
requestedBy: owner
|
|
6180
|
+
});
|
|
6181
|
+
}
|
|
6182
|
+
}
|
|
6183
|
+
conflictsRef.current = conflicts;
|
|
6184
|
+
if (conflicts.length > 0) {
|
|
6185
|
+
onConflictRef.current?.(conflicts);
|
|
6186
|
+
if (warnOnConflict) {
|
|
6187
|
+
console.warn(
|
|
6188
|
+
`[mujoco-react] Control writer "${owner}" conflicts with existing writer(s): ${conflicts.map((conflict) => `${conflict.actuatorIndex}:${conflict.owner}`).join(", ")}`
|
|
6189
|
+
);
|
|
6190
|
+
}
|
|
6191
|
+
return;
|
|
6192
|
+
}
|
|
6193
|
+
for (const actuatorIndex of group.ctrlAdr) {
|
|
6194
|
+
claims.set(actuatorIndex, { owner, token: tokenRef.current });
|
|
6195
|
+
}
|
|
6196
|
+
claimedModelRef.current = model;
|
|
6197
|
+
return release;
|
|
6198
|
+
}, [allowSameOwner, api, enabled, mjModelRef, owner, release, selector, status, warnOnConflict]);
|
|
6199
|
+
const canWrite = useCallback(() => enabled && groupRef.current !== null && conflictsRef.current.length === 0, [enabled]);
|
|
6200
|
+
const read = useCallback(() => {
|
|
6201
|
+
const data = mjDataRef.current;
|
|
6202
|
+
const group = groupRef.current;
|
|
6203
|
+
if (!data || !group) return new Float64Array(0);
|
|
6204
|
+
return group.readCtrl(data);
|
|
6205
|
+
}, [mjDataRef]);
|
|
6206
|
+
const write = useCallback((values, writeOptions = {}) => {
|
|
6207
|
+
const data = mjDataRef.current;
|
|
6208
|
+
const group = groupRef.current;
|
|
6209
|
+
if (!data || !group) return false;
|
|
6210
|
+
if (!writeOptions.force && !canWrite()) return false;
|
|
6211
|
+
group.writeCtrl(data, values);
|
|
6212
|
+
return true;
|
|
6213
|
+
}, [canWrite, mjDataRef]);
|
|
6214
|
+
return useMemo(() => ({
|
|
6215
|
+
owner,
|
|
6216
|
+
group: groupRef,
|
|
6217
|
+
conflicts: conflictsRef,
|
|
6218
|
+
canWrite,
|
|
6219
|
+
read,
|
|
6220
|
+
write,
|
|
6221
|
+
release
|
|
6222
|
+
}), [canWrite, owner, read, release, write]);
|
|
6223
|
+
}
|
|
6224
|
+
var geomNameCacheByModel2 = /* @__PURE__ */ new WeakMap();
|
|
6225
|
+
var bodyNameCacheByModel = /* @__PURE__ */ new WeakMap();
|
|
6226
|
+
function getCachedName(cacheByModel, model, id, address) {
|
|
6227
|
+
if (id < 0) return "";
|
|
6228
|
+
let cache = cacheByModel.get(model);
|
|
6229
|
+
if (!cache) {
|
|
6230
|
+
cache = /* @__PURE__ */ new Map();
|
|
6231
|
+
cacheByModel.set(model, cache);
|
|
6232
|
+
}
|
|
6233
|
+
let name = cache.get(id);
|
|
6234
|
+
if (name === void 0) {
|
|
6235
|
+
name = getName(model, address);
|
|
6236
|
+
cache.set(id, name);
|
|
6237
|
+
}
|
|
6238
|
+
return name;
|
|
6239
|
+
}
|
|
6240
|
+
function matchesFilter(entry, bodyNames, geomNames, includeWorldBody) {
|
|
6241
|
+
if (!includeWorldBody && (entry.body1 === 0 || entry.body2 === 0)) return false;
|
|
6242
|
+
if (bodyNames && !bodyNames.includes(entry.body1Name) && !bodyNames.includes(entry.body2Name)) {
|
|
6243
|
+
return false;
|
|
6244
|
+
}
|
|
6245
|
+
if (geomNames && !geomNames.includes(entry.geom1Name) && !geomNames.includes(entry.geom2Name)) {
|
|
6246
|
+
return false;
|
|
6247
|
+
}
|
|
6248
|
+
return true;
|
|
6249
|
+
}
|
|
6250
|
+
function useContactHistory(options = {}) {
|
|
6251
|
+
const entriesRef = useRef([]);
|
|
6252
|
+
const optionsRef = useRef(options);
|
|
6253
|
+
optionsRef.current = options;
|
|
6254
|
+
const clear = useCallback(() => {
|
|
6255
|
+
entriesRef.current = [];
|
|
6256
|
+
}, []);
|
|
6257
|
+
const countPair = useCallback((nameA, nameB) => {
|
|
6258
|
+
let count = 0;
|
|
6259
|
+
for (const entry of entriesRef.current) {
|
|
6260
|
+
const matchesBodies = entry.body1Name === nameA && entry.body2Name === nameB || entry.body1Name === nameB && entry.body2Name === nameA;
|
|
6261
|
+
const matchesGeoms = entry.geom1Name === nameA && entry.geom2Name === nameB || entry.geom1Name === nameB && entry.geom2Name === nameA;
|
|
6262
|
+
if (matchesBodies || matchesGeoms) count += 1;
|
|
6263
|
+
}
|
|
6264
|
+
return count;
|
|
6265
|
+
}, []);
|
|
6266
|
+
useAfterPhysicsStep(({ model, data }) => {
|
|
6267
|
+
if ((data.ncon ?? 0) <= 0) return;
|
|
6268
|
+
const {
|
|
6269
|
+
maxLength = 2e3,
|
|
6270
|
+
bodyNames,
|
|
6271
|
+
geomNames,
|
|
6272
|
+
includeWorldBody = false
|
|
6273
|
+
} = optionsRef.current;
|
|
6274
|
+
if (maxLength <= 0) return;
|
|
6275
|
+
const nextEntries = [];
|
|
6276
|
+
withContacts(data, (contacts) => {
|
|
6277
|
+
for (let index = 0; index < data.ncon; index += 1) {
|
|
6278
|
+
const contact = getContact(contacts, index);
|
|
6279
|
+
if (!contact) break;
|
|
6280
|
+
const body1 = model.geom_bodyid[contact.geom1] ?? -1;
|
|
6281
|
+
const body2 = model.geom_bodyid[contact.geom2] ?? -1;
|
|
6282
|
+
const entry = {
|
|
6283
|
+
geom1: contact.geom1,
|
|
6284
|
+
geom2: contact.geom2,
|
|
6285
|
+
geom1Name: getCachedName(geomNameCacheByModel2, model, contact.geom1, model.name_geomadr[contact.geom1]),
|
|
6286
|
+
geom2Name: getCachedName(geomNameCacheByModel2, model, contact.geom2, model.name_geomadr[contact.geom2]),
|
|
6287
|
+
body1,
|
|
6288
|
+
body2,
|
|
6289
|
+
body1Name: body1 >= 0 ? getCachedName(bodyNameCacheByModel, model, body1, model.name_bodyadr[body1]) : "",
|
|
6290
|
+
body2Name: body2 >= 0 ? getCachedName(bodyNameCacheByModel, model, body2, model.name_bodyadr[body2]) : "",
|
|
6291
|
+
pos: [contact.pos[0], contact.pos[1], contact.pos[2]],
|
|
6292
|
+
depth: contact.dist,
|
|
6293
|
+
time: data.time
|
|
6294
|
+
};
|
|
6295
|
+
if (matchesFilter(entry, bodyNames, geomNames, includeWorldBody)) {
|
|
6296
|
+
nextEntries.push(entry);
|
|
6297
|
+
}
|
|
6298
|
+
}
|
|
6299
|
+
});
|
|
6300
|
+
if (nextEntries.length === 0) return;
|
|
6301
|
+
entriesRef.current.push(...nextEntries);
|
|
6302
|
+
if (entriesRef.current.length > maxLength) {
|
|
6303
|
+
entriesRef.current.splice(0, entriesRef.current.length - maxLength);
|
|
6304
|
+
}
|
|
6305
|
+
});
|
|
6306
|
+
return {
|
|
6307
|
+
entries: entriesRef,
|
|
6308
|
+
clear,
|
|
6309
|
+
countPair
|
|
6310
|
+
};
|
|
6311
|
+
}
|
|
6312
|
+
function useKeyboardTeleop(config) {
|
|
6313
|
+
const { mjModelRef, mjDataRef, status } = useMujocoContext();
|
|
6314
|
+
const pressedRef = useRef(/* @__PURE__ */ new Set());
|
|
6315
|
+
const toggleStateRef = useRef(/* @__PURE__ */ new Map());
|
|
5934
6316
|
const enabledRef = useRef(config.enabled ?? true);
|
|
5935
6317
|
enabledRef.current = config.enabled ?? true;
|
|
5936
6318
|
const bindingsRef = useRef(config.bindings);
|
|
@@ -6119,29 +6501,114 @@ function useKeyboardIkTarget(config) {
|
|
|
6119
6501
|
}
|
|
6120
6502
|
});
|
|
6121
6503
|
}
|
|
6504
|
+
function isPromiseLike(value) {
|
|
6505
|
+
return typeof value === "object" && value !== null && "then" in value && typeof value.then === "function";
|
|
6506
|
+
}
|
|
6507
|
+
function isPolicyActionChunk(value) {
|
|
6508
|
+
return Array.isArray(value) && value.length > 0 && (Array.isArray(value[0]) || ArrayBuffer.isView(value[0]));
|
|
6509
|
+
}
|
|
6510
|
+
function toPolicyActions(output) {
|
|
6511
|
+
return isPolicyActionChunk(output) ? [...output] : [output];
|
|
6512
|
+
}
|
|
6513
|
+
function enqueuePolicyActions(queue, actions, observation, strategy) {
|
|
6514
|
+
if (strategy === "replace") {
|
|
6515
|
+
queue.splice(0, queue.length);
|
|
6516
|
+
}
|
|
6517
|
+
queue.push(...actions.map((action) => ({ action, observation })));
|
|
6518
|
+
}
|
|
6122
6519
|
function usePolicy(config) {
|
|
6123
6520
|
const lastActionTimeRef = useRef(0);
|
|
6124
6521
|
const lastObservationRef = useRef(null);
|
|
6125
6522
|
const lastActionRef = useRef(null);
|
|
6523
|
+
const actionQueueRef = useRef([]);
|
|
6524
|
+
const inFlightRef = useRef(false);
|
|
6525
|
+
const lastErrorRef = useRef(null);
|
|
6526
|
+
const epochRef = useRef(0);
|
|
6126
6527
|
const isRunningRef = useRef(config.enabled ?? true);
|
|
6127
6528
|
const configRef = useRef(config);
|
|
6128
6529
|
configRef.current = config;
|
|
6129
6530
|
isRunningRef.current = config.enabled ?? isRunningRef.current;
|
|
6531
|
+
const clearQueue = useCallback(() => {
|
|
6532
|
+
epochRef.current += 1;
|
|
6533
|
+
actionQueueRef.current.splice(0, actionQueueRef.current.length);
|
|
6534
|
+
inFlightRef.current = false;
|
|
6535
|
+
lastErrorRef.current = null;
|
|
6536
|
+
}, []);
|
|
6537
|
+
const reset = useCallback(() => {
|
|
6538
|
+
clearQueue();
|
|
6539
|
+
lastActionTimeRef.current = 0;
|
|
6540
|
+
lastObservationRef.current = null;
|
|
6541
|
+
lastActionRef.current = null;
|
|
6542
|
+
}, [clearQueue]);
|
|
6130
6543
|
useBeforePhysicsStep(({ model, data }) => {
|
|
6131
6544
|
if (!isRunningRef.current) return;
|
|
6132
6545
|
const cfg = configRef.current;
|
|
6133
6546
|
model.opt?.timestep ?? 2e-3;
|
|
6134
6547
|
const interval = 1 / cfg.frequency;
|
|
6135
6548
|
if (data.time - lastActionTimeRef.current >= interval) {
|
|
6549
|
+
const queuedAction = actionQueueRef.current.shift();
|
|
6550
|
+
if (queuedAction) {
|
|
6551
|
+
cfg.onAction({
|
|
6552
|
+
action: queuedAction.action,
|
|
6553
|
+
observation: queuedAction.observation,
|
|
6554
|
+
model,
|
|
6555
|
+
data
|
|
6556
|
+
});
|
|
6557
|
+
lastActionTimeRef.current = data.time;
|
|
6558
|
+
lastActionRef.current = queuedAction.action;
|
|
6559
|
+
}
|
|
6560
|
+
const prefetchThreshold = cfg.prefetchThreshold ?? 0;
|
|
6561
|
+
const shouldInfer = !inFlightRef.current && (!queuedAction || actionQueueRef.current.length <= prefetchThreshold);
|
|
6562
|
+
if (!shouldInfer) return;
|
|
6136
6563
|
const observation = cfg.onObservation({ model, data });
|
|
6137
|
-
const
|
|
6138
|
-
|
|
6564
|
+
const result = cfg.infer ? cfg.infer({ observation, model, data }) : observation;
|
|
6565
|
+
if (isPromiseLike(result)) {
|
|
6566
|
+
const epoch = epochRef.current;
|
|
6567
|
+
inFlightRef.current = true;
|
|
6568
|
+
result.then((output) => {
|
|
6569
|
+
if (epoch !== epochRef.current) return;
|
|
6570
|
+
enqueuePolicyActions(
|
|
6571
|
+
actionQueueRef.current,
|
|
6572
|
+
toPolicyActions(output),
|
|
6573
|
+
observation,
|
|
6574
|
+
cfg.queueStrategy ?? "append"
|
|
6575
|
+
);
|
|
6576
|
+
lastErrorRef.current = null;
|
|
6577
|
+
}).catch((error) => {
|
|
6578
|
+
if (epoch !== epochRef.current) return;
|
|
6579
|
+
lastErrorRef.current = error;
|
|
6580
|
+
cfg.onError?.(error);
|
|
6581
|
+
}).finally(() => {
|
|
6582
|
+
if (epoch !== epochRef.current) return;
|
|
6583
|
+
inFlightRef.current = false;
|
|
6584
|
+
});
|
|
6585
|
+
} else {
|
|
6586
|
+
const actions = toPolicyActions(result);
|
|
6587
|
+
if (queuedAction) {
|
|
6588
|
+
enqueuePolicyActions(
|
|
6589
|
+
actionQueueRef.current,
|
|
6590
|
+
actions,
|
|
6591
|
+
observation,
|
|
6592
|
+
cfg.queueStrategy ?? "append"
|
|
6593
|
+
);
|
|
6594
|
+
} else {
|
|
6595
|
+
const [action, ...queuedActions] = actions;
|
|
6596
|
+
if (!action) return;
|
|
6597
|
+
enqueuePolicyActions(
|
|
6598
|
+
actionQueueRef.current,
|
|
6599
|
+
queuedActions,
|
|
6600
|
+
observation,
|
|
6601
|
+
cfg.queueStrategy ?? "append"
|
|
6602
|
+
);
|
|
6603
|
+
cfg.onAction({ action, observation, model, data });
|
|
6604
|
+
lastActionRef.current = action;
|
|
6605
|
+
}
|
|
6606
|
+
}
|
|
6139
6607
|
lastActionTimeRef.current = data.time;
|
|
6140
6608
|
lastObservationRef.current = observation;
|
|
6141
|
-
lastActionRef.current = action;
|
|
6142
6609
|
}
|
|
6143
6610
|
});
|
|
6144
|
-
return {
|
|
6611
|
+
return useMemo(() => ({
|
|
6145
6612
|
get isRunning() {
|
|
6146
6613
|
return isRunningRef.current;
|
|
6147
6614
|
},
|
|
@@ -6150,15 +6617,304 @@ function usePolicy(config) {
|
|
|
6150
6617
|
},
|
|
6151
6618
|
stop: () => {
|
|
6152
6619
|
isRunningRef.current = false;
|
|
6620
|
+
if (configRef.current.clearQueueOnStop) reset();
|
|
6621
|
+
},
|
|
6622
|
+
clearQueue,
|
|
6623
|
+
reset,
|
|
6624
|
+
get inFlight() {
|
|
6625
|
+
return inFlightRef.current;
|
|
6626
|
+
},
|
|
6627
|
+
get queuedActions() {
|
|
6628
|
+
return actionQueueRef.current.length;
|
|
6153
6629
|
},
|
|
6154
6630
|
get lastObservation() {
|
|
6155
6631
|
return lastObservationRef.current;
|
|
6156
6632
|
},
|
|
6157
6633
|
get lastAction() {
|
|
6158
6634
|
return lastActionRef.current;
|
|
6635
|
+
},
|
|
6636
|
+
get lastError() {
|
|
6637
|
+
return lastErrorRef.current;
|
|
6159
6638
|
}
|
|
6639
|
+
}), [clearQueue, reset]);
|
|
6640
|
+
}
|
|
6641
|
+
function now() {
|
|
6642
|
+
return typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
6643
|
+
}
|
|
6644
|
+
function isAbortError(error) {
|
|
6645
|
+
return typeof DOMException !== "undefined" && error instanceof DOMException && error.name === "AbortError" || error instanceof Error && error.name === "AbortError";
|
|
6646
|
+
}
|
|
6647
|
+
function createAbortError(message) {
|
|
6648
|
+
if (typeof DOMException !== "undefined") {
|
|
6649
|
+
return new DOMException(message, "AbortError");
|
|
6650
|
+
}
|
|
6651
|
+
const error = new Error(message);
|
|
6652
|
+
error.name = "AbortError";
|
|
6653
|
+
return error;
|
|
6654
|
+
}
|
|
6655
|
+
function abortController(controller, reason) {
|
|
6656
|
+
if (!controller || controller.signal.aborted) return;
|
|
6657
|
+
if (reason !== void 0) {
|
|
6658
|
+
controller.abort(reason);
|
|
6659
|
+
} else {
|
|
6660
|
+
controller.abort();
|
|
6661
|
+
}
|
|
6662
|
+
}
|
|
6663
|
+
function createMergedAbortSignal(localSignal, externalSignal) {
|
|
6664
|
+
if (!externalSignal) return localSignal;
|
|
6665
|
+
if (externalSignal.aborted) {
|
|
6666
|
+
const controller2 = new AbortController();
|
|
6667
|
+
abortController(controller2, externalSignal.reason);
|
|
6668
|
+
return controller2.signal;
|
|
6669
|
+
}
|
|
6670
|
+
if (typeof AbortSignal !== "undefined" && typeof AbortSignal.any === "function") {
|
|
6671
|
+
return AbortSignal.any([localSignal, externalSignal]);
|
|
6672
|
+
}
|
|
6673
|
+
const controller = new AbortController();
|
|
6674
|
+
const abortFromLocal = () => abortController(controller, localSignal.reason);
|
|
6675
|
+
const abortFromExternal = () => abortController(controller, externalSignal.reason);
|
|
6676
|
+
localSignal.addEventListener("abort", abortFromLocal, { once: true });
|
|
6677
|
+
externalSignal.addEventListener("abort", abortFromExternal, { once: true });
|
|
6678
|
+
return controller.signal;
|
|
6679
|
+
}
|
|
6680
|
+
function vectorToArray(vector) {
|
|
6681
|
+
return Array.from(vector, (value) => Number(value));
|
|
6682
|
+
}
|
|
6683
|
+
function isPolicyVectorArray(value) {
|
|
6684
|
+
return Array.isArray(value) && value.every((entry) => Array.isArray(entry) || ArrayBuffer.isView(entry));
|
|
6685
|
+
}
|
|
6686
|
+
function isPolicyVector(value) {
|
|
6687
|
+
return Array.isArray(value) || ArrayBuffer.isView(value);
|
|
6688
|
+
}
|
|
6689
|
+
function defaultBuildRemotePolicyRequest(input) {
|
|
6690
|
+
const observation = vectorToArray(input.observation);
|
|
6691
|
+
return {
|
|
6692
|
+
observation,
|
|
6693
|
+
state: observation,
|
|
6694
|
+
time: input.data.time,
|
|
6695
|
+
reset: input.reset
|
|
6160
6696
|
};
|
|
6161
6697
|
}
|
|
6698
|
+
async function defaultReadRemotePolicyResponse(response) {
|
|
6699
|
+
const text = await response.text();
|
|
6700
|
+
if (text.length === 0) return null;
|
|
6701
|
+
return JSON.parse(text);
|
|
6702
|
+
}
|
|
6703
|
+
function defaultParseRemotePolicyResponse(responseBody) {
|
|
6704
|
+
if (responseBody && typeof responseBody === "object") {
|
|
6705
|
+
const body = responseBody;
|
|
6706
|
+
if (typeof body.error === "string" && body.error.length > 0) {
|
|
6707
|
+
throw new Error(body.error);
|
|
6708
|
+
}
|
|
6709
|
+
if (isPolicyVectorArray(body.actions) && body.actions.length > 0) {
|
|
6710
|
+
return body.actions;
|
|
6711
|
+
}
|
|
6712
|
+
if (isPolicyVector(body.action)) {
|
|
6713
|
+
return body.action;
|
|
6714
|
+
}
|
|
6715
|
+
}
|
|
6716
|
+
if (isPolicyVectorArray(responseBody) && responseBody.length > 0) {
|
|
6717
|
+
return responseBody;
|
|
6718
|
+
}
|
|
6719
|
+
if (isPolicyVector(responseBody)) {
|
|
6720
|
+
return responseBody;
|
|
6721
|
+
}
|
|
6722
|
+
throw new Error("Remote policy response must include `action` or `actions`.");
|
|
6723
|
+
}
|
|
6724
|
+
function createHttpError(response, responseBody) {
|
|
6725
|
+
const suffix = responseBody && typeof responseBody === "object" && "error" in responseBody ? `: ${String(responseBody.error)}` : "";
|
|
6726
|
+
return new Error(`Remote policy request failed with HTTP ${response.status}${suffix}`);
|
|
6727
|
+
}
|
|
6728
|
+
function useRemotePolicy(config) {
|
|
6729
|
+
const configRef = useRef(config);
|
|
6730
|
+
configRef.current = config;
|
|
6731
|
+
const requestCountRef = useRef(0);
|
|
6732
|
+
const responseCountRef = useRef(0);
|
|
6733
|
+
const remoteStatusRef = useRef("idle");
|
|
6734
|
+
const lastRequestBodyRef = useRef(null);
|
|
6735
|
+
const lastResponseBodyRef = useRef(null);
|
|
6736
|
+
const lastHttpStatusRef = useRef(null);
|
|
6737
|
+
const lastRequestMsRef = useRef(null);
|
|
6738
|
+
const abortControllerRef = useRef(null);
|
|
6739
|
+
const remoteEpochRef = useRef(0);
|
|
6740
|
+
const policy = usePolicy({
|
|
6741
|
+
...config,
|
|
6742
|
+
infer: async ({ observation, model, data }) => {
|
|
6743
|
+
const cfg = configRef.current;
|
|
6744
|
+
abortController(abortControllerRef.current, createAbortError("Remote policy request was superseded."));
|
|
6745
|
+
const controller = new AbortController();
|
|
6746
|
+
abortControllerRef.current = controller;
|
|
6747
|
+
const signal = createMergedAbortSignal(controller.signal, cfg.signal);
|
|
6748
|
+
const remoteEpoch = remoteEpochRef.current;
|
|
6749
|
+
const requestIndex = requestCountRef.current;
|
|
6750
|
+
const requestInput = {
|
|
6751
|
+
observation,
|
|
6752
|
+
model,
|
|
6753
|
+
data,
|
|
6754
|
+
reset: requestIndex === 0,
|
|
6755
|
+
requestIndex,
|
|
6756
|
+
signal
|
|
6757
|
+
};
|
|
6758
|
+
requestCountRef.current += 1;
|
|
6759
|
+
const requestStartedAt = now();
|
|
6760
|
+
const body = await (cfg.buildRequest?.(requestInput) ?? defaultBuildRemotePolicyRequest(requestInput));
|
|
6761
|
+
signal.throwIfAborted();
|
|
6762
|
+
if (remoteEpoch !== remoteEpochRef.current) {
|
|
6763
|
+
throw createAbortError("Remote policy request was reset.");
|
|
6764
|
+
}
|
|
6765
|
+
lastRequestBodyRef.current = body;
|
|
6766
|
+
remoteStatusRef.current = "requesting";
|
|
6767
|
+
cfg.onRequest?.({
|
|
6768
|
+
...requestInput,
|
|
6769
|
+
body,
|
|
6770
|
+
requestStartedAt
|
|
6771
|
+
});
|
|
6772
|
+
let response = null;
|
|
6773
|
+
let responseBody = null;
|
|
6774
|
+
try {
|
|
6775
|
+
const headers = new Headers(cfg.headers);
|
|
6776
|
+
if (!headers.has("content-type")) {
|
|
6777
|
+
headers.set("content-type", "application/json");
|
|
6778
|
+
}
|
|
6779
|
+
const fetcher = cfg.fetcher ?? fetch;
|
|
6780
|
+
response = await fetcher(String(cfg.endpoint), {
|
|
6781
|
+
...cfg.requestInit,
|
|
6782
|
+
method: cfg.method ?? "POST",
|
|
6783
|
+
credentials: cfg.credentials,
|
|
6784
|
+
headers,
|
|
6785
|
+
signal,
|
|
6786
|
+
body: typeof body === "string" ? body : JSON.stringify(body)
|
|
6787
|
+
});
|
|
6788
|
+
if (remoteEpoch === remoteEpochRef.current) {
|
|
6789
|
+
lastHttpStatusRef.current = response.status;
|
|
6790
|
+
}
|
|
6791
|
+
responseBody = await (cfg.readResponse?.(response) ?? defaultReadRemotePolicyResponse(response));
|
|
6792
|
+
signal.throwIfAborted();
|
|
6793
|
+
if (remoteEpoch !== remoteEpochRef.current) {
|
|
6794
|
+
throw createAbortError("Remote policy request was reset.");
|
|
6795
|
+
}
|
|
6796
|
+
lastResponseBodyRef.current = responseBody;
|
|
6797
|
+
if (!response.ok) {
|
|
6798
|
+
throw createHttpError(response, responseBody);
|
|
6799
|
+
}
|
|
6800
|
+
const responseFinishedAt = now();
|
|
6801
|
+
const info = {
|
|
6802
|
+
...requestInput,
|
|
6803
|
+
body,
|
|
6804
|
+
requestStartedAt,
|
|
6805
|
+
response,
|
|
6806
|
+
responseBody,
|
|
6807
|
+
responseFinishedAt,
|
|
6808
|
+
requestMs: responseFinishedAt - requestStartedAt
|
|
6809
|
+
};
|
|
6810
|
+
if (remoteEpoch === remoteEpochRef.current) {
|
|
6811
|
+
lastRequestMsRef.current = info.requestMs;
|
|
6812
|
+
responseCountRef.current += 1;
|
|
6813
|
+
}
|
|
6814
|
+
cfg.onResponse?.(info);
|
|
6815
|
+
const output = await (cfg.parseResponse?.(responseBody, info) ?? defaultParseRemotePolicyResponse(responseBody));
|
|
6816
|
+
if (remoteEpoch === remoteEpochRef.current) {
|
|
6817
|
+
remoteStatusRef.current = "ready";
|
|
6818
|
+
}
|
|
6819
|
+
return output;
|
|
6820
|
+
} catch (error) {
|
|
6821
|
+
if (response && remoteEpoch === remoteEpochRef.current) {
|
|
6822
|
+
lastHttpStatusRef.current = response.status;
|
|
6823
|
+
}
|
|
6824
|
+
if (isAbortError(error) || signal.aborted) {
|
|
6825
|
+
if (remoteEpoch === remoteEpochRef.current) {
|
|
6826
|
+
remoteStatusRef.current = "aborted";
|
|
6827
|
+
}
|
|
6828
|
+
throw error;
|
|
6829
|
+
}
|
|
6830
|
+
if (remoteEpoch === remoteEpochRef.current) {
|
|
6831
|
+
remoteStatusRef.current = "error";
|
|
6832
|
+
}
|
|
6833
|
+
throw error;
|
|
6834
|
+
} finally {
|
|
6835
|
+
if (abortControllerRef.current === controller) {
|
|
6836
|
+
abortControllerRef.current = null;
|
|
6837
|
+
}
|
|
6838
|
+
}
|
|
6839
|
+
}
|
|
6840
|
+
});
|
|
6841
|
+
return useMemo(() => {
|
|
6842
|
+
const abort = (reason) => {
|
|
6843
|
+
abortController(abortControllerRef.current, reason);
|
|
6844
|
+
if (abortControllerRef.current) {
|
|
6845
|
+
remoteStatusRef.current = "aborted";
|
|
6846
|
+
}
|
|
6847
|
+
};
|
|
6848
|
+
const resetRemoteState = () => {
|
|
6849
|
+
remoteEpochRef.current += 1;
|
|
6850
|
+
abort(createAbortError("Remote policy request was reset."));
|
|
6851
|
+
requestCountRef.current = 0;
|
|
6852
|
+
responseCountRef.current = 0;
|
|
6853
|
+
remoteStatusRef.current = "idle";
|
|
6854
|
+
lastRequestBodyRef.current = null;
|
|
6855
|
+
lastResponseBodyRef.current = null;
|
|
6856
|
+
lastHttpStatusRef.current = null;
|
|
6857
|
+
lastRequestMsRef.current = null;
|
|
6858
|
+
};
|
|
6859
|
+
return {
|
|
6860
|
+
get isRunning() {
|
|
6861
|
+
return policy.isRunning;
|
|
6862
|
+
},
|
|
6863
|
+
start: policy.start,
|
|
6864
|
+
stop: () => {
|
|
6865
|
+
if (configRef.current.abortOnStop ?? true) {
|
|
6866
|
+
abort(createAbortError("Remote policy request was stopped."));
|
|
6867
|
+
}
|
|
6868
|
+
policy.stop();
|
|
6869
|
+
if (configRef.current.clearQueueOnStop) {
|
|
6870
|
+
resetRemoteState();
|
|
6871
|
+
}
|
|
6872
|
+
},
|
|
6873
|
+
clearQueue: policy.clearQueue,
|
|
6874
|
+
abort,
|
|
6875
|
+
reset: () => {
|
|
6876
|
+
resetRemoteState();
|
|
6877
|
+
policy.reset();
|
|
6878
|
+
},
|
|
6879
|
+
get inFlight() {
|
|
6880
|
+
return policy.inFlight;
|
|
6881
|
+
},
|
|
6882
|
+
get queuedActions() {
|
|
6883
|
+
return policy.queuedActions;
|
|
6884
|
+
},
|
|
6885
|
+
get lastObservation() {
|
|
6886
|
+
return policy.lastObservation;
|
|
6887
|
+
},
|
|
6888
|
+
get lastAction() {
|
|
6889
|
+
return policy.lastAction;
|
|
6890
|
+
},
|
|
6891
|
+
get lastError() {
|
|
6892
|
+
return policy.lastError;
|
|
6893
|
+
},
|
|
6894
|
+
get remoteStatus() {
|
|
6895
|
+
return remoteStatusRef.current;
|
|
6896
|
+
},
|
|
6897
|
+
get requestCount() {
|
|
6898
|
+
return requestCountRef.current;
|
|
6899
|
+
},
|
|
6900
|
+
get responseCount() {
|
|
6901
|
+
return responseCountRef.current;
|
|
6902
|
+
},
|
|
6903
|
+
get lastRequestBody() {
|
|
6904
|
+
return lastRequestBodyRef.current;
|
|
6905
|
+
},
|
|
6906
|
+
get lastResponseBody() {
|
|
6907
|
+
return lastResponseBodyRef.current;
|
|
6908
|
+
},
|
|
6909
|
+
get lastHttpStatus() {
|
|
6910
|
+
return lastHttpStatusRef.current;
|
|
6911
|
+
},
|
|
6912
|
+
get lastRequestMs() {
|
|
6913
|
+
return lastRequestMsRef.current;
|
|
6914
|
+
}
|
|
6915
|
+
};
|
|
6916
|
+
}, [policy]);
|
|
6917
|
+
}
|
|
6162
6918
|
var EMPTY_OBSERVATION = {
|
|
6163
6919
|
values: new Float32Array(0),
|
|
6164
6920
|
layout: []
|
|
@@ -6179,6 +6935,134 @@ function useObservation(config) {
|
|
|
6179
6935
|
}
|
|
6180
6936
|
}), [mjDataRef, mjModelRef]);
|
|
6181
6937
|
}
|
|
6938
|
+
|
|
6939
|
+
// src/policyObservation.ts
|
|
6940
|
+
function pushValues(target, value, size) {
|
|
6941
|
+
if (typeof value === "number") {
|
|
6942
|
+
target.push(value);
|
|
6943
|
+
for (let index = 1; index < size; index += 1) target.push(0);
|
|
6944
|
+
return;
|
|
6945
|
+
}
|
|
6946
|
+
for (let index = 0; index < size; index += 1) {
|
|
6947
|
+
target.push(Number(value[index] ?? 0));
|
|
6948
|
+
}
|
|
6949
|
+
}
|
|
6950
|
+
function readNamedObservation(model, data, options) {
|
|
6951
|
+
const values = [];
|
|
6952
|
+
const layout = [];
|
|
6953
|
+
const missing = options.missing ?? "skip";
|
|
6954
|
+
for (const field of options.fields) {
|
|
6955
|
+
const start = values.length;
|
|
6956
|
+
const value = field.read({ model, data });
|
|
6957
|
+
if (value === null || value === void 0) {
|
|
6958
|
+
if (missing === "skip") continue;
|
|
6959
|
+
if (missing === "throw") {
|
|
6960
|
+
throw new Error(`Unable to read named observation field "${field.name}".`);
|
|
6961
|
+
}
|
|
6962
|
+
for (let index = 0; index < field.size; index += 1) values.push(0);
|
|
6963
|
+
} else {
|
|
6964
|
+
pushValues(values, value, field.size);
|
|
6965
|
+
}
|
|
6966
|
+
layout.push({
|
|
6967
|
+
name: field.name,
|
|
6968
|
+
start,
|
|
6969
|
+
size: field.size,
|
|
6970
|
+
units: field.units
|
|
6971
|
+
});
|
|
6972
|
+
}
|
|
6973
|
+
return {
|
|
6974
|
+
values: options.output === "float64" ? new Float64Array(values) : new Float32Array(values),
|
|
6975
|
+
layout
|
|
6976
|
+
};
|
|
6977
|
+
}
|
|
6978
|
+
function createNamedObservationBuilder(options) {
|
|
6979
|
+
return (model, data) => readNamedObservation(model, data, options);
|
|
6980
|
+
}
|
|
6981
|
+
function qposField(name, index, units = "qpos") {
|
|
6982
|
+
return {
|
|
6983
|
+
name,
|
|
6984
|
+
size: 1,
|
|
6985
|
+
units,
|
|
6986
|
+
read: ({ data }) => data.qpos[index]
|
|
6987
|
+
};
|
|
6988
|
+
}
|
|
6989
|
+
function qvelField(name, index, units = "qvel") {
|
|
6990
|
+
return {
|
|
6991
|
+
name,
|
|
6992
|
+
size: 1,
|
|
6993
|
+
units,
|
|
6994
|
+
read: ({ data }) => data.qvel[index]
|
|
6995
|
+
};
|
|
6996
|
+
}
|
|
6997
|
+
function ctrlField(name, index, units = "ctrl") {
|
|
6998
|
+
return {
|
|
6999
|
+
name,
|
|
7000
|
+
size: 1,
|
|
7001
|
+
units,
|
|
7002
|
+
read: ({ data }) => data.ctrl[index]
|
|
7003
|
+
};
|
|
7004
|
+
}
|
|
7005
|
+
function bodyPositionField(name, units = "world_position") {
|
|
7006
|
+
return {
|
|
7007
|
+
name: `body:${name}:xpos`,
|
|
7008
|
+
size: 3,
|
|
7009
|
+
units,
|
|
7010
|
+
read: ({ model, data }) => {
|
|
7011
|
+
const bodyId = findBodyByName(model, name);
|
|
7012
|
+
if (bodyId < 0) return null;
|
|
7013
|
+
const offset = bodyId * 3;
|
|
7014
|
+
return data.xpos.subarray(offset, offset + 3);
|
|
7015
|
+
}
|
|
7016
|
+
};
|
|
7017
|
+
}
|
|
7018
|
+
function geomPositionField(name, units = "world_position") {
|
|
7019
|
+
return {
|
|
7020
|
+
name: `geom:${name}:xpos`,
|
|
7021
|
+
size: 3,
|
|
7022
|
+
units,
|
|
7023
|
+
read: ({ model, data }) => {
|
|
7024
|
+
const geomId = findGeomByName(model, name);
|
|
7025
|
+
if (geomId < 0) return null;
|
|
7026
|
+
const offset = geomId * 3;
|
|
7027
|
+
return data.geom_xpos.subarray(offset, offset + 3);
|
|
7028
|
+
}
|
|
7029
|
+
};
|
|
7030
|
+
}
|
|
7031
|
+
function sitePositionField(name, units = "world_position") {
|
|
7032
|
+
return {
|
|
7033
|
+
name: `site:${name}:xpos`,
|
|
7034
|
+
size: 3,
|
|
7035
|
+
units,
|
|
7036
|
+
read: ({ model, data }) => {
|
|
7037
|
+
const siteId = findSiteByName(model, name);
|
|
7038
|
+
if (siteId < 0) return null;
|
|
7039
|
+
const offset = siteId * 3;
|
|
7040
|
+
return data.site_xpos.subarray(offset, offset + 3);
|
|
7041
|
+
}
|
|
7042
|
+
};
|
|
7043
|
+
}
|
|
7044
|
+
|
|
7045
|
+
// src/hooks/useNamedObservation.ts
|
|
7046
|
+
var EMPTY_NAMED_OBSERVATION = {
|
|
7047
|
+
values: new Float32Array(0),
|
|
7048
|
+
layout: []
|
|
7049
|
+
};
|
|
7050
|
+
function useNamedObservation(options) {
|
|
7051
|
+
const { mjModelRef, mjDataRef } = useMujocoContext();
|
|
7052
|
+
const optionsRef = useRef(options);
|
|
7053
|
+
optionsRef.current = options;
|
|
7054
|
+
return useMemo(() => ({
|
|
7055
|
+
read() {
|
|
7056
|
+
const model = mjModelRef.current;
|
|
7057
|
+
const data = mjDataRef.current;
|
|
7058
|
+
if (!model || !data) return EMPTY_NAMED_OBSERVATION;
|
|
7059
|
+
return readNamedObservation(model, data, optionsRef.current);
|
|
7060
|
+
},
|
|
7061
|
+
readValues() {
|
|
7062
|
+
return this.read().values;
|
|
7063
|
+
}
|
|
7064
|
+
}), [mjDataRef, mjModelRef]);
|
|
7065
|
+
}
|
|
6182
7066
|
function useTrajectoryRecorder(options = {}) {
|
|
6183
7067
|
const { mjModelRef } = useMujocoContext();
|
|
6184
7068
|
const recordingRef = useRef(false);
|
|
@@ -6414,6 +7298,223 @@ function useCameraFrameCapture(defaultOptions = {}) {
|
|
|
6414
7298
|
reset
|
|
6415
7299
|
};
|
|
6416
7300
|
}
|
|
7301
|
+
|
|
7302
|
+
// src/policyCameraFrames.ts
|
|
7303
|
+
function addPolicyImageAliases(images, stream, frame, includeObservationImageAliases) {
|
|
7304
|
+
const keys = /* @__PURE__ */ new Set();
|
|
7305
|
+
keys.add(stream.key);
|
|
7306
|
+
for (const alias of stream.aliases ?? []) keys.add(alias);
|
|
7307
|
+
if (includeObservationImageAliases) {
|
|
7308
|
+
keys.add(`observation.images.${stream.key}`);
|
|
7309
|
+
for (const alias of stream.aliases ?? []) {
|
|
7310
|
+
keys.add(`observation.images.${alias}`);
|
|
7311
|
+
}
|
|
7312
|
+
}
|
|
7313
|
+
for (const key of keys) {
|
|
7314
|
+
images[key] = frame.dataUrl;
|
|
7315
|
+
}
|
|
7316
|
+
}
|
|
7317
|
+
function describeFrameSource(key, frame) {
|
|
7318
|
+
return `${key}:${frame.source.kind}`;
|
|
7319
|
+
}
|
|
7320
|
+
function hasExplicitPolicyCameraSource(options) {
|
|
7321
|
+
return Boolean(
|
|
7322
|
+
options?.camera || options?.position || options?.quaternion || options?.source
|
|
7323
|
+
);
|
|
7324
|
+
}
|
|
7325
|
+
function createPolicyCameraFrameCapturePlan(options) {
|
|
7326
|
+
const {
|
|
7327
|
+
cameraKeys,
|
|
7328
|
+
defaults,
|
|
7329
|
+
streamOptions,
|
|
7330
|
+
includeObservationImageAliases,
|
|
7331
|
+
requireAll,
|
|
7332
|
+
...sourceOptions
|
|
7333
|
+
} = options;
|
|
7334
|
+
const mountedPlan = createMountedCameraFrameSequencePlan(cameraKeys, {
|
|
7335
|
+
...sourceOptions,
|
|
7336
|
+
defaults,
|
|
7337
|
+
cameraOptions: streamOptions
|
|
7338
|
+
});
|
|
7339
|
+
const streams = [];
|
|
7340
|
+
const missingKeys = new Set(mountedPlan.missingKeys);
|
|
7341
|
+
for (const key of cameraKeys) {
|
|
7342
|
+
const perStreamOptions = streamOptions?.[key];
|
|
7343
|
+
if (hasExplicitPolicyCameraSource(perStreamOptions)) {
|
|
7344
|
+
missingKeys.delete(key);
|
|
7345
|
+
streams.push({
|
|
7346
|
+
...defaults,
|
|
7347
|
+
...perStreamOptions,
|
|
7348
|
+
key,
|
|
7349
|
+
aliases: perStreamOptions?.aliases
|
|
7350
|
+
});
|
|
7351
|
+
continue;
|
|
7352
|
+
}
|
|
7353
|
+
const mountedCamera = mountedPlan.cameras.find((camera) => camera.key === key);
|
|
7354
|
+
if (!mountedCamera) continue;
|
|
7355
|
+
const { key: _mountedKey, ...captureOptions } = mountedCamera;
|
|
7356
|
+
streams.push({
|
|
7357
|
+
...captureOptions,
|
|
7358
|
+
key,
|
|
7359
|
+
aliases: perStreamOptions?.aliases
|
|
7360
|
+
});
|
|
7361
|
+
}
|
|
7362
|
+
const result = {
|
|
7363
|
+
cameraKeys: [...cameraKeys],
|
|
7364
|
+
streams,
|
|
7365
|
+
includeObservationImageAliases,
|
|
7366
|
+
mountedPlan,
|
|
7367
|
+
missingKeys: [...missingKeys]
|
|
7368
|
+
};
|
|
7369
|
+
if (requireAll && result.missingKeys.length > 0) {
|
|
7370
|
+
throw new Error(
|
|
7371
|
+
`Unable to resolve policy camera stream${result.missingKeys.length === 1 ? "" : "s"} for ${result.missingKeys.join(", ")}.`
|
|
7372
|
+
);
|
|
7373
|
+
}
|
|
7374
|
+
return result;
|
|
7375
|
+
}
|
|
7376
|
+
function createPolicyCameraFrameCapturePlanFromApi(api, options) {
|
|
7377
|
+
return createPolicyCameraFrameCapturePlan({
|
|
7378
|
+
...options,
|
|
7379
|
+
cameras: api.getCameras(),
|
|
7380
|
+
sites: api.getSites(),
|
|
7381
|
+
bodies: api.getBodies()
|
|
7382
|
+
});
|
|
7383
|
+
}
|
|
7384
|
+
async function capturePolicyCameraFrames(target, options) {
|
|
7385
|
+
const includeObservationImageAliases = options.includeObservationImageAliases ?? true;
|
|
7386
|
+
const entries = await Promise.all(
|
|
7387
|
+
options.streams.map(async ({ key, aliases, ...captureOptions }) => {
|
|
7388
|
+
const frame = await target.captureCameraFrame(captureOptions);
|
|
7389
|
+
return [key, { frame, aliases }];
|
|
7390
|
+
})
|
|
7391
|
+
);
|
|
7392
|
+
const frames = {};
|
|
7393
|
+
const images = {};
|
|
7394
|
+
const sourceParts = [];
|
|
7395
|
+
for (const [key, { frame, aliases }] of entries) {
|
|
7396
|
+
const stream = { key, aliases };
|
|
7397
|
+
frames[key] = frame;
|
|
7398
|
+
addPolicyImageAliases(
|
|
7399
|
+
images,
|
|
7400
|
+
stream,
|
|
7401
|
+
frame,
|
|
7402
|
+
includeObservationImageAliases
|
|
7403
|
+
);
|
|
7404
|
+
sourceParts.push(describeFrameSource(key, frame));
|
|
7405
|
+
}
|
|
7406
|
+
return {
|
|
7407
|
+
frames,
|
|
7408
|
+
images,
|
|
7409
|
+
sourceSummary: sourceParts.length > 0 ? sourceParts.join(" + ") : "not used by policy",
|
|
7410
|
+
capturedAt: Date.now()
|
|
7411
|
+
};
|
|
7412
|
+
}
|
|
7413
|
+
async function capturePolicyCameraFramesFromMountedStreams(target, options) {
|
|
7414
|
+
const plan = createPolicyCameraFrameCapturePlanFromApi(target, options);
|
|
7415
|
+
const result = await capturePolicyCameraFrames(target, plan);
|
|
7416
|
+
return { ...result, plan };
|
|
7417
|
+
}
|
|
7418
|
+
|
|
7419
|
+
// src/hooks/usePolicyCameraFrames.ts
|
|
7420
|
+
function mergePolicyCameraFrameCaptureOptions(defaultOptions, options) {
|
|
7421
|
+
return {
|
|
7422
|
+
...defaultOptions,
|
|
7423
|
+
...options,
|
|
7424
|
+
cameraKeys: options.cameraKeys ?? defaultOptions.cameraKeys,
|
|
7425
|
+
aliases: {
|
|
7426
|
+
...defaultOptions.aliases,
|
|
7427
|
+
...options.aliases
|
|
7428
|
+
},
|
|
7429
|
+
defaults: {
|
|
7430
|
+
...defaultOptions.defaults,
|
|
7431
|
+
...options.defaults
|
|
7432
|
+
},
|
|
7433
|
+
streamOptions: {
|
|
7434
|
+
...defaultOptions.streamOptions,
|
|
7435
|
+
...options.streamOptions
|
|
7436
|
+
}
|
|
7437
|
+
};
|
|
7438
|
+
}
|
|
7439
|
+
function usePolicyCameraFrames(defaultOptions) {
|
|
7440
|
+
const mujoco = useMujoco();
|
|
7441
|
+
const [status, setStatus] = useState("idle");
|
|
7442
|
+
const [error, setError] = useState(null);
|
|
7443
|
+
const reset = useCallback(() => {
|
|
7444
|
+
setStatus("idle");
|
|
7445
|
+
setError(null);
|
|
7446
|
+
}, []);
|
|
7447
|
+
const capture = useCallback(
|
|
7448
|
+
async (options = {}) => {
|
|
7449
|
+
if (!mujoco.api) {
|
|
7450
|
+
throw new Error("MuJoCo scene is not ready for policy camera capture.");
|
|
7451
|
+
}
|
|
7452
|
+
setStatus("capturing");
|
|
7453
|
+
setError(null);
|
|
7454
|
+
try {
|
|
7455
|
+
const result = await capturePolicyCameraFrames(mujoco.api, {
|
|
7456
|
+
...defaultOptions,
|
|
7457
|
+
...options,
|
|
7458
|
+
streams: options.streams ?? defaultOptions.streams
|
|
7459
|
+
});
|
|
7460
|
+
setStatus("captured");
|
|
7461
|
+
return result;
|
|
7462
|
+
} catch (nextError) {
|
|
7463
|
+
const error2 = nextError instanceof Error ? nextError : new Error("Unable to capture policy camera frames.");
|
|
7464
|
+
setError(error2);
|
|
7465
|
+
setStatus("error");
|
|
7466
|
+
throw error2;
|
|
7467
|
+
}
|
|
7468
|
+
},
|
|
7469
|
+
[defaultOptions, mujoco.api]
|
|
7470
|
+
);
|
|
7471
|
+
return {
|
|
7472
|
+
status,
|
|
7473
|
+
error,
|
|
7474
|
+
isCapturing: status === "capturing",
|
|
7475
|
+
capture,
|
|
7476
|
+
reset
|
|
7477
|
+
};
|
|
7478
|
+
}
|
|
7479
|
+
function usePolicyCameraFramesFromMountedStreams(defaultOptions) {
|
|
7480
|
+
const mujoco = useMujoco();
|
|
7481
|
+
const [status, setStatus] = useState("idle");
|
|
7482
|
+
const [error, setError] = useState(null);
|
|
7483
|
+
const reset = useCallback(() => {
|
|
7484
|
+
setStatus("idle");
|
|
7485
|
+
setError(null);
|
|
7486
|
+
}, []);
|
|
7487
|
+
const capture = useCallback(
|
|
7488
|
+
async (options = {}) => {
|
|
7489
|
+
if (!mujoco.api) {
|
|
7490
|
+
throw new Error("MuJoCo scene is not ready for mounted policy camera capture.");
|
|
7491
|
+
}
|
|
7492
|
+
setStatus("capturing");
|
|
7493
|
+
setError(null);
|
|
7494
|
+
try {
|
|
7495
|
+
const result = await capturePolicyCameraFramesFromMountedStreams(
|
|
7496
|
+
mujoco.api,
|
|
7497
|
+
mergePolicyCameraFrameCaptureOptions(defaultOptions, options)
|
|
7498
|
+
);
|
|
7499
|
+
setStatus("captured");
|
|
7500
|
+
return result;
|
|
7501
|
+
} catch (nextError) {
|
|
7502
|
+
const error2 = nextError instanceof Error ? nextError : new Error("Unable to capture mounted policy camera frames.");
|
|
7503
|
+
setError(error2);
|
|
7504
|
+
setStatus("error");
|
|
7505
|
+
throw error2;
|
|
7506
|
+
}
|
|
7507
|
+
},
|
|
7508
|
+
[defaultOptions, mujoco.api]
|
|
7509
|
+
);
|
|
7510
|
+
return {
|
|
7511
|
+
status,
|
|
7512
|
+
error,
|
|
7513
|
+
isCapturing: status === "capturing",
|
|
7514
|
+
capture,
|
|
7515
|
+
reset
|
|
7516
|
+
};
|
|
7517
|
+
}
|
|
6417
7518
|
function useCameraSequenceRecorder() {
|
|
6418
7519
|
const mujoco = useMujoco();
|
|
6419
7520
|
const [status, setStatus] = useState("idle");
|
|
@@ -6534,6 +7635,38 @@ function useMountedCameraSequenceRecorder(defaultOptions = {}) {
|
|
|
6534
7635
|
reset
|
|
6535
7636
|
};
|
|
6536
7637
|
}
|
|
7638
|
+
|
|
7639
|
+
// src/policyControls.ts
|
|
7640
|
+
function clampPolicyActionValue(model, actuatorIndex, value) {
|
|
7641
|
+
const ranges = model.actuator_ctrlrange;
|
|
7642
|
+
const min = ranges?.[actuatorIndex * 2] ?? -Infinity;
|
|
7643
|
+
const max = ranges?.[actuatorIndex * 2 + 1] ?? Infinity;
|
|
7644
|
+
return Math.max(min, Math.min(max, value));
|
|
7645
|
+
}
|
|
7646
|
+
function applyPolicyActionToControls(model, data, action, options = {}) {
|
|
7647
|
+
const actuatorOffset = options.actuatorOffset ?? 0;
|
|
7648
|
+
const actionSize = options.actionSize ?? action.length;
|
|
7649
|
+
const shouldClamp = options.clamp ?? true;
|
|
7650
|
+
const shouldSkipInvalid = options.skipInvalid ?? true;
|
|
7651
|
+
const count = Math.max(
|
|
7652
|
+
0,
|
|
7653
|
+
Math.min(actionSize, action.length, data.ctrl.length - actuatorOffset, model.nu - actuatorOffset)
|
|
7654
|
+
);
|
|
7655
|
+
const applied = [];
|
|
7656
|
+
const skipped = [];
|
|
7657
|
+
for (let index = 0; index < count; index += 1) {
|
|
7658
|
+
const actuatorIndex = actuatorOffset + index;
|
|
7659
|
+
const value = Number(action[index]);
|
|
7660
|
+
if (shouldSkipInvalid && !Number.isFinite(value)) {
|
|
7661
|
+
skipped.push(actuatorIndex);
|
|
7662
|
+
continue;
|
|
7663
|
+
}
|
|
7664
|
+
const nextValue = shouldClamp ? clampPolicyActionValue(model, actuatorIndex, value) : value;
|
|
7665
|
+
data.ctrl[actuatorIndex] = nextValue;
|
|
7666
|
+
applied.push(nextValue);
|
|
7667
|
+
}
|
|
7668
|
+
return { applied, skipped, actuatorOffset };
|
|
7669
|
+
}
|
|
6537
7670
|
function useCtrlNoise(config = {}) {
|
|
6538
7671
|
const { mjModelRef } = useMujocoContext();
|
|
6539
7672
|
const configRef = useRef(config);
|
|
@@ -6628,8 +7761,8 @@ function useCameraAnimation() {
|
|
|
6628
7761
|
useFrame((state) => {
|
|
6629
7762
|
const ca = cameraAnimRef.current;
|
|
6630
7763
|
if (!ca.active) return;
|
|
6631
|
-
const
|
|
6632
|
-
const progress = Math.min((
|
|
7764
|
+
const now2 = performance.now();
|
|
7765
|
+
const progress = Math.min((now2 - ca.startTime) / ca.duration, 1);
|
|
6633
7766
|
const ease = progress < 0.5 ? 4 * progress * progress * progress : 1 - Math.pow(-2 * progress + 2, 3) / 2;
|
|
6634
7767
|
camera.position.lerpVectors(ca.startPos, ca.endPos, ease);
|
|
6635
7768
|
camera.quaternion.slerpQuaternions(ca.startRot, ca.endRot, ease);
|
|
@@ -6698,6 +7831,12 @@ function useCameraAnimation() {
|
|
|
6698
7831
|
*
|
|
6699
7832
|
* Helpers for resolving dataset camera streams to mounted MuJoCo resources.
|
|
6700
7833
|
*/
|
|
7834
|
+
/**
|
|
7835
|
+
* @license
|
|
7836
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
7837
|
+
*
|
|
7838
|
+
* Project detector/image coordinates from a camera view into the rendered MuJoCo scene.
|
|
7839
|
+
*/
|
|
6701
7840
|
/**
|
|
6702
7841
|
* @license
|
|
6703
7842
|
* SPDX-License-Identifier: Apache-2.0
|
|
@@ -6795,12 +7934,30 @@ function useCameraAnimation() {
|
|
|
6795
7934
|
*
|
|
6796
7935
|
* useBodyState — per-body position/velocity tracking (spec 2.2)
|
|
6797
7936
|
*/
|
|
7937
|
+
/**
|
|
7938
|
+
* @license
|
|
7939
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
7940
|
+
*
|
|
7941
|
+
* Ref-based world pose hooks for named MuJoCo bodies, geoms, and sites.
|
|
7942
|
+
*/
|
|
6798
7943
|
/**
|
|
6799
7944
|
* @license
|
|
6800
7945
|
* SPDX-License-Identifier: Apache-2.0
|
|
6801
7946
|
*
|
|
6802
7947
|
* useCtrl — handle-based read/write access to a named actuator's ctrl value (spec 3.1)
|
|
6803
7948
|
*/
|
|
7949
|
+
/**
|
|
7950
|
+
* @license
|
|
7951
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
7952
|
+
*
|
|
7953
|
+
* Cooperative actuator/control ownership for policies, IK, teleop, and replay.
|
|
7954
|
+
*/
|
|
7955
|
+
/**
|
|
7956
|
+
* @license
|
|
7957
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
7958
|
+
*
|
|
7959
|
+
* Bounded contact history for rollout verification and debugging.
|
|
7960
|
+
*/
|
|
6804
7961
|
/**
|
|
6805
7962
|
* @license
|
|
6806
7963
|
* SPDX-License-Identifier: Apache-2.0
|
|
@@ -6813,6 +7970,24 @@ function useCameraAnimation() {
|
|
|
6813
7970
|
*
|
|
6814
7971
|
* usePolicy — policy decimation loop hook (spec 10.1)
|
|
6815
7972
|
*/
|
|
7973
|
+
/**
|
|
7974
|
+
* @license
|
|
7975
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
7976
|
+
*
|
|
7977
|
+
* useRemotePolicy — HTTP JSON inference wrapper around usePolicy.
|
|
7978
|
+
*/
|
|
7979
|
+
/**
|
|
7980
|
+
* @license
|
|
7981
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
7982
|
+
*
|
|
7983
|
+
* Named policy observation builders with layout and units metadata.
|
|
7984
|
+
*/
|
|
7985
|
+
/**
|
|
7986
|
+
* @license
|
|
7987
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
7988
|
+
*
|
|
7989
|
+
* Stable React handle for named policy observations.
|
|
7990
|
+
*/
|
|
6816
7991
|
/**
|
|
6817
7992
|
* @license
|
|
6818
7993
|
* SPDX-License-Identifier: Apache-2.0
|
|
@@ -6837,6 +8012,18 @@ function useCameraAnimation() {
|
|
|
6837
8012
|
*
|
|
6838
8013
|
* React state wrapper around MuJoCo/R3F offscreen camera-frame capture.
|
|
6839
8014
|
*/
|
|
8015
|
+
/**
|
|
8016
|
+
* @license
|
|
8017
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
8018
|
+
*
|
|
8019
|
+
* Helpers for turning Three/MuJoCo camera captures into policy image payloads.
|
|
8020
|
+
*/
|
|
8021
|
+
/**
|
|
8022
|
+
* @license
|
|
8023
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
8024
|
+
*
|
|
8025
|
+
* React wrapper for capturing policy image payloads from Three/MuJoCo cameras.
|
|
8026
|
+
*/
|
|
6840
8027
|
/**
|
|
6841
8028
|
* @license
|
|
6842
8029
|
* SPDX-License-Identifier: Apache-2.0
|
|
@@ -6849,6 +8036,12 @@ function useCameraAnimation() {
|
|
|
6849
8036
|
*
|
|
6850
8037
|
* React state wrapper for named MuJoCo camera/site/body sequence recording.
|
|
6851
8038
|
*/
|
|
8039
|
+
/**
|
|
8040
|
+
* @license
|
|
8041
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
8042
|
+
*
|
|
8043
|
+
* Helpers for applying policy action vectors to MuJoCo controls.
|
|
8044
|
+
*/
|
|
6852
8045
|
/**
|
|
6853
8046
|
* @license
|
|
6854
8047
|
* SPDX-License-Identifier: Apache-2.0
|
|
@@ -6879,6 +8072,6 @@ function useCameraAnimation() {
|
|
|
6879
8072
|
* useCameraAnimation — composable camera animation hook.
|
|
6880
8073
|
*/
|
|
6881
8074
|
|
|
6882
|
-
export { Body, ContactListener, ContactMarkers, Debug, DragInteraction, FlexRenderer, IkGizmo, InstancedGeomRenderer, MountedCameraFrameSequenceManifestStatus, MountedCameraFrameSequenceReadinessStatus, MountedCameraFrameSourceSuggestionMatch, MujocoCanvas, MujocoPhysics, MujocoProvider, MujocoSimProvider, SceneLights, SplatCollisionProxyPreview, TendonRenderer, TrajectoryPlayer, buildObservation, canFetchSplatCollisionProxyXml, captureFrame, captureFrameBlob, createContiguousControlGroup, createController, createControllerHook, createMountedCameraFrameSequenceManifest, createMountedCameraFrameSequencePlan, createMountedCameraFrameSequencePlanFromApi, createMountedCameraFrameSequenceReadiness, createMountedCameraFrameSourceSuggestions, fetchSplatCollisionProxyXml, findActuatorByName, findBodyByName, findGeomByName, findJointByName, findKeyframeByName, findSensorByName, findSiteByName, findTendonByName, getActuatedJoints, getCameraFrameCaptureSourceTarget, getControlMap, getMountedCameraFrameCaptureSource, getName, isMountedCameraFrameCaptureSource, loadScene, parseSplatCollisionProxyGeoms, recordMountedCameraFrameSequence, resolveControlGroup, resolveMountedCameraFrameSource, useActuators, useAfterPhysicsStep, useBeforePhysicsStep, useBodyMeshes, useBodyState, useCameraAnimation, useCameraFrameCapture, useCameraSequenceRecorder, useContactEvents, useContacts, useCtrl, useCtrlNoise, useFrameCapture, useGamepad, useGravityCompensation, useIkController, useJointState, useKeyboardIkTarget, useKeyboardTeleop, useMountedCameraSequenceRecorder, useMujoco, useMujocoWasm, useObservation, usePolicy, useSceneLights, useSelectionHighlight, useSensor, useSensors, useSitePosition, useSplatCollisionProxyGeoms, useTrajectoryPlayer, useTrajectoryRecorder, useVideoRecorder };
|
|
8075
|
+
export { Body, ContactListener, ContactMarkers, Debug, DragInteraction, FlexRenderer, IkGizmo, InstancedGeomRenderer, MountedCameraFrameSequenceManifestStatus, MountedCameraFrameSequenceReadinessStatus, MountedCameraFrameSourceSuggestionMatch, MujocoCanvas, MujocoPhysics, MujocoProvider, MujocoSimProvider, SceneLights, SplatCollisionProxyPreview, TendonRenderer, TrajectoryPlayer, applyPolicyActionToControls, bodyPositionField, buildObservation, canFetchSplatCollisionProxyXml, captureFrame, captureFrameBlob, capturePolicyCameraFrames, capturePolicyCameraFramesFromMountedStreams, clampPolicyActionValue, createContiguousControlGroup, createController, createControllerHook, createMountedCameraFrameSequenceManifest, createMountedCameraFrameSequencePlan, createMountedCameraFrameSequencePlanFromApi, createMountedCameraFrameSequenceReadiness, createMountedCameraFrameSourceSuggestions, createNamedObservationBuilder, createPolicyCameraFrameCapturePlan, createPolicyCameraFrameCapturePlanFromApi, ctrlField, fetchSplatCollisionProxyXml, findActuatorByName, findBodyByName, findGeomByName, findJointByName, findKeyframeByName, findSensorByName, findSiteByName, findTendonByName, geomPositionField, getActuatedJoints, getCameraFrameCaptureSourceTarget, getControlMap, getMountedCameraFrameCaptureSource, getName, imagePointToNdc, isMountedCameraFrameCaptureSource, loadScene, parseSplatCollisionProxyGeoms, projectImagePointTo3D, qposField, qvelField, readNamedObservation, recordMountedCameraFrameSequence, resolveControlGroup, resolveMountedCameraFrameSource, sitePositionField, useActuators, useAfterPhysicsStep, useBeforePhysicsStep, useBodyMeshes, useBodyPose, useBodyState, useCameraAnimation, useCameraFrameCapture, useCameraSequenceRecorder, useContactEvents, useContactHistory, useContacts, useControlWriter, useCtrl, useCtrlNoise, useFrameCapture, useGamepad, useGeomPose, useGravityCompensation, useIkController, useJointState, useKeyboardIkTarget, useKeyboardTeleop, useMountedCameraSequenceRecorder, useMujoco, useMujocoWasm, useNamedObservation, useObservation, usePolicy, usePolicyCameraFrames, usePolicyCameraFramesFromMountedStreams, useRemotePolicy, useSceneLights, useSelectionHighlight, useSensor, useSensors, useSitePose, useSitePosition, useSplatCollisionProxyGeoms, useTrajectoryPlayer, useTrajectoryRecorder, useVideoRecorder };
|
|
6883
8076
|
//# sourceMappingURL=index.js.map
|
|
6884
8077
|
//# sourceMappingURL=index.js.map
|