mujoco-react 0.3.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +148 -91
- package/dist/index.d.ts +52 -58
- package/dist/index.js +577 -559
- package/dist/index.js.map +1 -1
- package/package.json +6 -4
- package/src/components/ContactMarkers.tsx +9 -5
- package/src/components/Debug.tsx +5 -3
- package/src/components/DragInteraction.tsx +4 -2
- package/src/components/FlexRenderer.tsx +3 -2
- package/src/components/SceneRenderer.tsx +5 -2
- package/src/components/TendonRenderer.tsx +3 -2
- package/src/core/MujocoCanvas.tsx +1 -5
- package/src/core/MujocoPhysics.tsx +79 -0
- package/src/core/MujocoSimProvider.tsx +5 -11
- package/src/core/SceneLoader.ts +2 -8
- package/src/index.ts +2 -1
- package/src/types.ts +0 -10
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import loadMujoco from 'mujoco-js';
|
|
2
2
|
import { createContext, forwardRef, useEffect, useContext, useState, useRef, useCallback, useMemo } from 'react';
|
|
3
|
-
import { jsx, jsxs
|
|
3
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
4
4
|
import { Canvas, useThree, useFrame } from '@react-three/fiber';
|
|
5
5
|
import * as THREE11 from 'three';
|
|
6
6
|
import { PivotControls } from '@react-three/drei';
|
|
@@ -67,6 +67,248 @@ function getContact(data, i) {
|
|
|
67
67
|
return void 0;
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
|
+
var CapsuleGeometry = class extends THREE11.BufferGeometry {
|
|
71
|
+
parameters;
|
|
72
|
+
constructor(radius = 1, length = 1, capSegments = 4, radialSegments = 8) {
|
|
73
|
+
super();
|
|
74
|
+
this.type = "CapsuleGeometry";
|
|
75
|
+
this.parameters = { radius, length, capSegments, radialSegments };
|
|
76
|
+
const path = new THREE11.Path();
|
|
77
|
+
path.absarc(0, -length / 2, radius, Math.PI * 1.5, 0, false);
|
|
78
|
+
path.absarc(0, length / 2, radius, 0, Math.PI * 0.5, false);
|
|
79
|
+
const latheGeometry = new THREE11.LatheGeometry(path.getPoints(capSegments), radialSegments);
|
|
80
|
+
const self = this;
|
|
81
|
+
self.setIndex(latheGeometry.getIndex());
|
|
82
|
+
self.setAttribute("position", latheGeometry.getAttribute("position"));
|
|
83
|
+
self.setAttribute("normal", latheGeometry.getAttribute("normal"));
|
|
84
|
+
self.setAttribute("uv", latheGeometry.getAttribute("uv"));
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
var Reflector = class extends THREE11.Mesh {
|
|
88
|
+
isReflector = true;
|
|
89
|
+
camera;
|
|
90
|
+
reflectorPlane = new THREE11.Plane();
|
|
91
|
+
normal = new THREE11.Vector3();
|
|
92
|
+
reflectorWorldPosition = new THREE11.Vector3();
|
|
93
|
+
cameraWorldPosition = new THREE11.Vector3();
|
|
94
|
+
rotationMatrix = new THREE11.Matrix4();
|
|
95
|
+
lookAtPosition = new THREE11.Vector3(0, 0, -1);
|
|
96
|
+
clipPlane = new THREE11.Vector4();
|
|
97
|
+
view = new THREE11.Vector3();
|
|
98
|
+
target = new THREE11.Vector3();
|
|
99
|
+
q = new THREE11.Vector4();
|
|
100
|
+
textureMatrix = new THREE11.Matrix4();
|
|
101
|
+
virtualCamera;
|
|
102
|
+
renderTarget;
|
|
103
|
+
constructor(geometry, options = {}) {
|
|
104
|
+
super(geometry);
|
|
105
|
+
this.type = "Reflector";
|
|
106
|
+
this.camera = new THREE11.PerspectiveCamera();
|
|
107
|
+
const color = options.color !== void 0 ? new THREE11.Color(options.color) : new THREE11.Color(8355711);
|
|
108
|
+
const textureWidth = options.textureWidth || 512;
|
|
109
|
+
const textureHeight = options.textureHeight || 512;
|
|
110
|
+
const clipBias = options.clipBias || 0;
|
|
111
|
+
const multisample = options.multisample !== void 0 ? options.multisample : 4;
|
|
112
|
+
const blendTexture = options.texture || void 0;
|
|
113
|
+
const mixStrength = options.mixStrength !== void 0 ? options.mixStrength : 0.25;
|
|
114
|
+
this.virtualCamera = this.camera;
|
|
115
|
+
this.renderTarget = new THREE11.WebGLRenderTarget(textureWidth, textureHeight, {
|
|
116
|
+
samples: multisample,
|
|
117
|
+
type: THREE11.HalfFloatType
|
|
118
|
+
});
|
|
119
|
+
this.material = new THREE11.MeshPhysicalMaterial({
|
|
120
|
+
map: blendTexture,
|
|
121
|
+
color,
|
|
122
|
+
roughness: 0.5,
|
|
123
|
+
metalness: 0.1
|
|
124
|
+
});
|
|
125
|
+
this.material.onBeforeCompile = (shader) => {
|
|
126
|
+
shader.uniforms.tDiffuse = { value: this.renderTarget.texture };
|
|
127
|
+
shader.uniforms.textureMatrix = { value: this.textureMatrix };
|
|
128
|
+
shader.uniforms.mixStrength = { value: mixStrength };
|
|
129
|
+
const bodyStart = shader.vertexShader.indexOf("void main() {");
|
|
130
|
+
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}";
|
|
131
|
+
const fragmentBodyStart = shader.fragmentShader.indexOf("void main() {");
|
|
132
|
+
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}";
|
|
133
|
+
};
|
|
134
|
+
this.receiveShadow = true;
|
|
135
|
+
this.onBeforeRender = (renderer, scene, camera) => {
|
|
136
|
+
this.reflectorWorldPosition.setFromMatrixPosition(this.matrixWorld);
|
|
137
|
+
this.cameraWorldPosition.setFromMatrixPosition(camera.matrixWorld);
|
|
138
|
+
this.rotationMatrix.extractRotation(this.matrixWorld);
|
|
139
|
+
this.normal.set(0, 0, 1);
|
|
140
|
+
this.normal.applyMatrix4(this.rotationMatrix);
|
|
141
|
+
this.view.subVectors(this.reflectorWorldPosition, this.cameraWorldPosition);
|
|
142
|
+
if (this.view.dot(this.normal) > 0) return;
|
|
143
|
+
this.view.reflect(this.normal).negate();
|
|
144
|
+
this.view.add(this.reflectorWorldPosition);
|
|
145
|
+
this.rotationMatrix.extractRotation(camera.matrixWorld);
|
|
146
|
+
this.lookAtPosition.set(0, 0, -1);
|
|
147
|
+
this.lookAtPosition.applyMatrix4(this.rotationMatrix);
|
|
148
|
+
this.lookAtPosition.add(this.cameraWorldPosition);
|
|
149
|
+
this.target.subVectors(this.reflectorWorldPosition, this.lookAtPosition);
|
|
150
|
+
this.target.reflect(this.normal).negate();
|
|
151
|
+
this.target.add(this.reflectorWorldPosition);
|
|
152
|
+
this.virtualCamera.position.copy(this.view);
|
|
153
|
+
this.virtualCamera.up.set(0, 1, 0);
|
|
154
|
+
this.virtualCamera.up.applyMatrix4(this.rotationMatrix);
|
|
155
|
+
this.virtualCamera.up.reflect(this.normal);
|
|
156
|
+
this.virtualCamera.lookAt(this.target);
|
|
157
|
+
this.virtualCamera.far = camera.far;
|
|
158
|
+
this.virtualCamera.updateMatrixWorld();
|
|
159
|
+
this.virtualCamera.projectionMatrix.copy(camera.projectionMatrix);
|
|
160
|
+
this.textureMatrix.set(
|
|
161
|
+
0.5,
|
|
162
|
+
0,
|
|
163
|
+
0,
|
|
164
|
+
0.5,
|
|
165
|
+
0,
|
|
166
|
+
0.5,
|
|
167
|
+
0,
|
|
168
|
+
0.5,
|
|
169
|
+
0,
|
|
170
|
+
0,
|
|
171
|
+
0.5,
|
|
172
|
+
0.5,
|
|
173
|
+
0,
|
|
174
|
+
0,
|
|
175
|
+
0,
|
|
176
|
+
1
|
|
177
|
+
);
|
|
178
|
+
this.textureMatrix.multiply(this.virtualCamera.projectionMatrix);
|
|
179
|
+
this.textureMatrix.multiply(this.virtualCamera.matrixWorldInverse);
|
|
180
|
+
this.textureMatrix.multiply(this.matrixWorld);
|
|
181
|
+
this.reflectorPlane.setFromNormalAndCoplanarPoint(this.normal, this.reflectorWorldPosition);
|
|
182
|
+
this.reflectorPlane.applyMatrix4(this.virtualCamera.matrixWorldInverse);
|
|
183
|
+
this.clipPlane.set(this.reflectorPlane.normal.x, this.reflectorPlane.normal.y, this.reflectorPlane.normal.z, this.reflectorPlane.constant);
|
|
184
|
+
const projectionMatrix = this.virtualCamera.projectionMatrix;
|
|
185
|
+
this.q.x = (Math.sign(this.clipPlane.x) + projectionMatrix.elements[8]) / projectionMatrix.elements[0];
|
|
186
|
+
this.q.y = (Math.sign(this.clipPlane.y) + projectionMatrix.elements[9]) / projectionMatrix.elements[5];
|
|
187
|
+
this.q.z = -1;
|
|
188
|
+
this.q.w = (1 + projectionMatrix.elements[10]) / projectionMatrix.elements[14];
|
|
189
|
+
this.clipPlane.multiplyScalar(2 / this.clipPlane.dot(this.q));
|
|
190
|
+
projectionMatrix.elements[2] = this.clipPlane.x;
|
|
191
|
+
projectionMatrix.elements[6] = this.clipPlane.y;
|
|
192
|
+
projectionMatrix.elements[10] = this.clipPlane.z + 1 - clipBias;
|
|
193
|
+
projectionMatrix.elements[14] = this.clipPlane.w;
|
|
194
|
+
this.visible = false;
|
|
195
|
+
const currentRenderTarget = renderer.getRenderTarget();
|
|
196
|
+
const currentXrEnabled = renderer.xr.enabled;
|
|
197
|
+
const currentShadowAutoUpdate = renderer.shadowMap.autoUpdate;
|
|
198
|
+
renderer.xr.enabled = false;
|
|
199
|
+
renderer.shadowMap.autoUpdate = false;
|
|
200
|
+
renderer.setRenderTarget(this.renderTarget);
|
|
201
|
+
renderer.state.buffers.depth.setMask(true);
|
|
202
|
+
if (renderer.autoClear === false) renderer.clear();
|
|
203
|
+
renderer.render(scene, this.virtualCamera);
|
|
204
|
+
renderer.xr.enabled = currentXrEnabled;
|
|
205
|
+
renderer.shadowMap.autoUpdate = currentShadowAutoUpdate;
|
|
206
|
+
renderer.setRenderTarget(currentRenderTarget);
|
|
207
|
+
const viewport = camera.viewport;
|
|
208
|
+
if (viewport !== void 0) {
|
|
209
|
+
renderer.state.viewport(viewport);
|
|
210
|
+
}
|
|
211
|
+
this.visible = true;
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
getRenderTarget() {
|
|
215
|
+
return this.renderTarget;
|
|
216
|
+
}
|
|
217
|
+
dispose() {
|
|
218
|
+
this.renderTarget.dispose();
|
|
219
|
+
const mesh = this;
|
|
220
|
+
if (Array.isArray(mesh.material)) {
|
|
221
|
+
mesh.material.forEach((m) => m.dispose());
|
|
222
|
+
} else {
|
|
223
|
+
mesh.material.dispose();
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
// src/rendering/GeomBuilder.ts
|
|
229
|
+
var GeomBuilder = class {
|
|
230
|
+
mujoco;
|
|
231
|
+
constructor(mujoco) {
|
|
232
|
+
this.mujoco = mujoco;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Creates a Three.js Object3D (usually a Mesh) for a specific geometry in the MuJoCo model.
|
|
236
|
+
* Returns null if the geometry shouldn't be rendered (e.g., invisible collision triggers).
|
|
237
|
+
*/
|
|
238
|
+
create(mjModel, g) {
|
|
239
|
+
if (mjModel.geom_group[g] === 3) return null;
|
|
240
|
+
const type = mjModel.geom_type[g];
|
|
241
|
+
const size = mjModel.geom_size.subarray(g * 3, g * 3 + 3);
|
|
242
|
+
const pos = mjModel.geom_pos.subarray(g * 3, g * 3 + 3);
|
|
243
|
+
const quat = mjModel.geom_quat.subarray(g * 4, g * 4 + 4);
|
|
244
|
+
const matId = mjModel.geom_matid[g];
|
|
245
|
+
const color = new THREE11.Color(16777215);
|
|
246
|
+
let opacity = 1;
|
|
247
|
+
if (matId >= 0) {
|
|
248
|
+
const rgba = mjModel.mat_rgba.subarray(matId * 4, matId * 4 + 4);
|
|
249
|
+
color.setRGB(rgba[0], rgba[1], rgba[2]);
|
|
250
|
+
opacity = rgba[3];
|
|
251
|
+
} else {
|
|
252
|
+
const rgba = mjModel.geom_rgba.subarray(g * 4, g * 4 + 4);
|
|
253
|
+
color.setRGB(rgba[0], rgba[1], rgba[2]);
|
|
254
|
+
opacity = rgba[3];
|
|
255
|
+
}
|
|
256
|
+
const MG = this.mujoco.mjtGeom;
|
|
257
|
+
let geo = null;
|
|
258
|
+
const getVal = (v) => v?.value ?? v;
|
|
259
|
+
if (type === getVal(MG.mjGEOM_PLANE)) {
|
|
260
|
+
geo = new THREE11.PlaneGeometry(size[0] * 2 || 5, size[1] * 2 || 5);
|
|
261
|
+
} else if (type === getVal(MG.mjGEOM_SPHERE)) {
|
|
262
|
+
geo = new THREE11.SphereGeometry(size[0], 24, 24);
|
|
263
|
+
} else if (type === getVal(MG.mjGEOM_CAPSULE)) {
|
|
264
|
+
geo = new CapsuleGeometry(size[0], size[1] * 2, 24, 12);
|
|
265
|
+
geo.rotateX(Math.PI / 2);
|
|
266
|
+
} else if (type === getVal(MG.mjGEOM_BOX)) {
|
|
267
|
+
geo = new THREE11.BoxGeometry(size[0] * 2, size[1] * 2, size[2] * 2);
|
|
268
|
+
} else if (type === getVal(MG.mjGEOM_CYLINDER)) {
|
|
269
|
+
geo = new THREE11.CylinderGeometry(size[0], size[0], size[1] * 2, 24);
|
|
270
|
+
geo.rotateX(Math.PI / 2);
|
|
271
|
+
} else if (type === getVal(MG.mjGEOM_MESH)) {
|
|
272
|
+
const mId = mjModel.geom_dataid[g];
|
|
273
|
+
const vAdr = mjModel.mesh_vertadr[mId];
|
|
274
|
+
const vNum = mjModel.mesh_vertnum[mId];
|
|
275
|
+
const fAdr = mjModel.mesh_faceadr[mId];
|
|
276
|
+
const fNum = mjModel.mesh_facenum[mId];
|
|
277
|
+
geo = new THREE11.BufferGeometry();
|
|
278
|
+
geo.setAttribute("position", new THREE11.Float32BufferAttribute(mjModel.mesh_vert.subarray(vAdr * 3, (vAdr + vNum) * 3), 3));
|
|
279
|
+
geo.setIndex(Array.from(mjModel.mesh_face.subarray(fAdr * 3, (fAdr + fNum) * 3)));
|
|
280
|
+
geo.computeVertexNormals();
|
|
281
|
+
}
|
|
282
|
+
if (geo) {
|
|
283
|
+
let mesh;
|
|
284
|
+
if (type === getVal(MG.mjGEOM_PLANE)) {
|
|
285
|
+
mesh = new Reflector(geo, {
|
|
286
|
+
clipBias: 3e-3,
|
|
287
|
+
textureWidth: 1024,
|
|
288
|
+
textureHeight: 1024,
|
|
289
|
+
color,
|
|
290
|
+
mixStrength: 0.25
|
|
291
|
+
});
|
|
292
|
+
} else {
|
|
293
|
+
mesh = new THREE11.Mesh(geo, new THREE11.MeshStandardMaterial({
|
|
294
|
+
color,
|
|
295
|
+
transparent: opacity < 1,
|
|
296
|
+
opacity,
|
|
297
|
+
roughness: 0.6,
|
|
298
|
+
metalness: 0.2
|
|
299
|
+
}));
|
|
300
|
+
mesh.castShadow = true;
|
|
301
|
+
mesh.receiveShadow = true;
|
|
302
|
+
}
|
|
303
|
+
mesh.position.set(pos[0], pos[1], pos[2]);
|
|
304
|
+
mesh.quaternion.set(quat[1], quat[2], quat[3], quat[0]);
|
|
305
|
+
mesh.userData.bodyID = mjModel.geom_bodyid[g];
|
|
306
|
+
mesh.userData.geomID = g;
|
|
307
|
+
return mesh;
|
|
308
|
+
}
|
|
309
|
+
return null;
|
|
310
|
+
}
|
|
311
|
+
};
|
|
70
312
|
|
|
71
313
|
// src/core/SceneLoader.ts
|
|
72
314
|
function getName(mjModel, address) {
|
|
@@ -225,8 +467,6 @@ async function loadScene(mujoco, config, onProgress) {
|
|
|
225
467
|
onProgress?.("Loading model...");
|
|
226
468
|
const mjModel = mujoco.MjModel.loadFromXML(`/working/${config.sceneFile}`);
|
|
227
469
|
const mjData = new mujoco.MjData(mjModel);
|
|
228
|
-
const siteId = findSiteByName(mjModel, config.tcpSiteName ?? "tcp");
|
|
229
|
-
const gripperId = findActuatorByName(mjModel, config.gripperActuatorName ?? "gripper");
|
|
230
470
|
if (config.homeJoints) {
|
|
231
471
|
const homeCount = Math.min(config.homeJoints.length, mjModel.nu);
|
|
232
472
|
for (let i = 0; i < homeCount; i++) {
|
|
@@ -238,7 +478,7 @@ async function loadScene(mujoco, config, onProgress) {
|
|
|
238
478
|
}
|
|
239
479
|
}
|
|
240
480
|
mujoco.mj_forward(mjModel, mjData);
|
|
241
|
-
return { mjModel, mjData
|
|
481
|
+
return { mjModel, mjData };
|
|
242
482
|
}
|
|
243
483
|
function scanDependencies(xmlString, currentFile, parser, downloaded, queue) {
|
|
244
484
|
const xmlDoc = parser.parseFromString(xmlString, "text/xml");
|
|
@@ -267,20 +507,98 @@ function scanDependencies(xmlString, currentFile, parser, downloaded, queue) {
|
|
|
267
507
|
if (!downloaded.has(fullPath)) queue.push(fullPath);
|
|
268
508
|
});
|
|
269
509
|
}
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
510
|
+
function SceneRenderer(props) {
|
|
511
|
+
const { mjModelRef, mjDataRef, mujocoRef, onSelectionRef, status } = useMujocoSim();
|
|
512
|
+
const groupRef = useRef(null);
|
|
513
|
+
const bodyRefs = useRef([]);
|
|
514
|
+
const prevModelRef = useRef(null);
|
|
515
|
+
const geomBuilder = useMemo(() => {
|
|
516
|
+
if (status !== "ready") return null;
|
|
517
|
+
return new GeomBuilder(mujocoRef.current);
|
|
518
|
+
}, [status, mujocoRef]);
|
|
519
|
+
useEffect(() => {
|
|
520
|
+
if (status !== "ready" || !geomBuilder) return;
|
|
521
|
+
const model = mjModelRef.current;
|
|
522
|
+
const group = groupRef.current;
|
|
523
|
+
if (!model || !group) return;
|
|
524
|
+
if (prevModelRef.current === model) return;
|
|
525
|
+
prevModelRef.current = model;
|
|
526
|
+
while (group.children.length > 0) {
|
|
527
|
+
group.remove(group.children[0]);
|
|
528
|
+
}
|
|
529
|
+
const refs = [];
|
|
530
|
+
for (let i = 0; i < model.nbody; i++) {
|
|
531
|
+
const bodyGroup = new THREE11.Group();
|
|
532
|
+
bodyGroup.userData.bodyID = i;
|
|
533
|
+
for (let g = 0; g < model.ngeom; g++) {
|
|
534
|
+
if (model.geom_bodyid[g] === i) {
|
|
535
|
+
const mesh = geomBuilder.create(model, g);
|
|
536
|
+
if (mesh) bodyGroup.add(mesh);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
group.add(bodyGroup);
|
|
540
|
+
refs.push(bodyGroup);
|
|
541
|
+
}
|
|
542
|
+
bodyRefs.current = refs;
|
|
543
|
+
}, [status, geomBuilder, mjModelRef]);
|
|
544
|
+
useFrame(() => {
|
|
545
|
+
const data = mjDataRef.current;
|
|
546
|
+
if (!data) return;
|
|
547
|
+
const bodies = bodyRefs.current;
|
|
548
|
+
for (let i = 0; i < bodies.length; i++) {
|
|
549
|
+
const ref = bodies[i];
|
|
550
|
+
if (!ref) continue;
|
|
551
|
+
ref.position.set(
|
|
552
|
+
data.xpos[i * 3],
|
|
553
|
+
data.xpos[i * 3 + 1],
|
|
554
|
+
data.xpos[i * 3 + 2]
|
|
555
|
+
);
|
|
556
|
+
ref.quaternion.set(
|
|
557
|
+
data.xquat[i * 4 + 1],
|
|
558
|
+
data.xquat[i * 4 + 2],
|
|
559
|
+
data.xquat[i * 4 + 3],
|
|
560
|
+
data.xquat[i * 4]
|
|
561
|
+
);
|
|
562
|
+
}
|
|
563
|
+
});
|
|
564
|
+
return /* @__PURE__ */ jsx(
|
|
565
|
+
"group",
|
|
566
|
+
{
|
|
567
|
+
...props,
|
|
568
|
+
ref: groupRef,
|
|
569
|
+
onDoubleClick: (e) => {
|
|
570
|
+
if (typeof props.onDoubleClick === "function") props.onDoubleClick(e);
|
|
571
|
+
e.stopPropagation();
|
|
572
|
+
let obj = e.object;
|
|
573
|
+
while (obj && obj.userData.bodyID === void 0 && obj.parent) {
|
|
574
|
+
obj = obj.parent;
|
|
575
|
+
}
|
|
576
|
+
const bodyID = obj?.userData.bodyID;
|
|
577
|
+
if (typeof bodyID === "number" && bodyID > 0) {
|
|
578
|
+
const model = mjModelRef.current;
|
|
579
|
+
if (model && bodyID < model.nbody && onSelectionRef.current) {
|
|
580
|
+
const name = getName(model, model.name_bodyadr[bodyID]);
|
|
581
|
+
onSelectionRef.current(bodyID, name);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
);
|
|
587
|
+
}
|
|
588
|
+
var JOINT_TYPE_NAMES = ["free", "ball", "slide", "hinge"];
|
|
589
|
+
var GEOM_TYPE_NAMES = ["plane", "hfield", "sphere", "capsule", "ellipsoid", "cylinder", "box", "mesh"];
|
|
590
|
+
var SENSOR_TYPE_NAMES = {
|
|
591
|
+
0: "touch",
|
|
592
|
+
1: "accelerometer",
|
|
593
|
+
2: "velocimeter",
|
|
594
|
+
3: "gyro",
|
|
595
|
+
4: "force",
|
|
596
|
+
5: "torque",
|
|
597
|
+
6: "magnetometer",
|
|
598
|
+
7: "rangefinder",
|
|
599
|
+
8: "camprojection",
|
|
600
|
+
9: "jointpos",
|
|
601
|
+
10: "jointvel",
|
|
284
602
|
11: "tendonpos",
|
|
285
603
|
12: "tendonvel",
|
|
286
604
|
13: "actuatorpos",
|
|
@@ -372,7 +690,6 @@ function MujocoSimProvider({
|
|
|
372
690
|
substeps,
|
|
373
691
|
paused,
|
|
374
692
|
speed,
|
|
375
|
-
interpolate,
|
|
376
693
|
children
|
|
377
694
|
}) {
|
|
378
695
|
const { gl, camera } = useThree();
|
|
@@ -384,12 +701,8 @@ function MujocoSimProvider({
|
|
|
384
701
|
const pausedRef = useRef(paused ?? false);
|
|
385
702
|
const speedRef = useRef(speed ?? 1);
|
|
386
703
|
const substepsRef = useRef(substeps ?? 1);
|
|
387
|
-
const interpolateRef = useRef(interpolate ?? false);
|
|
388
704
|
const stepsToRunRef = useRef(0);
|
|
389
705
|
const loadGenRef = useRef(0);
|
|
390
|
-
useRef(null);
|
|
391
|
-
useRef(null);
|
|
392
|
-
useRef(0);
|
|
393
706
|
const onSelectionRef = useRef(onSelection);
|
|
394
707
|
onSelectionRef.current = onSelection;
|
|
395
708
|
const onStepRef = useRef(onStep);
|
|
@@ -407,9 +720,6 @@ function MujocoSimProvider({
|
|
|
407
720
|
useEffect(() => {
|
|
408
721
|
substepsRef.current = substeps ?? 1;
|
|
409
722
|
}, [substeps]);
|
|
410
|
-
useEffect(() => {
|
|
411
|
-
interpolateRef.current = interpolate ?? false;
|
|
412
|
-
}, [interpolate]);
|
|
413
723
|
useEffect(() => {
|
|
414
724
|
if (!gravity) return;
|
|
415
725
|
const model = mjModelRef.current;
|
|
@@ -477,7 +787,7 @@ function MujocoSimProvider({
|
|
|
477
787
|
}
|
|
478
788
|
}
|
|
479
789
|
}, [status]);
|
|
480
|
-
useFrame(() => {
|
|
790
|
+
useFrame((_state, delta) => {
|
|
481
791
|
const model = mjModelRef.current;
|
|
482
792
|
const data = mjDataRef.current;
|
|
483
793
|
if (!model || !data) return;
|
|
@@ -497,7 +807,8 @@ function MujocoSimProvider({
|
|
|
497
807
|
stepsToRunRef.current = 0;
|
|
498
808
|
} else {
|
|
499
809
|
const startSimTime = data.time;
|
|
500
|
-
const
|
|
810
|
+
const clampedDelta = Math.min(delta, 1 / 15);
|
|
811
|
+
const frameTime = clampedDelta * speedRef.current;
|
|
501
812
|
while (data.time - startSimTime < frameTime) {
|
|
502
813
|
for (let s = 0; s < numSubsteps; s++) {
|
|
503
814
|
mujoco.mj_step(model, data);
|
|
@@ -1104,7 +1415,10 @@ function MujocoSimProvider({
|
|
|
1104
1415
|
}),
|
|
1105
1416
|
[api, status]
|
|
1106
1417
|
);
|
|
1107
|
-
return /* @__PURE__ */
|
|
1418
|
+
return /* @__PURE__ */ jsxs(MujocoSimContext.Provider, { value: contextValue, children: [
|
|
1419
|
+
/* @__PURE__ */ jsx(SceneRenderer, {}),
|
|
1420
|
+
children
|
|
1421
|
+
] });
|
|
1108
1422
|
}
|
|
1109
1423
|
var MujocoCanvas = forwardRef(
|
|
1110
1424
|
function MujocoCanvas2({
|
|
@@ -1113,15 +1427,12 @@ var MujocoCanvas = forwardRef(
|
|
|
1113
1427
|
onError,
|
|
1114
1428
|
onStep,
|
|
1115
1429
|
onSelection,
|
|
1116
|
-
// Declarative physics config
|
|
1430
|
+
// Declarative physics config
|
|
1117
1431
|
gravity,
|
|
1118
1432
|
timestep,
|
|
1119
1433
|
substeps,
|
|
1120
1434
|
paused,
|
|
1121
1435
|
speed,
|
|
1122
|
-
interpolate,
|
|
1123
|
-
gravityCompensation,
|
|
1124
|
-
mjcfLights,
|
|
1125
1436
|
children,
|
|
1126
1437
|
...canvasProps
|
|
1127
1438
|
}, ref) {
|
|
@@ -1149,12 +1460,34 @@ var MujocoCanvas = forwardRef(
|
|
|
1149
1460
|
substeps,
|
|
1150
1461
|
paused,
|
|
1151
1462
|
speed,
|
|
1152
|
-
interpolate,
|
|
1153
1463
|
children
|
|
1154
1464
|
}
|
|
1155
1465
|
) });
|
|
1156
1466
|
}
|
|
1157
1467
|
);
|
|
1468
|
+
var MujocoPhysics = forwardRef(
|
|
1469
|
+
function MujocoPhysics2({ onError, children, ...props }, ref) {
|
|
1470
|
+
const { mujoco, status: wasmStatus, error: wasmError } = useMujoco();
|
|
1471
|
+
useEffect(() => {
|
|
1472
|
+
if (wasmStatus === "error" && onError) {
|
|
1473
|
+
onError(new Error(wasmError ?? "WASM load failed"));
|
|
1474
|
+
}
|
|
1475
|
+
}, [wasmStatus, wasmError, onError]);
|
|
1476
|
+
if (wasmStatus === "error" || wasmStatus === "loading" || !mujoco) {
|
|
1477
|
+
return null;
|
|
1478
|
+
}
|
|
1479
|
+
return /* @__PURE__ */ jsx(
|
|
1480
|
+
MujocoSimProvider,
|
|
1481
|
+
{
|
|
1482
|
+
mujoco,
|
|
1483
|
+
apiRef: ref,
|
|
1484
|
+
onError,
|
|
1485
|
+
...props,
|
|
1486
|
+
children
|
|
1487
|
+
}
|
|
1488
|
+
);
|
|
1489
|
+
}
|
|
1490
|
+
);
|
|
1158
1491
|
function shallowEqual(a, b) {
|
|
1159
1492
|
const keysA = Object.keys(a);
|
|
1160
1493
|
const keysB = Object.keys(b);
|
|
@@ -1437,526 +1770,208 @@ function syncGizmoToSite(data, siteId, target) {
|
|
|
1437
1770
|
0,
|
|
1438
1771
|
siteMat[6],
|
|
1439
1772
|
siteMat[7],
|
|
1440
|
-
siteMat[8],
|
|
1441
|
-
0,
|
|
1442
|
-
0,
|
|
1443
|
-
0,
|
|
1444
|
-
0,
|
|
1445
|
-
1
|
|
1446
|
-
);
|
|
1447
|
-
target.quaternion.setFromRotationMatrix(_syncMat4);
|
|
1448
|
-
}
|
|
1449
|
-
function IkControllerImpl({
|
|
1450
|
-
config,
|
|
1451
|
-
children
|
|
1452
|
-
}) {
|
|
1453
|
-
const { mjModelRef, mjDataRef, mujocoRef, configRef, resetCallbacks, status } = useMujocoSim();
|
|
1454
|
-
const ikEnabledRef = useRef(false);
|
|
1455
|
-
const ikCalculatingRef = useRef(false);
|
|
1456
|
-
const ikTargetRef = useRef(new THREE11.Group());
|
|
1457
|
-
const siteIdRef = useRef(-1);
|
|
1458
|
-
const genericIkRef = useRef(new GenericIK(mujocoRef.current));
|
|
1459
|
-
const firstIkEnableRef = useRef(true);
|
|
1460
|
-
const needsInitialSync = useRef(true);
|
|
1461
|
-
const gizmoAnimRef = useRef({
|
|
1462
|
-
active: false,
|
|
1463
|
-
startPos: new THREE11.Vector3(),
|
|
1464
|
-
endPos: new THREE11.Vector3(),
|
|
1465
|
-
startRot: new THREE11.Quaternion(),
|
|
1466
|
-
endRot: new THREE11.Quaternion(),
|
|
1467
|
-
startTime: 0,
|
|
1468
|
-
duration: 1e3
|
|
1469
|
-
});
|
|
1470
|
-
useEffect(() => {
|
|
1471
|
-
const model = mjModelRef.current;
|
|
1472
|
-
if (!model || status !== "ready") {
|
|
1473
|
-
siteIdRef.current = -1;
|
|
1474
|
-
return;
|
|
1475
|
-
}
|
|
1476
|
-
siteIdRef.current = findSiteByName(model, config.siteName);
|
|
1477
|
-
const data = mjDataRef.current;
|
|
1478
|
-
if (data && ikTargetRef.current) {
|
|
1479
|
-
syncGizmoToSite(data, siteIdRef.current, ikTargetRef.current);
|
|
1480
|
-
}
|
|
1481
|
-
}, [config.siteName, status, mjModelRef, mjDataRef]);
|
|
1482
|
-
const ikSolveFn = useCallback(
|
|
1483
|
-
(pos, quat, currentQ) => {
|
|
1484
|
-
if (config.ikSolveFn) return config.ikSolveFn(pos, quat, currentQ);
|
|
1485
|
-
const model = mjModelRef.current;
|
|
1486
|
-
const data = mjDataRef.current;
|
|
1487
|
-
if (!model || !data || siteIdRef.current === -1) return null;
|
|
1488
|
-
return genericIkRef.current.solve(
|
|
1489
|
-
model,
|
|
1490
|
-
data,
|
|
1491
|
-
siteIdRef.current,
|
|
1492
|
-
config.numJoints,
|
|
1493
|
-
pos,
|
|
1494
|
-
quat,
|
|
1495
|
-
currentQ,
|
|
1496
|
-
{
|
|
1497
|
-
damping: config.damping,
|
|
1498
|
-
maxIterations: config.maxIterations
|
|
1499
|
-
}
|
|
1500
|
-
);
|
|
1501
|
-
},
|
|
1502
|
-
[config.ikSolveFn, config.numJoints, config.damping, config.maxIterations, mjModelRef, mjDataRef]
|
|
1503
|
-
);
|
|
1504
|
-
const ikSolveFnRef = useRef(ikSolveFn);
|
|
1505
|
-
ikSolveFnRef.current = ikSolveFn;
|
|
1506
|
-
useFrame(() => {
|
|
1507
|
-
if (needsInitialSync.current && siteIdRef.current !== -1) {
|
|
1508
|
-
const data = mjDataRef.current;
|
|
1509
|
-
if (data && ikTargetRef.current) {
|
|
1510
|
-
syncGizmoToSite(data, siteIdRef.current, ikTargetRef.current);
|
|
1511
|
-
needsInitialSync.current = false;
|
|
1512
|
-
}
|
|
1513
|
-
}
|
|
1514
|
-
const ga = gizmoAnimRef.current;
|
|
1515
|
-
const target = ikTargetRef.current;
|
|
1516
|
-
if (!ga.active || !target) return;
|
|
1517
|
-
const now = performance.now();
|
|
1518
|
-
const elapsed = now - ga.startTime;
|
|
1519
|
-
const t = Math.min(elapsed / ga.duration, 1);
|
|
1520
|
-
const ease = 1 - Math.pow(1 - t, 3);
|
|
1521
|
-
target.position.lerpVectors(ga.startPos, ga.endPos, ease);
|
|
1522
|
-
target.quaternion.slerpQuaternions(ga.startRot, ga.endRot, ease);
|
|
1523
|
-
if (t >= 1) ga.active = false;
|
|
1524
|
-
});
|
|
1525
|
-
useBeforePhysicsStep((model, data) => {
|
|
1526
|
-
if (!ikEnabledRef.current) {
|
|
1527
|
-
ikCalculatingRef.current = false;
|
|
1528
|
-
return;
|
|
1529
|
-
}
|
|
1530
|
-
const target = ikTargetRef.current;
|
|
1531
|
-
if (!target) return;
|
|
1532
|
-
ikCalculatingRef.current = true;
|
|
1533
|
-
const numJoints = config.numJoints;
|
|
1534
|
-
const currentQ = [];
|
|
1535
|
-
for (let i = 0; i < numJoints; i++) currentQ.push(data.qpos[i]);
|
|
1536
|
-
const solution = ikSolveFnRef.current(target.position, target.quaternion, currentQ);
|
|
1537
|
-
if (solution) {
|
|
1538
|
-
for (let i = 0; i < numJoints; i++) data.ctrl[i] = solution[i];
|
|
1539
|
-
}
|
|
1540
|
-
});
|
|
1541
|
-
useEffect(() => {
|
|
1542
|
-
const cb = () => {
|
|
1543
|
-
const data = mjDataRef.current;
|
|
1544
|
-
if (data && ikTargetRef.current) {
|
|
1545
|
-
syncGizmoToSite(data, siteIdRef.current, ikTargetRef.current);
|
|
1546
|
-
}
|
|
1547
|
-
gizmoAnimRef.current.active = false;
|
|
1548
|
-
firstIkEnableRef.current = true;
|
|
1549
|
-
ikEnabledRef.current = false;
|
|
1550
|
-
needsInitialSync.current = true;
|
|
1551
|
-
};
|
|
1552
|
-
resetCallbacks.current.add(cb);
|
|
1553
|
-
return () => {
|
|
1554
|
-
resetCallbacks.current.delete(cb);
|
|
1555
|
-
};
|
|
1556
|
-
}, [resetCallbacks, mjDataRef]);
|
|
1557
|
-
const setIkEnabled = useCallback(
|
|
1558
|
-
(enabled) => {
|
|
1559
|
-
ikEnabledRef.current = enabled;
|
|
1560
|
-
const data = mjDataRef.current;
|
|
1561
|
-
if (enabled && data && !gizmoAnimRef.current.active && ikTargetRef.current) {
|
|
1562
|
-
syncGizmoToSite(data, siteIdRef.current, ikTargetRef.current);
|
|
1563
|
-
firstIkEnableRef.current = false;
|
|
1564
|
-
}
|
|
1565
|
-
},
|
|
1566
|
-
[mjDataRef]
|
|
1567
|
-
);
|
|
1568
|
-
const syncTargetToSiteApi = useCallback(() => {
|
|
1569
|
-
const data = mjDataRef.current;
|
|
1570
|
-
const target = ikTargetRef.current;
|
|
1571
|
-
if (data && target) syncGizmoToSite(data, siteIdRef.current, target);
|
|
1572
|
-
}, [mjDataRef]);
|
|
1573
|
-
const solveIK = useCallback(
|
|
1574
|
-
(pos, quat, currentQ) => {
|
|
1575
|
-
return ikSolveFnRef.current(pos, quat, currentQ);
|
|
1576
|
-
},
|
|
1577
|
-
[]
|
|
1578
|
-
);
|
|
1579
|
-
const moveTarget = useCallback(
|
|
1580
|
-
(pos, duration = 0) => {
|
|
1581
|
-
if (!ikEnabledRef.current) setIkEnabled(true);
|
|
1582
|
-
const target = ikTargetRef.current;
|
|
1583
|
-
if (!target) return;
|
|
1584
|
-
const targetPos = pos.clone();
|
|
1585
|
-
const targetRot = new THREE11.Quaternion().setFromEuler(
|
|
1586
|
-
new THREE11.Euler(Math.PI, 0, 0)
|
|
1587
|
-
);
|
|
1588
|
-
if (duration > 0) {
|
|
1589
|
-
const ga = gizmoAnimRef.current;
|
|
1590
|
-
ga.active = true;
|
|
1591
|
-
ga.startPos.copy(target.position);
|
|
1592
|
-
ga.endPos.copy(targetPos);
|
|
1593
|
-
ga.startRot.copy(target.quaternion);
|
|
1594
|
-
ga.endRot.copy(targetRot);
|
|
1595
|
-
ga.startTime = performance.now();
|
|
1596
|
-
ga.duration = duration;
|
|
1597
|
-
} else {
|
|
1598
|
-
gizmoAnimRef.current.active = false;
|
|
1599
|
-
target.position.copy(targetPos);
|
|
1600
|
-
target.quaternion.copy(targetRot);
|
|
1601
|
-
}
|
|
1602
|
-
},
|
|
1603
|
-
[setIkEnabled]
|
|
1604
|
-
);
|
|
1605
|
-
const getGizmoStats = useCallback(
|
|
1606
|
-
() => {
|
|
1607
|
-
const target = ikTargetRef.current;
|
|
1608
|
-
if (!ikCalculatingRef.current || !target) return null;
|
|
1609
|
-
return {
|
|
1610
|
-
pos: target.position.clone(),
|
|
1611
|
-
rot: new THREE11.Euler().setFromQuaternion(target.quaternion)
|
|
1612
|
-
};
|
|
1613
|
-
},
|
|
1614
|
-
[]
|
|
1615
|
-
);
|
|
1616
|
-
const contextValue = useMemo(
|
|
1617
|
-
() => ({
|
|
1618
|
-
ikEnabledRef,
|
|
1619
|
-
ikCalculatingRef,
|
|
1620
|
-
ikTargetRef,
|
|
1621
|
-
siteIdRef,
|
|
1622
|
-
setIkEnabled,
|
|
1623
|
-
moveTarget,
|
|
1624
|
-
syncTargetToSite: syncTargetToSiteApi,
|
|
1625
|
-
solveIK,
|
|
1626
|
-
getGizmoStats
|
|
1627
|
-
}),
|
|
1628
|
-
[setIkEnabled, moveTarget, syncTargetToSiteApi, solveIK, getGizmoStats]
|
|
1629
|
-
);
|
|
1630
|
-
return /* @__PURE__ */ jsx(IkContext.Provider, { value: contextValue, children });
|
|
1631
|
-
}
|
|
1632
|
-
var IkController = createController(
|
|
1633
|
-
{
|
|
1634
|
-
name: "IkController",
|
|
1635
|
-
defaultConfig: {
|
|
1636
|
-
damping: 0.01,
|
|
1637
|
-
maxIterations: 50
|
|
1638
|
-
}
|
|
1639
|
-
},
|
|
1640
|
-
IkControllerImpl
|
|
1641
|
-
);
|
|
1642
|
-
var CapsuleGeometry = class extends THREE11.BufferGeometry {
|
|
1643
|
-
parameters;
|
|
1644
|
-
constructor(radius = 1, length = 1, capSegments = 4, radialSegments = 8) {
|
|
1645
|
-
super();
|
|
1646
|
-
this.type = "CapsuleGeometry";
|
|
1647
|
-
this.parameters = { radius, length, capSegments, radialSegments };
|
|
1648
|
-
const path = new THREE11.Path();
|
|
1649
|
-
path.absarc(0, -length / 2, radius, Math.PI * 1.5, 0, false);
|
|
1650
|
-
path.absarc(0, length / 2, radius, 0, Math.PI * 0.5, false);
|
|
1651
|
-
const latheGeometry = new THREE11.LatheGeometry(path.getPoints(capSegments), radialSegments);
|
|
1652
|
-
const self = this;
|
|
1653
|
-
self.setIndex(latheGeometry.getIndex());
|
|
1654
|
-
self.setAttribute("position", latheGeometry.getAttribute("position"));
|
|
1655
|
-
self.setAttribute("normal", latheGeometry.getAttribute("normal"));
|
|
1656
|
-
self.setAttribute("uv", latheGeometry.getAttribute("uv"));
|
|
1657
|
-
}
|
|
1658
|
-
};
|
|
1659
|
-
var Reflector = class extends THREE11.Mesh {
|
|
1660
|
-
isReflector = true;
|
|
1661
|
-
camera;
|
|
1662
|
-
reflectorPlane = new THREE11.Plane();
|
|
1663
|
-
normal = new THREE11.Vector3();
|
|
1664
|
-
reflectorWorldPosition = new THREE11.Vector3();
|
|
1665
|
-
cameraWorldPosition = new THREE11.Vector3();
|
|
1666
|
-
rotationMatrix = new THREE11.Matrix4();
|
|
1667
|
-
lookAtPosition = new THREE11.Vector3(0, 0, -1);
|
|
1668
|
-
clipPlane = new THREE11.Vector4();
|
|
1669
|
-
view = new THREE11.Vector3();
|
|
1670
|
-
target = new THREE11.Vector3();
|
|
1671
|
-
q = new THREE11.Vector4();
|
|
1672
|
-
textureMatrix = new THREE11.Matrix4();
|
|
1673
|
-
virtualCamera;
|
|
1674
|
-
renderTarget;
|
|
1675
|
-
constructor(geometry, options = {}) {
|
|
1676
|
-
super(geometry);
|
|
1677
|
-
this.type = "Reflector";
|
|
1678
|
-
this.camera = new THREE11.PerspectiveCamera();
|
|
1679
|
-
const color = options.color !== void 0 ? new THREE11.Color(options.color) : new THREE11.Color(8355711);
|
|
1680
|
-
const textureWidth = options.textureWidth || 512;
|
|
1681
|
-
const textureHeight = options.textureHeight || 512;
|
|
1682
|
-
const clipBias = options.clipBias || 0;
|
|
1683
|
-
const multisample = options.multisample !== void 0 ? options.multisample : 4;
|
|
1684
|
-
const blendTexture = options.texture || void 0;
|
|
1685
|
-
const mixStrength = options.mixStrength !== void 0 ? options.mixStrength : 0.25;
|
|
1686
|
-
this.virtualCamera = this.camera;
|
|
1687
|
-
this.renderTarget = new THREE11.WebGLRenderTarget(textureWidth, textureHeight, {
|
|
1688
|
-
samples: multisample,
|
|
1689
|
-
type: THREE11.HalfFloatType
|
|
1690
|
-
});
|
|
1691
|
-
this.material = new THREE11.MeshPhysicalMaterial({
|
|
1692
|
-
map: blendTexture,
|
|
1693
|
-
color,
|
|
1694
|
-
roughness: 0.5,
|
|
1695
|
-
metalness: 0.1
|
|
1696
|
-
});
|
|
1697
|
-
this.material.onBeforeCompile = (shader) => {
|
|
1698
|
-
shader.uniforms.tDiffuse = { value: this.renderTarget.texture };
|
|
1699
|
-
shader.uniforms.textureMatrix = { value: this.textureMatrix };
|
|
1700
|
-
shader.uniforms.mixStrength = { value: mixStrength };
|
|
1701
|
-
const bodyStart = shader.vertexShader.indexOf("void main() {");
|
|
1702
|
-
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}";
|
|
1703
|
-
const fragmentBodyStart = shader.fragmentShader.indexOf("void main() {");
|
|
1704
|
-
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}";
|
|
1705
|
-
};
|
|
1706
|
-
this.receiveShadow = true;
|
|
1707
|
-
this.onBeforeRender = (renderer, scene, camera) => {
|
|
1708
|
-
this.reflectorWorldPosition.setFromMatrixPosition(this.matrixWorld);
|
|
1709
|
-
this.cameraWorldPosition.setFromMatrixPosition(camera.matrixWorld);
|
|
1710
|
-
this.rotationMatrix.extractRotation(this.matrixWorld);
|
|
1711
|
-
this.normal.set(0, 0, 1);
|
|
1712
|
-
this.normal.applyMatrix4(this.rotationMatrix);
|
|
1713
|
-
this.view.subVectors(this.reflectorWorldPosition, this.cameraWorldPosition);
|
|
1714
|
-
if (this.view.dot(this.normal) > 0) return;
|
|
1715
|
-
this.view.reflect(this.normal).negate();
|
|
1716
|
-
this.view.add(this.reflectorWorldPosition);
|
|
1717
|
-
this.rotationMatrix.extractRotation(camera.matrixWorld);
|
|
1718
|
-
this.lookAtPosition.set(0, 0, -1);
|
|
1719
|
-
this.lookAtPosition.applyMatrix4(this.rotationMatrix);
|
|
1720
|
-
this.lookAtPosition.add(this.cameraWorldPosition);
|
|
1721
|
-
this.target.subVectors(this.reflectorWorldPosition, this.lookAtPosition);
|
|
1722
|
-
this.target.reflect(this.normal).negate();
|
|
1723
|
-
this.target.add(this.reflectorWorldPosition);
|
|
1724
|
-
this.virtualCamera.position.copy(this.view);
|
|
1725
|
-
this.virtualCamera.up.set(0, 1, 0);
|
|
1726
|
-
this.virtualCamera.up.applyMatrix4(this.rotationMatrix);
|
|
1727
|
-
this.virtualCamera.up.reflect(this.normal);
|
|
1728
|
-
this.virtualCamera.lookAt(this.target);
|
|
1729
|
-
this.virtualCamera.far = camera.far;
|
|
1730
|
-
this.virtualCamera.updateMatrixWorld();
|
|
1731
|
-
this.virtualCamera.projectionMatrix.copy(camera.projectionMatrix);
|
|
1732
|
-
this.textureMatrix.set(
|
|
1733
|
-
0.5,
|
|
1734
|
-
0,
|
|
1735
|
-
0,
|
|
1736
|
-
0.5,
|
|
1737
|
-
0,
|
|
1738
|
-
0.5,
|
|
1739
|
-
0,
|
|
1740
|
-
0.5,
|
|
1741
|
-
0,
|
|
1742
|
-
0,
|
|
1743
|
-
0.5,
|
|
1744
|
-
0.5,
|
|
1745
|
-
0,
|
|
1746
|
-
0,
|
|
1747
|
-
0,
|
|
1748
|
-
1
|
|
1749
|
-
);
|
|
1750
|
-
this.textureMatrix.multiply(this.virtualCamera.projectionMatrix);
|
|
1751
|
-
this.textureMatrix.multiply(this.virtualCamera.matrixWorldInverse);
|
|
1752
|
-
this.textureMatrix.multiply(this.matrixWorld);
|
|
1753
|
-
this.reflectorPlane.setFromNormalAndCoplanarPoint(this.normal, this.reflectorWorldPosition);
|
|
1754
|
-
this.reflectorPlane.applyMatrix4(this.virtualCamera.matrixWorldInverse);
|
|
1755
|
-
this.clipPlane.set(this.reflectorPlane.normal.x, this.reflectorPlane.normal.y, this.reflectorPlane.normal.z, this.reflectorPlane.constant);
|
|
1756
|
-
const projectionMatrix = this.virtualCamera.projectionMatrix;
|
|
1757
|
-
this.q.x = (Math.sign(this.clipPlane.x) + projectionMatrix.elements[8]) / projectionMatrix.elements[0];
|
|
1758
|
-
this.q.y = (Math.sign(this.clipPlane.y) + projectionMatrix.elements[9]) / projectionMatrix.elements[5];
|
|
1759
|
-
this.q.z = -1;
|
|
1760
|
-
this.q.w = (1 + projectionMatrix.elements[10]) / projectionMatrix.elements[14];
|
|
1761
|
-
this.clipPlane.multiplyScalar(2 / this.clipPlane.dot(this.q));
|
|
1762
|
-
projectionMatrix.elements[2] = this.clipPlane.x;
|
|
1763
|
-
projectionMatrix.elements[6] = this.clipPlane.y;
|
|
1764
|
-
projectionMatrix.elements[10] = this.clipPlane.z + 1 - clipBias;
|
|
1765
|
-
projectionMatrix.elements[14] = this.clipPlane.w;
|
|
1766
|
-
this.visible = false;
|
|
1767
|
-
const currentRenderTarget = renderer.getRenderTarget();
|
|
1768
|
-
const currentXrEnabled = renderer.xr.enabled;
|
|
1769
|
-
const currentShadowAutoUpdate = renderer.shadowMap.autoUpdate;
|
|
1770
|
-
renderer.xr.enabled = false;
|
|
1771
|
-
renderer.shadowMap.autoUpdate = false;
|
|
1772
|
-
renderer.setRenderTarget(this.renderTarget);
|
|
1773
|
-
renderer.state.buffers.depth.setMask(true);
|
|
1774
|
-
if (renderer.autoClear === false) renderer.clear();
|
|
1775
|
-
renderer.render(scene, this.virtualCamera);
|
|
1776
|
-
renderer.xr.enabled = currentXrEnabled;
|
|
1777
|
-
renderer.shadowMap.autoUpdate = currentShadowAutoUpdate;
|
|
1778
|
-
renderer.setRenderTarget(currentRenderTarget);
|
|
1779
|
-
const viewport = camera.viewport;
|
|
1780
|
-
if (viewport !== void 0) {
|
|
1781
|
-
renderer.state.viewport(viewport);
|
|
1782
|
-
}
|
|
1783
|
-
this.visible = true;
|
|
1784
|
-
};
|
|
1785
|
-
}
|
|
1786
|
-
getRenderTarget() {
|
|
1787
|
-
return this.renderTarget;
|
|
1788
|
-
}
|
|
1789
|
-
dispose() {
|
|
1790
|
-
this.renderTarget.dispose();
|
|
1791
|
-
const mesh = this;
|
|
1792
|
-
if (Array.isArray(mesh.material)) {
|
|
1793
|
-
mesh.material.forEach((m) => m.dispose());
|
|
1794
|
-
} else {
|
|
1795
|
-
mesh.material.dispose();
|
|
1796
|
-
}
|
|
1797
|
-
}
|
|
1798
|
-
};
|
|
1799
|
-
|
|
1800
|
-
// src/rendering/GeomBuilder.ts
|
|
1801
|
-
var GeomBuilder = class {
|
|
1802
|
-
mujoco;
|
|
1803
|
-
constructor(mujoco) {
|
|
1804
|
-
this.mujoco = mujoco;
|
|
1805
|
-
}
|
|
1806
|
-
/**
|
|
1807
|
-
* Creates a Three.js Object3D (usually a Mesh) for a specific geometry in the MuJoCo model.
|
|
1808
|
-
* Returns null if the geometry shouldn't be rendered (e.g., invisible collision triggers).
|
|
1809
|
-
*/
|
|
1810
|
-
create(mjModel, g) {
|
|
1811
|
-
if (mjModel.geom_group[g] === 3) return null;
|
|
1812
|
-
const type = mjModel.geom_type[g];
|
|
1813
|
-
const size = mjModel.geom_size.subarray(g * 3, g * 3 + 3);
|
|
1814
|
-
const pos = mjModel.geom_pos.subarray(g * 3, g * 3 + 3);
|
|
1815
|
-
const quat = mjModel.geom_quat.subarray(g * 4, g * 4 + 4);
|
|
1816
|
-
const matId = mjModel.geom_matid[g];
|
|
1817
|
-
const color = new THREE11.Color(16777215);
|
|
1818
|
-
let opacity = 1;
|
|
1819
|
-
if (matId >= 0) {
|
|
1820
|
-
const rgba = mjModel.mat_rgba.subarray(matId * 4, matId * 4 + 4);
|
|
1821
|
-
color.setRGB(rgba[0], rgba[1], rgba[2]);
|
|
1822
|
-
opacity = rgba[3];
|
|
1823
|
-
} else {
|
|
1824
|
-
const rgba = mjModel.geom_rgba.subarray(g * 4, g * 4 + 4);
|
|
1825
|
-
color.setRGB(rgba[0], rgba[1], rgba[2]);
|
|
1826
|
-
opacity = rgba[3];
|
|
1827
|
-
}
|
|
1828
|
-
const MG = this.mujoco.mjtGeom;
|
|
1829
|
-
let geo = null;
|
|
1830
|
-
const getVal = (v) => v?.value ?? v;
|
|
1831
|
-
if (type === getVal(MG.mjGEOM_PLANE)) {
|
|
1832
|
-
geo = new THREE11.PlaneGeometry(size[0] * 2 || 5, size[1] * 2 || 5);
|
|
1833
|
-
} else if (type === getVal(MG.mjGEOM_SPHERE)) {
|
|
1834
|
-
geo = new THREE11.SphereGeometry(size[0], 24, 24);
|
|
1835
|
-
} else if (type === getVal(MG.mjGEOM_CAPSULE)) {
|
|
1836
|
-
geo = new CapsuleGeometry(size[0], size[1] * 2, 24, 12);
|
|
1837
|
-
geo.rotateX(Math.PI / 2);
|
|
1838
|
-
} else if (type === getVal(MG.mjGEOM_BOX)) {
|
|
1839
|
-
geo = new THREE11.BoxGeometry(size[0] * 2, size[1] * 2, size[2] * 2);
|
|
1840
|
-
} else if (type === getVal(MG.mjGEOM_CYLINDER)) {
|
|
1841
|
-
geo = new THREE11.CylinderGeometry(size[0], size[0], size[1] * 2, 24);
|
|
1842
|
-
geo.rotateX(Math.PI / 2);
|
|
1843
|
-
} else if (type === getVal(MG.mjGEOM_MESH)) {
|
|
1844
|
-
const mId = mjModel.geom_dataid[g];
|
|
1845
|
-
const vAdr = mjModel.mesh_vertadr[mId];
|
|
1846
|
-
const vNum = mjModel.mesh_vertnum[mId];
|
|
1847
|
-
const fAdr = mjModel.mesh_faceadr[mId];
|
|
1848
|
-
const fNum = mjModel.mesh_facenum[mId];
|
|
1849
|
-
geo = new THREE11.BufferGeometry();
|
|
1850
|
-
geo.setAttribute("position", new THREE11.Float32BufferAttribute(mjModel.mesh_vert.subarray(vAdr * 3, (vAdr + vNum) * 3), 3));
|
|
1851
|
-
geo.setIndex(Array.from(mjModel.mesh_face.subarray(fAdr * 3, (fAdr + fNum) * 3)));
|
|
1852
|
-
geo.computeVertexNormals();
|
|
1853
|
-
}
|
|
1854
|
-
if (geo) {
|
|
1855
|
-
let mesh;
|
|
1856
|
-
if (type === getVal(MG.mjGEOM_PLANE)) {
|
|
1857
|
-
mesh = new Reflector(geo, {
|
|
1858
|
-
clipBias: 3e-3,
|
|
1859
|
-
textureWidth: 1024,
|
|
1860
|
-
textureHeight: 1024,
|
|
1861
|
-
color,
|
|
1862
|
-
mixStrength: 0.25
|
|
1863
|
-
});
|
|
1864
|
-
} else {
|
|
1865
|
-
mesh = new THREE11.Mesh(geo, new THREE11.MeshStandardMaterial({
|
|
1866
|
-
color,
|
|
1867
|
-
transparent: opacity < 1,
|
|
1868
|
-
opacity,
|
|
1869
|
-
roughness: 0.6,
|
|
1870
|
-
metalness: 0.2
|
|
1871
|
-
}));
|
|
1872
|
-
mesh.castShadow = true;
|
|
1873
|
-
mesh.receiveShadow = true;
|
|
1874
|
-
}
|
|
1875
|
-
mesh.position.set(pos[0], pos[1], pos[2]);
|
|
1876
|
-
mesh.quaternion.set(quat[1], quat[2], quat[3], quat[0]);
|
|
1877
|
-
mesh.userData.bodyID = mjModel.geom_bodyid[g];
|
|
1878
|
-
mesh.userData.geomID = g;
|
|
1879
|
-
return mesh;
|
|
1880
|
-
}
|
|
1881
|
-
return null;
|
|
1882
|
-
}
|
|
1883
|
-
};
|
|
1884
|
-
function SceneRenderer() {
|
|
1885
|
-
const { mjModelRef, mjDataRef, mujocoRef, onSelectionRef, status } = useMujocoSim();
|
|
1886
|
-
const groupRef = useRef(null);
|
|
1887
|
-
const bodyRefs = useRef([]);
|
|
1888
|
-
const prevModelRef = useRef(null);
|
|
1889
|
-
const geomBuilder = useMemo(() => {
|
|
1890
|
-
if (status !== "ready") return null;
|
|
1891
|
-
return new GeomBuilder(mujocoRef.current);
|
|
1892
|
-
}, [status, mujocoRef]);
|
|
1773
|
+
siteMat[8],
|
|
1774
|
+
0,
|
|
1775
|
+
0,
|
|
1776
|
+
0,
|
|
1777
|
+
0,
|
|
1778
|
+
1
|
|
1779
|
+
);
|
|
1780
|
+
target.quaternion.setFromRotationMatrix(_syncMat4);
|
|
1781
|
+
}
|
|
1782
|
+
function IkControllerImpl({
|
|
1783
|
+
config,
|
|
1784
|
+
children
|
|
1785
|
+
}) {
|
|
1786
|
+
const { mjModelRef, mjDataRef, mujocoRef, configRef, resetCallbacks, status } = useMujocoSim();
|
|
1787
|
+
const ikEnabledRef = useRef(false);
|
|
1788
|
+
const ikCalculatingRef = useRef(false);
|
|
1789
|
+
const ikTargetRef = useRef(new THREE11.Group());
|
|
1790
|
+
const siteIdRef = useRef(-1);
|
|
1791
|
+
const genericIkRef = useRef(new GenericIK(mujocoRef.current));
|
|
1792
|
+
const firstIkEnableRef = useRef(true);
|
|
1793
|
+
const needsInitialSync = useRef(true);
|
|
1794
|
+
const gizmoAnimRef = useRef({
|
|
1795
|
+
active: false,
|
|
1796
|
+
startPos: new THREE11.Vector3(),
|
|
1797
|
+
endPos: new THREE11.Vector3(),
|
|
1798
|
+
startRot: new THREE11.Quaternion(),
|
|
1799
|
+
endRot: new THREE11.Quaternion(),
|
|
1800
|
+
startTime: 0,
|
|
1801
|
+
duration: 1e3
|
|
1802
|
+
});
|
|
1893
1803
|
useEffect(() => {
|
|
1894
|
-
if (status !== "ready" || !geomBuilder) return;
|
|
1895
1804
|
const model = mjModelRef.current;
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
prevModelRef.current = model;
|
|
1900
|
-
while (group.children.length > 0) {
|
|
1901
|
-
group.remove(group.children[0]);
|
|
1805
|
+
if (!model || status !== "ready") {
|
|
1806
|
+
siteIdRef.current = -1;
|
|
1807
|
+
return;
|
|
1902
1808
|
}
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1809
|
+
siteIdRef.current = findSiteByName(model, config.siteName);
|
|
1810
|
+
const data = mjDataRef.current;
|
|
1811
|
+
if (data && ikTargetRef.current) {
|
|
1812
|
+
syncGizmoToSite(data, siteIdRef.current, ikTargetRef.current);
|
|
1813
|
+
}
|
|
1814
|
+
}, [config.siteName, status, mjModelRef, mjDataRef]);
|
|
1815
|
+
const ikSolveFn = useCallback(
|
|
1816
|
+
(pos, quat, currentQ) => {
|
|
1817
|
+
if (config.ikSolveFn) return config.ikSolveFn(pos, quat, currentQ);
|
|
1818
|
+
const model = mjModelRef.current;
|
|
1819
|
+
const data = mjDataRef.current;
|
|
1820
|
+
if (!model || !data || siteIdRef.current === -1) return null;
|
|
1821
|
+
return genericIkRef.current.solve(
|
|
1822
|
+
model,
|
|
1823
|
+
data,
|
|
1824
|
+
siteIdRef.current,
|
|
1825
|
+
config.numJoints,
|
|
1826
|
+
pos,
|
|
1827
|
+
quat,
|
|
1828
|
+
currentQ,
|
|
1829
|
+
{
|
|
1830
|
+
damping: config.damping,
|
|
1831
|
+
maxIterations: config.maxIterations
|
|
1911
1832
|
}
|
|
1833
|
+
);
|
|
1834
|
+
},
|
|
1835
|
+
[config.ikSolveFn, config.numJoints, config.damping, config.maxIterations, mjModelRef, mjDataRef]
|
|
1836
|
+
);
|
|
1837
|
+
const ikSolveFnRef = useRef(ikSolveFn);
|
|
1838
|
+
ikSolveFnRef.current = ikSolveFn;
|
|
1839
|
+
useFrame(() => {
|
|
1840
|
+
if (needsInitialSync.current && siteIdRef.current !== -1) {
|
|
1841
|
+
const data = mjDataRef.current;
|
|
1842
|
+
if (data && ikTargetRef.current) {
|
|
1843
|
+
syncGizmoToSite(data, siteIdRef.current, ikTargetRef.current);
|
|
1844
|
+
needsInitialSync.current = false;
|
|
1912
1845
|
}
|
|
1913
|
-
group.add(bodyGroup);
|
|
1914
|
-
refs.push(bodyGroup);
|
|
1915
1846
|
}
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
const
|
|
1920
|
-
|
|
1921
|
-
const
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1847
|
+
const ga = gizmoAnimRef.current;
|
|
1848
|
+
const target = ikTargetRef.current;
|
|
1849
|
+
if (!ga.active || !target) return;
|
|
1850
|
+
const now = performance.now();
|
|
1851
|
+
const elapsed = now - ga.startTime;
|
|
1852
|
+
const t = Math.min(elapsed / ga.duration, 1);
|
|
1853
|
+
const ease = 1 - Math.pow(1 - t, 3);
|
|
1854
|
+
target.position.lerpVectors(ga.startPos, ga.endPos, ease);
|
|
1855
|
+
target.quaternion.slerpQuaternions(ga.startRot, ga.endRot, ease);
|
|
1856
|
+
if (t >= 1) ga.active = false;
|
|
1857
|
+
});
|
|
1858
|
+
useBeforePhysicsStep((model, data) => {
|
|
1859
|
+
if (!ikEnabledRef.current) {
|
|
1860
|
+
ikCalculatingRef.current = false;
|
|
1861
|
+
return;
|
|
1862
|
+
}
|
|
1863
|
+
const target = ikTargetRef.current;
|
|
1864
|
+
if (!target) return;
|
|
1865
|
+
ikCalculatingRef.current = true;
|
|
1866
|
+
const numJoints = config.numJoints;
|
|
1867
|
+
const currentQ = [];
|
|
1868
|
+
for (let i = 0; i < numJoints; i++) currentQ.push(data.qpos[i]);
|
|
1869
|
+
const solution = ikSolveFnRef.current(target.position, target.quaternion, currentQ);
|
|
1870
|
+
if (solution) {
|
|
1871
|
+
for (let i = 0; i < numJoints; i++) data.ctrl[i] = solution[i];
|
|
1936
1872
|
}
|
|
1937
1873
|
});
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
e.stopPropagation();
|
|
1944
|
-
let obj = e.object;
|
|
1945
|
-
while (obj && obj.userData.bodyID === void 0 && obj.parent) {
|
|
1946
|
-
obj = obj.parent;
|
|
1947
|
-
}
|
|
1948
|
-
const bodyID = obj?.userData.bodyID;
|
|
1949
|
-
if (typeof bodyID === "number" && bodyID > 0) {
|
|
1950
|
-
const model = mjModelRef.current;
|
|
1951
|
-
if (model && bodyID < model.nbody && onSelectionRef.current) {
|
|
1952
|
-
const name = getName(model, model.name_bodyadr[bodyID]);
|
|
1953
|
-
onSelectionRef.current(bodyID, name);
|
|
1954
|
-
}
|
|
1955
|
-
}
|
|
1874
|
+
useEffect(() => {
|
|
1875
|
+
const cb = () => {
|
|
1876
|
+
const data = mjDataRef.current;
|
|
1877
|
+
if (data && ikTargetRef.current) {
|
|
1878
|
+
syncGizmoToSite(data, siteIdRef.current, ikTargetRef.current);
|
|
1956
1879
|
}
|
|
1957
|
-
|
|
1880
|
+
gizmoAnimRef.current.active = false;
|
|
1881
|
+
firstIkEnableRef.current = true;
|
|
1882
|
+
ikEnabledRef.current = false;
|
|
1883
|
+
needsInitialSync.current = true;
|
|
1884
|
+
};
|
|
1885
|
+
resetCallbacks.current.add(cb);
|
|
1886
|
+
return () => {
|
|
1887
|
+
resetCallbacks.current.delete(cb);
|
|
1888
|
+
};
|
|
1889
|
+
}, [resetCallbacks, mjDataRef]);
|
|
1890
|
+
const setIkEnabled = useCallback(
|
|
1891
|
+
(enabled) => {
|
|
1892
|
+
ikEnabledRef.current = enabled;
|
|
1893
|
+
const data = mjDataRef.current;
|
|
1894
|
+
if (enabled && data && !gizmoAnimRef.current.active && ikTargetRef.current) {
|
|
1895
|
+
syncGizmoToSite(data, siteIdRef.current, ikTargetRef.current);
|
|
1896
|
+
firstIkEnableRef.current = false;
|
|
1897
|
+
}
|
|
1898
|
+
},
|
|
1899
|
+
[mjDataRef]
|
|
1900
|
+
);
|
|
1901
|
+
const syncTargetToSiteApi = useCallback(() => {
|
|
1902
|
+
const data = mjDataRef.current;
|
|
1903
|
+
const target = ikTargetRef.current;
|
|
1904
|
+
if (data && target) syncGizmoToSite(data, siteIdRef.current, target);
|
|
1905
|
+
}, [mjDataRef]);
|
|
1906
|
+
const solveIK = useCallback(
|
|
1907
|
+
(pos, quat, currentQ) => {
|
|
1908
|
+
return ikSolveFnRef.current(pos, quat, currentQ);
|
|
1909
|
+
},
|
|
1910
|
+
[]
|
|
1911
|
+
);
|
|
1912
|
+
const moveTarget = useCallback(
|
|
1913
|
+
(pos, duration = 0) => {
|
|
1914
|
+
if (!ikEnabledRef.current) setIkEnabled(true);
|
|
1915
|
+
const target = ikTargetRef.current;
|
|
1916
|
+
if (!target) return;
|
|
1917
|
+
const targetPos = pos.clone();
|
|
1918
|
+
const targetRot = new THREE11.Quaternion().setFromEuler(
|
|
1919
|
+
new THREE11.Euler(Math.PI, 0, 0)
|
|
1920
|
+
);
|
|
1921
|
+
if (duration > 0) {
|
|
1922
|
+
const ga = gizmoAnimRef.current;
|
|
1923
|
+
ga.active = true;
|
|
1924
|
+
ga.startPos.copy(target.position);
|
|
1925
|
+
ga.endPos.copy(targetPos);
|
|
1926
|
+
ga.startRot.copy(target.quaternion);
|
|
1927
|
+
ga.endRot.copy(targetRot);
|
|
1928
|
+
ga.startTime = performance.now();
|
|
1929
|
+
ga.duration = duration;
|
|
1930
|
+
} else {
|
|
1931
|
+
gizmoAnimRef.current.active = false;
|
|
1932
|
+
target.position.copy(targetPos);
|
|
1933
|
+
target.quaternion.copy(targetRot);
|
|
1934
|
+
}
|
|
1935
|
+
},
|
|
1936
|
+
[setIkEnabled]
|
|
1937
|
+
);
|
|
1938
|
+
const getGizmoStats = useCallback(
|
|
1939
|
+
() => {
|
|
1940
|
+
const target = ikTargetRef.current;
|
|
1941
|
+
if (!ikCalculatingRef.current || !target) return null;
|
|
1942
|
+
return {
|
|
1943
|
+
pos: target.position.clone(),
|
|
1944
|
+
rot: new THREE11.Euler().setFromQuaternion(target.quaternion)
|
|
1945
|
+
};
|
|
1946
|
+
},
|
|
1947
|
+
[]
|
|
1948
|
+
);
|
|
1949
|
+
const contextValue = useMemo(
|
|
1950
|
+
() => ({
|
|
1951
|
+
ikEnabledRef,
|
|
1952
|
+
ikCalculatingRef,
|
|
1953
|
+
ikTargetRef,
|
|
1954
|
+
siteIdRef,
|
|
1955
|
+
setIkEnabled,
|
|
1956
|
+
moveTarget,
|
|
1957
|
+
syncTargetToSite: syncTargetToSiteApi,
|
|
1958
|
+
solveIK,
|
|
1959
|
+
getGizmoStats
|
|
1960
|
+
}),
|
|
1961
|
+
[setIkEnabled, moveTarget, syncTargetToSiteApi, solveIK, getGizmoStats]
|
|
1958
1962
|
);
|
|
1963
|
+
return /* @__PURE__ */ jsx(IkContext.Provider, { value: contextValue, children });
|
|
1959
1964
|
}
|
|
1965
|
+
var IkController = createController(
|
|
1966
|
+
{
|
|
1967
|
+
name: "IkController",
|
|
1968
|
+
defaultConfig: {
|
|
1969
|
+
damping: 0.01,
|
|
1970
|
+
maxIterations: 50
|
|
1971
|
+
}
|
|
1972
|
+
},
|
|
1973
|
+
IkControllerImpl
|
|
1974
|
+
);
|
|
1960
1975
|
var _mat4 = new THREE11.Matrix4();
|
|
1961
1976
|
var _pos = new THREE11.Vector3();
|
|
1962
1977
|
var _quat = new THREE11.Quaternion();
|
|
@@ -2057,7 +2072,8 @@ function ContactMarkers({
|
|
|
2057
2072
|
maxContacts = 100,
|
|
2058
2073
|
radius = 8e-3,
|
|
2059
2074
|
color = "#22d3ee",
|
|
2060
|
-
visible = true
|
|
2075
|
+
visible = true,
|
|
2076
|
+
...groupProps
|
|
2061
2077
|
} = {}) {
|
|
2062
2078
|
const { mjDataRef, status } = useMujocoSim();
|
|
2063
2079
|
const meshRef = useRef(null);
|
|
@@ -2085,10 +2101,10 @@ function ContactMarkers({
|
|
|
2085
2101
|
mesh.instanceMatrix.needsUpdate = true;
|
|
2086
2102
|
});
|
|
2087
2103
|
if (status !== "ready") return null;
|
|
2088
|
-
return /* @__PURE__ */ jsxs("instancedMesh", { ref: meshRef, args: [void 0, void 0, maxContacts], frustumCulled: false, renderOrder: 999, children: [
|
|
2104
|
+
return /* @__PURE__ */ jsx("group", { ...groupProps, children: /* @__PURE__ */ jsxs("instancedMesh", { ref: meshRef, args: [void 0, void 0, maxContacts], frustumCulled: false, renderOrder: 999, children: [
|
|
2089
2105
|
/* @__PURE__ */ jsx("sphereGeometry", { args: [radius, 8, 8] }),
|
|
2090
2106
|
/* @__PURE__ */ jsx("meshBasicMaterial", { color, depthTest: false })
|
|
2091
|
-
] });
|
|
2107
|
+
] }) });
|
|
2092
2108
|
}
|
|
2093
2109
|
var _force = new Float64Array(3);
|
|
2094
2110
|
var _torque = new Float64Array(3);
|
|
@@ -2100,7 +2116,8 @@ var _raycaster = new THREE11.Raycaster();
|
|
|
2100
2116
|
var _mouse = new THREE11.Vector2();
|
|
2101
2117
|
function DragInteraction({
|
|
2102
2118
|
stiffness = 250,
|
|
2103
|
-
showArrow = true
|
|
2119
|
+
showArrow = true,
|
|
2120
|
+
...groupProps
|
|
2104
2121
|
}) {
|
|
2105
2122
|
const { mjDataRef, mujocoRef, mjModelRef, status } = useMujocoSim();
|
|
2106
2123
|
const { gl, camera, scene, controls } = useThree();
|
|
@@ -2257,7 +2274,7 @@ function DragInteraction({
|
|
|
2257
2274
|
}
|
|
2258
2275
|
});
|
|
2259
2276
|
if (status !== "ready") return null;
|
|
2260
|
-
return /* @__PURE__ */ jsx("group", { ref: groupRef });
|
|
2277
|
+
return /* @__PURE__ */ jsx("group", { ...groupProps, ref: groupRef });
|
|
2261
2278
|
}
|
|
2262
2279
|
function useSceneLights(intensity = 1) {
|
|
2263
2280
|
const { mjModelRef, status } = useMujocoSim();
|
|
@@ -2378,7 +2395,8 @@ function Debug({
|
|
|
2378
2395
|
showContacts = false,
|
|
2379
2396
|
showCOM = false,
|
|
2380
2397
|
showInertia = false,
|
|
2381
|
-
showTendons = false
|
|
2398
|
+
showTendons = false,
|
|
2399
|
+
...groupProps
|
|
2382
2400
|
}) {
|
|
2383
2401
|
const { mjModelRef, mjDataRef, status } = useMujocoSim();
|
|
2384
2402
|
const { scene } = useThree();
|
|
@@ -2641,7 +2659,7 @@ function Debug({
|
|
|
2641
2659
|
}
|
|
2642
2660
|
});
|
|
2643
2661
|
if (status !== "ready") return null;
|
|
2644
|
-
return /* @__PURE__ */ jsxs(
|
|
2662
|
+
return /* @__PURE__ */ jsxs("group", { ...groupProps, children: [
|
|
2645
2663
|
/* @__PURE__ */ jsx("group", { ref: groupRef }),
|
|
2646
2664
|
showContacts && /* @__PURE__ */ jsx("group", { ref: contactGroupRef })
|
|
2647
2665
|
] });
|
|
@@ -2649,7 +2667,7 @@ function Debug({
|
|
|
2649
2667
|
var DEFAULT_TENDON_COLOR = new THREE11.Color(0.3, 0.3, 0.8);
|
|
2650
2668
|
var DEFAULT_TENDON_WIDTH = 2e-3;
|
|
2651
2669
|
new THREE11.Vector3();
|
|
2652
|
-
function TendonRenderer() {
|
|
2670
|
+
function TendonRenderer(props) {
|
|
2653
2671
|
const { mjModelRef, mjDataRef, status } = useMujocoSim();
|
|
2654
2672
|
const groupRef = useRef(null);
|
|
2655
2673
|
const meshesRef = useRef([]);
|
|
@@ -2751,9 +2769,9 @@ function TendonRenderer() {
|
|
|
2751
2769
|
}
|
|
2752
2770
|
});
|
|
2753
2771
|
if (status !== "ready") return null;
|
|
2754
|
-
return /* @__PURE__ */ jsx("group", { ref: groupRef });
|
|
2772
|
+
return /* @__PURE__ */ jsx("group", { ...props, ref: groupRef });
|
|
2755
2773
|
}
|
|
2756
|
-
function FlexRenderer() {
|
|
2774
|
+
function FlexRenderer(props) {
|
|
2757
2775
|
const { mjModelRef, mjDataRef, status } = useMujocoSim();
|
|
2758
2776
|
const groupRef = useRef(null);
|
|
2759
2777
|
const meshesRef = useRef([]);
|
|
@@ -2817,7 +2835,7 @@ function FlexRenderer() {
|
|
|
2817
2835
|
}
|
|
2818
2836
|
});
|
|
2819
2837
|
if (status !== "ready") return null;
|
|
2820
|
-
return /* @__PURE__ */ jsx("group", { ref: groupRef });
|
|
2838
|
+
return /* @__PURE__ */ jsx("group", { ...props, ref: groupRef });
|
|
2821
2839
|
}
|
|
2822
2840
|
var geomNameCacheByModel = /* @__PURE__ */ new WeakMap();
|
|
2823
2841
|
function getGeomNameCached(model, geomId) {
|
|
@@ -3705,6 +3723,10 @@ function useCameraAnimation() {
|
|
|
3705
3723
|
* @license
|
|
3706
3724
|
* SPDX-License-Identifier: Apache-2.0
|
|
3707
3725
|
*/
|
|
3726
|
+
/**
|
|
3727
|
+
* @license
|
|
3728
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
3729
|
+
*/
|
|
3708
3730
|
/**
|
|
3709
3731
|
* @license
|
|
3710
3732
|
* SPDX-License-Identifier: Apache-2.0
|
|
@@ -3724,10 +3746,6 @@ function useCameraAnimation() {
|
|
|
3724
3746
|
* IkController — composable IK controller plugin.
|
|
3725
3747
|
* Extracts all IK logic from MujocoSimProvider into an opt-in component.
|
|
3726
3748
|
*/
|
|
3727
|
-
/**
|
|
3728
|
-
* @license
|
|
3729
|
-
* SPDX-License-Identifier: Apache-2.0
|
|
3730
|
-
*/
|
|
3731
3749
|
/**
|
|
3732
3750
|
* @license
|
|
3733
3751
|
* SPDX-License-Identifier: Apache-2.0
|
|
@@ -3889,6 +3907,6 @@ function useCameraAnimation() {
|
|
|
3889
3907
|
* useCameraAnimation — composable camera animation hook.
|
|
3890
3908
|
*/
|
|
3891
3909
|
|
|
3892
|
-
export { ContactListener, ContactMarkers, Debug, DragInteraction, FlexRenderer, IkController, IkGizmo, MujocoCanvas, MujocoProvider, MujocoSimProvider, SceneLights,
|
|
3910
|
+
export { ContactListener, ContactMarkers, Debug, DragInteraction, FlexRenderer, IkController, IkGizmo, MujocoCanvas, MujocoPhysics, MujocoProvider, MujocoSimProvider, SceneLights, SelectionHighlight, TendonRenderer, TrajectoryPlayer, createController, findActuatorByName, findBodyByName, findGeomByName, findJointByName, findKeyframeByName, findSensorByName, findSiteByName, findTendonByName, getContact, getName, loadScene, useActuators, useAfterPhysicsStep, useBeforePhysicsStep, useBodyState, useCameraAnimation, useContactEvents, useContacts, useCtrl, useCtrlNoise, useGamepad, useGravityCompensation, useIk, useJointState, useKeyboardTeleop, useMujoco, useMujocoSim, usePolicy, useSceneLights, useSelectionHighlight, useSensor, useSensors, useSitePosition, useTrajectoryPlayer, useTrajectoryRecorder, useVideoRecorder };
|
|
3893
3911
|
//# sourceMappingURL=index.js.map
|
|
3894
3912
|
//# sourceMappingURL=index.js.map
|