mujoco-react 1.0.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 +80 -98
- package/dist/index.d.ts +1 -7
- package/dist/index.js +533 -530
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/core/MujocoSimProvider.tsx +2 -0
- package/src/index.ts +0 -1
package/dist/index.js
CHANGED
|
@@ -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) {
|
|
@@ -265,20 +507,98 @@ function scanDependencies(xmlString, currentFile, parser, downloaded, queue) {
|
|
|
265
507
|
if (!downloaded.has(fullPath)) queue.push(fullPath);
|
|
266
508
|
});
|
|
267
509
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
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",
|
|
282
602
|
11: "tendonpos",
|
|
283
603
|
12: "tendonvel",
|
|
284
604
|
13: "actuatorpos",
|
|
@@ -1095,7 +1415,10 @@ function MujocoSimProvider({
|
|
|
1095
1415
|
}),
|
|
1096
1416
|
[api, status]
|
|
1097
1417
|
);
|
|
1098
|
-
return /* @__PURE__ */
|
|
1418
|
+
return /* @__PURE__ */ jsxs(MujocoSimContext.Provider, { value: contextValue, children: [
|
|
1419
|
+
/* @__PURE__ */ jsx(SceneRenderer, {}),
|
|
1420
|
+
children
|
|
1421
|
+
] });
|
|
1099
1422
|
}
|
|
1100
1423
|
var MujocoCanvas = forwardRef(
|
|
1101
1424
|
function MujocoCanvas2({
|
|
@@ -1448,527 +1771,207 @@ function syncGizmoToSite(data, siteId, target) {
|
|
|
1448
1771
|
siteMat[6],
|
|
1449
1772
|
siteMat[7],
|
|
1450
1773
|
siteMat[8],
|
|
1451
|
-
0,
|
|
1452
|
-
0,
|
|
1453
|
-
0,
|
|
1454
|
-
0,
|
|
1455
|
-
1
|
|
1456
|
-
);
|
|
1457
|
-
target.quaternion.setFromRotationMatrix(_syncMat4);
|
|
1458
|
-
}
|
|
1459
|
-
function IkControllerImpl({
|
|
1460
|
-
config,
|
|
1461
|
-
children
|
|
1462
|
-
}) {
|
|
1463
|
-
const { mjModelRef, mjDataRef, mujocoRef, configRef, resetCallbacks, status } = useMujocoSim();
|
|
1464
|
-
const ikEnabledRef = useRef(false);
|
|
1465
|
-
const ikCalculatingRef = useRef(false);
|
|
1466
|
-
const ikTargetRef = useRef(new THREE11.Group());
|
|
1467
|
-
const siteIdRef = useRef(-1);
|
|
1468
|
-
const genericIkRef = useRef(new GenericIK(mujocoRef.current));
|
|
1469
|
-
const firstIkEnableRef = useRef(true);
|
|
1470
|
-
const needsInitialSync = useRef(true);
|
|
1471
|
-
const gizmoAnimRef = useRef({
|
|
1472
|
-
active: false,
|
|
1473
|
-
startPos: new THREE11.Vector3(),
|
|
1474
|
-
endPos: new THREE11.Vector3(),
|
|
1475
|
-
startRot: new THREE11.Quaternion(),
|
|
1476
|
-
endRot: new THREE11.Quaternion(),
|
|
1477
|
-
startTime: 0,
|
|
1478
|
-
duration: 1e3
|
|
1479
|
-
});
|
|
1480
|
-
useEffect(() => {
|
|
1481
|
-
const model = mjModelRef.current;
|
|
1482
|
-
if (!model || status !== "ready") {
|
|
1483
|
-
siteIdRef.current = -1;
|
|
1484
|
-
return;
|
|
1485
|
-
}
|
|
1486
|
-
siteIdRef.current = findSiteByName(model, config.siteName);
|
|
1487
|
-
const data = mjDataRef.current;
|
|
1488
|
-
if (data && ikTargetRef.current) {
|
|
1489
|
-
syncGizmoToSite(data, siteIdRef.current, ikTargetRef.current);
|
|
1490
|
-
}
|
|
1491
|
-
}, [config.siteName, status, mjModelRef, mjDataRef]);
|
|
1492
|
-
const ikSolveFn = useCallback(
|
|
1493
|
-
(pos, quat, currentQ) => {
|
|
1494
|
-
if (config.ikSolveFn) return config.ikSolveFn(pos, quat, currentQ);
|
|
1495
|
-
const model = mjModelRef.current;
|
|
1496
|
-
const data = mjDataRef.current;
|
|
1497
|
-
if (!model || !data || siteIdRef.current === -1) return null;
|
|
1498
|
-
return genericIkRef.current.solve(
|
|
1499
|
-
model,
|
|
1500
|
-
data,
|
|
1501
|
-
siteIdRef.current,
|
|
1502
|
-
config.numJoints,
|
|
1503
|
-
pos,
|
|
1504
|
-
quat,
|
|
1505
|
-
currentQ,
|
|
1506
|
-
{
|
|
1507
|
-
damping: config.damping,
|
|
1508
|
-
maxIterations: config.maxIterations
|
|
1509
|
-
}
|
|
1510
|
-
);
|
|
1511
|
-
},
|
|
1512
|
-
[config.ikSolveFn, config.numJoints, config.damping, config.maxIterations, mjModelRef, mjDataRef]
|
|
1513
|
-
);
|
|
1514
|
-
const ikSolveFnRef = useRef(ikSolveFn);
|
|
1515
|
-
ikSolveFnRef.current = ikSolveFn;
|
|
1516
|
-
useFrame(() => {
|
|
1517
|
-
if (needsInitialSync.current && siteIdRef.current !== -1) {
|
|
1518
|
-
const data = mjDataRef.current;
|
|
1519
|
-
if (data && ikTargetRef.current) {
|
|
1520
|
-
syncGizmoToSite(data, siteIdRef.current, ikTargetRef.current);
|
|
1521
|
-
needsInitialSync.current = false;
|
|
1522
|
-
}
|
|
1523
|
-
}
|
|
1524
|
-
const ga = gizmoAnimRef.current;
|
|
1525
|
-
const target = ikTargetRef.current;
|
|
1526
|
-
if (!ga.active || !target) return;
|
|
1527
|
-
const now = performance.now();
|
|
1528
|
-
const elapsed = now - ga.startTime;
|
|
1529
|
-
const t = Math.min(elapsed / ga.duration, 1);
|
|
1530
|
-
const ease = 1 - Math.pow(1 - t, 3);
|
|
1531
|
-
target.position.lerpVectors(ga.startPos, ga.endPos, ease);
|
|
1532
|
-
target.quaternion.slerpQuaternions(ga.startRot, ga.endRot, ease);
|
|
1533
|
-
if (t >= 1) ga.active = false;
|
|
1534
|
-
});
|
|
1535
|
-
useBeforePhysicsStep((model, data) => {
|
|
1536
|
-
if (!ikEnabledRef.current) {
|
|
1537
|
-
ikCalculatingRef.current = false;
|
|
1538
|
-
return;
|
|
1539
|
-
}
|
|
1540
|
-
const target = ikTargetRef.current;
|
|
1541
|
-
if (!target) return;
|
|
1542
|
-
ikCalculatingRef.current = true;
|
|
1543
|
-
const numJoints = config.numJoints;
|
|
1544
|
-
const currentQ = [];
|
|
1545
|
-
for (let i = 0; i < numJoints; i++) currentQ.push(data.qpos[i]);
|
|
1546
|
-
const solution = ikSolveFnRef.current(target.position, target.quaternion, currentQ);
|
|
1547
|
-
if (solution) {
|
|
1548
|
-
for (let i = 0; i < numJoints; i++) data.ctrl[i] = solution[i];
|
|
1549
|
-
}
|
|
1550
|
-
});
|
|
1551
|
-
useEffect(() => {
|
|
1552
|
-
const cb = () => {
|
|
1553
|
-
const data = mjDataRef.current;
|
|
1554
|
-
if (data && ikTargetRef.current) {
|
|
1555
|
-
syncGizmoToSite(data, siteIdRef.current, ikTargetRef.current);
|
|
1556
|
-
}
|
|
1557
|
-
gizmoAnimRef.current.active = false;
|
|
1558
|
-
firstIkEnableRef.current = true;
|
|
1559
|
-
ikEnabledRef.current = false;
|
|
1560
|
-
needsInitialSync.current = true;
|
|
1561
|
-
};
|
|
1562
|
-
resetCallbacks.current.add(cb);
|
|
1563
|
-
return () => {
|
|
1564
|
-
resetCallbacks.current.delete(cb);
|
|
1565
|
-
};
|
|
1566
|
-
}, [resetCallbacks, mjDataRef]);
|
|
1567
|
-
const setIkEnabled = useCallback(
|
|
1568
|
-
(enabled) => {
|
|
1569
|
-
ikEnabledRef.current = enabled;
|
|
1570
|
-
const data = mjDataRef.current;
|
|
1571
|
-
if (enabled && data && !gizmoAnimRef.current.active && ikTargetRef.current) {
|
|
1572
|
-
syncGizmoToSite(data, siteIdRef.current, ikTargetRef.current);
|
|
1573
|
-
firstIkEnableRef.current = false;
|
|
1574
|
-
}
|
|
1575
|
-
},
|
|
1576
|
-
[mjDataRef]
|
|
1577
|
-
);
|
|
1578
|
-
const syncTargetToSiteApi = useCallback(() => {
|
|
1579
|
-
const data = mjDataRef.current;
|
|
1580
|
-
const target = ikTargetRef.current;
|
|
1581
|
-
if (data && target) syncGizmoToSite(data, siteIdRef.current, target);
|
|
1582
|
-
}, [mjDataRef]);
|
|
1583
|
-
const solveIK = useCallback(
|
|
1584
|
-
(pos, quat, currentQ) => {
|
|
1585
|
-
return ikSolveFnRef.current(pos, quat, currentQ);
|
|
1586
|
-
},
|
|
1587
|
-
[]
|
|
1588
|
-
);
|
|
1589
|
-
const moveTarget = useCallback(
|
|
1590
|
-
(pos, duration = 0) => {
|
|
1591
|
-
if (!ikEnabledRef.current) setIkEnabled(true);
|
|
1592
|
-
const target = ikTargetRef.current;
|
|
1593
|
-
if (!target) return;
|
|
1594
|
-
const targetPos = pos.clone();
|
|
1595
|
-
const targetRot = new THREE11.Quaternion().setFromEuler(
|
|
1596
|
-
new THREE11.Euler(Math.PI, 0, 0)
|
|
1597
|
-
);
|
|
1598
|
-
if (duration > 0) {
|
|
1599
|
-
const ga = gizmoAnimRef.current;
|
|
1600
|
-
ga.active = true;
|
|
1601
|
-
ga.startPos.copy(target.position);
|
|
1602
|
-
ga.endPos.copy(targetPos);
|
|
1603
|
-
ga.startRot.copy(target.quaternion);
|
|
1604
|
-
ga.endRot.copy(targetRot);
|
|
1605
|
-
ga.startTime = performance.now();
|
|
1606
|
-
ga.duration = duration;
|
|
1607
|
-
} else {
|
|
1608
|
-
gizmoAnimRef.current.active = false;
|
|
1609
|
-
target.position.copy(targetPos);
|
|
1610
|
-
target.quaternion.copy(targetRot);
|
|
1611
|
-
}
|
|
1612
|
-
},
|
|
1613
|
-
[setIkEnabled]
|
|
1614
|
-
);
|
|
1615
|
-
const getGizmoStats = useCallback(
|
|
1616
|
-
() => {
|
|
1617
|
-
const target = ikTargetRef.current;
|
|
1618
|
-
if (!ikCalculatingRef.current || !target) return null;
|
|
1619
|
-
return {
|
|
1620
|
-
pos: target.position.clone(),
|
|
1621
|
-
rot: new THREE11.Euler().setFromQuaternion(target.quaternion)
|
|
1622
|
-
};
|
|
1623
|
-
},
|
|
1624
|
-
[]
|
|
1625
|
-
);
|
|
1626
|
-
const contextValue = useMemo(
|
|
1627
|
-
() => ({
|
|
1628
|
-
ikEnabledRef,
|
|
1629
|
-
ikCalculatingRef,
|
|
1630
|
-
ikTargetRef,
|
|
1631
|
-
siteIdRef,
|
|
1632
|
-
setIkEnabled,
|
|
1633
|
-
moveTarget,
|
|
1634
|
-
syncTargetToSite: syncTargetToSiteApi,
|
|
1635
|
-
solveIK,
|
|
1636
|
-
getGizmoStats
|
|
1637
|
-
}),
|
|
1638
|
-
[setIkEnabled, moveTarget, syncTargetToSiteApi, solveIK, getGizmoStats]
|
|
1639
|
-
);
|
|
1640
|
-
return /* @__PURE__ */ jsx(IkContext.Provider, { value: contextValue, children });
|
|
1641
|
-
}
|
|
1642
|
-
var IkController = createController(
|
|
1643
|
-
{
|
|
1644
|
-
name: "IkController",
|
|
1645
|
-
defaultConfig: {
|
|
1646
|
-
damping: 0.01,
|
|
1647
|
-
maxIterations: 50
|
|
1648
|
-
}
|
|
1649
|
-
},
|
|
1650
|
-
IkControllerImpl
|
|
1651
|
-
);
|
|
1652
|
-
var CapsuleGeometry = class extends THREE11.BufferGeometry {
|
|
1653
|
-
parameters;
|
|
1654
|
-
constructor(radius = 1, length = 1, capSegments = 4, radialSegments = 8) {
|
|
1655
|
-
super();
|
|
1656
|
-
this.type = "CapsuleGeometry";
|
|
1657
|
-
this.parameters = { radius, length, capSegments, radialSegments };
|
|
1658
|
-
const path = new THREE11.Path();
|
|
1659
|
-
path.absarc(0, -length / 2, radius, Math.PI * 1.5, 0, false);
|
|
1660
|
-
path.absarc(0, length / 2, radius, 0, Math.PI * 0.5, false);
|
|
1661
|
-
const latheGeometry = new THREE11.LatheGeometry(path.getPoints(capSegments), radialSegments);
|
|
1662
|
-
const self = this;
|
|
1663
|
-
self.setIndex(latheGeometry.getIndex());
|
|
1664
|
-
self.setAttribute("position", latheGeometry.getAttribute("position"));
|
|
1665
|
-
self.setAttribute("normal", latheGeometry.getAttribute("normal"));
|
|
1666
|
-
self.setAttribute("uv", latheGeometry.getAttribute("uv"));
|
|
1667
|
-
}
|
|
1668
|
-
};
|
|
1669
|
-
var Reflector = class extends THREE11.Mesh {
|
|
1670
|
-
isReflector = true;
|
|
1671
|
-
camera;
|
|
1672
|
-
reflectorPlane = new THREE11.Plane();
|
|
1673
|
-
normal = new THREE11.Vector3();
|
|
1674
|
-
reflectorWorldPosition = new THREE11.Vector3();
|
|
1675
|
-
cameraWorldPosition = new THREE11.Vector3();
|
|
1676
|
-
rotationMatrix = new THREE11.Matrix4();
|
|
1677
|
-
lookAtPosition = new THREE11.Vector3(0, 0, -1);
|
|
1678
|
-
clipPlane = new THREE11.Vector4();
|
|
1679
|
-
view = new THREE11.Vector3();
|
|
1680
|
-
target = new THREE11.Vector3();
|
|
1681
|
-
q = new THREE11.Vector4();
|
|
1682
|
-
textureMatrix = new THREE11.Matrix4();
|
|
1683
|
-
virtualCamera;
|
|
1684
|
-
renderTarget;
|
|
1685
|
-
constructor(geometry, options = {}) {
|
|
1686
|
-
super(geometry);
|
|
1687
|
-
this.type = "Reflector";
|
|
1688
|
-
this.camera = new THREE11.PerspectiveCamera();
|
|
1689
|
-
const color = options.color !== void 0 ? new THREE11.Color(options.color) : new THREE11.Color(8355711);
|
|
1690
|
-
const textureWidth = options.textureWidth || 512;
|
|
1691
|
-
const textureHeight = options.textureHeight || 512;
|
|
1692
|
-
const clipBias = options.clipBias || 0;
|
|
1693
|
-
const multisample = options.multisample !== void 0 ? options.multisample : 4;
|
|
1694
|
-
const blendTexture = options.texture || void 0;
|
|
1695
|
-
const mixStrength = options.mixStrength !== void 0 ? options.mixStrength : 0.25;
|
|
1696
|
-
this.virtualCamera = this.camera;
|
|
1697
|
-
this.renderTarget = new THREE11.WebGLRenderTarget(textureWidth, textureHeight, {
|
|
1698
|
-
samples: multisample,
|
|
1699
|
-
type: THREE11.HalfFloatType
|
|
1700
|
-
});
|
|
1701
|
-
this.material = new THREE11.MeshPhysicalMaterial({
|
|
1702
|
-
map: blendTexture,
|
|
1703
|
-
color,
|
|
1704
|
-
roughness: 0.5,
|
|
1705
|
-
metalness: 0.1
|
|
1706
|
-
});
|
|
1707
|
-
this.material.onBeforeCompile = (shader) => {
|
|
1708
|
-
shader.uniforms.tDiffuse = { value: this.renderTarget.texture };
|
|
1709
|
-
shader.uniforms.textureMatrix = { value: this.textureMatrix };
|
|
1710
|
-
shader.uniforms.mixStrength = { value: mixStrength };
|
|
1711
|
-
const bodyStart = shader.vertexShader.indexOf("void main() {");
|
|
1712
|
-
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}";
|
|
1713
|
-
const fragmentBodyStart = shader.fragmentShader.indexOf("void main() {");
|
|
1714
|
-
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}";
|
|
1715
|
-
};
|
|
1716
|
-
this.receiveShadow = true;
|
|
1717
|
-
this.onBeforeRender = (renderer, scene, camera) => {
|
|
1718
|
-
this.reflectorWorldPosition.setFromMatrixPosition(this.matrixWorld);
|
|
1719
|
-
this.cameraWorldPosition.setFromMatrixPosition(camera.matrixWorld);
|
|
1720
|
-
this.rotationMatrix.extractRotation(this.matrixWorld);
|
|
1721
|
-
this.normal.set(0, 0, 1);
|
|
1722
|
-
this.normal.applyMatrix4(this.rotationMatrix);
|
|
1723
|
-
this.view.subVectors(this.reflectorWorldPosition, this.cameraWorldPosition);
|
|
1724
|
-
if (this.view.dot(this.normal) > 0) return;
|
|
1725
|
-
this.view.reflect(this.normal).negate();
|
|
1726
|
-
this.view.add(this.reflectorWorldPosition);
|
|
1727
|
-
this.rotationMatrix.extractRotation(camera.matrixWorld);
|
|
1728
|
-
this.lookAtPosition.set(0, 0, -1);
|
|
1729
|
-
this.lookAtPosition.applyMatrix4(this.rotationMatrix);
|
|
1730
|
-
this.lookAtPosition.add(this.cameraWorldPosition);
|
|
1731
|
-
this.target.subVectors(this.reflectorWorldPosition, this.lookAtPosition);
|
|
1732
|
-
this.target.reflect(this.normal).negate();
|
|
1733
|
-
this.target.add(this.reflectorWorldPosition);
|
|
1734
|
-
this.virtualCamera.position.copy(this.view);
|
|
1735
|
-
this.virtualCamera.up.set(0, 1, 0);
|
|
1736
|
-
this.virtualCamera.up.applyMatrix4(this.rotationMatrix);
|
|
1737
|
-
this.virtualCamera.up.reflect(this.normal);
|
|
1738
|
-
this.virtualCamera.lookAt(this.target);
|
|
1739
|
-
this.virtualCamera.far = camera.far;
|
|
1740
|
-
this.virtualCamera.updateMatrixWorld();
|
|
1741
|
-
this.virtualCamera.projectionMatrix.copy(camera.projectionMatrix);
|
|
1742
|
-
this.textureMatrix.set(
|
|
1743
|
-
0.5,
|
|
1744
|
-
0,
|
|
1745
|
-
0,
|
|
1746
|
-
0.5,
|
|
1747
|
-
0,
|
|
1748
|
-
0.5,
|
|
1749
|
-
0,
|
|
1750
|
-
0.5,
|
|
1751
|
-
0,
|
|
1752
|
-
0,
|
|
1753
|
-
0.5,
|
|
1754
|
-
0.5,
|
|
1755
|
-
0,
|
|
1756
|
-
0,
|
|
1757
|
-
0,
|
|
1758
|
-
1
|
|
1759
|
-
);
|
|
1760
|
-
this.textureMatrix.multiply(this.virtualCamera.projectionMatrix);
|
|
1761
|
-
this.textureMatrix.multiply(this.virtualCamera.matrixWorldInverse);
|
|
1762
|
-
this.textureMatrix.multiply(this.matrixWorld);
|
|
1763
|
-
this.reflectorPlane.setFromNormalAndCoplanarPoint(this.normal, this.reflectorWorldPosition);
|
|
1764
|
-
this.reflectorPlane.applyMatrix4(this.virtualCamera.matrixWorldInverse);
|
|
1765
|
-
this.clipPlane.set(this.reflectorPlane.normal.x, this.reflectorPlane.normal.y, this.reflectorPlane.normal.z, this.reflectorPlane.constant);
|
|
1766
|
-
const projectionMatrix = this.virtualCamera.projectionMatrix;
|
|
1767
|
-
this.q.x = (Math.sign(this.clipPlane.x) + projectionMatrix.elements[8]) / projectionMatrix.elements[0];
|
|
1768
|
-
this.q.y = (Math.sign(this.clipPlane.y) + projectionMatrix.elements[9]) / projectionMatrix.elements[5];
|
|
1769
|
-
this.q.z = -1;
|
|
1770
|
-
this.q.w = (1 + projectionMatrix.elements[10]) / projectionMatrix.elements[14];
|
|
1771
|
-
this.clipPlane.multiplyScalar(2 / this.clipPlane.dot(this.q));
|
|
1772
|
-
projectionMatrix.elements[2] = this.clipPlane.x;
|
|
1773
|
-
projectionMatrix.elements[6] = this.clipPlane.y;
|
|
1774
|
-
projectionMatrix.elements[10] = this.clipPlane.z + 1 - clipBias;
|
|
1775
|
-
projectionMatrix.elements[14] = this.clipPlane.w;
|
|
1776
|
-
this.visible = false;
|
|
1777
|
-
const currentRenderTarget = renderer.getRenderTarget();
|
|
1778
|
-
const currentXrEnabled = renderer.xr.enabled;
|
|
1779
|
-
const currentShadowAutoUpdate = renderer.shadowMap.autoUpdate;
|
|
1780
|
-
renderer.xr.enabled = false;
|
|
1781
|
-
renderer.shadowMap.autoUpdate = false;
|
|
1782
|
-
renderer.setRenderTarget(this.renderTarget);
|
|
1783
|
-
renderer.state.buffers.depth.setMask(true);
|
|
1784
|
-
if (renderer.autoClear === false) renderer.clear();
|
|
1785
|
-
renderer.render(scene, this.virtualCamera);
|
|
1786
|
-
renderer.xr.enabled = currentXrEnabled;
|
|
1787
|
-
renderer.shadowMap.autoUpdate = currentShadowAutoUpdate;
|
|
1788
|
-
renderer.setRenderTarget(currentRenderTarget);
|
|
1789
|
-
const viewport = camera.viewport;
|
|
1790
|
-
if (viewport !== void 0) {
|
|
1791
|
-
renderer.state.viewport(viewport);
|
|
1792
|
-
}
|
|
1793
|
-
this.visible = true;
|
|
1794
|
-
};
|
|
1795
|
-
}
|
|
1796
|
-
getRenderTarget() {
|
|
1797
|
-
return this.renderTarget;
|
|
1798
|
-
}
|
|
1799
|
-
dispose() {
|
|
1800
|
-
this.renderTarget.dispose();
|
|
1801
|
-
const mesh = this;
|
|
1802
|
-
if (Array.isArray(mesh.material)) {
|
|
1803
|
-
mesh.material.forEach((m) => m.dispose());
|
|
1804
|
-
} else {
|
|
1805
|
-
mesh.material.dispose();
|
|
1806
|
-
}
|
|
1807
|
-
}
|
|
1808
|
-
};
|
|
1809
|
-
|
|
1810
|
-
// src/rendering/GeomBuilder.ts
|
|
1811
|
-
var GeomBuilder = class {
|
|
1812
|
-
mujoco;
|
|
1813
|
-
constructor(mujoco) {
|
|
1814
|
-
this.mujoco = mujoco;
|
|
1815
|
-
}
|
|
1816
|
-
/**
|
|
1817
|
-
* Creates a Three.js Object3D (usually a Mesh) for a specific geometry in the MuJoCo model.
|
|
1818
|
-
* Returns null if the geometry shouldn't be rendered (e.g., invisible collision triggers).
|
|
1819
|
-
*/
|
|
1820
|
-
create(mjModel, g) {
|
|
1821
|
-
if (mjModel.geom_group[g] === 3) return null;
|
|
1822
|
-
const type = mjModel.geom_type[g];
|
|
1823
|
-
const size = mjModel.geom_size.subarray(g * 3, g * 3 + 3);
|
|
1824
|
-
const pos = mjModel.geom_pos.subarray(g * 3, g * 3 + 3);
|
|
1825
|
-
const quat = mjModel.geom_quat.subarray(g * 4, g * 4 + 4);
|
|
1826
|
-
const matId = mjModel.geom_matid[g];
|
|
1827
|
-
const color = new THREE11.Color(16777215);
|
|
1828
|
-
let opacity = 1;
|
|
1829
|
-
if (matId >= 0) {
|
|
1830
|
-
const rgba = mjModel.mat_rgba.subarray(matId * 4, matId * 4 + 4);
|
|
1831
|
-
color.setRGB(rgba[0], rgba[1], rgba[2]);
|
|
1832
|
-
opacity = rgba[3];
|
|
1833
|
-
} else {
|
|
1834
|
-
const rgba = mjModel.geom_rgba.subarray(g * 4, g * 4 + 4);
|
|
1835
|
-
color.setRGB(rgba[0], rgba[1], rgba[2]);
|
|
1836
|
-
opacity = rgba[3];
|
|
1837
|
-
}
|
|
1838
|
-
const MG = this.mujoco.mjtGeom;
|
|
1839
|
-
let geo = null;
|
|
1840
|
-
const getVal = (v) => v?.value ?? v;
|
|
1841
|
-
if (type === getVal(MG.mjGEOM_PLANE)) {
|
|
1842
|
-
geo = new THREE11.PlaneGeometry(size[0] * 2 || 5, size[1] * 2 || 5);
|
|
1843
|
-
} else if (type === getVal(MG.mjGEOM_SPHERE)) {
|
|
1844
|
-
geo = new THREE11.SphereGeometry(size[0], 24, 24);
|
|
1845
|
-
} else if (type === getVal(MG.mjGEOM_CAPSULE)) {
|
|
1846
|
-
geo = new CapsuleGeometry(size[0], size[1] * 2, 24, 12);
|
|
1847
|
-
geo.rotateX(Math.PI / 2);
|
|
1848
|
-
} else if (type === getVal(MG.mjGEOM_BOX)) {
|
|
1849
|
-
geo = new THREE11.BoxGeometry(size[0] * 2, size[1] * 2, size[2] * 2);
|
|
1850
|
-
} else if (type === getVal(MG.mjGEOM_CYLINDER)) {
|
|
1851
|
-
geo = new THREE11.CylinderGeometry(size[0], size[0], size[1] * 2, 24);
|
|
1852
|
-
geo.rotateX(Math.PI / 2);
|
|
1853
|
-
} else if (type === getVal(MG.mjGEOM_MESH)) {
|
|
1854
|
-
const mId = mjModel.geom_dataid[g];
|
|
1855
|
-
const vAdr = mjModel.mesh_vertadr[mId];
|
|
1856
|
-
const vNum = mjModel.mesh_vertnum[mId];
|
|
1857
|
-
const fAdr = mjModel.mesh_faceadr[mId];
|
|
1858
|
-
const fNum = mjModel.mesh_facenum[mId];
|
|
1859
|
-
geo = new THREE11.BufferGeometry();
|
|
1860
|
-
geo.setAttribute("position", new THREE11.Float32BufferAttribute(mjModel.mesh_vert.subarray(vAdr * 3, (vAdr + vNum) * 3), 3));
|
|
1861
|
-
geo.setIndex(Array.from(mjModel.mesh_face.subarray(fAdr * 3, (fAdr + fNum) * 3)));
|
|
1862
|
-
geo.computeVertexNormals();
|
|
1863
|
-
}
|
|
1864
|
-
if (geo) {
|
|
1865
|
-
let mesh;
|
|
1866
|
-
if (type === getVal(MG.mjGEOM_PLANE)) {
|
|
1867
|
-
mesh = new Reflector(geo, {
|
|
1868
|
-
clipBias: 3e-3,
|
|
1869
|
-
textureWidth: 1024,
|
|
1870
|
-
textureHeight: 1024,
|
|
1871
|
-
color,
|
|
1872
|
-
mixStrength: 0.25
|
|
1873
|
-
});
|
|
1874
|
-
} else {
|
|
1875
|
-
mesh = new THREE11.Mesh(geo, new THREE11.MeshStandardMaterial({
|
|
1876
|
-
color,
|
|
1877
|
-
transparent: opacity < 1,
|
|
1878
|
-
opacity,
|
|
1879
|
-
roughness: 0.6,
|
|
1880
|
-
metalness: 0.2
|
|
1881
|
-
}));
|
|
1882
|
-
mesh.castShadow = true;
|
|
1883
|
-
mesh.receiveShadow = true;
|
|
1884
|
-
}
|
|
1885
|
-
mesh.position.set(pos[0], pos[1], pos[2]);
|
|
1886
|
-
mesh.quaternion.set(quat[1], quat[2], quat[3], quat[0]);
|
|
1887
|
-
mesh.userData.bodyID = mjModel.geom_bodyid[g];
|
|
1888
|
-
mesh.userData.geomID = g;
|
|
1889
|
-
return mesh;
|
|
1890
|
-
}
|
|
1891
|
-
return null;
|
|
1892
|
-
}
|
|
1893
|
-
};
|
|
1894
|
-
function SceneRenderer(props) {
|
|
1895
|
-
const { mjModelRef, mjDataRef, mujocoRef, onSelectionRef, status } = useMujocoSim();
|
|
1896
|
-
const groupRef = useRef(null);
|
|
1897
|
-
const bodyRefs = useRef([]);
|
|
1898
|
-
const prevModelRef = useRef(null);
|
|
1899
|
-
const geomBuilder = useMemo(() => {
|
|
1900
|
-
if (status !== "ready") return null;
|
|
1901
|
-
return new GeomBuilder(mujocoRef.current);
|
|
1902
|
-
}, [status, mujocoRef]);
|
|
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
|
+
});
|
|
1903
1803
|
useEffect(() => {
|
|
1904
|
-
if (status !== "ready" || !geomBuilder) return;
|
|
1905
1804
|
const model = mjModelRef.current;
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
prevModelRef.current = model;
|
|
1910
|
-
while (group.children.length > 0) {
|
|
1911
|
-
group.remove(group.children[0]);
|
|
1805
|
+
if (!model || status !== "ready") {
|
|
1806
|
+
siteIdRef.current = -1;
|
|
1807
|
+
return;
|
|
1912
1808
|
}
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
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
|
|
1921
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;
|
|
1922
1845
|
}
|
|
1923
|
-
group.add(bodyGroup);
|
|
1924
|
-
refs.push(bodyGroup);
|
|
1925
1846
|
}
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
const
|
|
1930
|
-
|
|
1931
|
-
const
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
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];
|
|
1946
1872
|
}
|
|
1947
1873
|
});
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
onDoubleClick: (e) => {
|
|
1954
|
-
if (typeof props.onDoubleClick === "function") props.onDoubleClick(e);
|
|
1955
|
-
e.stopPropagation();
|
|
1956
|
-
let obj = e.object;
|
|
1957
|
-
while (obj && obj.userData.bodyID === void 0 && obj.parent) {
|
|
1958
|
-
obj = obj.parent;
|
|
1959
|
-
}
|
|
1960
|
-
const bodyID = obj?.userData.bodyID;
|
|
1961
|
-
if (typeof bodyID === "number" && bodyID > 0) {
|
|
1962
|
-
const model = mjModelRef.current;
|
|
1963
|
-
if (model && bodyID < model.nbody && onSelectionRef.current) {
|
|
1964
|
-
const name = getName(model, model.name_bodyadr[bodyID]);
|
|
1965
|
-
onSelectionRef.current(bodyID, name);
|
|
1966
|
-
}
|
|
1967
|
-
}
|
|
1874
|
+
useEffect(() => {
|
|
1875
|
+
const cb = () => {
|
|
1876
|
+
const data = mjDataRef.current;
|
|
1877
|
+
if (data && ikTargetRef.current) {
|
|
1878
|
+
syncGizmoToSite(data, siteIdRef.current, ikTargetRef.current);
|
|
1968
1879
|
}
|
|
1969
|
-
|
|
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]
|
|
1970
1962
|
);
|
|
1963
|
+
return /* @__PURE__ */ jsx(IkContext.Provider, { value: contextValue, children });
|
|
1971
1964
|
}
|
|
1965
|
+
var IkController = createController(
|
|
1966
|
+
{
|
|
1967
|
+
name: "IkController",
|
|
1968
|
+
defaultConfig: {
|
|
1969
|
+
damping: 0.01,
|
|
1970
|
+
maxIterations: 50
|
|
1971
|
+
}
|
|
1972
|
+
},
|
|
1973
|
+
IkControllerImpl
|
|
1974
|
+
);
|
|
1972
1975
|
var _mat4 = new THREE11.Matrix4();
|
|
1973
1976
|
var _pos = new THREE11.Vector3();
|
|
1974
1977
|
var _quat = new THREE11.Quaternion();
|
|
@@ -3720,6 +3723,10 @@ function useCameraAnimation() {
|
|
|
3720
3723
|
* @license
|
|
3721
3724
|
* SPDX-License-Identifier: Apache-2.0
|
|
3722
3725
|
*/
|
|
3726
|
+
/**
|
|
3727
|
+
* @license
|
|
3728
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
3729
|
+
*/
|
|
3723
3730
|
/**
|
|
3724
3731
|
* @license
|
|
3725
3732
|
* SPDX-License-Identifier: Apache-2.0
|
|
@@ -3739,10 +3746,6 @@ function useCameraAnimation() {
|
|
|
3739
3746
|
* IkController — composable IK controller plugin.
|
|
3740
3747
|
* Extracts all IK logic from MujocoSimProvider into an opt-in component.
|
|
3741
3748
|
*/
|
|
3742
|
-
/**
|
|
3743
|
-
* @license
|
|
3744
|
-
* SPDX-License-Identifier: Apache-2.0
|
|
3745
|
-
*/
|
|
3746
3749
|
/**
|
|
3747
3750
|
* @license
|
|
3748
3751
|
* SPDX-License-Identifier: Apache-2.0
|
|
@@ -3904,6 +3907,6 @@ function useCameraAnimation() {
|
|
|
3904
3907
|
* useCameraAnimation — composable camera animation hook.
|
|
3905
3908
|
*/
|
|
3906
3909
|
|
|
3907
|
-
export { ContactListener, ContactMarkers, Debug, DragInteraction, FlexRenderer, IkController, IkGizmo, MujocoCanvas, MujocoPhysics, 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 };
|
|
3908
3911
|
//# sourceMappingURL=index.js.map
|
|
3909
3912
|
//# sourceMappingURL=index.js.map
|