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/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-5WFYM73E.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-5WFYM73E.js';
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 THREE11 from 'three';
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
- isMounted.current = true;
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") && isMounted.current) {
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 (isMounted.current) {
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 (isMounted.current) {
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
- isMounted.current = false;
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.nu; i++) {
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.nu) return -1;
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.nu) return -1;
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 hasRange = mjModel.actuator_ctrlrange[2 * actuatorId] < mjModel.actuator_ctrlrange[2 * actuatorId + 1];
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
- range: hasRange ? [mjModel.actuator_ctrlrange[2 * actuatorId], mjModel.actuator_ctrlrange[2 * actuatorId + 1]] : unlimitedRange()
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.nu; i++) {
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.nu }, (_, i) => i);
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.nu; actuatorId++) {
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.nu }, (_, i) => i).filter((id) => getActuatorJointId(mjModel, id) >= 0);
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
- applyInitialPose(mjModel, mjData, config);
782
- mujoco.mj_forward(mjModel, mjData);
783
- return { mjModel, mjData };
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
- applyInitialPose(mjModel, mjData, config);
866
- mujoco.mj_forward(mjModel, mjData);
867
- return { mjModel, mjData };
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 THREE11.DataTexture(rgba, width, height, THREE11.RGBAFormat);
948
- texture.colorSpace = THREE11.LinearSRGBColorSpace;
949
- texture.wrapS = THREE11.RepeatWrapping;
950
- texture.wrapT = THREE11.RepeatWrapping;
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 THREE11.Color(16777215);
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 THREE11.PlaneGeometry(size[0] * 2 || 5, size[1] * 2 || 5);
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 THREE11.SphereGeometry(size[0], 24, 24);
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 THREE11.BoxGeometry(size[0] * 2, size[1] * 2, size[2] * 2);
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 THREE11.CylinderGeometry(size[0], size[0], size[1] * 2, 24);
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 THREE11.BufferGeometry();
1019
- geo.setAttribute("position", new THREE11.Float32BufferAttribute(positions, 3));
1020
- geo.setAttribute("uv", new THREE11.Float32BufferAttribute(uvs, 2));
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 THREE11.BufferGeometry();
1024
- geo.setAttribute("position", new THREE11.Float32BufferAttribute(mjModel.mesh_vert.subarray(vAdr * 3, (vAdr + vNum) * 3), 3));
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 THREE11.Mesh(geo, new THREE11.MeshStandardMaterial({
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 THREE11.Group();
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
- THREE11.MathUtils.lerp(interpolation.previousXpos[i3], interpolation.currentXpos[i3], alpha),
1135
- THREE11.MathUtils.lerp(interpolation.previousXpos[i3 + 1], interpolation.currentXpos[i3 + 1], alpha),
1136
- THREE11.MathUtils.lerp(interpolation.previousXpos[i3 + 2], interpolation.currentXpos[i3 + 2], alpha)
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 THREE11.Quaternion();
1197
- var _currentQuat = new THREE11.Quaternion();
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 THREE11.Raycaster();
1816
- var _ndc = new THREE11.Vector2();
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 THREE11.Vector3 ? value.clone() : new THREE11.Vector3(value[0], value[1], value[2]);
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 THREE11.Quaternion) {
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 THREE11.Vector3()));
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 THREE11.PerspectiveCamera ? fallbackCamera.clone() : new THREE11.PerspectiveCamera(45, width / height, 0.01, 100);
1844
- if (camera instanceof THREE11.PerspectiveCamera) {
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 THREE11.Raycaster();
2010
- var _projNdc = new THREE11.Vector2();
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 THREE11.Matrix4();
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 THREE11.Quaternion().setFromRotationMatrix(matrix);
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 THREE11.Vector3 ? value.clone() : new THREE11.Vector3(value[0], value[1], value[2]);
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 THREE11.Quaternion ? value.clone() : new THREE11.Quaternion(value[0], value[1], value[2], value[3]);
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 THREE11.Vector3(position[0], position[1], position[2]);
2069
- const resolvedQuaternion = new THREE11.Quaternion(
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 THREE11.Matrix4().set(
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
- data.time = snapshot.time;
2613
- data.qpos.set(snapshot.qpos);
2614
- data.qvel.set(snapshot.qvel);
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 (id >= 0 && value !== void 0) data.ctrl[id] = value;
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
- if (id >= 0) data.ctrl[id] = val;
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 THREE11.Vector3(
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 IK for a target 6-DOF pose.
3852
- * @param model MuJoCo model
3853
- * @param data MuJoCo data (qpos will be temporarily modified, then restored)
3854
- * @param siteId Index of the end-effector site to control
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
- solve(model, data, siteId, qposAdr, targetPos, targetQuat, currentQ, opts) {
3967
+ solveJoints(model, data, siteId, jointIds, targetPos, targetQuat, currentQpos = data.qpos, opts) {
3863
3968
  const o = resolveOptions(opts);
3864
- const n = qposAdr.length;
3865
- const savedQpos = new Float64Array(data.qpos.length);
3866
- savedQpos.set(data.qpos);
3867
- const R_target = quatToMat3(targetQuat);
3868
- const q = new Float64Array(n);
3869
- for (let i = 0; i < n; i++) q[i] = currentQ[i];
3870
- const J = new Float64Array(6 * n);
3871
- const JJt = new Float64Array(36);
3872
- const rhs = new Float64Array(6);
3873
- const x = new Float64Array(6);
3874
- const dq = new Float64Array(n);
3875
- const baseSitePos = new Float64Array(3);
3876
- const baseSiteMat = new Float64Array(9);
3877
- const pertSitePos = new Float64Array(3);
3878
- const pertSiteMat = new Float64Array(9);
3879
- let bestQ = null;
3880
- let bestErr = Infinity;
3881
- const patience = 4;
3882
- let noImprove = 0;
3883
- if (n === 0) return null;
3884
- for (let iter = 0; iter < o.maxIterations; iter++) {
3885
- for (let i = 0; i < n; i++) data.qpos[qposAdr[i]] = q[i];
3886
- this.mujoco.mj_forward(model, data);
3887
- const sp = data.site_xpos;
3888
- const sm = data.site_xmat;
3889
- const off3 = siteId * 3;
3890
- const off9 = siteId * 9;
3891
- for (let i = 0; i < 3; i++) baseSitePos[i] = sp[off3 + i];
3892
- for (let i = 0; i < 9; i++) baseSiteMat[i] = sm[off9 + i];
3893
- const posErr0 = targetPos.x - baseSitePos[0];
3894
- const posErr1 = targetPos.y - baseSitePos[1];
3895
- const posErr2 = targetPos.z - baseSitePos[2];
3896
- const rotErr = orientationError(baseSiteMat, R_target);
3897
- const error = [
3898
- posErr0 * o.posWeight,
3899
- posErr1 * o.posWeight,
3900
- posErr2 * o.posWeight,
3901
- rotErr[0] * o.rotWeight,
3902
- rotErr[1] * o.rotWeight,
3903
- rotErr[2] * o.rotWeight
3904
- ];
3905
- const errNorm = Math.sqrt(
3906
- error[0] * error[0] + error[1] * error[1] + error[2] * error[2] + error[3] * error[3] + error[4] * error[4] + error[5] * error[5]
3907
- );
3908
- if (errNorm < bestErr - 1e-9) {
3909
- bestErr = errNorm;
3910
- bestQ = Array.from(q);
3911
- noImprove = 0;
3912
- } else {
3913
- noImprove++;
3914
- }
3915
- if (errNorm < o.tolerance) break;
3916
- if (noImprove >= patience) break;
3917
- for (let j = 0; j < n; j++) {
3918
- const adr = qposAdr[j];
3919
- const saved = data.qpos[adr];
3920
- data.qpos[adr] = q[j] + o.epsilon;
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
- for (let i = 0; i < 3; i++) pertSitePos[i] = sp[off3 + i];
3923
- for (let i = 0; i < 9; i++) pertSiteMat[i] = sm[off9 + i];
3924
- J[0 * n + j] = (pertSitePos[0] - baseSitePos[0]) / o.epsilon * o.posWeight;
3925
- J[1 * n + j] = (pertSitePos[1] - baseSitePos[1]) / o.epsilon * o.posWeight;
3926
- J[2 * n + j] = (pertSitePos[2] - baseSitePos[2]) / o.epsilon * o.posWeight;
3927
- const dRot = angularDelta(baseSiteMat, pertSiteMat);
3928
- J[3 * n + j] = dRot[0] / o.epsilon * o.rotWeight;
3929
- J[4 * n + j] = dRot[1] / o.epsilon * o.rotWeight;
3930
- J[5 * n + j] = dRot[2] / o.epsilon * o.rotWeight;
3931
- data.qpos[adr] = saved;
3932
- }
3933
- for (let i = 0; i < n; i++) data.qpos[qposAdr[i]] = q[i];
3934
- for (let r = 0; r < 6; r++) {
3935
- for (let c = 0; c < 6; c++) {
3936
- let sum = 0;
3937
- for (let k = 0; k < n; k++) {
3938
- sum += J[r * n + k] * J[c * n + k];
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
- JJt[r * 6 + c] = sum + (r === c ? o.damping : 0);
3941
- }
3942
- }
3943
- for (let i = 0; i < 6; i++) rhs[i] = error[i];
3944
- solve6x6(JJt, rhs, x);
3945
- for (let j = 0; j < n; j++) {
3946
- let sum = 0;
3947
- for (let r = 0; r < 6; r++) {
3948
- sum += J[r * n + j] * x[r];
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
- dq[j] = sum;
4092
+ this.mujoco.mj_integratePos(model, qbuffer, velocity, 1);
4093
+ clamp();
3951
4094
  }
3952
- for (let i = 0; i < n; i++) q[i] += dq[i];
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-6) {
3994
- return [
3995
- 0.5 * (Re[7] - Re[5]),
3996
- 0.5 * (Re[2] - Re[6]),
3997
- 0.5 * (Re[3] - Re[1])
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 THREE11.Matrix4();
4210
+ var _syncMat4 = new THREE12.Matrix4();
4072
4211
  function toIkVector3(value) {
4073
- return "x" in value ? new THREE11.Vector3(value.x, value.y, value.z) : new THREE11.Vector3(value[0], value[1], value[2]);
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 THREE11.Quaternion(value.x, value.y, value.z, value.w) : new THREE11.Quaternion(value[0], value[1], value[2], value[3]);
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 THREE11.Group());
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 THREE11.Vector3(),
4127
- endPos: new THREE11.Vector3(),
4128
- startRot: new THREE11.Quaternion(),
4129
- endRot: new THREE11.Quaternion(),
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 THREE11.Quaternion().setFromEuler(
4291
- new THREE11.Euler(Math.PI, 0, 0)
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 THREE11.Euler().setFromQuaternion(target.quaternion)
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 THREE11.Matrix4();
4413
- var _pos = new THREE11.Vector3();
4414
- var _quat = new THREE11.Quaternion();
4415
- var _scale = new THREE11.Vector3(1, 1, 1);
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 THREE11.Object3D();
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 THREE11.Vector3();
4569
- var _bodyQuat = new THREE11.Quaternion();
4570
- var _worldHit = new THREE11.Vector3();
4571
- var _raycaster2 = new THREE11.Raycaster();
4572
- var _mouse = new THREE11.Vector2();
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 THREE11.Vector3());
4584
- const grabWorldRef = useRef(new THREE11.Vector3());
4585
- const mouseWorldRef = useRef(new THREE11.Vector3());
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 THREE11.ArrowHelper(
4591
- new THREE11.Vector3(0, 1, 0),
4592
- new THREE11.Vector3(),
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 THREE11.Color(dr, dg, db);
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 THREE11.DirectionalLight(color, finalIntensity);
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 THREE11.SpotLight(color, finalIntensity, 0, angle, exponent / 128);
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 THREE11.Vector3();
5153
- new THREE11.Vector3();
5154
- var _quat2 = new THREE11.Quaternion();
5155
- var _cameraMatrix = new THREE11.Matrix4();
5156
- var _contactPos = new THREE11.Vector3();
5157
- var _contactNormal = new THREE11.Vector3();
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 THREE11.Vector3 ? value.clone() : new THREE11.Vector3(value[0], value[1], value[2]);
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 THREE11.Color(color).getStyle();
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 THREE11.CanvasTexture(canvas);
5175
- const sprite = new THREE11.Sprite(
5176
- new THREE11.SpriteMaterial({
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 THREE11.Vector3());
5192
- const configuredUp = toVector32(camera.up, new THREE11.Vector3(0, 0, 1)).normalize();
5193
- const cameraQuaternion = new THREE11.Quaternion();
5194
- const forward = new THREE11.Vector3();
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 THREE11.Quaternion) {
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 THREE11.Vector3(0, 0, -1))
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 THREE11.Matrix4().lookAt(cameraPosition, target2, configuredUp)
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(THREE11.MathUtils.degToRad(fov) / 2) * depth;
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 THREE11.Group();
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 THREE11.LineSegments(
5260
- new THREE11.BufferGeometry().setFromPoints(frustumPoints),
5261
- new THREE11.LineBasicMaterial({
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 THREE11.LineSegments(
5272
- new THREE11.BufferGeometry().setFromPoints([cameraPosition, target]),
5273
- new THREE11.LineBasicMaterial({
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 THREE11.Group();
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 THREE11.Mesh(
5289
- new THREE11.BoxGeometry(0.045 * markerScale, 0.028 * markerScale, 0.022 * markerScale),
5290
- new THREE11.MeshBasicMaterial({ color, depthTest: false })
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 THREE11.Mesh(
5293
- new THREE11.BoxGeometry(0.025 * markerScale, 0.018 * markerScale, 0.014 * markerScale),
5294
- new THREE11.MeshBasicMaterial({ color: aimColor, depthTest: false })
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 THREE11.Mesh(
5301
- new THREE11.SphereGeometry(0.018 * markerScale, 16, 10),
5302
- new THREE11.MeshBasicMaterial({ color: aimColor, depthTest: false })
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 THREE11.SphereGeometry(s[3 * i], 12, 8);
5480
+ geometry = new THREE12.SphereGeometry(s[3 * i], 12, 8);
5342
5481
  break;
5343
5482
  case 3:
5344
- geometry = new THREE11.CapsuleGeometry(s[3 * i], s[3 * i + 1] * 2, 6, 8);
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 THREE11.CylinderGeometry(s[3 * i], s[3 * i], s[3 * i + 1] * 2, 12);
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 THREE11.BoxGeometry(s[3 * i] * 2, s[3 * i + 1] * 2, s[3 * i + 2] * 2);
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 THREE11.MeshBasicMaterial({ color: 65280, wireframe: true, transparent: true, opacity: 0.3 });
5355
- const mesh = new THREE11.Mesh(geometry, mat);
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 THREE11.OctahedronGeometry(radius);
5379
- const mat = new THREE11.MeshBasicMaterial({ color: 16711935, depthTest: false });
5380
- const mesh = new THREE11.Mesh(geometry, mat);
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 THREE11.CanvasTexture(canvas);
5393
- const spriteMat = new THREE11.SpriteMaterial({ map: tex, depthTest: false, transparent: true });
5394
- const sprite = new THREE11.Sprite(spriteMat);
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 THREE11.ArrowHelper(
5418
- new THREE11.Vector3(0, 0, 1),
5419
- new THREE11.Vector3(),
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 THREE11.LineBasicMaterial({ color, depthTest: false });
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 THREE11.Group();
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 THREE11.Mesh(
5447
- new THREE11.BoxGeometry(0.014, 9e-3, 6e-3),
5448
- new THREE11.MeshBasicMaterial({ color: 3718648, depthTest: false })
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 THREE11.ArrowHelper(
5454
- new THREE11.Vector3(0, 0, -1),
5455
- new THREE11.Vector3(),
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 THREE11.LineBasicMaterial({
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 THREE11.BufferGeometry();
5608
+ const frustumGeometry = new THREE12.BufferGeometry();
5470
5609
  frustumGeometry.setAttribute(
5471
5610
  "position",
5472
- new THREE11.Float32BufferAttribute(new Float32Array(8 * 2 * 3), 3)
5611
+ new THREE12.Float32BufferAttribute(new Float32Array(8 * 2 * 3), 3)
5473
5612
  );
5474
- const frustum = new THREE11.LineSegments(
5613
+ const frustum = new THREE12.LineSegments(
5475
5614
  frustumGeometry,
5476
- new THREE11.LineBasicMaterial({
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 THREE11.CanvasTexture(canvas);
5496
- const sprite = new THREE11.Sprite(
5497
- new THREE11.SpriteMaterial({
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 THREE11.SphereGeometry(5e-3, 6, 6);
5517
- const mat = new THREE11.MeshBasicMaterial({ color: 16711680 });
5518
- const mesh = new THREE11.Mesh(geometry, mat);
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(THREE11.MathUtils.degToRad(fovy) / 2) * CAMERA_DEBUG_FRUSTUM_DEPTH;
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 THREE11.ArrowHelper(
5674
- new THREE11.Vector3(0, 1, 0),
5675
- new THREE11.Vector3(),
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 THREE11.Color(0.3, 0.3, 0.8);
5879
+ var DEFAULT_TENDON_COLOR = new THREE12.Color(0.3, 0.3, 0.8);
5741
5880
  var DEFAULT_TENDON_WIDTH = 2e-3;
5742
- new THREE11.Vector3();
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 THREE11.MeshStandardMaterial({
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 THREE11.Vector3());
5772
- const curve = new THREE11.CatmullRomCurve3(points, false);
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 THREE11.TubeGeometry(curve, segments, DEFAULT_TENDON_WIDTH, 6, false);
5775
- const mesh = new THREE11.Mesh(geometry, material);
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 THREE11.Vector3());
5969
+ curve.points.push(new THREE12.Vector3());
5831
5970
  }
5832
5971
  }
5833
5972
  mesh.geometry.dispose();
5834
- mesh.geometry = new THREE11.TubeGeometry(
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 THREE11.BufferGeometry();
6020
+ const geometry = new THREE12.BufferGeometry();
5862
6021
  const positions = new Float32Array(vertNum * 3);
5863
- geometry.setAttribute("position", new THREE11.BufferAttribute(positions, 3));
5864
- geometry.computeVertexNormals();
5865
- let color = new THREE11.Color(0.5, 0.5, 0.5);
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 THREE11.Color(
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 material = new THREE11.MeshStandardMaterial({
5874
- color,
5875
- roughness: 0.7,
5876
- side: THREE11.DoubleSide
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 THREE11.Matrix4();
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 THREE11.Mesh) return object;
6094
+ if (object instanceof THREE12.Mesh) return object;
5937
6095
  let mesh = null;
5938
6096
  object.traverse((child) => {
5939
- if (!mesh && child instanceof THREE11.Mesh) mesh = child;
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 THREE11.Matrix4();
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 THREE11.Vector3());
6373
- const quaternionRef = useRef(new THREE11.Quaternion());
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 THREE11.Vector3());
6567
- const quaternion = useRef(new THREE11.Quaternion());
6568
- const linearVelocity = useRef(new THREE11.Vector3());
6569
- const angularVelocity = useRef(new THREE11.Vector3());
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 THREE11.Matrix4();
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 THREE11.Vector3());
6630
- const quaternionRef = useRef(new THREE11.Quaternion());
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
- actuatorIdRef.current = id;
6840
+ const adr = getActuatorControlAddress(model, id, input);
6841
+ actuatorIdRef.current = adr;
6691
6842
  if (id >= 0) {
6692
6843
  rangeRef.current = [
6693
- model.actuator_ctrlrange[id * 2],
6694
- model.actuator_ctrlrange[id * 2 + 1]
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 THREE11.Vector3();
7124
- var _axis = new THREE11.Vector3();
7125
- var _quat3 = new THREE11.Quaternion();
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 THREE11.Color(color);
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 THREE11.Vector3(0, 0, 0));
8771
+ const orbitTargetRef = useRef(new THREE12.Vector3(0, 0, 0));
8621
8772
  const cameraAnimRef = useRef({
8622
8773
  active: false,
8623
- startPos: new THREE11.Vector3(),
8624
- endPos: new THREE11.Vector3(),
8625
- startRot: new THREE11.Quaternion(),
8626
- endRot: new THREE11.Quaternion(),
8627
- startTarget: new THREE11.Vector3(),
8628
- endTarget: new THREE11.Vector3(),
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