mujoco-react 10.9.0 → 12.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 +5 -0
- package/dist/{chunk-5WFYM73E.js → chunk-7BCIHJMW.js} +2 -2
- package/dist/chunk-7BCIHJMW.js.map +1 -0
- package/dist/index.d.ts +21 -23
- package/dist/index.js +563 -427
- 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-BjHt_Urb.d.ts} +59 -5
- package/package.json +6 -5
- package/src/components/FlexRenderer.tsx +13 -14
- package/src/core/GenericIK.ts +247 -232
- 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 +50 -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-7BCIHJMW.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-7BCIHJMW.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,8 +3914,6 @@ 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,
|
|
@@ -3845,130 +3936,168 @@ function resolveOptions(opts) {
|
|
|
3845
3936
|
};
|
|
3846
3937
|
}
|
|
3847
3938
|
var GenericIK = class {
|
|
3848
|
-
mujoco;
|
|
3849
3939
|
constructor(mujoco) {
|
|
3850
3940
|
this.mujoco = mujoco;
|
|
3851
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
|
+
}
|
|
3852
3961
|
/**
|
|
3853
|
-
* Solve
|
|
3854
|
-
*
|
|
3855
|
-
*
|
|
3856
|
-
*
|
|
3857
|
-
* @param qposAdr qpos addresses for scalar joints in solve order
|
|
3858
|
-
* @param targetPos Target position in world frame
|
|
3859
|
-
* @param targetQuat Target orientation in world frame
|
|
3860
|
-
* @param currentQ Current joint angles matching qposAdr order
|
|
3861
|
-
* @param opts Optional solver parameters
|
|
3862
|
-
* @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.
|
|
3863
3966
|
*/
|
|
3864
|
-
|
|
3967
|
+
solveJoints(model, data, siteId, jointIds, targetPos, targetQuat, currentQpos = data.qpos, opts) {
|
|
3865
3968
|
const o = resolveOptions(opts);
|
|
3866
|
-
const
|
|
3867
|
-
|
|
3868
|
-
|
|
3869
|
-
|
|
3870
|
-
|
|
3871
|
-
|
|
3872
|
-
|
|
3873
|
-
|
|
3874
|
-
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
3881
|
-
|
|
3882
|
-
|
|
3883
|
-
const
|
|
3884
|
-
|
|
3885
|
-
|
|
3886
|
-
|
|
3887
|
-
|
|
3888
|
-
|
|
3889
|
-
|
|
3890
|
-
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
|
|
3894
|
-
|
|
3895
|
-
|
|
3896
|
-
|
|
3897
|
-
const
|
|
3898
|
-
|
|
3899
|
-
|
|
3900
|
-
|
|
3901
|
-
|
|
3902
|
-
|
|
3903
|
-
|
|
3904
|
-
|
|
3905
|
-
|
|
3906
|
-
|
|
3907
|
-
|
|
3908
|
-
|
|
3909
|
-
|
|
3910
|
-
|
|
3911
|
-
|
|
3912
|
-
|
|
3913
|
-
|
|
3914
|
-
|
|
3915
|
-
|
|
3916
|
-
|
|
3917
|
-
|
|
3918
|
-
|
|
3919
|
-
|
|
3920
|
-
|
|
3921
|
-
|
|
3922
|
-
|
|
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());
|
|
3923
4054
|
this.mujoco.mj_forward(model, data);
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
|
|
3928
|
-
|
|
3929
|
-
|
|
3930
|
-
|
|
3931
|
-
|
|
3932
|
-
|
|
3933
|
-
|
|
3934
|
-
|
|
3935
|
-
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
|
|
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;
|
|
3941
4077
|
}
|
|
3942
|
-
|
|
3943
|
-
|
|
3944
|
-
|
|
3945
|
-
|
|
3946
|
-
|
|
3947
|
-
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
|
|
3951
|
-
|
|
3952
|
-
|
|
3953
|
-
|
|
3954
|
-
|
|
3955
|
-
let step = dq[i];
|
|
3956
|
-
if (step > o.maxStepRad) step = o.maxStepRad;
|
|
3957
|
-
else if (step < -o.maxStepRad) step = -o.maxStepRad;
|
|
3958
|
-
let next = q[i] + step;
|
|
3959
|
-
const limits = o.jointLimits?.[i];
|
|
3960
|
-
if (limits) {
|
|
3961
|
-
const low = Math.min(limits[0], limits[1]);
|
|
3962
|
-
const high = Math.max(limits[0], limits[1]);
|
|
3963
|
-
if (next < low) next = low;
|
|
3964
|
-
else if (next > high) next = high;
|
|
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));
|
|
3965
4091
|
}
|
|
3966
|
-
|
|
4092
|
+
this.mujoco.mj_integratePos(model, qbuffer, velocity, 1);
|
|
4093
|
+
clamp();
|
|
3967
4094
|
}
|
|
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);
|
|
3968
4100
|
}
|
|
3969
|
-
data.qpos.set(savedQpos);
|
|
3970
|
-
this.mujoco.mj_forward(model, data);
|
|
3971
|
-
return bestQ;
|
|
3972
4101
|
}
|
|
3973
4102
|
};
|
|
3974
4103
|
function quatToMat3(q) {
|
|
@@ -4005,36 +4134,31 @@ function orientationError(R_cur, R_tgt) {
|
|
|
4005
4134
|
if (angle < 1e-6) {
|
|
4006
4135
|
return [0, 0, 0];
|
|
4007
4136
|
}
|
|
4008
|
-
if (angle > Math.PI - 1e-
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
|
|
4012
|
-
|
|
4013
|
-
|
|
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];
|
|
4014
4159
|
}
|
|
4015
4160
|
const s = angle / (2 * Math.sin(angle));
|
|
4016
|
-
return [
|
|
4017
|
-
s * (Re[7] - Re[5]),
|
|
4018
|
-
s * (Re[2] - Re[6]),
|
|
4019
|
-
s * (Re[3] - Re[1])
|
|
4020
|
-
];
|
|
4021
|
-
}
|
|
4022
|
-
function angularDelta(R_base, R_pert) {
|
|
4023
|
-
const dR = new Float64Array(9);
|
|
4024
|
-
for (let i = 0; i < 3; i++) {
|
|
4025
|
-
for (let j = 0; j < 3; j++) {
|
|
4026
|
-
let s = 0;
|
|
4027
|
-
for (let k = 0; k < 3; k++) {
|
|
4028
|
-
s += R_pert[i * 3 + k] * R_base[j * 3 + k];
|
|
4029
|
-
}
|
|
4030
|
-
dR[i * 3 + j] = s;
|
|
4031
|
-
}
|
|
4032
|
-
}
|
|
4033
|
-
return [
|
|
4034
|
-
0.5 * (dR[7] - dR[5]),
|
|
4035
|
-
0.5 * (dR[2] - dR[6]),
|
|
4036
|
-
0.5 * (dR[3] - dR[1])
|
|
4037
|
-
];
|
|
4161
|
+
return [s * (Re[7] - Re[5]), s * (Re[2] - Re[6]), s * (Re[3] - Re[1])];
|
|
4038
4162
|
}
|
|
4039
4163
|
function solve6x6(A, b, x) {
|
|
4040
4164
|
const N = 6;
|
|
@@ -4083,12 +4207,12 @@ function solve6x6(A, b, x) {
|
|
|
4083
4207
|
}
|
|
4084
4208
|
|
|
4085
4209
|
// src/hooks/useIkController.ts
|
|
4086
|
-
var _syncMat4 = new
|
|
4210
|
+
var _syncMat4 = new THREE12.Matrix4();
|
|
4087
4211
|
function toIkVector3(value) {
|
|
4088
|
-
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]);
|
|
4089
4213
|
}
|
|
4090
4214
|
function toIkQuaternion(value) {
|
|
4091
|
-
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]);
|
|
4092
4216
|
}
|
|
4093
4217
|
function syncGizmoToSite(data, siteId, target) {
|
|
4094
4218
|
if (siteId === -1) return;
|
|
@@ -4121,7 +4245,7 @@ var useIkController = createControllerHook(
|
|
|
4121
4245
|
const { mjModelRef, mjDataRef, mujocoRef, resetCallbacks, status } = useMujocoContext();
|
|
4122
4246
|
const ikEnabledRef = useRef(false);
|
|
4123
4247
|
const ikCalculatingRef = useRef(false);
|
|
4124
|
-
const ikTargetRef = useRef(new
|
|
4248
|
+
const ikTargetRef = useRef(new THREE12.Group());
|
|
4125
4249
|
const siteIdRef = useRef(-1);
|
|
4126
4250
|
const controlGroupRef = useRef(null);
|
|
4127
4251
|
const genericIkRef = useRef(null);
|
|
@@ -4138,10 +4262,10 @@ var useIkController = createControllerHook(
|
|
|
4138
4262
|
const needsInitialSync = useRef(true);
|
|
4139
4263
|
const gizmoAnimRef = useRef({
|
|
4140
4264
|
active: false,
|
|
4141
|
-
startPos: new
|
|
4142
|
-
endPos: new
|
|
4143
|
-
startRot: new
|
|
4144
|
-
endRot: new
|
|
4265
|
+
startPos: new THREE12.Vector3(),
|
|
4266
|
+
endPos: new THREE12.Vector3(),
|
|
4267
|
+
startRot: new THREE12.Quaternion(),
|
|
4268
|
+
endRot: new THREE12.Quaternion(),
|
|
4145
4269
|
startTime: 0,
|
|
4146
4270
|
duration: 1e3
|
|
4147
4271
|
});
|
|
@@ -4302,8 +4426,8 @@ var useIkController = createControllerHook(
|
|
|
4302
4426
|
const target = ikTargetRef.current;
|
|
4303
4427
|
if (!target) return;
|
|
4304
4428
|
const targetPos = toIkVector3(pos);
|
|
4305
|
-
const targetRot = new
|
|
4306
|
-
new
|
|
4429
|
+
const targetRot = new THREE12.Quaternion().setFromEuler(
|
|
4430
|
+
new THREE12.Euler(Math.PI, 0, 0)
|
|
4307
4431
|
);
|
|
4308
4432
|
if (duration > 0) {
|
|
4309
4433
|
const ga = gizmoAnimRef.current;
|
|
@@ -4328,7 +4452,7 @@ var useIkController = createControllerHook(
|
|
|
4328
4452
|
if (!ikCalculatingRef.current || !target) return null;
|
|
4329
4453
|
return {
|
|
4330
4454
|
pos: target.position.clone(),
|
|
4331
|
-
rot: new
|
|
4455
|
+
rot: new THREE12.Euler().setFromQuaternion(target.quaternion)
|
|
4332
4456
|
};
|
|
4333
4457
|
},
|
|
4334
4458
|
[]
|
|
@@ -4424,10 +4548,10 @@ function Body({
|
|
|
4424
4548
|
if (!hasChildren) return null;
|
|
4425
4549
|
return /* @__PURE__ */ jsx("group", { ref: groupRef, children });
|
|
4426
4550
|
}
|
|
4427
|
-
var _mat4 = new
|
|
4428
|
-
var _pos = new
|
|
4429
|
-
var _quat = new
|
|
4430
|
-
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);
|
|
4431
4555
|
function IkGizmo({ controller, siteName, scale = 0.18, onDrag }) {
|
|
4432
4556
|
const { mjModelRef, mjDataRef, status } = useMujocoContext();
|
|
4433
4557
|
const { ikTargetRef, siteIdRef, ikEnabledRef, setIkEnabled } = controller;
|
|
@@ -4526,7 +4650,7 @@ function IkGizmo({ controller, siteName, scale = 0.18, onDrag }) {
|
|
|
4526
4650
|
}
|
|
4527
4651
|
);
|
|
4528
4652
|
}
|
|
4529
|
-
var _dummy = new
|
|
4653
|
+
var _dummy = new THREE12.Object3D();
|
|
4530
4654
|
function ContactMarkers({
|
|
4531
4655
|
maxContacts = 100,
|
|
4532
4656
|
radius = 8e-3,
|
|
@@ -4580,11 +4704,11 @@ function ContactMarkers({
|
|
|
4580
4704
|
var _force = new Float64Array(3);
|
|
4581
4705
|
var _torque = new Float64Array(3);
|
|
4582
4706
|
var _point = new Float64Array(3);
|
|
4583
|
-
var _bodyPos = new
|
|
4584
|
-
var _bodyQuat = new
|
|
4585
|
-
var _worldHit = new
|
|
4586
|
-
var _raycaster2 = new
|
|
4587
|
-
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();
|
|
4588
4712
|
function DragInteraction({
|
|
4589
4713
|
stiffness = 250,
|
|
4590
4714
|
showArrow = true,
|
|
@@ -4595,16 +4719,16 @@ function DragInteraction({
|
|
|
4595
4719
|
const draggingRef = useRef(false);
|
|
4596
4720
|
const bodyIdRef = useRef(-1);
|
|
4597
4721
|
const grabDistanceRef = useRef(0);
|
|
4598
|
-
const localHitRef = useRef(new
|
|
4599
|
-
const grabWorldRef = useRef(new
|
|
4600
|
-
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());
|
|
4601
4725
|
const arrowRef = useRef(null);
|
|
4602
4726
|
const groupRef = useRef(null);
|
|
4603
4727
|
useEffect(() => {
|
|
4604
4728
|
if (!showArrow || !groupRef.current) return;
|
|
4605
|
-
const arrow = new
|
|
4606
|
-
new
|
|
4607
|
-
new
|
|
4729
|
+
const arrow = new THREE12.ArrowHelper(
|
|
4730
|
+
new THREE12.Vector3(0, 1, 0),
|
|
4731
|
+
new THREE12.Vector3(),
|
|
4608
4732
|
0.1,
|
|
4609
4733
|
16729156
|
|
4610
4734
|
);
|
|
@@ -4787,7 +4911,7 @@ function useSceneLights(intensity = 1) {
|
|
|
4787
4911
|
const dr = lightDiffuse ? lightDiffuse[3 * i] : 1;
|
|
4788
4912
|
const dg = lightDiffuse ? lightDiffuse[3 * i + 1] : 1;
|
|
4789
4913
|
const db = lightDiffuse ? lightDiffuse[3 * i + 2] : 1;
|
|
4790
|
-
const color = new
|
|
4914
|
+
const color = new THREE12.Color(dr, dg, db);
|
|
4791
4915
|
const px = lightPos[3 * i];
|
|
4792
4916
|
const py = lightPos[3 * i + 1];
|
|
4793
4917
|
const pz = lightPos[3 * i + 2];
|
|
@@ -4795,7 +4919,7 @@ function useSceneLights(intensity = 1) {
|
|
|
4795
4919
|
const dy = lightDir[3 * i + 1];
|
|
4796
4920
|
const dz = lightDir[3 * i + 2];
|
|
4797
4921
|
if (isDirectional) {
|
|
4798
|
-
const light = new
|
|
4922
|
+
const light = new THREE12.DirectionalLight(color, finalIntensity);
|
|
4799
4923
|
light.position.set(px, py, pz);
|
|
4800
4924
|
light.target.position.set(px + dx, py + dy, pz + dz);
|
|
4801
4925
|
light.castShadow = castShadow;
|
|
@@ -4818,7 +4942,7 @@ function useSceneLights(intensity = 1) {
|
|
|
4818
4942
|
const cutoff = lightCutoff ? lightCutoff[i] : 45;
|
|
4819
4943
|
const exponent = lightExponent ? lightExponent[i] : 10;
|
|
4820
4944
|
const angle = cutoff * Math.PI / 180;
|
|
4821
|
-
const light = new
|
|
4945
|
+
const light = new THREE12.SpotLight(color, finalIntensity, 0, angle, exponent / 128);
|
|
4822
4946
|
light.position.set(px, py, pz);
|
|
4823
4947
|
light.target.position.set(px + dx, py + dy, pz + dz);
|
|
4824
4948
|
light.castShadow = castShadow;
|
|
@@ -5164,31 +5288,31 @@ var JOINT_COLORS = {
|
|
|
5164
5288
|
3: 16776960
|
|
5165
5289
|
// hinge - yellow
|
|
5166
5290
|
};
|
|
5167
|
-
var _v3a = new
|
|
5168
|
-
new
|
|
5169
|
-
var _quat2 = new
|
|
5170
|
-
var _cameraMatrix = new
|
|
5171
|
-
var _contactPos = new
|
|
5172
|
-
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();
|
|
5173
5297
|
var MAX_CONTACT_ARROWS = 50;
|
|
5174
5298
|
var CAMERA_DEBUG_LENGTH = 0.12;
|
|
5175
5299
|
var CAMERA_DEBUG_FRUSTUM_DEPTH = 0.08;
|
|
5176
5300
|
function toVector32(value, fallback) {
|
|
5177
5301
|
if (!value) return fallback.clone();
|
|
5178
|
-
return value instanceof
|
|
5302
|
+
return value instanceof THREE12.Vector3 ? value.clone() : new THREE12.Vector3(value[0], value[1], value[2]);
|
|
5179
5303
|
}
|
|
5180
5304
|
function createCameraLabel(text, color) {
|
|
5181
5305
|
const canvas = document.createElement("canvas");
|
|
5182
5306
|
canvas.width = 256;
|
|
5183
5307
|
canvas.height = 64;
|
|
5184
5308
|
const ctx = canvas.getContext("2d");
|
|
5185
|
-
ctx.fillStyle = new
|
|
5309
|
+
ctx.fillStyle = new THREE12.Color(color).getStyle();
|
|
5186
5310
|
ctx.font = "bold 32px monospace";
|
|
5187
5311
|
ctx.textAlign = "center";
|
|
5188
5312
|
ctx.fillText(text, 128, 42);
|
|
5189
|
-
const texture = new
|
|
5190
|
-
const sprite = new
|
|
5191
|
-
new
|
|
5313
|
+
const texture = new THREE12.CanvasTexture(canvas);
|
|
5314
|
+
const sprite = new THREE12.Sprite(
|
|
5315
|
+
new THREE12.SpriteMaterial({
|
|
5192
5316
|
map: texture,
|
|
5193
5317
|
depthTest: false,
|
|
5194
5318
|
transparent: true
|
|
@@ -5203,12 +5327,12 @@ function createVirtualCameraDebugObject(camera, index) {
|
|
|
5203
5327
|
const color = camera.color ?? "#ff3d71";
|
|
5204
5328
|
const aimColor = camera.aimColor ?? "#38bdf8";
|
|
5205
5329
|
const markerScale = camera.markerScale ?? 1;
|
|
5206
|
-
const cameraPosition = toVector32(camera.position, new
|
|
5207
|
-
const configuredUp = toVector32(camera.up, new
|
|
5208
|
-
const cameraQuaternion = new
|
|
5209
|
-
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();
|
|
5210
5334
|
if (camera.quaternion) {
|
|
5211
|
-
if (camera.quaternion instanceof
|
|
5335
|
+
if (camera.quaternion instanceof THREE12.Quaternion) {
|
|
5212
5336
|
cameraQuaternion.copy(camera.quaternion);
|
|
5213
5337
|
} else {
|
|
5214
5338
|
cameraQuaternion.set(
|
|
@@ -5222,13 +5346,13 @@ function createVirtualCameraDebugObject(camera, index) {
|
|
|
5222
5346
|
} else {
|
|
5223
5347
|
const target2 = toVector32(
|
|
5224
5348
|
camera.lookAt,
|
|
5225
|
-
cameraPosition.clone().add(new
|
|
5349
|
+
cameraPosition.clone().add(new THREE12.Vector3(0, 0, -1))
|
|
5226
5350
|
);
|
|
5227
5351
|
forward.copy(target2).sub(cameraPosition);
|
|
5228
5352
|
if (forward.lengthSq() < 1e-8) forward.set(0, 0, -1);
|
|
5229
5353
|
forward.normalize();
|
|
5230
5354
|
cameraQuaternion.setFromRotationMatrix(
|
|
5231
|
-
new
|
|
5355
|
+
new THREE12.Matrix4().lookAt(cameraPosition, target2, configuredUp)
|
|
5232
5356
|
);
|
|
5233
5357
|
}
|
|
5234
5358
|
const target = camera.lookAt ? toVector32(camera.lookAt, cameraPosition.clone().add(forward)) : cameraPosition.clone().addScaledVector(forward, 0.4);
|
|
@@ -5240,7 +5364,7 @@ function createVirtualCameraDebugObject(camera, index) {
|
|
|
5240
5364
|
if (right.lengthSq() < 1e-8) right.set(1, 0, 0);
|
|
5241
5365
|
right.normalize();
|
|
5242
5366
|
const orthogonalUp = right.clone().cross(forward).normalize();
|
|
5243
|
-
const frustumHeight = 2 * Math.tan(
|
|
5367
|
+
const frustumHeight = 2 * Math.tan(THREE12.MathUtils.degToRad(fov) / 2) * depth;
|
|
5244
5368
|
const frustumWidth = frustumHeight * aspect;
|
|
5245
5369
|
const center = cameraPosition.clone().addScaledVector(forward, depth);
|
|
5246
5370
|
const halfRight = right.clone().multiplyScalar(frustumWidth / 2);
|
|
@@ -5267,13 +5391,13 @@ function createVirtualCameraDebugObject(camera, index) {
|
|
|
5267
5391
|
bottomLeft,
|
|
5268
5392
|
topLeft
|
|
5269
5393
|
];
|
|
5270
|
-
const group = new
|
|
5394
|
+
const group = new THREE12.Group();
|
|
5271
5395
|
group.name = camera.name ?? `virtual-camera-${index}`;
|
|
5272
5396
|
group.renderOrder = 999;
|
|
5273
5397
|
group.frustumCulled = false;
|
|
5274
|
-
const frustum = new
|
|
5275
|
-
new
|
|
5276
|
-
new
|
|
5398
|
+
const frustum = new THREE12.LineSegments(
|
|
5399
|
+
new THREE12.BufferGeometry().setFromPoints(frustumPoints),
|
|
5400
|
+
new THREE12.LineBasicMaterial({
|
|
5277
5401
|
color,
|
|
5278
5402
|
transparent: true,
|
|
5279
5403
|
opacity: 0.9,
|
|
@@ -5283,9 +5407,9 @@ function createVirtualCameraDebugObject(camera, index) {
|
|
|
5283
5407
|
frustum.renderOrder = 999;
|
|
5284
5408
|
frustum.frustumCulled = false;
|
|
5285
5409
|
group.add(frustum);
|
|
5286
|
-
const aim = new
|
|
5287
|
-
new
|
|
5288
|
-
new
|
|
5410
|
+
const aim = new THREE12.LineSegments(
|
|
5411
|
+
new THREE12.BufferGeometry().setFromPoints([cameraPosition, target]),
|
|
5412
|
+
new THREE12.LineBasicMaterial({
|
|
5289
5413
|
color: aimColor,
|
|
5290
5414
|
transparent: true,
|
|
5291
5415
|
opacity: 0.95,
|
|
@@ -5295,26 +5419,26 @@ function createVirtualCameraDebugObject(camera, index) {
|
|
|
5295
5419
|
aim.renderOrder = 999;
|
|
5296
5420
|
aim.frustumCulled = false;
|
|
5297
5421
|
group.add(aim);
|
|
5298
|
-
const markerGroup = new
|
|
5422
|
+
const markerGroup = new THREE12.Group();
|
|
5299
5423
|
markerGroup.position.copy(cameraPosition);
|
|
5300
5424
|
markerGroup.quaternion.copy(cameraQuaternion);
|
|
5301
5425
|
markerGroup.renderOrder = 999;
|
|
5302
5426
|
markerGroup.frustumCulled = false;
|
|
5303
|
-
markerGroup.add(new
|
|
5304
|
-
new
|
|
5305
|
-
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 })
|
|
5306
5430
|
));
|
|
5307
|
-
const lens = new
|
|
5308
|
-
new
|
|
5309
|
-
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 })
|
|
5310
5434
|
);
|
|
5311
5435
|
lens.position.set(0, 0, -0.021 * markerScale);
|
|
5312
5436
|
markerGroup.add(lens);
|
|
5313
5437
|
if (camera.name) markerGroup.add(createCameraLabel(camera.name, color));
|
|
5314
5438
|
group.add(markerGroup);
|
|
5315
|
-
const targetMarker = new
|
|
5316
|
-
new
|
|
5317
|
-
new
|
|
5439
|
+
const targetMarker = new THREE12.Mesh(
|
|
5440
|
+
new THREE12.SphereGeometry(0.018 * markerScale, 16, 10),
|
|
5441
|
+
new THREE12.MeshBasicMaterial({ color: aimColor, depthTest: false })
|
|
5318
5442
|
);
|
|
5319
5443
|
targetMarker.position.copy(target);
|
|
5320
5444
|
targetMarker.renderOrder = 999;
|
|
@@ -5353,21 +5477,21 @@ function Debug({
|
|
|
5353
5477
|
let geometry = null;
|
|
5354
5478
|
switch (type) {
|
|
5355
5479
|
case 2:
|
|
5356
|
-
geometry = new
|
|
5480
|
+
geometry = new THREE12.SphereGeometry(s[3 * i], 12, 8);
|
|
5357
5481
|
break;
|
|
5358
5482
|
case 3:
|
|
5359
|
-
geometry = new
|
|
5483
|
+
geometry = new THREE12.CapsuleGeometry(s[3 * i], s[3 * i + 1] * 2, 6, 8);
|
|
5360
5484
|
break;
|
|
5361
5485
|
case 5:
|
|
5362
|
-
geometry = new
|
|
5486
|
+
geometry = new THREE12.CylinderGeometry(s[3 * i], s[3 * i], s[3 * i + 1] * 2, 12);
|
|
5363
5487
|
break;
|
|
5364
5488
|
case 6:
|
|
5365
|
-
geometry = new
|
|
5489
|
+
geometry = new THREE12.BoxGeometry(s[3 * i] * 2, s[3 * i + 1] * 2, s[3 * i + 2] * 2);
|
|
5366
5490
|
break;
|
|
5367
5491
|
}
|
|
5368
5492
|
if (geometry) {
|
|
5369
|
-
const mat = new
|
|
5370
|
-
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);
|
|
5371
5495
|
mesh.userData.geomId = i;
|
|
5372
5496
|
mesh.userData.bodyId = model.geom_bodyid[i];
|
|
5373
5497
|
geoms.push(mesh);
|
|
@@ -5390,9 +5514,9 @@ function Debug({
|
|
|
5390
5514
|
}
|
|
5391
5515
|
if (maxGeomSize > 0) radius = maxGeomSize * 0.15;
|
|
5392
5516
|
}
|
|
5393
|
-
const geometry = new
|
|
5394
|
-
const mat = new
|
|
5395
|
-
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);
|
|
5396
5520
|
mesh.renderOrder = 999;
|
|
5397
5521
|
mesh.frustumCulled = false;
|
|
5398
5522
|
mesh.userData.siteId = i;
|
|
@@ -5404,9 +5528,9 @@ function Debug({
|
|
|
5404
5528
|
ctx.font = "bold 36px monospace";
|
|
5405
5529
|
ctx.textAlign = "center";
|
|
5406
5530
|
ctx.fillText(getName(model, model.name_siteadr[i]), 128, 42);
|
|
5407
|
-
const tex = new
|
|
5408
|
-
const spriteMat = new
|
|
5409
|
-
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);
|
|
5410
5534
|
const labelScale = radius * 15;
|
|
5411
5535
|
sprite.scale.set(labelScale, labelScale * 0.25, 1);
|
|
5412
5536
|
sprite.position.y = radius * 2;
|
|
@@ -5429,9 +5553,9 @@ function Debug({
|
|
|
5429
5553
|
}
|
|
5430
5554
|
}
|
|
5431
5555
|
const arrowLen = Math.max(maxGeomSize * 0.8, 0.05);
|
|
5432
|
-
const arrow = new
|
|
5433
|
-
new
|
|
5434
|
-
new
|
|
5556
|
+
const arrow = new THREE12.ArrowHelper(
|
|
5557
|
+
new THREE12.Vector3(0, 0, 1),
|
|
5558
|
+
new THREE12.Vector3(),
|
|
5435
5559
|
arrowLen,
|
|
5436
5560
|
color,
|
|
5437
5561
|
arrowLen * 0.25,
|
|
@@ -5439,7 +5563,7 @@ function Debug({
|
|
|
5439
5563
|
);
|
|
5440
5564
|
arrow.renderOrder = 999;
|
|
5441
5565
|
arrow.frustumCulled = false;
|
|
5442
|
-
arrow.line.material = new
|
|
5566
|
+
arrow.line.material = new THREE12.LineBasicMaterial({ color, depthTest: false });
|
|
5443
5567
|
arrow.cone.material.depthTest = false;
|
|
5444
5568
|
arrow.line.renderOrder = 999;
|
|
5445
5569
|
arrow.line.frustumCulled = false;
|
|
@@ -5454,20 +5578,20 @@ function Debug({
|
|
|
5454
5578
|
}
|
|
5455
5579
|
if (showCameras && model.ncam && model.name_camadr) {
|
|
5456
5580
|
for (let i = 0; i < model.ncam; i++) {
|
|
5457
|
-
const group = new
|
|
5581
|
+
const group = new THREE12.Group();
|
|
5458
5582
|
group.userData.cameraId = i;
|
|
5459
5583
|
group.renderOrder = 999;
|
|
5460
5584
|
group.frustumCulled = false;
|
|
5461
|
-
const marker = new
|
|
5462
|
-
new
|
|
5463
|
-
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 })
|
|
5464
5588
|
);
|
|
5465
5589
|
marker.renderOrder = 999;
|
|
5466
5590
|
marker.frustumCulled = false;
|
|
5467
5591
|
group.add(marker);
|
|
5468
|
-
const forward = new
|
|
5469
|
-
new
|
|
5470
|
-
new
|
|
5592
|
+
const forward = new THREE12.ArrowHelper(
|
|
5593
|
+
new THREE12.Vector3(0, 0, -1),
|
|
5594
|
+
new THREE12.Vector3(),
|
|
5471
5595
|
CAMERA_DEBUG_LENGTH,
|
|
5472
5596
|
3718648,
|
|
5473
5597
|
CAMERA_DEBUG_LENGTH * 0.24,
|
|
@@ -5475,20 +5599,20 @@ function Debug({
|
|
|
5475
5599
|
);
|
|
5476
5600
|
forward.renderOrder = 999;
|
|
5477
5601
|
forward.frustumCulled = false;
|
|
5478
|
-
forward.line.material = new
|
|
5602
|
+
forward.line.material = new THREE12.LineBasicMaterial({
|
|
5479
5603
|
color: 3718648,
|
|
5480
5604
|
depthTest: false
|
|
5481
5605
|
});
|
|
5482
5606
|
forward.cone.material.depthTest = false;
|
|
5483
5607
|
group.add(forward);
|
|
5484
|
-
const frustumGeometry = new
|
|
5608
|
+
const frustumGeometry = new THREE12.BufferGeometry();
|
|
5485
5609
|
frustumGeometry.setAttribute(
|
|
5486
5610
|
"position",
|
|
5487
|
-
new
|
|
5611
|
+
new THREE12.Float32BufferAttribute(new Float32Array(8 * 2 * 3), 3)
|
|
5488
5612
|
);
|
|
5489
|
-
const frustum = new
|
|
5613
|
+
const frustum = new THREE12.LineSegments(
|
|
5490
5614
|
frustumGeometry,
|
|
5491
|
-
new
|
|
5615
|
+
new THREE12.LineBasicMaterial({
|
|
5492
5616
|
color: 3718648,
|
|
5493
5617
|
transparent: true,
|
|
5494
5618
|
opacity: 0.8,
|
|
@@ -5507,9 +5631,9 @@ function Debug({
|
|
|
5507
5631
|
ctx.font = "bold 32px monospace";
|
|
5508
5632
|
ctx.textAlign = "center";
|
|
5509
5633
|
ctx.fillText(getName(model, model.name_camadr[i]), 128, 42);
|
|
5510
|
-
const texture = new
|
|
5511
|
-
const sprite = new
|
|
5512
|
-
new
|
|
5634
|
+
const texture = new THREE12.CanvasTexture(canvas);
|
|
5635
|
+
const sprite = new THREE12.Sprite(
|
|
5636
|
+
new THREE12.SpriteMaterial({
|
|
5513
5637
|
map: texture,
|
|
5514
5638
|
depthTest: false,
|
|
5515
5639
|
transparent: true
|
|
@@ -5528,9 +5652,9 @@ function Debug({
|
|
|
5528
5652
|
}
|
|
5529
5653
|
if (showCOM) {
|
|
5530
5654
|
for (let i = 1; i < model.nbody; i++) {
|
|
5531
|
-
const geometry = new
|
|
5532
|
-
const mat = new
|
|
5533
|
-
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);
|
|
5534
5658
|
mesh.userData.bodyId = i;
|
|
5535
5659
|
comMarkers.push(mesh);
|
|
5536
5660
|
}
|
|
@@ -5640,7 +5764,7 @@ function Debug({
|
|
|
5640
5764
|
);
|
|
5641
5765
|
group.quaternion.setFromRotationMatrix(_cameraMatrix);
|
|
5642
5766
|
const fovy = model.cam_fovy?.[cameraId] ?? 45;
|
|
5643
|
-
const halfHeight = Math.tan(
|
|
5767
|
+
const halfHeight = Math.tan(THREE12.MathUtils.degToRad(fovy) / 2) * CAMERA_DEBUG_FRUSTUM_DEPTH;
|
|
5644
5768
|
const halfWidth = halfHeight * 4 / 3;
|
|
5645
5769
|
const positions = group.userData.frustum.geometry.attributes.position;
|
|
5646
5770
|
const array = positions.array;
|
|
@@ -5685,9 +5809,9 @@ function Debug({
|
|
|
5685
5809
|
contactPoolInitRef.current = true;
|
|
5686
5810
|
const pool = [];
|
|
5687
5811
|
for (let i = 0; i < MAX_CONTACT_ARROWS; i++) {
|
|
5688
|
-
const arrow = new
|
|
5689
|
-
new
|
|
5690
|
-
new
|
|
5812
|
+
const arrow = new THREE12.ArrowHelper(
|
|
5813
|
+
new THREE12.Vector3(0, 1, 0),
|
|
5814
|
+
new THREE12.Vector3(),
|
|
5691
5815
|
0.1,
|
|
5692
5816
|
16729156,
|
|
5693
5817
|
0.03,
|
|
@@ -5752,9 +5876,9 @@ function Debug({
|
|
|
5752
5876
|
}
|
|
5753
5877
|
);
|
|
5754
5878
|
}
|
|
5755
|
-
var DEFAULT_TENDON_COLOR = new
|
|
5879
|
+
var DEFAULT_TENDON_COLOR = new THREE12.Color(0.3, 0.3, 0.8);
|
|
5756
5880
|
var DEFAULT_TENDON_WIDTH = 2e-3;
|
|
5757
|
-
new
|
|
5881
|
+
new THREE12.Vector3();
|
|
5758
5882
|
function TendonRenderer(props) {
|
|
5759
5883
|
const { mjModelRef, mjDataRef, status } = useMujocoContext();
|
|
5760
5884
|
const groupRef = useRef(null);
|
|
@@ -5768,7 +5892,7 @@ function TendonRenderer(props) {
|
|
|
5768
5892
|
if (!model || !data || !group) return;
|
|
5769
5893
|
const ntendon = model.ntendon ?? 0;
|
|
5770
5894
|
if (ntendon === 0) return;
|
|
5771
|
-
const material = new
|
|
5895
|
+
const material = new THREE12.MeshStandardMaterial({
|
|
5772
5896
|
color: DEFAULT_TENDON_COLOR,
|
|
5773
5897
|
roughness: 0.6,
|
|
5774
5898
|
metalness: 0.1
|
|
@@ -5783,11 +5907,11 @@ function TendonRenderer(props) {
|
|
|
5783
5907
|
curves.push(null);
|
|
5784
5908
|
continue;
|
|
5785
5909
|
}
|
|
5786
|
-
const points = Array.from({ length: wrapNum }, () => new
|
|
5787
|
-
const curve = new
|
|
5910
|
+
const points = Array.from({ length: wrapNum }, () => new THREE12.Vector3());
|
|
5911
|
+
const curve = new THREE12.CatmullRomCurve3(points, false);
|
|
5788
5912
|
const segments = Math.max(wrapNum * 2, 4);
|
|
5789
|
-
const geometry = new
|
|
5790
|
-
const mesh = new
|
|
5913
|
+
const geometry = new THREE12.TubeGeometry(curve, segments, DEFAULT_TENDON_WIDTH, 6, false);
|
|
5914
|
+
const mesh = new THREE12.Mesh(geometry, material);
|
|
5791
5915
|
mesh.frustumCulled = false;
|
|
5792
5916
|
group.add(mesh);
|
|
5793
5917
|
meshes.push(mesh);
|
|
@@ -5842,11 +5966,11 @@ function TendonRenderer(props) {
|
|
|
5842
5966
|
if (curve.points.length !== validCount) {
|
|
5843
5967
|
curve.points.length = validCount;
|
|
5844
5968
|
while (curve.points.length < validCount) {
|
|
5845
|
-
curve.points.push(new
|
|
5969
|
+
curve.points.push(new THREE12.Vector3());
|
|
5846
5970
|
}
|
|
5847
5971
|
}
|
|
5848
5972
|
mesh.geometry.dispose();
|
|
5849
|
-
mesh.geometry = new
|
|
5973
|
+
mesh.geometry = new THREE12.TubeGeometry(
|
|
5850
5974
|
curve,
|
|
5851
5975
|
Math.max(validCount * 2, 4),
|
|
5852
5976
|
DEFAULT_TENDON_WIDTH,
|
|
@@ -5859,6 +5983,26 @@ function TendonRenderer(props) {
|
|
|
5859
5983
|
if (status !== "ready") return null;
|
|
5860
5984
|
return /* @__PURE__ */ jsx("group", { ...props, ref: groupRef });
|
|
5861
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
|
+
}
|
|
5862
6006
|
function FlexRenderer(props) {
|
|
5863
6007
|
const { mjModelRef, mjDataRef, status } = useMujocoContext();
|
|
5864
6008
|
const groupRef = useRef(null);
|
|
@@ -5873,24 +6017,23 @@ function FlexRenderer(props) {
|
|
|
5873
6017
|
const vertAdr = model.flex_vertadr[f];
|
|
5874
6018
|
const vertNum = model.flex_vertnum[f];
|
|
5875
6019
|
if (vertNum === 0) continue;
|
|
5876
|
-
const geometry = new
|
|
6020
|
+
const geometry = new THREE12.BufferGeometry();
|
|
5877
6021
|
const positions = new Float32Array(vertNum * 3);
|
|
5878
|
-
geometry.setAttribute("position", new
|
|
5879
|
-
|
|
5880
|
-
|
|
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);
|
|
5881
6026
|
if (model.flex_rgba) {
|
|
5882
|
-
color = new
|
|
6027
|
+
color = new THREE12.Color(
|
|
5883
6028
|
model.flex_rgba[4 * f],
|
|
5884
6029
|
model.flex_rgba[4 * f + 1],
|
|
5885
6030
|
model.flex_rgba[4 * f + 2]
|
|
5886
6031
|
);
|
|
5887
6032
|
}
|
|
5888
|
-
const
|
|
5889
|
-
|
|
5890
|
-
|
|
5891
|
-
|
|
5892
|
-
});
|
|
5893
|
-
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;
|
|
5894
6037
|
mesh.userData.flexId = f;
|
|
5895
6038
|
mesh.userData.vertAdr = vertAdr;
|
|
5896
6039
|
mesh.userData.vertNum = vertNum;
|
|
@@ -5919,14 +6062,14 @@ function FlexRenderer(props) {
|
|
|
5919
6062
|
posAttr.setXYZ(v, data.flexvert_xpos[srcIdx], data.flexvert_xpos[srcIdx + 1], data.flexvert_xpos[srcIdx + 2]);
|
|
5920
6063
|
}
|
|
5921
6064
|
posAttr.needsUpdate = true;
|
|
5922
|
-
mesh.geometry.computeVertexNormals();
|
|
6065
|
+
if (mesh instanceof THREE12.Mesh) mesh.geometry.computeVertexNormals();
|
|
5923
6066
|
}
|
|
5924
6067
|
});
|
|
5925
6068
|
if (status !== "ready") return null;
|
|
5926
6069
|
return /* @__PURE__ */ jsx("group", { ...props, ref: groupRef });
|
|
5927
6070
|
}
|
|
5928
6071
|
var GEOM_TYPE_NAMES2 = ["plane", "hfield", "sphere", "capsule", "ellipsoid", "cylinder", "box", "mesh"];
|
|
5929
|
-
var _matrix = new
|
|
6072
|
+
var _matrix = new THREE12.Matrix4();
|
|
5930
6073
|
function getGeomInfo(model, geomId) {
|
|
5931
6074
|
const size = model.geom_size.subarray(geomId * 3, geomId * 3 + 3);
|
|
5932
6075
|
const type = model.geom_type[geomId];
|
|
@@ -5948,10 +6091,10 @@ function geomSignature(model, geomId) {
|
|
|
5948
6091
|
return [type, size, mat, data, rgba].join("|");
|
|
5949
6092
|
}
|
|
5950
6093
|
function firstMesh(object) {
|
|
5951
|
-
if (object instanceof
|
|
6094
|
+
if (object instanceof THREE12.Mesh) return object;
|
|
5952
6095
|
let mesh = null;
|
|
5953
6096
|
object.traverse((child) => {
|
|
5954
|
-
if (!mesh && child instanceof
|
|
6097
|
+
if (!mesh && child instanceof THREE12.Mesh) mesh = child;
|
|
5955
6098
|
});
|
|
5956
6099
|
return mesh;
|
|
5957
6100
|
}
|
|
@@ -6368,24 +6511,16 @@ function useActuators() {
|
|
|
6368
6511
|
if (status !== "ready") return [];
|
|
6369
6512
|
const model = mjModelRef.current;
|
|
6370
6513
|
if (!model) return [];
|
|
6371
|
-
const actuators =
|
|
6372
|
-
for (let i = 0; i < model.nu; i++) {
|
|
6373
|
-
const name = getName(model, model.name_actuatoradr[i]);
|
|
6374
|
-
const lo = model.actuator_ctrlrange[i * 2];
|
|
6375
|
-
const hi = model.actuator_ctrlrange[i * 2 + 1];
|
|
6376
|
-
const hasRange = lo < hi;
|
|
6377
|
-
const range = hasRange ? [lo, hi] : [-Infinity, Infinity];
|
|
6378
|
-
actuators.push({ id: i, name, range });
|
|
6379
|
-
}
|
|
6514
|
+
const actuators = Array.from({ length: model.nactuator }, (_, id) => getActuatorInfo(model, id));
|
|
6380
6515
|
return actuators;
|
|
6381
6516
|
}, [status, mjModelRef]);
|
|
6382
6517
|
}
|
|
6383
|
-
var _mat42 = new
|
|
6518
|
+
var _mat42 = new THREE12.Matrix4();
|
|
6384
6519
|
function useSitePosition(siteName) {
|
|
6385
6520
|
const { mjModelRef, mjDataRef, status } = useMujocoContext();
|
|
6386
6521
|
const siteIdRef = useRef(-1);
|
|
6387
|
-
const positionRef = useRef(new
|
|
6388
|
-
const quaternionRef = useRef(new
|
|
6522
|
+
const positionRef = useRef(new THREE12.Vector3());
|
|
6523
|
+
const quaternionRef = useRef(new THREE12.Quaternion());
|
|
6389
6524
|
useEffect(() => {
|
|
6390
6525
|
const model = mjModelRef.current;
|
|
6391
6526
|
if (!model || status !== "ready") {
|
|
@@ -6578,10 +6713,10 @@ function useJointState(name, options = {}) {
|
|
|
6578
6713
|
function useBodyState(name) {
|
|
6579
6714
|
const { mjModelRef, status } = useMujocoContext();
|
|
6580
6715
|
const bodyIdRef = useRef(-1);
|
|
6581
|
-
const position = useRef(new
|
|
6582
|
-
const quaternion = useRef(new
|
|
6583
|
-
const linearVelocity = useRef(new
|
|
6584
|
-
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());
|
|
6585
6720
|
useEffect(() => {
|
|
6586
6721
|
const model = mjModelRef.current;
|
|
6587
6722
|
if (!model || status !== "ready") return;
|
|
@@ -6607,7 +6742,7 @@ function useBodyState(name) {
|
|
|
6607
6742
|
});
|
|
6608
6743
|
return { position, quaternion, linearVelocity, angularVelocity };
|
|
6609
6744
|
}
|
|
6610
|
-
var _matrix2 = new
|
|
6745
|
+
var _matrix2 = new THREE12.Matrix4();
|
|
6611
6746
|
function quaternionFromMatrixArray(target, values, offset) {
|
|
6612
6747
|
_matrix2.set(
|
|
6613
6748
|
values[offset],
|
|
@@ -6641,8 +6776,8 @@ function useNamedPose(kind, name) {
|
|
|
6641
6776
|
const { mjModelRef, status } = useMujocoContext();
|
|
6642
6777
|
const idRef = useRef(-1);
|
|
6643
6778
|
const foundRef = useRef(false);
|
|
6644
|
-
const positionRef = useRef(new
|
|
6645
|
-
const quaternionRef = useRef(new
|
|
6779
|
+
const positionRef = useRef(new THREE12.Vector3());
|
|
6780
|
+
const quaternionRef = useRef(new THREE12.Quaternion());
|
|
6646
6781
|
useEffect(() => {
|
|
6647
6782
|
const model = mjModelRef.current;
|
|
6648
6783
|
if (!model || status !== "ready") {
|
|
@@ -6694,7 +6829,7 @@ function useGeomPose(name) {
|
|
|
6694
6829
|
function useSitePose(name) {
|
|
6695
6830
|
return useNamedPose("site", name);
|
|
6696
6831
|
}
|
|
6697
|
-
function useCtrl(name) {
|
|
6832
|
+
function useCtrl(name, input) {
|
|
6698
6833
|
const { mjModelRef, mjDataRef, status } = useMujocoContext();
|
|
6699
6834
|
const actuatorIdRef = useRef(-1);
|
|
6700
6835
|
const rangeRef = useRef([0, 0]);
|
|
@@ -6702,14 +6837,15 @@ function useCtrl(name) {
|
|
|
6702
6837
|
const model = mjModelRef.current;
|
|
6703
6838
|
if (!model || status !== "ready") return;
|
|
6704
6839
|
const id = findActuatorByName(model, name);
|
|
6705
|
-
|
|
6840
|
+
const adr = getActuatorControlAddress(model, id, input);
|
|
6841
|
+
actuatorIdRef.current = adr;
|
|
6706
6842
|
if (id >= 0) {
|
|
6707
6843
|
rangeRef.current = [
|
|
6708
|
-
model.actuator_ctrlrange[
|
|
6709
|
-
model.actuator_ctrlrange[
|
|
6844
|
+
model.actuator_ctrlrange[adr * 2],
|
|
6845
|
+
model.actuator_ctrlrange[adr * 2 + 1]
|
|
6710
6846
|
];
|
|
6711
6847
|
}
|
|
6712
|
-
}, [name, status, mjModelRef]);
|
|
6848
|
+
}, [name, input, status, mjModelRef]);
|
|
6713
6849
|
return useMemo(() => ({
|
|
6714
6850
|
read() {
|
|
6715
6851
|
const data = mjDataRef.current;
|
|
@@ -6879,7 +7015,7 @@ function useControlGroup(group, options = {}) {
|
|
|
6879
7015
|
if (id < 0) {
|
|
6880
7016
|
console.warn(`[mujoco-react] useControlGroup: actuator "${name}" was not found.`);
|
|
6881
7017
|
}
|
|
6882
|
-
return id;
|
|
7018
|
+
return getActuatorControlAddress(model, id);
|
|
6883
7019
|
});
|
|
6884
7020
|
}, [mjModelRef, status, namesKey]);
|
|
6885
7021
|
const patch = useCallback(
|
|
@@ -7085,7 +7221,7 @@ function useKeyboardTeleop(config) {
|
|
|
7085
7221
|
const cache = /* @__PURE__ */ new Map();
|
|
7086
7222
|
for (const binding of Object.values(config.bindings)) {
|
|
7087
7223
|
if (!cache.has(binding.actuator)) {
|
|
7088
|
-
cache.set(binding.actuator, findActuatorByName(model, binding.actuator));
|
|
7224
|
+
cache.set(binding.actuator, getActuatorControlAddress(model, findActuatorByName(model, binding.actuator)));
|
|
7089
7225
|
}
|
|
7090
7226
|
}
|
|
7091
7227
|
actuatorCacheRef.current = cache;
|
|
@@ -7135,9 +7271,9 @@ function useKeyboardTeleop(config) {
|
|
|
7135
7271
|
}
|
|
7136
7272
|
var DEFAULT_TRANSLATE_SPEED = 0.25;
|
|
7137
7273
|
var DEFAULT_ROTATE_SPEED = 1;
|
|
7138
|
-
var _translation = new
|
|
7139
|
-
var _axis = new
|
|
7140
|
-
var _quat3 = new
|
|
7274
|
+
var _translation = new THREE12.Vector3();
|
|
7275
|
+
var _axis = new THREE12.Vector3();
|
|
7276
|
+
var _quat3 = new THREE12.Quaternion();
|
|
7141
7277
|
function actionSign(action) {
|
|
7142
7278
|
return action.endsWith("+") ? 1 : -1;
|
|
7143
7279
|
}
|
|
@@ -7914,10 +8050,10 @@ function useGamepad(config) {
|
|
|
7914
8050
|
axisCacheRef.current.clear();
|
|
7915
8051
|
buttonCacheRef.current.clear();
|
|
7916
8052
|
for (const [idx, name] of Object.entries(config.axes ?? {})) {
|
|
7917
|
-
axisCacheRef.current.set(Number(idx), findActuatorByName(model, name));
|
|
8053
|
+
axisCacheRef.current.set(Number(idx), getActuatorControlAddress(model, findActuatorByName(model, name)));
|
|
7918
8054
|
}
|
|
7919
8055
|
for (const [idx, name] of Object.entries(config.buttons ?? {})) {
|
|
7920
|
-
buttonCacheRef.current.set(Number(idx), findActuatorByName(model, name));
|
|
8056
|
+
buttonCacheRef.current.set(Number(idx), getActuatorControlAddress(model, findActuatorByName(model, name)));
|
|
7921
8057
|
}
|
|
7922
8058
|
}, [config.axes, config.buttons, status, mjModelRef]);
|
|
7923
8059
|
useBeforePhysicsStep(({ data }) => {
|
|
@@ -8605,7 +8741,7 @@ function useSelectionHighlight(bodyId, options = {}) {
|
|
|
8605
8741
|
}
|
|
8606
8742
|
}
|
|
8607
8743
|
prevRef.current = [];
|
|
8608
|
-
const highlightColor = new
|
|
8744
|
+
const highlightColor = new THREE12.Color(color);
|
|
8609
8745
|
for (const mesh of meshes) {
|
|
8610
8746
|
const mat = mesh.material;
|
|
8611
8747
|
if (mat.emissive) {
|
|
@@ -8632,15 +8768,15 @@ function useSelectionHighlight(bodyId, options = {}) {
|
|
|
8632
8768
|
}
|
|
8633
8769
|
function useCameraAnimation() {
|
|
8634
8770
|
const { camera } = useThree();
|
|
8635
|
-
const orbitTargetRef = useRef(new
|
|
8771
|
+
const orbitTargetRef = useRef(new THREE12.Vector3(0, 0, 0));
|
|
8636
8772
|
const cameraAnimRef = useRef({
|
|
8637
8773
|
active: false,
|
|
8638
|
-
startPos: new
|
|
8639
|
-
endPos: new
|
|
8640
|
-
startRot: new
|
|
8641
|
-
endRot: new
|
|
8642
|
-
startTarget: new
|
|
8643
|
-
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(),
|
|
8644
8780
|
startTime: 0,
|
|
8645
8781
|
duration: 0,
|
|
8646
8782
|
resolve: null
|