mujoco-react 10.8.0 → 11.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 +15 -0
- package/dist/{chunk-5WFYM73E.js → chunk-UCQ5MJP4.js} +2 -2
- package/dist/chunk-UCQ5MJP4.js.map +1 -0
- package/dist/index.d.ts +23 -23
- package/dist/index.js +566 -415
- package/dist/index.js.map +1 -1
- package/dist/onnx.d.ts +1 -1
- package/dist/spark.d.ts +1 -1
- package/dist/spark.js +1 -1
- package/dist/{types-Bu8Aki_j.d.ts → types-BP8iiYei.d.ts} +48 -5
- package/package.json +6 -5
- package/src/components/FlexRenderer.tsx +13 -14
- package/src/core/GenericIK.ts +251 -214
- package/src/core/MujocoCanvas.tsx +2 -0
- package/src/core/MujocoPhysics.tsx +3 -0
- package/src/core/MujocoProvider.tsx +23 -9
- package/src/core/MujocoSimProvider.tsx +31 -34
- package/src/core/SceneLoader.ts +63 -30
- package/src/core/engineState.ts +58 -0
- package/src/hooks/useActuators.ts +3 -13
- package/src/hooks/useControlGroup.ts +2 -2
- package/src/hooks/useCtrl.ts +7 -6
- package/src/hooks/useGamepad.ts +3 -3
- package/src/hooks/useKeyboardTeleop.ts +2 -2
- package/src/index.ts +2 -0
- package/src/rendering/flexTopology.ts +24 -0
- package/src/types.ts +42 -4
- package/dist/chunk-5WFYM73E.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,14 +1,55 @@
|
|
|
1
|
-
import { withContacts, getContact, captureCameraFrame, captureCameraFrameBlob, createCameraFrameCaptureSession, CAMERA_FRAME_CAPTURE_PRE_RENDER_USER_DATA_KEY, CAPTURE_EXCLUDE_KEY, createCaptureCamera, prepareCaptureCamera } from './chunk-
|
|
2
|
-
export { CAMERA_FRAME_CAPTURE_PRE_RENDER_USER_DATA_KEY, CAMERA_FRAME_CAPTURE_RENDER_USER_DATA_KEY, CAPTURE_EXCLUDE_KEY, ModelActuators, ModelBodies, ModelCameras, ModelGeoms, ModelJoints, ModelKeyframes, ModelResources, ModelSensors, ModelSites, ScenarioLighting, SplatEnvironment, SplatEnvironmentReadinessStatus, VisualScenarioEffects, captureCameraFrame, captureCameraFrameBlob, captureCameraFrameTensor, createCameraFrameCaptureSession, createPairedSplatEnvironment, createSparkSplatViewerUrl, createSplatEnvironmentUserData, createSplatSceneConfig, createVisualScenarioExecutionContext, dataUrlToPolicyImageTensor, getContact, getScenarioBackground, getScenarioCameraPosition, getSplatEnvironmentReadiness, imageDataToPolicyImageTensor, pixelsToPolicyImageTensor, registerModelResources, renderCameraFrameToCanvas, useSplatEnvironment, useSplatSceneConfig, useVisualScenarioEffects, useVisualScenarioExecutionContext, withSplatEnvironment } from './chunk-
|
|
1
|
+
import { withContacts, getContact, captureCameraFrame, captureCameraFrameBlob, createCameraFrameCaptureSession, CAMERA_FRAME_CAPTURE_PRE_RENDER_USER_DATA_KEY, CAPTURE_EXCLUDE_KEY, createCaptureCamera, prepareCaptureCamera } from './chunk-UCQ5MJP4.js';
|
|
2
|
+
export { CAMERA_FRAME_CAPTURE_PRE_RENDER_USER_DATA_KEY, CAMERA_FRAME_CAPTURE_RENDER_USER_DATA_KEY, CAPTURE_EXCLUDE_KEY, ModelActuators, ModelBodies, ModelCameras, ModelGeoms, ModelJoints, ModelKeyframes, ModelResources, ModelSensors, ModelSites, ScenarioLighting, SplatEnvironment, SplatEnvironmentReadinessStatus, VisualScenarioEffects, captureCameraFrame, captureCameraFrameBlob, captureCameraFrameTensor, createCameraFrameCaptureSession, createPairedSplatEnvironment, createSparkSplatViewerUrl, createSplatEnvironmentUserData, createSplatSceneConfig, createVisualScenarioExecutionContext, dataUrlToPolicyImageTensor, getContact, getScenarioBackground, getScenarioCameraPosition, getSplatEnvironmentReadiness, imageDataToPolicyImageTensor, pixelsToPolicyImageTensor, registerModelResources, renderCameraFrameToCanvas, useSplatEnvironment, useSplatSceneConfig, useVisualScenarioEffects, useVisualScenarioExecutionContext, withSplatEnvironment } from './chunk-UCQ5MJP4.js';
|
|
3
3
|
import loadMujoco from '@mujoco/mujoco';
|
|
4
4
|
import defaultMujocoWasmUrl from '@mujoco/mujoco/mujoco.wasm?url';
|
|
5
5
|
import { createContext, forwardRef, useRef, useEffect, useContext, useState, useCallback, useMemo, useLayoutEffect, useId } from 'react';
|
|
6
6
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
7
7
|
import { Canvas, useThree, useFrame } from '@react-three/fiber';
|
|
8
|
-
import * as
|
|
8
|
+
import * as THREE12 from 'three';
|
|
9
9
|
import { mergeVertices } from 'three/examples/jsm/utils/BufferGeometryUtils.js';
|
|
10
10
|
import { PivotControls } from '@react-three/drei';
|
|
11
11
|
|
|
12
|
+
// src/core/engineState.ts
|
|
13
|
+
var threadCounts = /* @__PURE__ */ new WeakMap();
|
|
14
|
+
function configureModuleThreads(module, count) {
|
|
15
|
+
threadCounts.set(module, count);
|
|
16
|
+
}
|
|
17
|
+
function initializeDataThreads(module, data) {
|
|
18
|
+
const count = threadCounts.get(module) ?? 0;
|
|
19
|
+
if (count > 0) module.mju_threadpool(data, count);
|
|
20
|
+
}
|
|
21
|
+
function captureSimulationState(module, model, data) {
|
|
22
|
+
const signature = module.mjtState.mjSTATE_INTEGRATION.value;
|
|
23
|
+
const buffer = new module.DoubleBuffer(module.mj_stateSize(model, signature));
|
|
24
|
+
try {
|
|
25
|
+
module.mj_getState(model, data, buffer, signature);
|
|
26
|
+
return {
|
|
27
|
+
time: data.time,
|
|
28
|
+
qpos: new Float64Array(data.qpos),
|
|
29
|
+
qvel: new Float64Array(data.qvel),
|
|
30
|
+
ctrl: new Float64Array(data.ctrl),
|
|
31
|
+
act: new Float64Array(data.act),
|
|
32
|
+
qfrc_applied: new Float64Array(data.qfrc_applied),
|
|
33
|
+
integration: { signature, values: new Float64Array(buffer.GetView()), model }
|
|
34
|
+
};
|
|
35
|
+
} finally {
|
|
36
|
+
buffer.delete();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function restoreSimulationState(module, model, data, snapshot) {
|
|
40
|
+
const integration = snapshot.integration;
|
|
41
|
+
if (integration && (integration.model !== model || integration.values.length !== module.mj_stateSize(model, integration.signature))) {
|
|
42
|
+
throw new Error("Snapshot belongs to a different model; capture a new snapshot after loading a scene");
|
|
43
|
+
}
|
|
44
|
+
for (const key of ["qpos", "qvel", "ctrl", "act", "qfrc_applied"]) {
|
|
45
|
+
if (snapshot[key].length !== data[key].length)
|
|
46
|
+
throw new Error(`Snapshot ${key} size does not match model`);
|
|
47
|
+
}
|
|
48
|
+
if (integration) module.mj_setState(model, data, Array.from(integration.values), integration.signature);
|
|
49
|
+
data.time = snapshot.time;
|
|
50
|
+
for (const key of ["qpos", "qvel", "ctrl", "act", "qfrc_applied"]) data[key].set(snapshot[key]);
|
|
51
|
+
module.mj_forward(model, data);
|
|
52
|
+
}
|
|
12
53
|
var MujocoContext = createContext({
|
|
13
54
|
mujoco: null,
|
|
14
55
|
status: "loading",
|
|
@@ -36,6 +77,7 @@ function MujocoProvider({
|
|
|
36
77
|
mtWasmUrl,
|
|
37
78
|
threadedLoader,
|
|
38
79
|
wasmVariant = "single",
|
|
80
|
+
threadCount = 0,
|
|
39
81
|
timeout = 3e4,
|
|
40
82
|
children,
|
|
41
83
|
onError
|
|
@@ -43,12 +85,21 @@ function MujocoProvider({
|
|
|
43
85
|
const [status, setStatus] = useState("loading");
|
|
44
86
|
const [error, setError] = useState(null);
|
|
45
87
|
const moduleRef = useRef(null);
|
|
46
|
-
const isMounted = useRef(true);
|
|
47
88
|
const onErrorRef = useRef(onError);
|
|
48
89
|
onErrorRef.current = onError;
|
|
49
90
|
useEffect(() => {
|
|
50
|
-
|
|
91
|
+
let disposed = false;
|
|
92
|
+
moduleRef.current = null;
|
|
93
|
+
setStatus("loading");
|
|
94
|
+
setError(null);
|
|
51
95
|
const variant = resolveWasmVariant(wasmVariant, threadedLoader, mtWasmUrl);
|
|
96
|
+
if (!Number.isInteger(threadCount) || threadCount < 0 || threadCount > 0 && variant !== "threaded" || variant === "threaded" && !canUseThreadedWasm()) {
|
|
97
|
+
const err = new Error("threadCount must be a nonnegative integer; threaded physics requires the MT loader and cross-origin isolation");
|
|
98
|
+
setError(err.message);
|
|
99
|
+
setStatus("error");
|
|
100
|
+
onErrorRef.current?.(err);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
52
103
|
if (variant === "threaded" && !threadedLoader) {
|
|
53
104
|
const err = new Error('MujocoProvider wasmVariant="threaded" requires a threadedLoader from @mujoco/mujoco/mt');
|
|
54
105
|
setError(err.message);
|
|
@@ -71,35 +122,38 @@ function MujocoProvider({
|
|
|
71
122
|
const wasmPromise = load({
|
|
72
123
|
locateFile: (path) => path.endsWith(".wasm") ? selectedWasmUrl : path,
|
|
73
124
|
printErr: (text) => {
|
|
74
|
-
if (text.includes("Aborted") &&
|
|
125
|
+
if (text.includes("Aborted") && !disposed) {
|
|
75
126
|
setError("Simulation crashed. Reload page.");
|
|
76
127
|
setStatus("error");
|
|
77
128
|
}
|
|
78
129
|
}
|
|
79
130
|
});
|
|
131
|
+
let timeoutId;
|
|
80
132
|
const timeoutPromise = new Promise(
|
|
81
|
-
(_, reject) => setTimeout(() => reject(new Error(`WASM module load timed out after ${timeout}ms`)), timeout)
|
|
133
|
+
(_, reject) => timeoutId = setTimeout(() => reject(new Error(`WASM module load timed out after ${timeout}ms`)), timeout)
|
|
82
134
|
);
|
|
83
135
|
Promise.race([wasmPromise, timeoutPromise]).then((inst) => {
|
|
84
|
-
if (
|
|
136
|
+
if (!disposed) {
|
|
85
137
|
if (!isMujocoModule(inst)) {
|
|
86
138
|
throw new Error("MuJoCo WASM module initialized with an unexpected shape");
|
|
87
139
|
}
|
|
140
|
+
configureModuleThreads(inst, threadCount);
|
|
88
141
|
moduleRef.current = inst;
|
|
89
142
|
setStatus("ready");
|
|
90
143
|
}
|
|
91
144
|
}).catch((err) => {
|
|
92
|
-
if (
|
|
145
|
+
if (!disposed) {
|
|
93
146
|
const msg = err.message || "Failed to init spatial simulation";
|
|
94
147
|
setError(msg);
|
|
95
148
|
setStatus("error");
|
|
96
149
|
onErrorRef.current?.(new Error(msg));
|
|
97
150
|
}
|
|
98
|
-
});
|
|
151
|
+
}).finally(() => clearTimeout(timeoutId));
|
|
99
152
|
return () => {
|
|
100
|
-
|
|
153
|
+
disposed = true;
|
|
154
|
+
clearTimeout(timeoutId);
|
|
101
155
|
};
|
|
102
|
-
}, [wasmUrl, mtWasmUrl, threadedLoader, wasmVariant, timeout]);
|
|
156
|
+
}, [wasmUrl, mtWasmUrl, threadedLoader, wasmVariant, threadCount, timeout]);
|
|
103
157
|
return /* @__PURE__ */ jsx(
|
|
104
158
|
MujocoContext.Provider,
|
|
105
159
|
{
|
|
@@ -108,23 +162,6 @@ function MujocoProvider({
|
|
|
108
162
|
}
|
|
109
163
|
);
|
|
110
164
|
}
|
|
111
|
-
var CapsuleGeometry = class extends THREE11.BufferGeometry {
|
|
112
|
-
parameters;
|
|
113
|
-
constructor(radius = 1, length = 1, capSegments = 4, radialSegments = 8) {
|
|
114
|
-
super();
|
|
115
|
-
this.type = "CapsuleGeometry";
|
|
116
|
-
this.parameters = { radius, length, capSegments, radialSegments };
|
|
117
|
-
const path = new THREE11.Path();
|
|
118
|
-
path.absarc(0, -length / 2, radius, Math.PI * 1.5, 0, false);
|
|
119
|
-
path.absarc(0, length / 2, radius, 0, Math.PI * 0.5, false);
|
|
120
|
-
const latheGeometry = new THREE11.LatheGeometry(path.getPoints(capSegments), radialSegments);
|
|
121
|
-
const self = this;
|
|
122
|
-
self.setIndex(latheGeometry.getIndex());
|
|
123
|
-
self.setAttribute("position", latheGeometry.getAttribute("position"));
|
|
124
|
-
self.setAttribute("normal", latheGeometry.getAttribute("normal"));
|
|
125
|
-
self.setAttribute("uv", latheGeometry.getAttribute("uv"));
|
|
126
|
-
}
|
|
127
|
-
};
|
|
128
165
|
|
|
129
166
|
// src/core/SceneLoader.ts
|
|
130
167
|
var JOINT_TYPE_NAMES = {
|
|
@@ -150,7 +187,7 @@ function findSiteByName(mjModel, name) {
|
|
|
150
187
|
return -1;
|
|
151
188
|
}
|
|
152
189
|
function findActuatorByName(mjModel, name) {
|
|
153
|
-
for (let i = 0; i < mjModel.
|
|
190
|
+
for (let i = 0; i < mjModel.nactuator; i++) {
|
|
154
191
|
if (getName(mjModel, mjModel.name_actuatoradr[i]).includes(name)) return i;
|
|
155
192
|
}
|
|
156
193
|
return -1;
|
|
@@ -201,7 +238,7 @@ function findTendonByName(mjModel, name) {
|
|
|
201
238
|
return -1;
|
|
202
239
|
}
|
|
203
240
|
function getActuatedScalarQposAdr(mjModel, actuatorId) {
|
|
204
|
-
if (actuatorId < 0 || actuatorId >= mjModel.
|
|
241
|
+
if (actuatorId < 0 || actuatorId >= mjModel.nactuator) return -1;
|
|
205
242
|
const trnType = mjModel.actuator_trntype?.[actuatorId];
|
|
206
243
|
if (trnType !== void 0 && trnType !== 0 && trnType !== 1) return -1;
|
|
207
244
|
const jointId = mjModel.actuator_trnid[2 * actuatorId];
|
|
@@ -220,7 +257,7 @@ function isScalarJoint(mjModel, jointId) {
|
|
|
220
257
|
return jointId >= 0 && jointId < mjModel.njnt && getScalarJointDim(mjModel.jnt_type[jointId]) === 1;
|
|
221
258
|
}
|
|
222
259
|
function getActuatorJointId(mjModel, actuatorId) {
|
|
223
|
-
if (actuatorId < 0 || actuatorId >= mjModel.
|
|
260
|
+
if (actuatorId < 0 || actuatorId >= mjModel.nactuator) return -1;
|
|
224
261
|
const trnType = mjModel.actuator_trntype?.[actuatorId];
|
|
225
262
|
if (trnType !== void 0 && trnType !== 0 && trnType !== 1) return -1;
|
|
226
263
|
const jointId = mjModel.actuator_trnid[2 * actuatorId];
|
|
@@ -242,13 +279,38 @@ function getJointInfo(mjModel, jointId) {
|
|
|
242
279
|
};
|
|
243
280
|
}
|
|
244
281
|
function getActuatorInfo(mjModel, actuatorId) {
|
|
245
|
-
const
|
|
282
|
+
const ctrlAdr = mjModel.actuator_ctrladr[actuatorId];
|
|
283
|
+
const ctrlCount = mjModel.actuator_ctrlnum[actuatorId];
|
|
284
|
+
const ranges = Array.from({ length: ctrlCount }, (_, input) => {
|
|
285
|
+
const adr = ctrlAdr + input;
|
|
286
|
+
const lo = mjModel.actuator_ctrlrange[2 * adr], hi = mjModel.actuator_ctrlrange[2 * adr + 1];
|
|
287
|
+
return lo < hi ? [lo, hi] : [-Infinity, Infinity];
|
|
288
|
+
});
|
|
246
289
|
return {
|
|
247
290
|
id: actuatorId,
|
|
248
291
|
name: getName(mjModel, mjModel.name_actuatoradr[actuatorId]),
|
|
249
|
-
|
|
292
|
+
ctrlAdr,
|
|
293
|
+
ctrlCount,
|
|
294
|
+
ranges,
|
|
295
|
+
range: ranges[0] ?? [-Infinity, Infinity]
|
|
250
296
|
};
|
|
251
297
|
}
|
|
298
|
+
function getActuatorControlAddress(model, id, input) {
|
|
299
|
+
if (id < 0) return -1;
|
|
300
|
+
const count = model.actuator_ctrlnum[id];
|
|
301
|
+
if (input === void 0 && count !== 1) throw new Error("This actuator requires an explicit input index or a complete control vector");
|
|
302
|
+
const index = input ?? 0;
|
|
303
|
+
if (!Number.isInteger(index) || index < 0 || index >= count) throw new Error("Actuator input index out of range");
|
|
304
|
+
return model.actuator_ctrladr[id] + index;
|
|
305
|
+
}
|
|
306
|
+
function writeActuatorControl(model, data, id, value) {
|
|
307
|
+
if (id < 0) return;
|
|
308
|
+
const values = typeof value === "number" ? [value] : value;
|
|
309
|
+
if (values.length !== model.actuator_ctrlnum[id] || !values.every(Number.isFinite)) {
|
|
310
|
+
throw new Error("Actuator control must contain one finite value per input");
|
|
311
|
+
}
|
|
312
|
+
data.ctrl.set(values, model.actuator_ctrladr[id]);
|
|
313
|
+
}
|
|
252
314
|
function includesResourceName(names, name) {
|
|
253
315
|
return names.includes(name);
|
|
254
316
|
}
|
|
@@ -284,7 +346,7 @@ function orderedActuatorIdsFromSelector(mjModel, selector) {
|
|
|
284
346
|
return selector.map((name) => findActuatorByName(mjModel, name)).filter((id) => id >= 0 && getActuatorJointId(mjModel, id) >= 0);
|
|
285
347
|
}
|
|
286
348
|
const ids = [];
|
|
287
|
-
for (let i = 0; i < mjModel.
|
|
349
|
+
for (let i = 0; i < mjModel.nactuator; i++) {
|
|
288
350
|
if (getActuatorJointId(mjModel, i) < 0) continue;
|
|
289
351
|
const info = getActuatorInfo(mjModel, i);
|
|
290
352
|
if (matchesSelector(info, selector)) ids.push(i);
|
|
@@ -323,9 +385,9 @@ function unique(values) {
|
|
|
323
385
|
return result;
|
|
324
386
|
}
|
|
325
387
|
function findActuatorForJoint(mjModel, jointId, preferredActuatorIds) {
|
|
326
|
-
const search = preferredActuatorIds ?? Array.from({ length: mjModel.
|
|
388
|
+
const search = preferredActuatorIds ?? Array.from({ length: mjModel.nactuator }, (_, i) => i);
|
|
327
389
|
for (const actuatorId of search) {
|
|
328
|
-
if (getActuatorJointId(mjModel, actuatorId) === jointId) return actuatorId;
|
|
390
|
+
if (mjModel.actuator_ctrlnum[actuatorId] === 1 && getActuatorJointId(mjModel, actuatorId) === jointId) return actuatorId;
|
|
329
391
|
}
|
|
330
392
|
return -1;
|
|
331
393
|
}
|
|
@@ -345,12 +407,12 @@ function buildControlGroup(mjModel, jointIds, preferredActuatorIds) {
|
|
|
345
407
|
if (actuatorId >= 0) {
|
|
346
408
|
const actuator = getActuatorInfo(mjModel, actuatorId);
|
|
347
409
|
actuators.push(actuator);
|
|
348
|
-
ctrlAdr.push(actuatorId);
|
|
410
|
+
ctrlAdr.push(getActuatorControlAddress(mjModel, actuatorId));
|
|
349
411
|
joints.push({
|
|
350
412
|
...joint,
|
|
351
413
|
actuatorId,
|
|
352
414
|
actuatorName: actuator.name,
|
|
353
|
-
ctrlAdr: actuatorId,
|
|
415
|
+
ctrlAdr: getActuatorControlAddress(mjModel, actuatorId),
|
|
354
416
|
ctrlRange: actuator.range
|
|
355
417
|
});
|
|
356
418
|
} else {
|
|
@@ -390,28 +452,29 @@ function buildControlGroup(mjModel, jointIds, preferredActuatorIds) {
|
|
|
390
452
|
}
|
|
391
453
|
function getActuatedJoints(mjModel) {
|
|
392
454
|
const result = [];
|
|
393
|
-
for (let actuatorId = 0; actuatorId < mjModel.
|
|
455
|
+
for (let actuatorId = 0; actuatorId < mjModel.nactuator; actuatorId++) {
|
|
394
456
|
const jointId = getActuatorJointId(mjModel, actuatorId);
|
|
395
|
-
if (jointId < 0) continue;
|
|
457
|
+
if (jointId < 0 || mjModel.actuator_ctrlnum[actuatorId] !== 1) continue;
|
|
396
458
|
const actuator = getActuatorInfo(mjModel, actuatorId);
|
|
397
459
|
result.push({
|
|
398
460
|
...getJointInfo(mjModel, jointId),
|
|
399
461
|
actuatorId,
|
|
400
462
|
actuatorName: actuator.name,
|
|
401
|
-
ctrlAdr: actuatorId,
|
|
463
|
+
ctrlAdr: getActuatorControlAddress(mjModel, actuatorId),
|
|
402
464
|
ctrlRange: actuator.range
|
|
403
465
|
});
|
|
404
466
|
}
|
|
405
467
|
return result;
|
|
406
468
|
}
|
|
407
469
|
function getControlMap(mjModel) {
|
|
408
|
-
const actuatorIds = Array.from({ length: mjModel.
|
|
470
|
+
const actuatorIds = Array.from({ length: mjModel.nactuator }, (_, i) => i).filter((id) => mjModel.actuator_ctrlnum[id] === 1 && getActuatorJointId(mjModel, id) >= 0);
|
|
409
471
|
const jointIds = actuatorIds.map((id) => getActuatorJointId(mjModel, id));
|
|
410
472
|
return buildControlGroup(mjModel, jointIds, actuatorIds) ?? createContiguousControlGroup(mjModel, 0);
|
|
411
473
|
}
|
|
412
474
|
function resolveControlGroup(mjModel, selector) {
|
|
413
475
|
if (selector.actuators) {
|
|
414
476
|
const actuatorIds = orderedActuatorIdsFromSelector(mjModel, selector.actuators);
|
|
477
|
+
for (const id of actuatorIds) getActuatorControlAddress(mjModel, id);
|
|
415
478
|
const jointIds = actuatorIds.map((id) => getActuatorJointId(mjModel, id));
|
|
416
479
|
return buildControlGroup(mjModel, jointIds, actuatorIds);
|
|
417
480
|
}
|
|
@@ -429,6 +492,9 @@ function resolveControlGroup(mjModel, selector) {
|
|
|
429
492
|
return getControlMap(mjModel);
|
|
430
493
|
}
|
|
431
494
|
function createContiguousControlGroup(mjModel, count) {
|
|
495
|
+
if (count > 0 && (mjModel.nactuator !== mjModel.nu || Array.from(mjModel.actuator_ctrlnum).some((n2) => n2 !== 1))) {
|
|
496
|
+
throw new Error("Legacy numJoints control groups require scalar-input actuators; use named controls");
|
|
497
|
+
}
|
|
432
498
|
const n = Math.max(0, Math.min(count, mjModel.nq, mjModel.nu));
|
|
433
499
|
const joints = [];
|
|
434
500
|
const actuators = [];
|
|
@@ -778,12 +844,20 @@ async function loadSceneFromFiles(mujoco, config, onProgress) {
|
|
|
778
844
|
onProgress?.("Loading model...");
|
|
779
845
|
const mjModel = loadModelFromPath(mujoco, `/working/${config.sceneFile}`);
|
|
780
846
|
const mjData = new mujoco.MjData(mjModel);
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
847
|
+
try {
|
|
848
|
+
initializeDataThreads(mujoco, mjData);
|
|
849
|
+
applyInitialPose(mjModel, mjData, config);
|
|
850
|
+
mujoco.mj_forward(mjModel, mjData);
|
|
851
|
+
return { mjModel, mjData };
|
|
852
|
+
} catch (error) {
|
|
853
|
+
mjData.delete();
|
|
854
|
+
mjModel.delete();
|
|
855
|
+
throw error;
|
|
856
|
+
}
|
|
784
857
|
}
|
|
785
858
|
function applyInitialPose(mjModel, mjData, config) {
|
|
786
859
|
if (!config.homeJoints) return;
|
|
860
|
+
if (config.homeJoints.length && (mjModel.nactuator !== mjModel.nu || Array.from(mjModel.actuator_ctrlnum).some((n) => n !== 1))) throw new Error("homeJoints requires scalar-input actuators; use a keyframe for multi-input models");
|
|
787
861
|
const homeCount = Math.min(config.homeJoints.length, Math.max(mjModel.nu, mjModel.nq));
|
|
788
862
|
for (let i = 0; i < homeCount; i++) {
|
|
789
863
|
if (i < mjModel.nu) mjData.ctrl[i] = config.homeJoints[i];
|
|
@@ -862,9 +936,16 @@ async function loadScene(mujoco, config, onProgress) {
|
|
|
862
936
|
onProgress?.("Loading model...");
|
|
863
937
|
const mjModel = loadModelFromPath(mujoco, `/working/${config.sceneFile}`);
|
|
864
938
|
const mjData = new mujoco.MjData(mjModel);
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
939
|
+
try {
|
|
940
|
+
initializeDataThreads(mujoco, mjData);
|
|
941
|
+
applyInitialPose(mjModel, mjData, config);
|
|
942
|
+
mujoco.mj_forward(mjModel, mjData);
|
|
943
|
+
return { mjModel, mjData };
|
|
944
|
+
} catch (error) {
|
|
945
|
+
mjData.delete();
|
|
946
|
+
mjModel.delete();
|
|
947
|
+
throw error;
|
|
948
|
+
}
|
|
868
949
|
}
|
|
869
950
|
function scanDependencies(xmlString, currentFile, parser, downloaded, queue) {
|
|
870
951
|
for (const fullPath of collectDependencyPaths(xmlString, currentFile, parser)) {
|
|
@@ -894,6 +975,23 @@ function collectDependencyPaths(xmlString, currentFile, parser) {
|
|
|
894
975
|
});
|
|
895
976
|
return paths;
|
|
896
977
|
}
|
|
978
|
+
var CapsuleGeometry = class extends THREE12.BufferGeometry {
|
|
979
|
+
parameters;
|
|
980
|
+
constructor(radius = 1, length = 1, capSegments = 4, radialSegments = 8) {
|
|
981
|
+
super();
|
|
982
|
+
this.type = "CapsuleGeometry";
|
|
983
|
+
this.parameters = { radius, length, capSegments, radialSegments };
|
|
984
|
+
const path = new THREE12.Path();
|
|
985
|
+
path.absarc(0, -length / 2, radius, Math.PI * 1.5, 0, false);
|
|
986
|
+
path.absarc(0, length / 2, radius, 0, Math.PI * 0.5, false);
|
|
987
|
+
const latheGeometry = new THREE12.LatheGeometry(path.getPoints(capSegments), radialSegments);
|
|
988
|
+
const self = this;
|
|
989
|
+
self.setIndex(latheGeometry.getIndex());
|
|
990
|
+
self.setAttribute("position", latheGeometry.getAttribute("position"));
|
|
991
|
+
self.setAttribute("normal", latheGeometry.getAttribute("normal"));
|
|
992
|
+
self.setAttribute("uv", latheGeometry.getAttribute("uv"));
|
|
993
|
+
}
|
|
994
|
+
};
|
|
897
995
|
|
|
898
996
|
// src/rendering/GeomBuilder.ts
|
|
899
997
|
var DEFAULT_MESH_NORMAL_SMOOTHING_TOLERANCE = 1e-4;
|
|
@@ -944,10 +1042,10 @@ var GeomBuilder = class {
|
|
|
944
1042
|
rgba[out + 2] = b;
|
|
945
1043
|
rgba[out + 3] = a;
|
|
946
1044
|
}
|
|
947
|
-
const texture = new
|
|
948
|
-
texture.colorSpace =
|
|
949
|
-
texture.wrapS =
|
|
950
|
-
texture.wrapT =
|
|
1045
|
+
const texture = new THREE12.DataTexture(rgba, width, height, THREE12.RGBAFormat);
|
|
1046
|
+
texture.colorSpace = THREE12.LinearSRGBColorSpace;
|
|
1047
|
+
texture.wrapS = THREE12.RepeatWrapping;
|
|
1048
|
+
texture.wrapT = THREE12.RepeatWrapping;
|
|
951
1049
|
texture.flipY = true;
|
|
952
1050
|
const repeatOffset = matId * 2;
|
|
953
1051
|
const repeatS = mjModel.mat_texrepeat?.[repeatOffset] ?? 1;
|
|
@@ -968,7 +1066,7 @@ var GeomBuilder = class {
|
|
|
968
1066
|
const pos = mjModel.geom_pos.subarray(g * 3, g * 3 + 3);
|
|
969
1067
|
const quat = mjModel.geom_quat.subarray(g * 4, g * 4 + 4);
|
|
970
1068
|
const matId = mjModel.geom_matid[g];
|
|
971
|
-
const color = new
|
|
1069
|
+
const color = new THREE12.Color(16777215);
|
|
972
1070
|
const map = this.getMaterialTexture(mjModel, matId);
|
|
973
1071
|
let opacity = 1;
|
|
974
1072
|
if (matId >= 0) {
|
|
@@ -984,16 +1082,16 @@ var GeomBuilder = class {
|
|
|
984
1082
|
let geo = null;
|
|
985
1083
|
const getVal = (v) => v?.value ?? v;
|
|
986
1084
|
if (type === getVal(MG.mjGEOM_PLANE)) {
|
|
987
|
-
geo = new
|
|
1085
|
+
geo = new THREE12.PlaneGeometry(size[0] * 2 || 5, size[1] * 2 || 5);
|
|
988
1086
|
} else if (type === getVal(MG.mjGEOM_SPHERE)) {
|
|
989
|
-
geo = new
|
|
1087
|
+
geo = new THREE12.SphereGeometry(size[0], 24, 24);
|
|
990
1088
|
} else if (type === getVal(MG.mjGEOM_CAPSULE)) {
|
|
991
1089
|
geo = new CapsuleGeometry(size[0], size[1] * 2, 24, 12);
|
|
992
1090
|
geo.rotateX(Math.PI / 2);
|
|
993
1091
|
} else if (type === getVal(MG.mjGEOM_BOX)) {
|
|
994
|
-
geo = new
|
|
1092
|
+
geo = new THREE12.BoxGeometry(size[0] * 2, size[1] * 2, size[2] * 2);
|
|
995
1093
|
} else if (type === getVal(MG.mjGEOM_CYLINDER)) {
|
|
996
|
-
geo = new
|
|
1094
|
+
geo = new THREE12.CylinderGeometry(size[0], size[0], size[1] * 2, 24);
|
|
997
1095
|
geo.rotateX(Math.PI / 2);
|
|
998
1096
|
} else if (type === getVal(MG.mjGEOM_MESH)) {
|
|
999
1097
|
const mId = mjModel.geom_dataid[g];
|
|
@@ -1015,13 +1113,13 @@ var GeomBuilder = class {
|
|
|
1015
1113
|
uvs[c * 2] = mjModel.mesh_texcoord[t * 2];
|
|
1016
1114
|
uvs[c * 2 + 1] = 1 - mjModel.mesh_texcoord[t * 2 + 1];
|
|
1017
1115
|
}
|
|
1018
|
-
geo = new
|
|
1019
|
-
geo.setAttribute("position", new
|
|
1020
|
-
geo.setAttribute("uv", new
|
|
1116
|
+
geo = new THREE12.BufferGeometry();
|
|
1117
|
+
geo.setAttribute("position", new THREE12.Float32BufferAttribute(positions, 3));
|
|
1118
|
+
geo.setAttribute("uv", new THREE12.Float32BufferAttribute(uvs, 2));
|
|
1021
1119
|
geo.computeVertexNormals();
|
|
1022
1120
|
} else {
|
|
1023
|
-
geo = new
|
|
1024
|
-
geo.setAttribute("position", new
|
|
1121
|
+
geo = new THREE12.BufferGeometry();
|
|
1122
|
+
geo.setAttribute("position", new THREE12.Float32BufferAttribute(mjModel.mesh_vert.subarray(vAdr * 3, (vAdr + vNum) * 3), 3));
|
|
1025
1123
|
geo.setIndex(Array.from(mjModel.mesh_face.subarray(fAdr * 3, (fAdr + fNum) * 3)));
|
|
1026
1124
|
const smoothingTolerance = this.getMeshNormalSmoothingTolerance();
|
|
1027
1125
|
if (smoothingTolerance !== null) {
|
|
@@ -1037,7 +1135,7 @@ var GeomBuilder = class {
|
|
|
1037
1135
|
materialMap.repeat.multiplyScalar(2.5);
|
|
1038
1136
|
materialMap.needsUpdate = true;
|
|
1039
1137
|
}
|
|
1040
|
-
const mesh = new
|
|
1138
|
+
const mesh = new THREE12.Mesh(geo, new THREE12.MeshStandardMaterial({
|
|
1041
1139
|
color,
|
|
1042
1140
|
map: materialMap,
|
|
1043
1141
|
transparent: opacity < 1,
|
|
@@ -1102,7 +1200,7 @@ function SceneRenderer({ renderOptions, ...props }) {
|
|
|
1102
1200
|
}
|
|
1103
1201
|
const refs = [];
|
|
1104
1202
|
for (let i = 0; i < model.nbody; i++) {
|
|
1105
|
-
const bodyGroup = new
|
|
1203
|
+
const bodyGroup = new THREE12.Group();
|
|
1106
1204
|
bodyGroup.userData.bodyID = i;
|
|
1107
1205
|
const bodyName = getName(model, model.name_bodyadr[i]);
|
|
1108
1206
|
if (!hiddenBodiesRef.current.has(bodyName)) {
|
|
@@ -1131,9 +1229,9 @@ function SceneRenderer({ renderOptions, ...props }) {
|
|
|
1131
1229
|
const alpha = interpolation.alpha;
|
|
1132
1230
|
const i3 = i * 3;
|
|
1133
1231
|
ref.position.set(
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1232
|
+
THREE12.MathUtils.lerp(interpolation.previousXpos[i3], interpolation.currentXpos[i3], alpha),
|
|
1233
|
+
THREE12.MathUtils.lerp(interpolation.previousXpos[i3 + 1], interpolation.currentXpos[i3 + 1], alpha),
|
|
1234
|
+
THREE12.MathUtils.lerp(interpolation.previousXpos[i3 + 2], interpolation.currentXpos[i3 + 2], alpha)
|
|
1137
1235
|
);
|
|
1138
1236
|
const i4 = i * 4;
|
|
1139
1237
|
_previousQuat.set(
|
|
@@ -1193,8 +1291,8 @@ function SceneRenderer({ renderOptions, ...props }) {
|
|
|
1193
1291
|
}
|
|
1194
1292
|
);
|
|
1195
1293
|
}
|
|
1196
|
-
var _previousQuat = new
|
|
1197
|
-
var _currentQuat = new
|
|
1294
|
+
var _previousQuat = new THREE12.Quaternion();
|
|
1295
|
+
var _currentQuat = new THREE12.Quaternion();
|
|
1198
1296
|
var CameraViewportRegistryContext = createContext(null);
|
|
1199
1297
|
var nextViewportId = 0;
|
|
1200
1298
|
var VIEWPORT_RENDER_PRIORITY = 1;
|
|
@@ -1812,17 +1910,17 @@ async function recordMountedCameraFrameSequence(api, options) {
|
|
|
1812
1910
|
readiness
|
|
1813
1911
|
};
|
|
1814
1912
|
}
|
|
1815
|
-
var _raycaster = new
|
|
1816
|
-
var _ndc = new
|
|
1913
|
+
var _raycaster = new THREE12.Raycaster();
|
|
1914
|
+
var _ndc = new THREE12.Vector2();
|
|
1817
1915
|
function toVector3(value, fallback) {
|
|
1818
1916
|
if (!value) return fallback.clone();
|
|
1819
|
-
return value instanceof
|
|
1917
|
+
return value instanceof THREE12.Vector3 ? value.clone() : new THREE12.Vector3(value[0], value[1], value[2]);
|
|
1820
1918
|
}
|
|
1821
1919
|
function applyCameraPose(camera, options, fallbackCamera) {
|
|
1822
1920
|
camera.position.copy(toVector3(options.position, fallbackCamera.position));
|
|
1823
1921
|
camera.up.copy(toVector3(options.up, fallbackCamera.up));
|
|
1824
1922
|
if (options.quaternion) {
|
|
1825
|
-
if (options.quaternion instanceof
|
|
1923
|
+
if (options.quaternion instanceof THREE12.Quaternion) {
|
|
1826
1924
|
camera.quaternion.copy(options.quaternion);
|
|
1827
1925
|
} else {
|
|
1828
1926
|
camera.quaternion.set(
|
|
@@ -1833,15 +1931,15 @@ function applyCameraPose(camera, options, fallbackCamera) {
|
|
|
1833
1931
|
);
|
|
1834
1932
|
}
|
|
1835
1933
|
} else if (options.lookAt) {
|
|
1836
|
-
camera.lookAt(toVector3(options.lookAt, new
|
|
1934
|
+
camera.lookAt(toVector3(options.lookAt, new THREE12.Vector3()));
|
|
1837
1935
|
} else {
|
|
1838
1936
|
camera.quaternion.copy(fallbackCamera.quaternion);
|
|
1839
1937
|
}
|
|
1840
1938
|
camera.updateMatrixWorld();
|
|
1841
1939
|
}
|
|
1842
1940
|
function createProjectionCamera(fallbackCamera, options, width, height) {
|
|
1843
|
-
const camera = options.camera ? options.camera.clone() : fallbackCamera instanceof
|
|
1844
|
-
if (camera instanceof
|
|
1941
|
+
const camera = options.camera ? options.camera.clone() : fallbackCamera instanceof THREE12.PerspectiveCamera ? fallbackCamera.clone() : new THREE12.PerspectiveCamera(45, width / height, 0.01, 100);
|
|
1942
|
+
if (camera instanceof THREE12.PerspectiveCamera) {
|
|
1845
1943
|
camera.aspect = width / height;
|
|
1846
1944
|
camera.fov = options.fov ?? camera.fov;
|
|
1847
1945
|
camera.near = options.near ?? camera.near;
|
|
@@ -2006,8 +2104,8 @@ var _applyPoint = new Float64Array(3);
|
|
|
2006
2104
|
var _rayPnt = new Float64Array(3);
|
|
2007
2105
|
var _rayVec = new Float64Array(3);
|
|
2008
2106
|
var _rayGeomId = new Int32Array(1);
|
|
2009
|
-
var _projRaycaster = new
|
|
2010
|
-
var _projNdc = new
|
|
2107
|
+
var _projRaycaster = new THREE12.Raycaster();
|
|
2108
|
+
var _projNdc = new THREE12.Vector2();
|
|
2011
2109
|
function waitForNextAnimationFrame2() {
|
|
2012
2110
|
return new Promise((resolve) => {
|
|
2013
2111
|
requestAnimationFrame(() => resolve());
|
|
@@ -2032,7 +2130,7 @@ function quaternionFromMujocoQuat(values, offset) {
|
|
|
2032
2130
|
];
|
|
2033
2131
|
}
|
|
2034
2132
|
function quaternionFromXmat(values, offset) {
|
|
2035
|
-
const matrix = new
|
|
2133
|
+
const matrix = new THREE12.Matrix4();
|
|
2036
2134
|
matrix.set(
|
|
2037
2135
|
values[offset],
|
|
2038
2136
|
values[offset + 1],
|
|
@@ -2051,7 +2149,7 @@ function quaternionFromXmat(values, offset) {
|
|
|
2051
2149
|
0,
|
|
2052
2150
|
1
|
|
2053
2151
|
);
|
|
2054
|
-
const quaternion = new
|
|
2152
|
+
const quaternion = new THREE12.Quaternion().setFromRotationMatrix(matrix);
|
|
2055
2153
|
return [quaternion.x, quaternion.y, quaternion.z, quaternion.w];
|
|
2056
2154
|
}
|
|
2057
2155
|
function omitResolvedCameraSelectors(options) {
|
|
@@ -2059,14 +2157,14 @@ function omitResolvedCameraSelectors(options) {
|
|
|
2059
2157
|
return rest;
|
|
2060
2158
|
}
|
|
2061
2159
|
function vector3FromCaptureValue(value) {
|
|
2062
|
-
return value instanceof
|
|
2160
|
+
return value instanceof THREE12.Vector3 ? value.clone() : new THREE12.Vector3(value[0], value[1], value[2]);
|
|
2063
2161
|
}
|
|
2064
2162
|
function quaternionFromCaptureValue(value) {
|
|
2065
|
-
return value instanceof
|
|
2163
|
+
return value instanceof THREE12.Quaternion ? value.clone() : new THREE12.Quaternion(value[0], value[1], value[2], value[3]);
|
|
2066
2164
|
}
|
|
2067
2165
|
function applyMountedCameraPoseOffsets(options, position, quaternion) {
|
|
2068
|
-
const resolvedPosition = new
|
|
2069
|
-
const resolvedQuaternion = new
|
|
2166
|
+
const resolvedPosition = new THREE12.Vector3(position[0], position[1], position[2]);
|
|
2167
|
+
const resolvedQuaternion = new THREE12.Quaternion(
|
|
2070
2168
|
quaternion[0],
|
|
2071
2169
|
quaternion[1],
|
|
2072
2170
|
quaternion[2],
|
|
@@ -2151,7 +2249,7 @@ function mujocoCameraProjectionMatrix(model, cameraId, width, height, near, far)
|
|
|
2151
2249
|
const cy = height * (0.5 + principalY / sensorHeight);
|
|
2152
2250
|
const znear = near ?? 0.01;
|
|
2153
2251
|
const zfar = far ?? 100;
|
|
2154
|
-
return new
|
|
2252
|
+
return new THREE12.Matrix4().set(
|
|
2155
2253
|
2 * fx / width,
|
|
2156
2254
|
0,
|
|
2157
2255
|
1 - 2 * cx / width,
|
|
@@ -2293,6 +2391,7 @@ function MujocoSimProvider({
|
|
|
2293
2391
|
onSelection,
|
|
2294
2392
|
gravity,
|
|
2295
2393
|
timestep,
|
|
2394
|
+
integrator,
|
|
2296
2395
|
substeps,
|
|
2297
2396
|
paused,
|
|
2298
2397
|
speed,
|
|
@@ -2349,6 +2448,15 @@ function MujocoSimProvider({
|
|
|
2349
2448
|
model.opt.gravity[1] = gravity[1];
|
|
2350
2449
|
model.opt.gravity[2] = gravity[2];
|
|
2351
2450
|
}, [gravity]);
|
|
2451
|
+
const integratorRef = useRef(integrator);
|
|
2452
|
+
integratorRef.current = integrator;
|
|
2453
|
+
const applyIntegrator = (model) => {
|
|
2454
|
+
const value = integratorRef.current;
|
|
2455
|
+
if (value !== void 0) model.opt.integrator = { Euler: 0, RK4: 1, implicit: 2, implicitfast: 3, discrete: 4 }[value];
|
|
2456
|
+
};
|
|
2457
|
+
useEffect(() => {
|
|
2458
|
+
if (mjModelRef.current) applyIntegrator(mjModelRef.current);
|
|
2459
|
+
}, [integrator]);
|
|
2352
2460
|
useEffect(() => {
|
|
2353
2461
|
if (timestep === void 0) return;
|
|
2354
2462
|
const model = mjModelRef.current;
|
|
@@ -2394,11 +2502,12 @@ function MujocoSimProvider({
|
|
|
2394
2502
|
try {
|
|
2395
2503
|
const result = await loadScene(mujoco, buildMergedConfig(config));
|
|
2396
2504
|
if (disposed) {
|
|
2397
|
-
result.mjModel.delete();
|
|
2398
2505
|
result.mjData.delete();
|
|
2506
|
+
result.mjModel.delete();
|
|
2399
2507
|
return;
|
|
2400
2508
|
}
|
|
2401
2509
|
mujocoRef.current = mujoco;
|
|
2510
|
+
applyIntegrator(result.mjModel);
|
|
2402
2511
|
mjModelRef.current = result.mjModel;
|
|
2403
2512
|
mjDataRef.current = result.mjData;
|
|
2404
2513
|
physicsAccumulatorRef.current = 0;
|
|
@@ -2423,8 +2532,8 @@ function MujocoSimProvider({
|
|
|
2423
2532
|
})();
|
|
2424
2533
|
return () => {
|
|
2425
2534
|
disposed = true;
|
|
2426
|
-
mjModelRef.current?.delete();
|
|
2427
2535
|
mjDataRef.current?.delete();
|
|
2536
|
+
mjModelRef.current?.delete();
|
|
2428
2537
|
mjModelRef.current = null;
|
|
2429
2538
|
mjDataRef.current = null;
|
|
2430
2539
|
physicsAccumulatorRef.current = 0;
|
|
@@ -2596,26 +2705,15 @@ function MujocoSimProvider({
|
|
|
2596
2705
|
const saveState = useCallback(() => {
|
|
2597
2706
|
const data = mjDataRef.current;
|
|
2598
2707
|
if (!data) return { time: 0, qpos: new Float64Array(0), qvel: new Float64Array(0), ctrl: new Float64Array(0), act: new Float64Array(0), qfrc_applied: new Float64Array(0) };
|
|
2599
|
-
return
|
|
2600
|
-
time: data.time,
|
|
2601
|
-
qpos: new Float64Array(data.qpos),
|
|
2602
|
-
qvel: new Float64Array(data.qvel),
|
|
2603
|
-
ctrl: new Float64Array(data.ctrl),
|
|
2604
|
-
act: new Float64Array(data.act),
|
|
2605
|
-
qfrc_applied: new Float64Array(data.qfrc_applied)
|
|
2606
|
-
};
|
|
2708
|
+
return captureSimulationState(mujocoRef.current, mjModelRef.current, data);
|
|
2607
2709
|
}, []);
|
|
2608
2710
|
const restoreState = useCallback((snapshot) => {
|
|
2609
2711
|
const model = mjModelRef.current;
|
|
2610
2712
|
const data = mjDataRef.current;
|
|
2611
2713
|
if (!model || !data) return;
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
data.ctrl.set(snapshot.ctrl);
|
|
2616
|
-
if (snapshot.act.length > 0) data.act.set(snapshot.act);
|
|
2617
|
-
data.qfrc_applied.set(snapshot.qfrc_applied);
|
|
2618
|
-
mujocoRef.current.mj_forward(model, data);
|
|
2714
|
+
restoreSimulationState(mujocoRef.current, model, data, snapshot);
|
|
2715
|
+
physicsAccumulatorRef.current = 0;
|
|
2716
|
+
interpolationStateRef.current.valid = false;
|
|
2619
2717
|
}, [mujoco]);
|
|
2620
2718
|
const setQpos = useCallback((values) => {
|
|
2621
2719
|
const model = mjModelRef.current;
|
|
@@ -2643,11 +2741,11 @@ function MujocoSimProvider({
|
|
|
2643
2741
|
if (!model || !data) return;
|
|
2644
2742
|
if (typeof nameOrValues === "string") {
|
|
2645
2743
|
const id = findActuatorByName(model, nameOrValues);
|
|
2646
|
-
if (
|
|
2744
|
+
if (value !== void 0) writeActuatorControl(model, data, id, value);
|
|
2647
2745
|
} else {
|
|
2648
2746
|
for (const [name, val] of Object.entries(nameOrValues)) {
|
|
2649
2747
|
const id = findActuatorByName(model, name);
|
|
2650
|
-
|
|
2748
|
+
writeActuatorControl(model, data, id, val);
|
|
2651
2749
|
}
|
|
2652
2750
|
}
|
|
2653
2751
|
}, []);
|
|
@@ -2818,15 +2916,7 @@ function MujocoSimProvider({
|
|
|
2818
2916
|
const getActuatorsApi = useCallback(() => {
|
|
2819
2917
|
const model = mjModelRef.current;
|
|
2820
2918
|
if (!model) return [];
|
|
2821
|
-
const result =
|
|
2822
|
-
for (let i = 0; i < model.nu; i++) {
|
|
2823
|
-
const hasRange = model.actuator_ctrlrange[2 * i] < model.actuator_ctrlrange[2 * i + 1];
|
|
2824
|
-
result.push({
|
|
2825
|
-
id: i,
|
|
2826
|
-
name: getName(model, model.name_actuatoradr[i]),
|
|
2827
|
-
range: hasRange ? [model.actuator_ctrlrange[2 * i], model.actuator_ctrlrange[2 * i + 1]] : [-Infinity, Infinity]
|
|
2828
|
-
});
|
|
2829
|
-
}
|
|
2919
|
+
const result = Array.from({ length: model.nactuator }, (_, id) => getActuatorInfo(model, id));
|
|
2830
2920
|
return result;
|
|
2831
2921
|
}, []);
|
|
2832
2922
|
const getControlMapApi = useCallback(() => {
|
|
@@ -3015,7 +3105,7 @@ function MujocoSimProvider({
|
|
|
3015
3105
|
const geomId = _rayGeomId[0];
|
|
3016
3106
|
const bodyId = geomId >= 0 ? model.geom_bodyid[geomId] : -1;
|
|
3017
3107
|
return {
|
|
3018
|
-
point: new
|
|
3108
|
+
point: new THREE12.Vector3(
|
|
3019
3109
|
origin.x + dir.x * dist,
|
|
3020
3110
|
origin.y + dir.y * dist,
|
|
3021
3111
|
origin.z + dir.z * dist
|
|
@@ -3072,17 +3162,18 @@ function MujocoSimProvider({
|
|
|
3072
3162
|
const loadSceneApi = useCallback(async (newConfig) => {
|
|
3073
3163
|
const gen = ++loadGenRef.current;
|
|
3074
3164
|
try {
|
|
3075
|
-
mjModelRef.current?.delete();
|
|
3076
3165
|
mjDataRef.current?.delete();
|
|
3166
|
+
mjModelRef.current?.delete();
|
|
3077
3167
|
mjModelRef.current = null;
|
|
3078
3168
|
mjDataRef.current = null;
|
|
3079
3169
|
setStatus("loading");
|
|
3080
3170
|
const result = await loadScene(mujoco, buildMergedConfig(newConfig));
|
|
3081
3171
|
if (gen !== loadGenRef.current) {
|
|
3082
|
-
result.mjModel.delete();
|
|
3083
3172
|
result.mjData.delete();
|
|
3173
|
+
result.mjModel.delete();
|
|
3084
3174
|
return;
|
|
3085
3175
|
}
|
|
3176
|
+
applyIntegrator(result.mjModel);
|
|
3086
3177
|
mjModelRef.current = result.mjModel;
|
|
3087
3178
|
mjDataRef.current = result.mjData;
|
|
3088
3179
|
physicsAccumulatorRef.current = 0;
|
|
@@ -3624,6 +3715,7 @@ var MujocoCanvas = forwardRef(
|
|
|
3624
3715
|
// Declarative physics config
|
|
3625
3716
|
gravity,
|
|
3626
3717
|
timestep,
|
|
3718
|
+
integrator,
|
|
3627
3719
|
substeps,
|
|
3628
3720
|
paused,
|
|
3629
3721
|
speed,
|
|
@@ -3660,6 +3752,7 @@ var MujocoCanvas = forwardRef(
|
|
|
3660
3752
|
onSelection,
|
|
3661
3753
|
gravity,
|
|
3662
3754
|
timestep,
|
|
3755
|
+
integrator,
|
|
3663
3756
|
substeps,
|
|
3664
3757
|
paused,
|
|
3665
3758
|
speed,
|
|
@@ -3821,15 +3914,14 @@ function createControllerHook(options, useImpl) {
|
|
|
3821
3914
|
};
|
|
3822
3915
|
return useController;
|
|
3823
3916
|
}
|
|
3824
|
-
|
|
3825
|
-
// src/core/GenericIK.ts
|
|
3826
3917
|
var DEFAULTS = {
|
|
3827
3918
|
maxIterations: 50,
|
|
3828
3919
|
damping: 0.01,
|
|
3829
3920
|
tolerance: 1e-3,
|
|
3830
3921
|
epsilon: 1e-6,
|
|
3831
3922
|
posWeight: 1,
|
|
3832
|
-
rotWeight: 0.3
|
|
3923
|
+
rotWeight: 0.3,
|
|
3924
|
+
maxStepRad: 0.5
|
|
3833
3925
|
};
|
|
3834
3926
|
function resolveOptions(opts) {
|
|
3835
3927
|
return {
|
|
@@ -3839,121 +3931,173 @@ function resolveOptions(opts) {
|
|
|
3839
3931
|
epsilon: opts?.epsilon ?? DEFAULTS.epsilon,
|
|
3840
3932
|
posWeight: opts?.posWeight ?? DEFAULTS.posWeight,
|
|
3841
3933
|
rotWeight: opts?.rotWeight ?? DEFAULTS.rotWeight,
|
|
3934
|
+
maxStepRad: opts?.maxStepRad ?? DEFAULTS.maxStepRad,
|
|
3842
3935
|
jointLimits: opts?.jointLimits
|
|
3843
3936
|
};
|
|
3844
3937
|
}
|
|
3845
3938
|
var GenericIK = class {
|
|
3846
|
-
mujoco;
|
|
3847
3939
|
constructor(mujoco) {
|
|
3848
3940
|
this.mujoco = mujoco;
|
|
3849
3941
|
}
|
|
3942
|
+
/** Scalar hinge/slide interface. Returns values in qposAdr order. */
|
|
3943
|
+
solve(model, data, siteId, qposAdr, targetPos, targetQuat, currentQ, opts) {
|
|
3944
|
+
if (currentQ.length !== qposAdr.length) throw new Error("IK currentQ must match qposAdr length");
|
|
3945
|
+
const jointIds = Array.from(qposAdr, (adr) => {
|
|
3946
|
+
const id = Array.from({ length: model.njnt }, (_, i) => i).find(
|
|
3947
|
+
(i) => model.jnt_qposadr[i] === adr
|
|
3948
|
+
);
|
|
3949
|
+
if (id === void 0 || model.jnt_type[id] < 2) {
|
|
3950
|
+
throw new Error(
|
|
3951
|
+
"Scalar IK requires hinge/slide qpos addresses; use solveJoints for ball/free joints"
|
|
3952
|
+
);
|
|
3953
|
+
}
|
|
3954
|
+
return id;
|
|
3955
|
+
});
|
|
3956
|
+
const initial = new Float64Array(data.qpos);
|
|
3957
|
+
for (let i = 0; i < qposAdr.length; i++) initial[qposAdr[i]] = currentQ[i];
|
|
3958
|
+
const result = this.solveJoints(model, data, siteId, jointIds, targetPos, targetQuat, initial, opts);
|
|
3959
|
+
return result ? Array.from(qposAdr, (adr) => result[adr]) : null;
|
|
3960
|
+
}
|
|
3850
3961
|
/**
|
|
3851
|
-
* Solve
|
|
3852
|
-
*
|
|
3853
|
-
*
|
|
3854
|
-
*
|
|
3855
|
-
* @param qposAdr qpos addresses for scalar joints in solve order
|
|
3856
|
-
* @param targetPos Target position in world frame
|
|
3857
|
-
* @param targetQuat Target orientation in world frame
|
|
3858
|
-
* @param currentQ Current joint angles matching qposAdr order
|
|
3859
|
-
* @param opts Optional solver parameters
|
|
3860
|
-
* @returns Joint angles array, or null if solver diverged
|
|
3962
|
+
* Solve selected joint IDs, including ball/free joints. Initial and returned
|
|
3963
|
+
* configurations have model.nq entries, with MuJoCo wxyz quaternions.
|
|
3964
|
+
* Jacobian columns use nv/dof addresses; mj_integratePos updates quaternions.
|
|
3965
|
+
* The caller's qpos is restored even if the solve throws.
|
|
3861
3966
|
*/
|
|
3862
|
-
|
|
3967
|
+
solveJoints(model, data, siteId, jointIds, targetPos, targetQuat, currentQpos = data.qpos, opts) {
|
|
3863
3968
|
const o = resolveOptions(opts);
|
|
3864
|
-
const
|
|
3865
|
-
|
|
3866
|
-
|
|
3867
|
-
|
|
3868
|
-
|
|
3869
|
-
|
|
3870
|
-
|
|
3871
|
-
|
|
3872
|
-
|
|
3873
|
-
|
|
3874
|
-
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
3881
|
-
const
|
|
3882
|
-
|
|
3883
|
-
|
|
3884
|
-
|
|
3885
|
-
|
|
3886
|
-
|
|
3887
|
-
|
|
3888
|
-
|
|
3889
|
-
|
|
3890
|
-
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
|
|
3894
|
-
|
|
3895
|
-
const
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
3899
|
-
|
|
3900
|
-
|
|
3901
|
-
|
|
3902
|
-
|
|
3903
|
-
|
|
3904
|
-
|
|
3905
|
-
|
|
3906
|
-
|
|
3907
|
-
|
|
3908
|
-
|
|
3909
|
-
|
|
3910
|
-
|
|
3911
|
-
|
|
3912
|
-
|
|
3913
|
-
|
|
3914
|
-
|
|
3915
|
-
|
|
3916
|
-
|
|
3917
|
-
|
|
3918
|
-
|
|
3919
|
-
|
|
3920
|
-
|
|
3969
|
+
const targetRotation = new THREE12.Quaternion(targetQuat.x, targetQuat.y, targetQuat.z, targetQuat.w);
|
|
3970
|
+
if (!Number.isInteger(siteId) || siteId < 0 || siteId >= model.nsite)
|
|
3971
|
+
throw new Error("Invalid IK site ID");
|
|
3972
|
+
if (currentQpos.length !== model.nq || !Array.from(currentQpos).every(Number.isFinite))
|
|
3973
|
+
throw new Error("IK configuration must contain nq finite values");
|
|
3974
|
+
if (![
|
|
3975
|
+
targetPos.x,
|
|
3976
|
+
targetPos.y,
|
|
3977
|
+
targetPos.z,
|
|
3978
|
+
targetQuat.x,
|
|
3979
|
+
targetQuat.y,
|
|
3980
|
+
targetQuat.z,
|
|
3981
|
+
targetQuat.w
|
|
3982
|
+
].every(Number.isFinite) || targetRotation.lengthSq() === 0)
|
|
3983
|
+
throw new Error("IK target must be finite with a nonzero quaternion");
|
|
3984
|
+
if (!(o.damping > 0) || !(o.maxStepRad > 0) || !Number.isFinite(o.maxStepRad) || !Number.isInteger(o.maxIterations) || o.maxIterations < 1)
|
|
3985
|
+
throw new Error("Invalid IK solver options");
|
|
3986
|
+
const ids = Array.from(jointIds);
|
|
3987
|
+
if (new Set(ids).size !== ids.length || ids.some((id) => !Number.isInteger(id) || id < 0 || id >= model.njnt))
|
|
3988
|
+
throw new Error("IK joint IDs must be unique and valid");
|
|
3989
|
+
const dofs = ids.flatMap(
|
|
3990
|
+
(id) => Array.from(
|
|
3991
|
+
{ length: model.jnt_type[id] === 0 ? 6 : model.jnt_type[id] === 1 ? 3 : 1 },
|
|
3992
|
+
(_, i) => model.jnt_dofadr[id] + i
|
|
3993
|
+
)
|
|
3994
|
+
);
|
|
3995
|
+
const n = dofs.length;
|
|
3996
|
+
if (!n) return null;
|
|
3997
|
+
const savedQpos = new Float64Array(data.qpos);
|
|
3998
|
+
const buffers = [];
|
|
3999
|
+
try {
|
|
4000
|
+
const alloc = (size) => {
|
|
4001
|
+
const b = new this.mujoco.DoubleBuffer(size);
|
|
4002
|
+
buffers.push(b);
|
|
4003
|
+
return b;
|
|
4004
|
+
};
|
|
4005
|
+
const jacp = alloc(3 * model.nv), jacr = alloc(3 * model.nv), qbuffer = alloc(model.nq);
|
|
4006
|
+
qbuffer.GetView().set(currentQpos);
|
|
4007
|
+
const target = quatToMat3(targetRotation.normalize());
|
|
4008
|
+
const J = new Float64Array(6 * n), JJt = new Float64Array(36), rhs = new Float64Array(6), x = new Float64Array(6);
|
|
4009
|
+
const velocity = new Array(model.nv).fill(0);
|
|
4010
|
+
let best = null, bestErr = Infinity, noImprove = 0;
|
|
4011
|
+
const clamp = () => {
|
|
4012
|
+
const q = qbuffer.GetView();
|
|
4013
|
+
ids.forEach((id, i) => {
|
|
4014
|
+
const adr = model.jnt_qposadr[id], type = model.jnt_type[id];
|
|
4015
|
+
if (type >= 2) {
|
|
4016
|
+
const limits = o.jointLimits?.[i] ?? (model.jnt_limited[id] ? [model.jnt_range[2 * id], model.jnt_range[2 * id + 1]] : null);
|
|
4017
|
+
if (limits)
|
|
4018
|
+
q[adr] = Math.max(Math.min(...limits), Math.min(Math.max(...limits), q[adr]));
|
|
4019
|
+
} else {
|
|
4020
|
+
const offset = adr + (type === 0 ? 3 : 0);
|
|
4021
|
+
const quat = new THREE12.Quaternion(
|
|
4022
|
+
q[offset + 1],
|
|
4023
|
+
q[offset + 2],
|
|
4024
|
+
q[offset + 3],
|
|
4025
|
+
q[offset]
|
|
4026
|
+
).normalize();
|
|
4027
|
+
if (type === 1 && model.jnt_limited[id]) {
|
|
4028
|
+
if (quat.w < 0) {
|
|
4029
|
+
quat.x *= -1;
|
|
4030
|
+
quat.y *= -1;
|
|
4031
|
+
quat.z *= -1;
|
|
4032
|
+
quat.w *= -1;
|
|
4033
|
+
}
|
|
4034
|
+
const angle = 2 * Math.acos(Math.min(1, Math.max(-1, quat.w)));
|
|
4035
|
+
const limit = model.jnt_range[2 * id + 1];
|
|
4036
|
+
if (angle > limit)
|
|
4037
|
+
quat.identity().slerp(
|
|
4038
|
+
new THREE12.Quaternion(
|
|
4039
|
+
q[offset + 1],
|
|
4040
|
+
q[offset + 2],
|
|
4041
|
+
q[offset + 3],
|
|
4042
|
+
q[offset]
|
|
4043
|
+
).normalize(),
|
|
4044
|
+
limit / angle
|
|
4045
|
+
);
|
|
4046
|
+
}
|
|
4047
|
+
q.set([quat.w, quat.x, quat.y, quat.z], offset);
|
|
4048
|
+
}
|
|
4049
|
+
});
|
|
4050
|
+
};
|
|
4051
|
+
clamp();
|
|
4052
|
+
for (let iter = 0; iter < o.maxIterations; iter++) {
|
|
4053
|
+
data.qpos.set(qbuffer.GetView());
|
|
3921
4054
|
this.mujoco.mj_forward(model, data);
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
|
|
3928
|
-
|
|
3929
|
-
|
|
3930
|
-
|
|
3931
|
-
|
|
3932
|
-
|
|
3933
|
-
|
|
3934
|
-
|
|
3935
|
-
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
|
|
4055
|
+
const pos = data.site_xpos.subarray(3 * siteId, 3 * siteId + 3);
|
|
4056
|
+
const rot = orientationError(data.site_xmat.subarray(9 * siteId, 9 * siteId + 9), target);
|
|
4057
|
+
const error = [
|
|
4058
|
+
(targetPos.x - pos[0]) * o.posWeight,
|
|
4059
|
+
(targetPos.y - pos[1]) * o.posWeight,
|
|
4060
|
+
(targetPos.z - pos[2]) * o.posWeight,
|
|
4061
|
+
...rot.map((v) => v * o.rotWeight)
|
|
4062
|
+
];
|
|
4063
|
+
const norm = Math.hypot(...error);
|
|
4064
|
+
if (!Number.isFinite(norm)) break;
|
|
4065
|
+
if (norm < bestErr - 1e-9) {
|
|
4066
|
+
bestErr = norm;
|
|
4067
|
+
best = Array.from(qbuffer.GetView());
|
|
4068
|
+
noImprove = 0;
|
|
4069
|
+
} else noImprove++;
|
|
4070
|
+
if (norm < o.tolerance || noImprove >= 4) break;
|
|
4071
|
+
this.mujoco.mj_jacSite(model, data, jacp, jacr, siteId);
|
|
4072
|
+
const jp = jacp.GetView(), jr = jacr.GetView();
|
|
4073
|
+
for (let r = 0; r < 3; r++)
|
|
4074
|
+
for (let j = 0; j < n; j++) {
|
|
4075
|
+
J[r * n + j] = jp[r * model.nv + dofs[j]] * o.posWeight;
|
|
4076
|
+
J[(r + 3) * n + j] = jr[r * model.nv + dofs[j]] * o.rotWeight;
|
|
3939
4077
|
}
|
|
3940
|
-
|
|
3941
|
-
|
|
3942
|
-
|
|
3943
|
-
|
|
3944
|
-
|
|
3945
|
-
|
|
3946
|
-
|
|
3947
|
-
|
|
3948
|
-
|
|
4078
|
+
for (let r = 0; r < 6; r++)
|
|
4079
|
+
for (let c = 0; c < 6; c++) {
|
|
4080
|
+
let sum = 0;
|
|
4081
|
+
for (let k = 0; k < n; k++) sum += J[r * n + k] * J[c * n + k];
|
|
4082
|
+
JJt[r * 6 + c] = sum + (r === c ? o.damping : 0);
|
|
4083
|
+
}
|
|
4084
|
+
rhs.set(error);
|
|
4085
|
+
solve6x6(JJt, rhs, x);
|
|
4086
|
+
velocity.fill(0);
|
|
4087
|
+
for (let j = 0; j < n; j++) {
|
|
4088
|
+
let step = 0;
|
|
4089
|
+
for (let r = 0; r < 6; r++) step += J[r * n + j] * x[r];
|
|
4090
|
+
velocity[dofs[j]] = Math.max(-o.maxStepRad, Math.min(o.maxStepRad, step));
|
|
3949
4091
|
}
|
|
3950
|
-
|
|
4092
|
+
this.mujoco.mj_integratePos(model, qbuffer, velocity, 1);
|
|
4093
|
+
clamp();
|
|
3951
4094
|
}
|
|
3952
|
-
|
|
4095
|
+
return best;
|
|
4096
|
+
} finally {
|
|
4097
|
+
for (const buffer of buffers) buffer.delete();
|
|
4098
|
+
data.qpos.set(savedQpos);
|
|
4099
|
+
this.mujoco.mj_forward(model, data);
|
|
3953
4100
|
}
|
|
3954
|
-
data.qpos.set(savedQpos);
|
|
3955
|
-
this.mujoco.mj_forward(model, data);
|
|
3956
|
-
return bestQ;
|
|
3957
4101
|
}
|
|
3958
4102
|
};
|
|
3959
4103
|
function quatToMat3(q) {
|
|
@@ -3990,36 +4134,31 @@ function orientationError(R_cur, R_tgt) {
|
|
|
3990
4134
|
if (angle < 1e-6) {
|
|
3991
4135
|
return [0, 0, 0];
|
|
3992
4136
|
}
|
|
3993
|
-
if (angle > Math.PI - 1e-
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
|
|
4137
|
+
if (angle > Math.PI - 1e-4) {
|
|
4138
|
+
const matrix = new THREE12.Matrix4().set(
|
|
4139
|
+
Re[0],
|
|
4140
|
+
Re[1],
|
|
4141
|
+
Re[2],
|
|
4142
|
+
0,
|
|
4143
|
+
Re[3],
|
|
4144
|
+
Re[4],
|
|
4145
|
+
Re[5],
|
|
4146
|
+
0,
|
|
4147
|
+
Re[6],
|
|
4148
|
+
Re[7],
|
|
4149
|
+
Re[8],
|
|
4150
|
+
0,
|
|
4151
|
+
0,
|
|
4152
|
+
0,
|
|
4153
|
+
0,
|
|
4154
|
+
1
|
|
4155
|
+
);
|
|
4156
|
+
const q = new THREE12.Quaternion().setFromRotationMatrix(matrix).normalize();
|
|
4157
|
+
const scale = angle / Math.hypot(q.x, q.y, q.z) * (q.w < 0 ? -1 : 1);
|
|
4158
|
+
return [q.x * scale, q.y * scale, q.z * scale];
|
|
3999
4159
|
}
|
|
4000
4160
|
const s = angle / (2 * Math.sin(angle));
|
|
4001
|
-
return [
|
|
4002
|
-
s * (Re[7] - Re[5]),
|
|
4003
|
-
s * (Re[2] - Re[6]),
|
|
4004
|
-
s * (Re[3] - Re[1])
|
|
4005
|
-
];
|
|
4006
|
-
}
|
|
4007
|
-
function angularDelta(R_base, R_pert) {
|
|
4008
|
-
const dR = new Float64Array(9);
|
|
4009
|
-
for (let i = 0; i < 3; i++) {
|
|
4010
|
-
for (let j = 0; j < 3; j++) {
|
|
4011
|
-
let s = 0;
|
|
4012
|
-
for (let k = 0; k < 3; k++) {
|
|
4013
|
-
s += R_pert[i * 3 + k] * R_base[j * 3 + k];
|
|
4014
|
-
}
|
|
4015
|
-
dR[i * 3 + j] = s;
|
|
4016
|
-
}
|
|
4017
|
-
}
|
|
4018
|
-
return [
|
|
4019
|
-
0.5 * (dR[7] - dR[5]),
|
|
4020
|
-
0.5 * (dR[2] - dR[6]),
|
|
4021
|
-
0.5 * (dR[3] - dR[1])
|
|
4022
|
-
];
|
|
4161
|
+
return [s * (Re[7] - Re[5]), s * (Re[2] - Re[6]), s * (Re[3] - Re[1])];
|
|
4023
4162
|
}
|
|
4024
4163
|
function solve6x6(A, b, x) {
|
|
4025
4164
|
const N = 6;
|
|
@@ -4068,12 +4207,12 @@ function solve6x6(A, b, x) {
|
|
|
4068
4207
|
}
|
|
4069
4208
|
|
|
4070
4209
|
// src/hooks/useIkController.ts
|
|
4071
|
-
var _syncMat4 = new
|
|
4210
|
+
var _syncMat4 = new THREE12.Matrix4();
|
|
4072
4211
|
function toIkVector3(value) {
|
|
4073
|
-
return "x" in value ? new
|
|
4212
|
+
return "x" in value ? new THREE12.Vector3(value.x, value.y, value.z) : new THREE12.Vector3(value[0], value[1], value[2]);
|
|
4074
4213
|
}
|
|
4075
4214
|
function toIkQuaternion(value) {
|
|
4076
|
-
return "x" in value ? new
|
|
4215
|
+
return "x" in value ? new THREE12.Quaternion(value.x, value.y, value.z, value.w) : new THREE12.Quaternion(value[0], value[1], value[2], value[3]);
|
|
4077
4216
|
}
|
|
4078
4217
|
function syncGizmoToSite(data, siteId, target) {
|
|
4079
4218
|
if (siteId === -1) return;
|
|
@@ -4106,7 +4245,7 @@ var useIkController = createControllerHook(
|
|
|
4106
4245
|
const { mjModelRef, mjDataRef, mujocoRef, resetCallbacks, status } = useMujocoContext();
|
|
4107
4246
|
const ikEnabledRef = useRef(false);
|
|
4108
4247
|
const ikCalculatingRef = useRef(false);
|
|
4109
|
-
const ikTargetRef = useRef(new
|
|
4248
|
+
const ikTargetRef = useRef(new THREE12.Group());
|
|
4110
4249
|
const siteIdRef = useRef(-1);
|
|
4111
4250
|
const controlGroupRef = useRef(null);
|
|
4112
4251
|
const genericIkRef = useRef(null);
|
|
@@ -4123,10 +4262,10 @@ var useIkController = createControllerHook(
|
|
|
4123
4262
|
const needsInitialSync = useRef(true);
|
|
4124
4263
|
const gizmoAnimRef = useRef({
|
|
4125
4264
|
active: false,
|
|
4126
|
-
startPos: new
|
|
4127
|
-
endPos: new
|
|
4128
|
-
startRot: new
|
|
4129
|
-
endRot: new
|
|
4265
|
+
startPos: new THREE12.Vector3(),
|
|
4266
|
+
endPos: new THREE12.Vector3(),
|
|
4267
|
+
startRot: new THREE12.Quaternion(),
|
|
4268
|
+
endRot: new THREE12.Quaternion(),
|
|
4130
4269
|
startTime: 0,
|
|
4131
4270
|
duration: 1e3
|
|
4132
4271
|
});
|
|
@@ -4287,8 +4426,8 @@ var useIkController = createControllerHook(
|
|
|
4287
4426
|
const target = ikTargetRef.current;
|
|
4288
4427
|
if (!target) return;
|
|
4289
4428
|
const targetPos = toIkVector3(pos);
|
|
4290
|
-
const targetRot = new
|
|
4291
|
-
new
|
|
4429
|
+
const targetRot = new THREE12.Quaternion().setFromEuler(
|
|
4430
|
+
new THREE12.Euler(Math.PI, 0, 0)
|
|
4292
4431
|
);
|
|
4293
4432
|
if (duration > 0) {
|
|
4294
4433
|
const ga = gizmoAnimRef.current;
|
|
@@ -4313,7 +4452,7 @@ var useIkController = createControllerHook(
|
|
|
4313
4452
|
if (!ikCalculatingRef.current || !target) return null;
|
|
4314
4453
|
return {
|
|
4315
4454
|
pos: target.position.clone(),
|
|
4316
|
-
rot: new
|
|
4455
|
+
rot: new THREE12.Euler().setFromQuaternion(target.quaternion)
|
|
4317
4456
|
};
|
|
4318
4457
|
},
|
|
4319
4458
|
[]
|
|
@@ -4409,10 +4548,10 @@ function Body({
|
|
|
4409
4548
|
if (!hasChildren) return null;
|
|
4410
4549
|
return /* @__PURE__ */ jsx("group", { ref: groupRef, children });
|
|
4411
4550
|
}
|
|
4412
|
-
var _mat4 = new
|
|
4413
|
-
var _pos = new
|
|
4414
|
-
var _quat = new
|
|
4415
|
-
var _scale = new
|
|
4551
|
+
var _mat4 = new THREE12.Matrix4();
|
|
4552
|
+
var _pos = new THREE12.Vector3();
|
|
4553
|
+
var _quat = new THREE12.Quaternion();
|
|
4554
|
+
var _scale = new THREE12.Vector3(1, 1, 1);
|
|
4416
4555
|
function IkGizmo({ controller, siteName, scale = 0.18, onDrag }) {
|
|
4417
4556
|
const { mjModelRef, mjDataRef, status } = useMujocoContext();
|
|
4418
4557
|
const { ikTargetRef, siteIdRef, ikEnabledRef, setIkEnabled } = controller;
|
|
@@ -4511,7 +4650,7 @@ function IkGizmo({ controller, siteName, scale = 0.18, onDrag }) {
|
|
|
4511
4650
|
}
|
|
4512
4651
|
);
|
|
4513
4652
|
}
|
|
4514
|
-
var _dummy = new
|
|
4653
|
+
var _dummy = new THREE12.Object3D();
|
|
4515
4654
|
function ContactMarkers({
|
|
4516
4655
|
maxContacts = 100,
|
|
4517
4656
|
radius = 8e-3,
|
|
@@ -4565,11 +4704,11 @@ function ContactMarkers({
|
|
|
4565
4704
|
var _force = new Float64Array(3);
|
|
4566
4705
|
var _torque = new Float64Array(3);
|
|
4567
4706
|
var _point = new Float64Array(3);
|
|
4568
|
-
var _bodyPos = new
|
|
4569
|
-
var _bodyQuat = new
|
|
4570
|
-
var _worldHit = new
|
|
4571
|
-
var _raycaster2 = new
|
|
4572
|
-
var _mouse = new
|
|
4707
|
+
var _bodyPos = new THREE12.Vector3();
|
|
4708
|
+
var _bodyQuat = new THREE12.Quaternion();
|
|
4709
|
+
var _worldHit = new THREE12.Vector3();
|
|
4710
|
+
var _raycaster2 = new THREE12.Raycaster();
|
|
4711
|
+
var _mouse = new THREE12.Vector2();
|
|
4573
4712
|
function DragInteraction({
|
|
4574
4713
|
stiffness = 250,
|
|
4575
4714
|
showArrow = true,
|
|
@@ -4580,16 +4719,16 @@ function DragInteraction({
|
|
|
4580
4719
|
const draggingRef = useRef(false);
|
|
4581
4720
|
const bodyIdRef = useRef(-1);
|
|
4582
4721
|
const grabDistanceRef = useRef(0);
|
|
4583
|
-
const localHitRef = useRef(new
|
|
4584
|
-
const grabWorldRef = useRef(new
|
|
4585
|
-
const mouseWorldRef = useRef(new
|
|
4722
|
+
const localHitRef = useRef(new THREE12.Vector3());
|
|
4723
|
+
const grabWorldRef = useRef(new THREE12.Vector3());
|
|
4724
|
+
const mouseWorldRef = useRef(new THREE12.Vector3());
|
|
4586
4725
|
const arrowRef = useRef(null);
|
|
4587
4726
|
const groupRef = useRef(null);
|
|
4588
4727
|
useEffect(() => {
|
|
4589
4728
|
if (!showArrow || !groupRef.current) return;
|
|
4590
|
-
const arrow = new
|
|
4591
|
-
new
|
|
4592
|
-
new
|
|
4729
|
+
const arrow = new THREE12.ArrowHelper(
|
|
4730
|
+
new THREE12.Vector3(0, 1, 0),
|
|
4731
|
+
new THREE12.Vector3(),
|
|
4593
4732
|
0.1,
|
|
4594
4733
|
16729156
|
|
4595
4734
|
);
|
|
@@ -4772,7 +4911,7 @@ function useSceneLights(intensity = 1) {
|
|
|
4772
4911
|
const dr = lightDiffuse ? lightDiffuse[3 * i] : 1;
|
|
4773
4912
|
const dg = lightDiffuse ? lightDiffuse[3 * i + 1] : 1;
|
|
4774
4913
|
const db = lightDiffuse ? lightDiffuse[3 * i + 2] : 1;
|
|
4775
|
-
const color = new
|
|
4914
|
+
const color = new THREE12.Color(dr, dg, db);
|
|
4776
4915
|
const px = lightPos[3 * i];
|
|
4777
4916
|
const py = lightPos[3 * i + 1];
|
|
4778
4917
|
const pz = lightPos[3 * i + 2];
|
|
@@ -4780,7 +4919,7 @@ function useSceneLights(intensity = 1) {
|
|
|
4780
4919
|
const dy = lightDir[3 * i + 1];
|
|
4781
4920
|
const dz = lightDir[3 * i + 2];
|
|
4782
4921
|
if (isDirectional) {
|
|
4783
|
-
const light = new
|
|
4922
|
+
const light = new THREE12.DirectionalLight(color, finalIntensity);
|
|
4784
4923
|
light.position.set(px, py, pz);
|
|
4785
4924
|
light.target.position.set(px + dx, py + dy, pz + dz);
|
|
4786
4925
|
light.castShadow = castShadow;
|
|
@@ -4803,7 +4942,7 @@ function useSceneLights(intensity = 1) {
|
|
|
4803
4942
|
const cutoff = lightCutoff ? lightCutoff[i] : 45;
|
|
4804
4943
|
const exponent = lightExponent ? lightExponent[i] : 10;
|
|
4805
4944
|
const angle = cutoff * Math.PI / 180;
|
|
4806
|
-
const light = new
|
|
4945
|
+
const light = new THREE12.SpotLight(color, finalIntensity, 0, angle, exponent / 128);
|
|
4807
4946
|
light.position.set(px, py, pz);
|
|
4808
4947
|
light.target.position.set(px + dx, py + dy, pz + dz);
|
|
4809
4948
|
light.castShadow = castShadow;
|
|
@@ -5149,31 +5288,31 @@ var JOINT_COLORS = {
|
|
|
5149
5288
|
3: 16776960
|
|
5150
5289
|
// hinge - yellow
|
|
5151
5290
|
};
|
|
5152
|
-
var _v3a = new
|
|
5153
|
-
new
|
|
5154
|
-
var _quat2 = new
|
|
5155
|
-
var _cameraMatrix = new
|
|
5156
|
-
var _contactPos = new
|
|
5157
|
-
var _contactNormal = new
|
|
5291
|
+
var _v3a = new THREE12.Vector3();
|
|
5292
|
+
new THREE12.Vector3();
|
|
5293
|
+
var _quat2 = new THREE12.Quaternion();
|
|
5294
|
+
var _cameraMatrix = new THREE12.Matrix4();
|
|
5295
|
+
var _contactPos = new THREE12.Vector3();
|
|
5296
|
+
var _contactNormal = new THREE12.Vector3();
|
|
5158
5297
|
var MAX_CONTACT_ARROWS = 50;
|
|
5159
5298
|
var CAMERA_DEBUG_LENGTH = 0.12;
|
|
5160
5299
|
var CAMERA_DEBUG_FRUSTUM_DEPTH = 0.08;
|
|
5161
5300
|
function toVector32(value, fallback) {
|
|
5162
5301
|
if (!value) return fallback.clone();
|
|
5163
|
-
return value instanceof
|
|
5302
|
+
return value instanceof THREE12.Vector3 ? value.clone() : new THREE12.Vector3(value[0], value[1], value[2]);
|
|
5164
5303
|
}
|
|
5165
5304
|
function createCameraLabel(text, color) {
|
|
5166
5305
|
const canvas = document.createElement("canvas");
|
|
5167
5306
|
canvas.width = 256;
|
|
5168
5307
|
canvas.height = 64;
|
|
5169
5308
|
const ctx = canvas.getContext("2d");
|
|
5170
|
-
ctx.fillStyle = new
|
|
5309
|
+
ctx.fillStyle = new THREE12.Color(color).getStyle();
|
|
5171
5310
|
ctx.font = "bold 32px monospace";
|
|
5172
5311
|
ctx.textAlign = "center";
|
|
5173
5312
|
ctx.fillText(text, 128, 42);
|
|
5174
|
-
const texture = new
|
|
5175
|
-
const sprite = new
|
|
5176
|
-
new
|
|
5313
|
+
const texture = new THREE12.CanvasTexture(canvas);
|
|
5314
|
+
const sprite = new THREE12.Sprite(
|
|
5315
|
+
new THREE12.SpriteMaterial({
|
|
5177
5316
|
map: texture,
|
|
5178
5317
|
depthTest: false,
|
|
5179
5318
|
transparent: true
|
|
@@ -5188,12 +5327,12 @@ function createVirtualCameraDebugObject(camera, index) {
|
|
|
5188
5327
|
const color = camera.color ?? "#ff3d71";
|
|
5189
5328
|
const aimColor = camera.aimColor ?? "#38bdf8";
|
|
5190
5329
|
const markerScale = camera.markerScale ?? 1;
|
|
5191
|
-
const cameraPosition = toVector32(camera.position, new
|
|
5192
|
-
const configuredUp = toVector32(camera.up, new
|
|
5193
|
-
const cameraQuaternion = new
|
|
5194
|
-
const forward = new
|
|
5330
|
+
const cameraPosition = toVector32(camera.position, new THREE12.Vector3());
|
|
5331
|
+
const configuredUp = toVector32(camera.up, new THREE12.Vector3(0, 0, 1)).normalize();
|
|
5332
|
+
const cameraQuaternion = new THREE12.Quaternion();
|
|
5333
|
+
const forward = new THREE12.Vector3();
|
|
5195
5334
|
if (camera.quaternion) {
|
|
5196
|
-
if (camera.quaternion instanceof
|
|
5335
|
+
if (camera.quaternion instanceof THREE12.Quaternion) {
|
|
5197
5336
|
cameraQuaternion.copy(camera.quaternion);
|
|
5198
5337
|
} else {
|
|
5199
5338
|
cameraQuaternion.set(
|
|
@@ -5207,13 +5346,13 @@ function createVirtualCameraDebugObject(camera, index) {
|
|
|
5207
5346
|
} else {
|
|
5208
5347
|
const target2 = toVector32(
|
|
5209
5348
|
camera.lookAt,
|
|
5210
|
-
cameraPosition.clone().add(new
|
|
5349
|
+
cameraPosition.clone().add(new THREE12.Vector3(0, 0, -1))
|
|
5211
5350
|
);
|
|
5212
5351
|
forward.copy(target2).sub(cameraPosition);
|
|
5213
5352
|
if (forward.lengthSq() < 1e-8) forward.set(0, 0, -1);
|
|
5214
5353
|
forward.normalize();
|
|
5215
5354
|
cameraQuaternion.setFromRotationMatrix(
|
|
5216
|
-
new
|
|
5355
|
+
new THREE12.Matrix4().lookAt(cameraPosition, target2, configuredUp)
|
|
5217
5356
|
);
|
|
5218
5357
|
}
|
|
5219
5358
|
const target = camera.lookAt ? toVector32(camera.lookAt, cameraPosition.clone().add(forward)) : cameraPosition.clone().addScaledVector(forward, 0.4);
|
|
@@ -5225,7 +5364,7 @@ function createVirtualCameraDebugObject(camera, index) {
|
|
|
5225
5364
|
if (right.lengthSq() < 1e-8) right.set(1, 0, 0);
|
|
5226
5365
|
right.normalize();
|
|
5227
5366
|
const orthogonalUp = right.clone().cross(forward).normalize();
|
|
5228
|
-
const frustumHeight = 2 * Math.tan(
|
|
5367
|
+
const frustumHeight = 2 * Math.tan(THREE12.MathUtils.degToRad(fov) / 2) * depth;
|
|
5229
5368
|
const frustumWidth = frustumHeight * aspect;
|
|
5230
5369
|
const center = cameraPosition.clone().addScaledVector(forward, depth);
|
|
5231
5370
|
const halfRight = right.clone().multiplyScalar(frustumWidth / 2);
|
|
@@ -5252,13 +5391,13 @@ function createVirtualCameraDebugObject(camera, index) {
|
|
|
5252
5391
|
bottomLeft,
|
|
5253
5392
|
topLeft
|
|
5254
5393
|
];
|
|
5255
|
-
const group = new
|
|
5394
|
+
const group = new THREE12.Group();
|
|
5256
5395
|
group.name = camera.name ?? `virtual-camera-${index}`;
|
|
5257
5396
|
group.renderOrder = 999;
|
|
5258
5397
|
group.frustumCulled = false;
|
|
5259
|
-
const frustum = new
|
|
5260
|
-
new
|
|
5261
|
-
new
|
|
5398
|
+
const frustum = new THREE12.LineSegments(
|
|
5399
|
+
new THREE12.BufferGeometry().setFromPoints(frustumPoints),
|
|
5400
|
+
new THREE12.LineBasicMaterial({
|
|
5262
5401
|
color,
|
|
5263
5402
|
transparent: true,
|
|
5264
5403
|
opacity: 0.9,
|
|
@@ -5268,9 +5407,9 @@ function createVirtualCameraDebugObject(camera, index) {
|
|
|
5268
5407
|
frustum.renderOrder = 999;
|
|
5269
5408
|
frustum.frustumCulled = false;
|
|
5270
5409
|
group.add(frustum);
|
|
5271
|
-
const aim = new
|
|
5272
|
-
new
|
|
5273
|
-
new
|
|
5410
|
+
const aim = new THREE12.LineSegments(
|
|
5411
|
+
new THREE12.BufferGeometry().setFromPoints([cameraPosition, target]),
|
|
5412
|
+
new THREE12.LineBasicMaterial({
|
|
5274
5413
|
color: aimColor,
|
|
5275
5414
|
transparent: true,
|
|
5276
5415
|
opacity: 0.95,
|
|
@@ -5280,26 +5419,26 @@ function createVirtualCameraDebugObject(camera, index) {
|
|
|
5280
5419
|
aim.renderOrder = 999;
|
|
5281
5420
|
aim.frustumCulled = false;
|
|
5282
5421
|
group.add(aim);
|
|
5283
|
-
const markerGroup = new
|
|
5422
|
+
const markerGroup = new THREE12.Group();
|
|
5284
5423
|
markerGroup.position.copy(cameraPosition);
|
|
5285
5424
|
markerGroup.quaternion.copy(cameraQuaternion);
|
|
5286
5425
|
markerGroup.renderOrder = 999;
|
|
5287
5426
|
markerGroup.frustumCulled = false;
|
|
5288
|
-
markerGroup.add(new
|
|
5289
|
-
new
|
|
5290
|
-
new
|
|
5427
|
+
markerGroup.add(new THREE12.Mesh(
|
|
5428
|
+
new THREE12.BoxGeometry(0.045 * markerScale, 0.028 * markerScale, 0.022 * markerScale),
|
|
5429
|
+
new THREE12.MeshBasicMaterial({ color, depthTest: false })
|
|
5291
5430
|
));
|
|
5292
|
-
const lens = new
|
|
5293
|
-
new
|
|
5294
|
-
new
|
|
5431
|
+
const lens = new THREE12.Mesh(
|
|
5432
|
+
new THREE12.BoxGeometry(0.025 * markerScale, 0.018 * markerScale, 0.014 * markerScale),
|
|
5433
|
+
new THREE12.MeshBasicMaterial({ color: aimColor, depthTest: false })
|
|
5295
5434
|
);
|
|
5296
5435
|
lens.position.set(0, 0, -0.021 * markerScale);
|
|
5297
5436
|
markerGroup.add(lens);
|
|
5298
5437
|
if (camera.name) markerGroup.add(createCameraLabel(camera.name, color));
|
|
5299
5438
|
group.add(markerGroup);
|
|
5300
|
-
const targetMarker = new
|
|
5301
|
-
new
|
|
5302
|
-
new
|
|
5439
|
+
const targetMarker = new THREE12.Mesh(
|
|
5440
|
+
new THREE12.SphereGeometry(0.018 * markerScale, 16, 10),
|
|
5441
|
+
new THREE12.MeshBasicMaterial({ color: aimColor, depthTest: false })
|
|
5303
5442
|
);
|
|
5304
5443
|
targetMarker.position.copy(target);
|
|
5305
5444
|
targetMarker.renderOrder = 999;
|
|
@@ -5338,21 +5477,21 @@ function Debug({
|
|
|
5338
5477
|
let geometry = null;
|
|
5339
5478
|
switch (type) {
|
|
5340
5479
|
case 2:
|
|
5341
|
-
geometry = new
|
|
5480
|
+
geometry = new THREE12.SphereGeometry(s[3 * i], 12, 8);
|
|
5342
5481
|
break;
|
|
5343
5482
|
case 3:
|
|
5344
|
-
geometry = new
|
|
5483
|
+
geometry = new THREE12.CapsuleGeometry(s[3 * i], s[3 * i + 1] * 2, 6, 8);
|
|
5345
5484
|
break;
|
|
5346
5485
|
case 5:
|
|
5347
|
-
geometry = new
|
|
5486
|
+
geometry = new THREE12.CylinderGeometry(s[3 * i], s[3 * i], s[3 * i + 1] * 2, 12);
|
|
5348
5487
|
break;
|
|
5349
5488
|
case 6:
|
|
5350
|
-
geometry = new
|
|
5489
|
+
geometry = new THREE12.BoxGeometry(s[3 * i] * 2, s[3 * i + 1] * 2, s[3 * i + 2] * 2);
|
|
5351
5490
|
break;
|
|
5352
5491
|
}
|
|
5353
5492
|
if (geometry) {
|
|
5354
|
-
const mat = new
|
|
5355
|
-
const mesh = new
|
|
5493
|
+
const mat = new THREE12.MeshBasicMaterial({ color: 65280, wireframe: true, transparent: true, opacity: 0.3 });
|
|
5494
|
+
const mesh = new THREE12.Mesh(geometry, mat);
|
|
5356
5495
|
mesh.userData.geomId = i;
|
|
5357
5496
|
mesh.userData.bodyId = model.geom_bodyid[i];
|
|
5358
5497
|
geoms.push(mesh);
|
|
@@ -5375,9 +5514,9 @@ function Debug({
|
|
|
5375
5514
|
}
|
|
5376
5515
|
if (maxGeomSize > 0) radius = maxGeomSize * 0.15;
|
|
5377
5516
|
}
|
|
5378
|
-
const geometry = new
|
|
5379
|
-
const mat = new
|
|
5380
|
-
const mesh = new
|
|
5517
|
+
const geometry = new THREE12.OctahedronGeometry(radius);
|
|
5518
|
+
const mat = new THREE12.MeshBasicMaterial({ color: 16711935, depthTest: false });
|
|
5519
|
+
const mesh = new THREE12.Mesh(geometry, mat);
|
|
5381
5520
|
mesh.renderOrder = 999;
|
|
5382
5521
|
mesh.frustumCulled = false;
|
|
5383
5522
|
mesh.userData.siteId = i;
|
|
@@ -5389,9 +5528,9 @@ function Debug({
|
|
|
5389
5528
|
ctx.font = "bold 36px monospace";
|
|
5390
5529
|
ctx.textAlign = "center";
|
|
5391
5530
|
ctx.fillText(getName(model, model.name_siteadr[i]), 128, 42);
|
|
5392
|
-
const tex = new
|
|
5393
|
-
const spriteMat = new
|
|
5394
|
-
const sprite = new
|
|
5531
|
+
const tex = new THREE12.CanvasTexture(canvas);
|
|
5532
|
+
const spriteMat = new THREE12.SpriteMaterial({ map: tex, depthTest: false, transparent: true });
|
|
5533
|
+
const sprite = new THREE12.Sprite(spriteMat);
|
|
5395
5534
|
const labelScale = radius * 15;
|
|
5396
5535
|
sprite.scale.set(labelScale, labelScale * 0.25, 1);
|
|
5397
5536
|
sprite.position.y = radius * 2;
|
|
@@ -5414,9 +5553,9 @@ function Debug({
|
|
|
5414
5553
|
}
|
|
5415
5554
|
}
|
|
5416
5555
|
const arrowLen = Math.max(maxGeomSize * 0.8, 0.05);
|
|
5417
|
-
const arrow = new
|
|
5418
|
-
new
|
|
5419
|
-
new
|
|
5556
|
+
const arrow = new THREE12.ArrowHelper(
|
|
5557
|
+
new THREE12.Vector3(0, 0, 1),
|
|
5558
|
+
new THREE12.Vector3(),
|
|
5420
5559
|
arrowLen,
|
|
5421
5560
|
color,
|
|
5422
5561
|
arrowLen * 0.25,
|
|
@@ -5424,7 +5563,7 @@ function Debug({
|
|
|
5424
5563
|
);
|
|
5425
5564
|
arrow.renderOrder = 999;
|
|
5426
5565
|
arrow.frustumCulled = false;
|
|
5427
|
-
arrow.line.material = new
|
|
5566
|
+
arrow.line.material = new THREE12.LineBasicMaterial({ color, depthTest: false });
|
|
5428
5567
|
arrow.cone.material.depthTest = false;
|
|
5429
5568
|
arrow.line.renderOrder = 999;
|
|
5430
5569
|
arrow.line.frustumCulled = false;
|
|
@@ -5439,20 +5578,20 @@ function Debug({
|
|
|
5439
5578
|
}
|
|
5440
5579
|
if (showCameras && model.ncam && model.name_camadr) {
|
|
5441
5580
|
for (let i = 0; i < model.ncam; i++) {
|
|
5442
|
-
const group = new
|
|
5581
|
+
const group = new THREE12.Group();
|
|
5443
5582
|
group.userData.cameraId = i;
|
|
5444
5583
|
group.renderOrder = 999;
|
|
5445
5584
|
group.frustumCulled = false;
|
|
5446
|
-
const marker = new
|
|
5447
|
-
new
|
|
5448
|
-
new
|
|
5585
|
+
const marker = new THREE12.Mesh(
|
|
5586
|
+
new THREE12.BoxGeometry(0.014, 9e-3, 6e-3),
|
|
5587
|
+
new THREE12.MeshBasicMaterial({ color: 3718648, depthTest: false })
|
|
5449
5588
|
);
|
|
5450
5589
|
marker.renderOrder = 999;
|
|
5451
5590
|
marker.frustumCulled = false;
|
|
5452
5591
|
group.add(marker);
|
|
5453
|
-
const forward = new
|
|
5454
|
-
new
|
|
5455
|
-
new
|
|
5592
|
+
const forward = new THREE12.ArrowHelper(
|
|
5593
|
+
new THREE12.Vector3(0, 0, -1),
|
|
5594
|
+
new THREE12.Vector3(),
|
|
5456
5595
|
CAMERA_DEBUG_LENGTH,
|
|
5457
5596
|
3718648,
|
|
5458
5597
|
CAMERA_DEBUG_LENGTH * 0.24,
|
|
@@ -5460,20 +5599,20 @@ function Debug({
|
|
|
5460
5599
|
);
|
|
5461
5600
|
forward.renderOrder = 999;
|
|
5462
5601
|
forward.frustumCulled = false;
|
|
5463
|
-
forward.line.material = new
|
|
5602
|
+
forward.line.material = new THREE12.LineBasicMaterial({
|
|
5464
5603
|
color: 3718648,
|
|
5465
5604
|
depthTest: false
|
|
5466
5605
|
});
|
|
5467
5606
|
forward.cone.material.depthTest = false;
|
|
5468
5607
|
group.add(forward);
|
|
5469
|
-
const frustumGeometry = new
|
|
5608
|
+
const frustumGeometry = new THREE12.BufferGeometry();
|
|
5470
5609
|
frustumGeometry.setAttribute(
|
|
5471
5610
|
"position",
|
|
5472
|
-
new
|
|
5611
|
+
new THREE12.Float32BufferAttribute(new Float32Array(8 * 2 * 3), 3)
|
|
5473
5612
|
);
|
|
5474
|
-
const frustum = new
|
|
5613
|
+
const frustum = new THREE12.LineSegments(
|
|
5475
5614
|
frustumGeometry,
|
|
5476
|
-
new
|
|
5615
|
+
new THREE12.LineBasicMaterial({
|
|
5477
5616
|
color: 3718648,
|
|
5478
5617
|
transparent: true,
|
|
5479
5618
|
opacity: 0.8,
|
|
@@ -5492,9 +5631,9 @@ function Debug({
|
|
|
5492
5631
|
ctx.font = "bold 32px monospace";
|
|
5493
5632
|
ctx.textAlign = "center";
|
|
5494
5633
|
ctx.fillText(getName(model, model.name_camadr[i]), 128, 42);
|
|
5495
|
-
const texture = new
|
|
5496
|
-
const sprite = new
|
|
5497
|
-
new
|
|
5634
|
+
const texture = new THREE12.CanvasTexture(canvas);
|
|
5635
|
+
const sprite = new THREE12.Sprite(
|
|
5636
|
+
new THREE12.SpriteMaterial({
|
|
5498
5637
|
map: texture,
|
|
5499
5638
|
depthTest: false,
|
|
5500
5639
|
transparent: true
|
|
@@ -5513,9 +5652,9 @@ function Debug({
|
|
|
5513
5652
|
}
|
|
5514
5653
|
if (showCOM) {
|
|
5515
5654
|
for (let i = 1; i < model.nbody; i++) {
|
|
5516
|
-
const geometry = new
|
|
5517
|
-
const mat = new
|
|
5518
|
-
const mesh = new
|
|
5655
|
+
const geometry = new THREE12.SphereGeometry(5e-3, 6, 6);
|
|
5656
|
+
const mat = new THREE12.MeshBasicMaterial({ color: 16711680 });
|
|
5657
|
+
const mesh = new THREE12.Mesh(geometry, mat);
|
|
5519
5658
|
mesh.userData.bodyId = i;
|
|
5520
5659
|
comMarkers.push(mesh);
|
|
5521
5660
|
}
|
|
@@ -5625,7 +5764,7 @@ function Debug({
|
|
|
5625
5764
|
);
|
|
5626
5765
|
group.quaternion.setFromRotationMatrix(_cameraMatrix);
|
|
5627
5766
|
const fovy = model.cam_fovy?.[cameraId] ?? 45;
|
|
5628
|
-
const halfHeight = Math.tan(
|
|
5767
|
+
const halfHeight = Math.tan(THREE12.MathUtils.degToRad(fovy) / 2) * CAMERA_DEBUG_FRUSTUM_DEPTH;
|
|
5629
5768
|
const halfWidth = halfHeight * 4 / 3;
|
|
5630
5769
|
const positions = group.userData.frustum.geometry.attributes.position;
|
|
5631
5770
|
const array = positions.array;
|
|
@@ -5670,9 +5809,9 @@ function Debug({
|
|
|
5670
5809
|
contactPoolInitRef.current = true;
|
|
5671
5810
|
const pool = [];
|
|
5672
5811
|
for (let i = 0; i < MAX_CONTACT_ARROWS; i++) {
|
|
5673
|
-
const arrow = new
|
|
5674
|
-
new
|
|
5675
|
-
new
|
|
5812
|
+
const arrow = new THREE12.ArrowHelper(
|
|
5813
|
+
new THREE12.Vector3(0, 1, 0),
|
|
5814
|
+
new THREE12.Vector3(),
|
|
5676
5815
|
0.1,
|
|
5677
5816
|
16729156,
|
|
5678
5817
|
0.03,
|
|
@@ -5737,9 +5876,9 @@ function Debug({
|
|
|
5737
5876
|
}
|
|
5738
5877
|
);
|
|
5739
5878
|
}
|
|
5740
|
-
var DEFAULT_TENDON_COLOR = new
|
|
5879
|
+
var DEFAULT_TENDON_COLOR = new THREE12.Color(0.3, 0.3, 0.8);
|
|
5741
5880
|
var DEFAULT_TENDON_WIDTH = 2e-3;
|
|
5742
|
-
new
|
|
5881
|
+
new THREE12.Vector3();
|
|
5743
5882
|
function TendonRenderer(props) {
|
|
5744
5883
|
const { mjModelRef, mjDataRef, status } = useMujocoContext();
|
|
5745
5884
|
const groupRef = useRef(null);
|
|
@@ -5753,7 +5892,7 @@ function TendonRenderer(props) {
|
|
|
5753
5892
|
if (!model || !data || !group) return;
|
|
5754
5893
|
const ntendon = model.ntendon ?? 0;
|
|
5755
5894
|
if (ntendon === 0) return;
|
|
5756
|
-
const material = new
|
|
5895
|
+
const material = new THREE12.MeshStandardMaterial({
|
|
5757
5896
|
color: DEFAULT_TENDON_COLOR,
|
|
5758
5897
|
roughness: 0.6,
|
|
5759
5898
|
metalness: 0.1
|
|
@@ -5768,11 +5907,11 @@ function TendonRenderer(props) {
|
|
|
5768
5907
|
curves.push(null);
|
|
5769
5908
|
continue;
|
|
5770
5909
|
}
|
|
5771
|
-
const points = Array.from({ length: wrapNum }, () => new
|
|
5772
|
-
const curve = new
|
|
5910
|
+
const points = Array.from({ length: wrapNum }, () => new THREE12.Vector3());
|
|
5911
|
+
const curve = new THREE12.CatmullRomCurve3(points, false);
|
|
5773
5912
|
const segments = Math.max(wrapNum * 2, 4);
|
|
5774
|
-
const geometry = new
|
|
5775
|
-
const mesh = new
|
|
5913
|
+
const geometry = new THREE12.TubeGeometry(curve, segments, DEFAULT_TENDON_WIDTH, 6, false);
|
|
5914
|
+
const mesh = new THREE12.Mesh(geometry, material);
|
|
5776
5915
|
mesh.frustumCulled = false;
|
|
5777
5916
|
group.add(mesh);
|
|
5778
5917
|
meshes.push(mesh);
|
|
@@ -5827,11 +5966,11 @@ function TendonRenderer(props) {
|
|
|
5827
5966
|
if (curve.points.length !== validCount) {
|
|
5828
5967
|
curve.points.length = validCount;
|
|
5829
5968
|
while (curve.points.length < validCount) {
|
|
5830
|
-
curve.points.push(new
|
|
5969
|
+
curve.points.push(new THREE12.Vector3());
|
|
5831
5970
|
}
|
|
5832
5971
|
}
|
|
5833
5972
|
mesh.geometry.dispose();
|
|
5834
|
-
mesh.geometry = new
|
|
5973
|
+
mesh.geometry = new THREE12.TubeGeometry(
|
|
5835
5974
|
curve,
|
|
5836
5975
|
Math.max(validCount * 2, 4),
|
|
5837
5976
|
DEFAULT_TENDON_WIDTH,
|
|
@@ -5844,6 +5983,26 @@ function TendonRenderer(props) {
|
|
|
5844
5983
|
if (status !== "ready") return null;
|
|
5845
5984
|
return /* @__PURE__ */ jsx("group", { ...props, ref: groupRef });
|
|
5846
5985
|
}
|
|
5986
|
+
|
|
5987
|
+
// src/rendering/flexTopology.ts
|
|
5988
|
+
function getFlexTopology(model, id) {
|
|
5989
|
+
const dim = model.flex_dim[id];
|
|
5990
|
+
if (dim === 3) {
|
|
5991
|
+
const start = model.flex_shelldataadr[id];
|
|
5992
|
+
return {
|
|
5993
|
+
kind: "mesh",
|
|
5994
|
+
indices: Array.from(model.flex_shell.subarray(start, start + 3 * model.flex_shellnum[id]))
|
|
5995
|
+
};
|
|
5996
|
+
}
|
|
5997
|
+
if (dim === 1 || dim === 2) {
|
|
5998
|
+
const start = model.flex_elemdataadr[id];
|
|
5999
|
+
return {
|
|
6000
|
+
kind: dim === 2 ? "mesh" : "lines",
|
|
6001
|
+
indices: Array.from(model.flex_elem.subarray(start, start + (dim + 1) * model.flex_elemnum[id]))
|
|
6002
|
+
};
|
|
6003
|
+
}
|
|
6004
|
+
return { kind: "points", indices: [] };
|
|
6005
|
+
}
|
|
5847
6006
|
function FlexRenderer(props) {
|
|
5848
6007
|
const { mjModelRef, mjDataRef, status } = useMujocoContext();
|
|
5849
6008
|
const groupRef = useRef(null);
|
|
@@ -5858,24 +6017,23 @@ function FlexRenderer(props) {
|
|
|
5858
6017
|
const vertAdr = model.flex_vertadr[f];
|
|
5859
6018
|
const vertNum = model.flex_vertnum[f];
|
|
5860
6019
|
if (vertNum === 0) continue;
|
|
5861
|
-
const geometry = new
|
|
6020
|
+
const geometry = new THREE12.BufferGeometry();
|
|
5862
6021
|
const positions = new Float32Array(vertNum * 3);
|
|
5863
|
-
geometry.setAttribute("position", new
|
|
5864
|
-
|
|
5865
|
-
|
|
6022
|
+
geometry.setAttribute("position", new THREE12.BufferAttribute(positions, 3));
|
|
6023
|
+
const topology = getFlexTopology(model, f);
|
|
6024
|
+
if (topology.indices.length) geometry.setIndex(topology.indices);
|
|
6025
|
+
let color = new THREE12.Color(0.5, 0.5, 0.5);
|
|
5866
6026
|
if (model.flex_rgba) {
|
|
5867
|
-
color = new
|
|
6027
|
+
color = new THREE12.Color(
|
|
5868
6028
|
model.flex_rgba[4 * f],
|
|
5869
6029
|
model.flex_rgba[4 * f + 1],
|
|
5870
6030
|
model.flex_rgba[4 * f + 2]
|
|
5871
6031
|
);
|
|
5872
6032
|
}
|
|
5873
|
-
const
|
|
5874
|
-
|
|
5875
|
-
|
|
5876
|
-
|
|
5877
|
-
});
|
|
5878
|
-
const mesh = new THREE11.Mesh(geometry, material);
|
|
6033
|
+
const opacity = model.flex_rgba[4 * f + 3];
|
|
6034
|
+
const materialOptions = { color, opacity, transparent: opacity < 1 };
|
|
6035
|
+
const mesh = topology.kind === "mesh" ? new THREE12.Mesh(geometry, new THREE12.MeshStandardMaterial({ ...materialOptions, roughness: 0.7, side: THREE12.DoubleSide })) : topology.kind === "lines" ? new THREE12.LineSegments(geometry, new THREE12.LineBasicMaterial(materialOptions)) : new THREE12.Points(geometry, new THREE12.PointsMaterial({ ...materialOptions, size: 0.01 }));
|
|
6036
|
+
mesh.frustumCulled = false;
|
|
5879
6037
|
mesh.userData.flexId = f;
|
|
5880
6038
|
mesh.userData.vertAdr = vertAdr;
|
|
5881
6039
|
mesh.userData.vertNum = vertNum;
|
|
@@ -5904,14 +6062,14 @@ function FlexRenderer(props) {
|
|
|
5904
6062
|
posAttr.setXYZ(v, data.flexvert_xpos[srcIdx], data.flexvert_xpos[srcIdx + 1], data.flexvert_xpos[srcIdx + 2]);
|
|
5905
6063
|
}
|
|
5906
6064
|
posAttr.needsUpdate = true;
|
|
5907
|
-
mesh.geometry.computeVertexNormals();
|
|
6065
|
+
if (mesh instanceof THREE12.Mesh) mesh.geometry.computeVertexNormals();
|
|
5908
6066
|
}
|
|
5909
6067
|
});
|
|
5910
6068
|
if (status !== "ready") return null;
|
|
5911
6069
|
return /* @__PURE__ */ jsx("group", { ...props, ref: groupRef });
|
|
5912
6070
|
}
|
|
5913
6071
|
var GEOM_TYPE_NAMES2 = ["plane", "hfield", "sphere", "capsule", "ellipsoid", "cylinder", "box", "mesh"];
|
|
5914
|
-
var _matrix = new
|
|
6072
|
+
var _matrix = new THREE12.Matrix4();
|
|
5915
6073
|
function getGeomInfo(model, geomId) {
|
|
5916
6074
|
const size = model.geom_size.subarray(geomId * 3, geomId * 3 + 3);
|
|
5917
6075
|
const type = model.geom_type[geomId];
|
|
@@ -5933,10 +6091,10 @@ function geomSignature(model, geomId) {
|
|
|
5933
6091
|
return [type, size, mat, data, rgba].join("|");
|
|
5934
6092
|
}
|
|
5935
6093
|
function firstMesh(object) {
|
|
5936
|
-
if (object instanceof
|
|
6094
|
+
if (object instanceof THREE12.Mesh) return object;
|
|
5937
6095
|
let mesh = null;
|
|
5938
6096
|
object.traverse((child) => {
|
|
5939
|
-
if (!mesh && child instanceof
|
|
6097
|
+
if (!mesh && child instanceof THREE12.Mesh) mesh = child;
|
|
5940
6098
|
});
|
|
5941
6099
|
return mesh;
|
|
5942
6100
|
}
|
|
@@ -6353,24 +6511,16 @@ function useActuators() {
|
|
|
6353
6511
|
if (status !== "ready") return [];
|
|
6354
6512
|
const model = mjModelRef.current;
|
|
6355
6513
|
if (!model) return [];
|
|
6356
|
-
const actuators =
|
|
6357
|
-
for (let i = 0; i < model.nu; i++) {
|
|
6358
|
-
const name = getName(model, model.name_actuatoradr[i]);
|
|
6359
|
-
const lo = model.actuator_ctrlrange[i * 2];
|
|
6360
|
-
const hi = model.actuator_ctrlrange[i * 2 + 1];
|
|
6361
|
-
const hasRange = lo < hi;
|
|
6362
|
-
const range = hasRange ? [lo, hi] : [-Infinity, Infinity];
|
|
6363
|
-
actuators.push({ id: i, name, range });
|
|
6364
|
-
}
|
|
6514
|
+
const actuators = Array.from({ length: model.nactuator }, (_, id) => getActuatorInfo(model, id));
|
|
6365
6515
|
return actuators;
|
|
6366
6516
|
}, [status, mjModelRef]);
|
|
6367
6517
|
}
|
|
6368
|
-
var _mat42 = new
|
|
6518
|
+
var _mat42 = new THREE12.Matrix4();
|
|
6369
6519
|
function useSitePosition(siteName) {
|
|
6370
6520
|
const { mjModelRef, mjDataRef, status } = useMujocoContext();
|
|
6371
6521
|
const siteIdRef = useRef(-1);
|
|
6372
|
-
const positionRef = useRef(new
|
|
6373
|
-
const quaternionRef = useRef(new
|
|
6522
|
+
const positionRef = useRef(new THREE12.Vector3());
|
|
6523
|
+
const quaternionRef = useRef(new THREE12.Quaternion());
|
|
6374
6524
|
useEffect(() => {
|
|
6375
6525
|
const model = mjModelRef.current;
|
|
6376
6526
|
if (!model || status !== "ready") {
|
|
@@ -6563,10 +6713,10 @@ function useJointState(name, options = {}) {
|
|
|
6563
6713
|
function useBodyState(name) {
|
|
6564
6714
|
const { mjModelRef, status } = useMujocoContext();
|
|
6565
6715
|
const bodyIdRef = useRef(-1);
|
|
6566
|
-
const position = useRef(new
|
|
6567
|
-
const quaternion = useRef(new
|
|
6568
|
-
const linearVelocity = useRef(new
|
|
6569
|
-
const angularVelocity = useRef(new
|
|
6716
|
+
const position = useRef(new THREE12.Vector3());
|
|
6717
|
+
const quaternion = useRef(new THREE12.Quaternion());
|
|
6718
|
+
const linearVelocity = useRef(new THREE12.Vector3());
|
|
6719
|
+
const angularVelocity = useRef(new THREE12.Vector3());
|
|
6570
6720
|
useEffect(() => {
|
|
6571
6721
|
const model = mjModelRef.current;
|
|
6572
6722
|
if (!model || status !== "ready") return;
|
|
@@ -6592,7 +6742,7 @@ function useBodyState(name) {
|
|
|
6592
6742
|
});
|
|
6593
6743
|
return { position, quaternion, linearVelocity, angularVelocity };
|
|
6594
6744
|
}
|
|
6595
|
-
var _matrix2 = new
|
|
6745
|
+
var _matrix2 = new THREE12.Matrix4();
|
|
6596
6746
|
function quaternionFromMatrixArray(target, values, offset) {
|
|
6597
6747
|
_matrix2.set(
|
|
6598
6748
|
values[offset],
|
|
@@ -6626,8 +6776,8 @@ function useNamedPose(kind, name) {
|
|
|
6626
6776
|
const { mjModelRef, status } = useMujocoContext();
|
|
6627
6777
|
const idRef = useRef(-1);
|
|
6628
6778
|
const foundRef = useRef(false);
|
|
6629
|
-
const positionRef = useRef(new
|
|
6630
|
-
const quaternionRef = useRef(new
|
|
6779
|
+
const positionRef = useRef(new THREE12.Vector3());
|
|
6780
|
+
const quaternionRef = useRef(new THREE12.Quaternion());
|
|
6631
6781
|
useEffect(() => {
|
|
6632
6782
|
const model = mjModelRef.current;
|
|
6633
6783
|
if (!model || status !== "ready") {
|
|
@@ -6679,7 +6829,7 @@ function useGeomPose(name) {
|
|
|
6679
6829
|
function useSitePose(name) {
|
|
6680
6830
|
return useNamedPose("site", name);
|
|
6681
6831
|
}
|
|
6682
|
-
function useCtrl(name) {
|
|
6832
|
+
function useCtrl(name, input) {
|
|
6683
6833
|
const { mjModelRef, mjDataRef, status } = useMujocoContext();
|
|
6684
6834
|
const actuatorIdRef = useRef(-1);
|
|
6685
6835
|
const rangeRef = useRef([0, 0]);
|
|
@@ -6687,14 +6837,15 @@ function useCtrl(name) {
|
|
|
6687
6837
|
const model = mjModelRef.current;
|
|
6688
6838
|
if (!model || status !== "ready") return;
|
|
6689
6839
|
const id = findActuatorByName(model, name);
|
|
6690
|
-
|
|
6840
|
+
const adr = getActuatorControlAddress(model, id, input);
|
|
6841
|
+
actuatorIdRef.current = adr;
|
|
6691
6842
|
if (id >= 0) {
|
|
6692
6843
|
rangeRef.current = [
|
|
6693
|
-
model.actuator_ctrlrange[
|
|
6694
|
-
model.actuator_ctrlrange[
|
|
6844
|
+
model.actuator_ctrlrange[adr * 2],
|
|
6845
|
+
model.actuator_ctrlrange[adr * 2 + 1]
|
|
6695
6846
|
];
|
|
6696
6847
|
}
|
|
6697
|
-
}, [name, status, mjModelRef]);
|
|
6848
|
+
}, [name, input, status, mjModelRef]);
|
|
6698
6849
|
return useMemo(() => ({
|
|
6699
6850
|
read() {
|
|
6700
6851
|
const data = mjDataRef.current;
|
|
@@ -6864,7 +7015,7 @@ function useControlGroup(group, options = {}) {
|
|
|
6864
7015
|
if (id < 0) {
|
|
6865
7016
|
console.warn(`[mujoco-react] useControlGroup: actuator "${name}" was not found.`);
|
|
6866
7017
|
}
|
|
6867
|
-
return id;
|
|
7018
|
+
return getActuatorControlAddress(model, id);
|
|
6868
7019
|
});
|
|
6869
7020
|
}, [mjModelRef, status, namesKey]);
|
|
6870
7021
|
const patch = useCallback(
|
|
@@ -7070,7 +7221,7 @@ function useKeyboardTeleop(config) {
|
|
|
7070
7221
|
const cache = /* @__PURE__ */ new Map();
|
|
7071
7222
|
for (const binding of Object.values(config.bindings)) {
|
|
7072
7223
|
if (!cache.has(binding.actuator)) {
|
|
7073
|
-
cache.set(binding.actuator, findActuatorByName(model, binding.actuator));
|
|
7224
|
+
cache.set(binding.actuator, getActuatorControlAddress(model, findActuatorByName(model, binding.actuator)));
|
|
7074
7225
|
}
|
|
7075
7226
|
}
|
|
7076
7227
|
actuatorCacheRef.current = cache;
|
|
@@ -7120,9 +7271,9 @@ function useKeyboardTeleop(config) {
|
|
|
7120
7271
|
}
|
|
7121
7272
|
var DEFAULT_TRANSLATE_SPEED = 0.25;
|
|
7122
7273
|
var DEFAULT_ROTATE_SPEED = 1;
|
|
7123
|
-
var _translation = new
|
|
7124
|
-
var _axis = new
|
|
7125
|
-
var _quat3 = new
|
|
7274
|
+
var _translation = new THREE12.Vector3();
|
|
7275
|
+
var _axis = new THREE12.Vector3();
|
|
7276
|
+
var _quat3 = new THREE12.Quaternion();
|
|
7126
7277
|
function actionSign(action) {
|
|
7127
7278
|
return action.endsWith("+") ? 1 : -1;
|
|
7128
7279
|
}
|
|
@@ -7899,10 +8050,10 @@ function useGamepad(config) {
|
|
|
7899
8050
|
axisCacheRef.current.clear();
|
|
7900
8051
|
buttonCacheRef.current.clear();
|
|
7901
8052
|
for (const [idx, name] of Object.entries(config.axes ?? {})) {
|
|
7902
|
-
axisCacheRef.current.set(Number(idx), findActuatorByName(model, name));
|
|
8053
|
+
axisCacheRef.current.set(Number(idx), getActuatorControlAddress(model, findActuatorByName(model, name)));
|
|
7903
8054
|
}
|
|
7904
8055
|
for (const [idx, name] of Object.entries(config.buttons ?? {})) {
|
|
7905
|
-
buttonCacheRef.current.set(Number(idx), findActuatorByName(model, name));
|
|
8056
|
+
buttonCacheRef.current.set(Number(idx), getActuatorControlAddress(model, findActuatorByName(model, name)));
|
|
7906
8057
|
}
|
|
7907
8058
|
}, [config.axes, config.buttons, status, mjModelRef]);
|
|
7908
8059
|
useBeforePhysicsStep(({ data }) => {
|
|
@@ -8590,7 +8741,7 @@ function useSelectionHighlight(bodyId, options = {}) {
|
|
|
8590
8741
|
}
|
|
8591
8742
|
}
|
|
8592
8743
|
prevRef.current = [];
|
|
8593
|
-
const highlightColor = new
|
|
8744
|
+
const highlightColor = new THREE12.Color(color);
|
|
8594
8745
|
for (const mesh of meshes) {
|
|
8595
8746
|
const mat = mesh.material;
|
|
8596
8747
|
if (mat.emissive) {
|
|
@@ -8617,15 +8768,15 @@ function useSelectionHighlight(bodyId, options = {}) {
|
|
|
8617
8768
|
}
|
|
8618
8769
|
function useCameraAnimation() {
|
|
8619
8770
|
const { camera } = useThree();
|
|
8620
|
-
const orbitTargetRef = useRef(new
|
|
8771
|
+
const orbitTargetRef = useRef(new THREE12.Vector3(0, 0, 0));
|
|
8621
8772
|
const cameraAnimRef = useRef({
|
|
8622
8773
|
active: false,
|
|
8623
|
-
startPos: new
|
|
8624
|
-
endPos: new
|
|
8625
|
-
startRot: new
|
|
8626
|
-
endRot: new
|
|
8627
|
-
startTarget: new
|
|
8628
|
-
endTarget: new
|
|
8774
|
+
startPos: new THREE12.Vector3(),
|
|
8775
|
+
endPos: new THREE12.Vector3(),
|
|
8776
|
+
startRot: new THREE12.Quaternion(),
|
|
8777
|
+
endRot: new THREE12.Quaternion(),
|
|
8778
|
+
startTarget: new THREE12.Vector3(),
|
|
8779
|
+
endTarget: new THREE12.Vector3(),
|
|
8629
8780
|
startTime: 0,
|
|
8630
8781
|
duration: 0,
|
|
8631
8782
|
resolve: null
|