mujoco-react 8.11.0 → 9.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/spark.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import * as _sparkjsdev_spark from '@sparkjsdev/spark';
3
- import { l as SplatEnvironmentProps } from './types-BmneHLBM.js';
3
+ import { n as SplatEnvironmentProps } from './types-izZlUweI.js';
4
4
  import 'react';
5
5
  import '@react-three/fiber';
6
6
  import 'three';
package/dist/spark.js CHANGED
@@ -1,4 +1,4 @@
1
- import { useSplatEnvironment, SplatEnvironment } from './chunk-SEWQULWO.js';
1
+ import { useSplatEnvironment, SplatEnvironment } from './chunk-33CV6HSV.js';
2
2
  import { useThree } from '@react-three/fiber';
3
3
  import { useState, useEffect, useCallback, useMemo, useRef } from 'react';
4
4
  import * as THREE from 'three';
@@ -327,7 +327,7 @@ interface LoadFromFilesOptions {
327
327
  homeJoints?: number[];
328
328
  xmlPatches?: XmlPatch[];
329
329
  sceneObjects?: SceneObject[];
330
- onReset?: (model: MujocoModel, data: MujocoData) => void;
330
+ onReset?: (input: ResetCallbackInput) => void;
331
331
  }
332
332
  interface SceneConfig {
333
333
  /** Base URL for fetching model files. The loader fetches `src + sceneFile` and follows dependencies. */
@@ -347,7 +347,7 @@ interface SceneConfig {
347
347
  sceneObjects?: SceneObject[];
348
348
  homeJoints?: number[];
349
349
  xmlPatches?: XmlPatch[];
350
- onReset?: (model: MujocoModel, data: MujocoData) => void;
350
+ onReset?: (input: ResetCallbackInput) => void;
351
351
  }
352
352
  type ResourceSelector<TInfo, TName extends string = string> = TName | readonly TName[] | RegExp | ((info: TInfo) => boolean);
353
353
  interface IkConfig {
@@ -380,7 +380,7 @@ interface IkContextValue {
380
380
  setIkEnabled: (enabled: boolean) => void;
381
381
  moveTarget: (pos: THREE.Vector3, duration?: number) => void;
382
382
  syncTargetToSite: () => void;
383
- solveIK: (pos: THREE.Vector3, quat: THREE.Quaternion, currentQ: number[]) => number[] | null;
383
+ solveIK: (input: IkSolveInput) => number[] | null;
384
384
  getGizmoStats: () => {
385
385
  pos: THREE.Vector3;
386
386
  rot: THREE.Euler;
@@ -398,14 +398,38 @@ interface PhysicsConfig {
398
398
  paused?: boolean;
399
399
  speed?: number;
400
400
  }
401
- type IKSolveFn = (pos: THREE.Vector3, quat: THREE.Quaternion, currentQ: number[], context?: IKSolveContext) => number[] | null;
401
+ type IKSolveFn = (input: IkSolveInput) => number[] | null;
402
+ interface IkSolveInput {
403
+ position: THREE.Vector3;
404
+ quaternion: THREE.Quaternion;
405
+ currentQ: number[];
406
+ context?: IKSolveContext;
407
+ }
402
408
  interface IKSolveContext {
403
409
  model: MujocoModel;
404
410
  data: MujocoData;
405
411
  siteId: number;
406
412
  controlGroup: ControlGroupInfo;
407
413
  }
408
- type PhysicsStepCallback = (model: MujocoModel, data: MujocoData) => void;
414
+ interface PhysicsStepInput {
415
+ model: MujocoModel;
416
+ data: MujocoData;
417
+ }
418
+ interface ResetCallbackInput extends PhysicsStepInput {
419
+ }
420
+ interface ReadyCallbackInput {
421
+ api: MujocoSimAPI;
422
+ }
423
+ interface StepCallbackInput {
424
+ time: number;
425
+ model: MujocoModel;
426
+ data: MujocoData;
427
+ }
428
+ interface SelectionCallbackInput {
429
+ bodyId: number;
430
+ name: string;
431
+ }
432
+ type PhysicsStepCallback = (input: PhysicsStepInput) => void;
409
433
  interface StateSnapshot {
410
434
  time: number;
411
435
  qpos: Float64Array;
@@ -606,7 +630,11 @@ interface IkGizmoProps {
606
630
  controller: IkContextValue;
607
631
  siteName?: string;
608
632
  scale?: number;
609
- onDrag?: (position: THREE.Vector3, quaternion: THREE.Quaternion) => void;
633
+ onDrag?: (input: IkGizmoDragInput) => void;
634
+ }
635
+ interface IkGizmoDragInput {
636
+ position: THREE.Vector3;
637
+ quaternion: THREE.Quaternion;
610
638
  }
611
639
  interface DragInteractionProps {
612
640
  stiffness?: number;
@@ -718,7 +746,11 @@ interface VisualScenarioEffectsProps {
718
746
  background?: THREE.ColorRepresentation;
719
747
  fogNear?: number;
720
748
  fogFar?: number;
721
- materialFilter?: (object: THREE.Object3D, material: THREE.Material) => boolean;
749
+ materialFilter?: (input: VisualScenarioMaterialFilterInput) => boolean;
750
+ }
751
+ interface VisualScenarioMaterialFilterInput {
752
+ object: THREE.Object3D;
753
+ material: THREE.Material;
722
754
  }
723
755
  type TrajectoryInput = TrajectoryFrame[] | number[][];
724
756
  interface TrajectoryPlayerProps {
@@ -728,9 +760,16 @@ interface TrajectoryPlayerProps {
728
760
  loop?: boolean;
729
761
  playing?: boolean;
730
762
  mode?: 'kinematic' | 'physics';
731
- onFrame?: (frameIdx: number) => void;
763
+ onFrame?: (input: TrajectoryFrameCallbackInput) => void;
732
764
  onComplete?: () => void;
733
- onStateChange?: (state: PlaybackState) => void;
765
+ onStateChange?: (input: TrajectoryStateChangeInput) => void;
766
+ }
767
+ interface TrajectoryFrameCallbackInput {
768
+ frameIndex: number;
769
+ frame: TrajectoryFrame | number[] | undefined;
770
+ }
771
+ interface TrajectoryStateChangeInput {
772
+ state: PlaybackState;
734
773
  }
735
774
  interface ContactListenerProps {
736
775
  body: Bodies;
@@ -814,10 +853,10 @@ type MujocoCanvasProps = Omit<CanvasProps, 'onError'> & {
814
853
  config: SceneConfig;
815
854
  /** R3F content rendered while the MuJoCo WASM module is still loading. */
816
855
  loadingFallback?: ReactNode;
817
- onReady?: (api: MujocoSimAPI) => void;
856
+ onReady?: (input: ReadyCallbackInput) => void;
818
857
  onError?: (error: Error) => void;
819
- onStep?: (time: number) => void;
820
- onSelection?: (bodyId: number, name: string) => void;
858
+ onStep?: (input: StepCallbackInput) => void;
859
+ onSelection?: (input: SelectionCallbackInput) => void;
821
860
  gravity?: [number, number, number];
822
861
  timestep?: number;
823
862
  substeps?: number;
@@ -868,4 +907,4 @@ interface JointStateResult {
868
907
  velocity: React__default.RefObject<number | Float64Array>;
869
908
  }
870
909
 
871
- export { type TrajectoryInput as $, type ActuatedJointInfo as A, type BodyProps as B, type ControlGroupInfo as C, type DragInteractionProps as D, type SitePositionResult as E, type Sensors as F, type GeomInfo as G, type SensorHandle as H, type IkConfig as I, type SensorInfo as J, type Joints as K, type JointStateResult as L, type MujocoContextValue as M, type Bodies as N, type ObservationConfig as O, type PhysicsStepCallback as P, type BodyStateResult as Q, type Actuators as R, type SceneConfig as S, type TrajectoryPlayerProps as T, type CtrlHandle as U, type VisualScenarioEffectsProps as V, type ContactInfo as W, type KeyboardTeleopConfig as X, type PolicyConfig as Y, type PolicyVector as Z, type ObservationHandle as _, type MujocoCanvasProps as a, type PlaybackState as a0, type TrajectoryFrame as a1, type BodyInfo as a2, type ControlJointInfo as a3, type Geoms as a4, type IKSolveFn as a5, type JointInfo as a6, type KeyBinding as a7, type Keyframes as a8, type ModelOptions as a9, type SensorResult as aA, type SiteInfo as aB, type SplatAssetConfig as aC, type SplatScenarioConfig as aD, type StateSnapshot as aE, type TrajectoryData as aF, type XmlPatch as aG, getContact as aH, registerRobotResources as aI, type MujocoContact as aa, type MujocoContactArray as ab, type ObservationLayoutItem as ac, type ObservationOutput as ad, type PhysicsConfig as ae, type PolicyActionInput as af, type PolicyInferenceInput as ag, type PolicyObservationInput as ah, type RayHit as ai, type Register as aj, type RegisteredRobotMap as ak, type ResourceSelector as al, RobotActuators as am, RobotBodies as an, RobotGeoms as ao, RobotJoints as ap, RobotKeyframes as aq, type RobotResource as ar, RobotResources as as, RobotSensors as at, RobotSites as au, type Robots as av, type ScenarioCameraConfig as aw, type ScenarioMaterialConfig as ax, type SceneMarker as ay, type SceneObject as az, type MujocoSimAPI as b, type MujocoModule as c, type MujocoModel as d, type MujocoData as e, type ControlGroupSelector as f, type ObservationResult as g, type IkContextValue as h, type IkGizmoProps as i, type SceneLightsProps as j, type ScenarioLightingProps as k, type SplatEnvironmentProps as l, type VisualScenarioConfig as m, type SplatRendererKind as n, type PairedSplatEnvironmentConfig as o, type SplatFormat as p, type SplatCollisionProxyConfig as q, type SplatCollisionPrimitive as r, type ScenarioLightingPreset as s, type SplatEnvironmentMetadataInput as t, type SplatEnvironmentMetadata as u, type SplatSceneInput as v, type DebugProps as w, type ContactListenerProps as x, type ActuatorInfo as y, type Sites as z };
910
+ export { type PolicyConfig as $, type ActuatedJointInfo as A, type BodyProps as B, type ControlGroupInfo as C, type DragInteractionProps as D, type ActuatorInfo as E, type Sites as F, type GeomInfo as G, type SitePositionResult as H, type IkConfig as I, type Sensors as J, type SensorHandle as K, type SensorInfo as L, type MujocoContextValue as M, type Joints as N, type ObservationConfig as O, type PhysicsStepCallback as P, type JointStateResult as Q, type ReadyCallbackInput as R, type SceneConfig as S, type TrajectoryPlayerProps as T, type Bodies as U, type VisualScenarioEffectsProps as V, type BodyStateResult as W, type Actuators as X, type CtrlHandle as Y, type ContactInfo as Z, type KeyboardTeleopConfig as _, type MujocoCanvasProps as a, type PolicyVector as a0, type ObservationHandle as a1, type TrajectoryInput as a2, type TrajectoryStateChangeInput as a3, type PlaybackState as a4, type TrajectoryFrame as a5, type BodyInfo as a6, type ControlJointInfo as a7, type Geoms as a8, type IKSolveFn as a9, RobotResources as aA, RobotSensors as aB, RobotSites as aC, type Robots as aD, type ScenarioCameraConfig as aE, type ScenarioMaterialConfig as aF, type SceneMarker as aG, type SceneObject as aH, type SensorResult as aI, type SiteInfo as aJ, type SplatAssetConfig as aK, type SplatScenarioConfig as aL, type StateSnapshot as aM, type TrajectoryData as aN, type TrajectoryFrameCallbackInput as aO, type VisualScenarioMaterialFilterInput as aP, type XmlPatch as aQ, getContact as aR, registerRobotResources as aS, type IkGizmoDragInput as aa, type IkSolveInput as ab, type JointInfo as ac, type KeyBinding as ad, type Keyframes as ae, type ModelOptions as af, type MujocoContact as ag, type MujocoContactArray as ah, type ObservationLayoutItem as ai, type ObservationOutput as aj, type PhysicsConfig as ak, type PhysicsStepInput as al, type PolicyActionInput as am, type PolicyInferenceInput as an, type PolicyObservationInput as ao, type RayHit as ap, type Register as aq, type RegisteredRobotMap as ar, type ResetCallbackInput as as, type ResourceSelector as at, RobotActuators as au, RobotBodies as av, RobotGeoms as aw, RobotJoints as ax, RobotKeyframes as ay, type RobotResource as az, type MujocoSimAPI as b, type StepCallbackInput as c, type SelectionCallbackInput as d, type MujocoModule as e, type MujocoModel as f, type MujocoData as g, type ControlGroupSelector as h, type ObservationResult as i, type IkContextValue as j, type IkGizmoProps as k, type SceneLightsProps as l, type ScenarioLightingProps as m, type SplatEnvironmentProps as n, type VisualScenarioConfig as o, type SplatRendererKind as p, type PairedSplatEnvironmentConfig as q, type SplatFormat as r, type SplatCollisionProxyConfig as s, type SplatCollisionPrimitive as t, type ScenarioLightingPreset as u, type SplatEnvironmentMetadataInput as v, type SplatEnvironmentMetadata as w, type SplatSceneInput as x, type DebugProps as y, type ContactListenerProps as z };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mujoco-react",
3
- "version": "8.11.0",
3
+ "version": "9.0.0",
4
4
  "description": "Composable React Three Fiber building blocks for MuJoCo WASM simulations",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -168,7 +168,7 @@ export function DragInteraction({
168
168
  }, [gl, camera, scene, controls, mjDataRef]);
169
169
 
170
170
  // Apply spring force each physics frame
171
- useBeforePhysicsStep((model, data) => {
171
+ useBeforePhysicsStep(({ model, data }) => {
172
172
  if (!draggingRef.current || bodyIdRef.current <= 0) return;
173
173
 
174
174
  const bid = bodyIdRef.current;
@@ -26,7 +26,7 @@ const _scale = new THREE.Vector3(1, 1, 1);
26
26
  * - `controller` — IkContextValue from `useIkController()`.
27
27
  * - `siteName` — MuJoCo site to track. Defaults to the controller's configured site.
28
28
  * - `scale` — Gizmo handle scale. Default: 0.18.
29
- * - `onDrag` — Custom drag callback `(pos, quat) => void`.
29
+ * - `onDrag` — Custom drag callback `({ position, quaternion }) => void`.
30
30
  * When omitted, dragging enables IK and writes to the IK target.
31
31
  * When provided, the consumer handles what happens during drag.
32
32
  */
@@ -112,7 +112,7 @@ export function IkGizmo({ controller, siteName, scale = 0.18, onDrag }: IkGizmoP
112
112
  world.decompose(_pos, _quat, _scale);
113
113
  if (onDrag) {
114
114
  // Custom: consumer handles the drag
115
- onDrag(_pos.clone(), _quat.clone());
115
+ onDrag({ position: _pos.clone(), quaternion: _quat.clone() });
116
116
  } else {
117
117
  // Default: write to IK target
118
118
  const target = ikTargetRef.current;
@@ -139,7 +139,7 @@ export function SceneRenderer(props: Omit<ThreeElements['group'], 'ref'>) {
139
139
  const model = mjModelRef.current;
140
140
  if (model && bodyID < model.nbody && onSelectionRef.current) {
141
141
  const name = getName(model, model.name_bodyadr[bodyID]);
142
- onSelectionRef.current(bodyID, name);
142
+ onSelectionRef.current({ bodyId: bodyID, name });
143
143
  }
144
144
  }
145
145
  }}
@@ -54,7 +54,10 @@ export function TrajectoryPlayer({
54
54
  const currentFrame = player.frame;
55
55
  if (currentFrame !== lastReportedFrameRef.current && player.playing) {
56
56
  lastReportedFrameRef.current = currentFrame;
57
- onFrameRef.current(currentFrame);
57
+ onFrameRef.current({
58
+ frameIndex: currentFrame,
59
+ frame: trajectory[currentFrame],
60
+ });
58
61
  }
59
62
  });
60
63
 
@@ -490,7 +490,7 @@ function applyScenarioMaterials(
490
490
  for (const material of normalizeMaterials(object.material)) {
491
491
  const mutable = getMutableScenarioMaterial(material);
492
492
  if (!mutable) continue;
493
- if (materialFilter && !materialFilter(object, material)) continue;
493
+ if (materialFilter && !materialFilter({ object, material })) continue;
494
494
 
495
495
  if (!snapshots.has(material)) {
496
496
  snapshots.set(material, {
@@ -6,19 +6,25 @@
6
6
  import { forwardRef, useEffect } from 'react';
7
7
  import { useMujocoWasm } from './MujocoProvider';
8
8
  import { MujocoSimProvider } from './MujocoSimProvider';
9
- import type { MujocoSimAPI, SceneConfig } from '../types';
9
+ import type {
10
+ MujocoSimAPI,
11
+ ReadyCallbackInput,
12
+ SceneConfig,
13
+ SelectionCallbackInput,
14
+ StepCallbackInput,
15
+ } from '../types';
10
16
 
11
17
  export interface MujocoPhysicsProps {
12
18
  /** Scene/robot configuration. */
13
19
  config: SceneConfig;
14
20
  /** Fires when model is loaded and API is ready. */
15
- onReady?: (api: MujocoSimAPI) => void;
21
+ onReady?: (input: ReadyCallbackInput) => void;
16
22
  /** Fires on scene load failure. */
17
23
  onError?: (error: Error) => void;
18
24
  /** Called each physics step. */
19
- onStep?: (time: number) => void;
25
+ onStep?: (input: StepCallbackInput) => void;
20
26
  /** Called on body double-click selection. */
21
- onSelection?: (bodyId: number, name: string) => void;
27
+ onSelection?: (input: SelectionCallbackInput) => void;
22
28
  /** Override model gravity. */
23
29
  gravity?: [number, number, number];
24
30
  /** Override model.opt.timestep. */
@@ -31,11 +31,14 @@ import {
31
31
  MujocoSimAPI,
32
32
  PhysicsStepCallback,
33
33
  RayHit,
34
+ ReadyCallbackInput,
34
35
  SceneConfig,
35
36
  SceneObject,
37
+ SelectionCallbackInput,
36
38
  SensorInfo,
37
39
  SiteInfo,
38
40
  StateSnapshot,
41
+ StepCallbackInput,
39
42
  XmlPatch,
40
43
  } from '../types';
41
44
  import {
@@ -117,7 +120,7 @@ export interface MujocoSimContextValue {
117
120
  interpolateRef: React.RefObject<boolean>;
118
121
  interpolationStateRef: React.RefObject<BodyInterpolationState>;
119
122
  onSelectionRef: React.RefObject<
120
- ((bodyId: number, name: string) => void) | undefined
123
+ ((input: SelectionCallbackInput) => void) | undefined
121
124
  >;
122
125
  beforeStepCallbacks: React.RefObject<Set<PhysicsStepCallback>>;
123
126
  afterStepCallbacks: React.RefObject<Set<PhysicsStepCallback>>;
@@ -197,7 +200,7 @@ export function useBeforePhysicsStep(callback: PhysicsStepCallback) {
197
200
  callbackRef.current = callback;
198
201
 
199
202
  useEffect(() => {
200
- const wrapped: PhysicsStepCallback = (model, data) => callbackRef.current(model, data);
203
+ const wrapped: PhysicsStepCallback = (input) => callbackRef.current(input);
201
204
  beforeStepCallbacks.current.add(wrapped);
202
205
  return () => { beforeStepCallbacks.current.delete(wrapped); };
203
206
  }, [beforeStepCallbacks]);
@@ -209,7 +212,7 @@ export function useAfterPhysicsStep(callback: PhysicsStepCallback) {
209
212
  callbackRef.current = callback;
210
213
 
211
214
  useEffect(() => {
212
- const wrapped: PhysicsStepCallback = (model, data) => callbackRef.current(model, data);
215
+ const wrapped: PhysicsStepCallback = (input) => callbackRef.current(input);
213
216
  afterStepCallbacks.current.add(wrapped);
214
217
  return () => { afterStepCallbacks.current.delete(wrapped); };
215
218
  }, [afterStepCallbacks]);
@@ -219,10 +222,10 @@ interface MujocoSimProviderProps {
219
222
  mujoco: MujocoModule;
220
223
  config: SceneConfig;
221
224
  apiRef?: React.ForwardedRef<MujocoSimAPI>;
222
- onReady?: (api: MujocoSimAPI) => void;
225
+ onReady?: (input: ReadyCallbackInput) => void;
223
226
  onError?: (error: Error) => void;
224
- onStep?: (time: number) => void;
225
- onSelection?: (bodyId: number, name: string) => void;
227
+ onStep?: (input: StepCallbackInput) => void;
228
+ onSelection?: (input: SelectionCallbackInput) => void;
226
229
  // Declarative physics config props
227
230
  gravity?: [number, number, number];
228
231
  timestep?: number;
@@ -380,7 +383,7 @@ export function MujocoSimProvider({
380
383
  useEffect(() => {
381
384
  if (status === 'ready') {
382
385
  const api = apiRef.current;
383
- if (onReady) onReady(api);
386
+ if (onReady) onReady({ api });
384
387
  // Assign the forwarded ref
385
388
  if (externalApiRef) {
386
389
  if (typeof externalApiRef === 'function') {
@@ -409,7 +412,7 @@ export function MujocoSimProvider({
409
412
 
410
413
  // Before-step callbacks
411
414
  for (const cb of beforeStepCallbacks.current) {
412
- cb(model, data);
415
+ cb({ model, data });
413
416
  }
414
417
 
415
418
  const numSubsteps = substepsRef.current;
@@ -466,17 +469,17 @@ export function MujocoSimProvider({
466
469
  interpolationStateRef.current.valid = true;
467
470
 
468
471
  if (!stepped) {
469
- onStepRef.current?.(data.time);
472
+ onStepRef.current?.({ time: data.time, model, data });
470
473
  return;
471
474
  }
472
475
  }
473
476
 
474
477
  // After-step callbacks
475
478
  for (const cb of afterStepCallbacks.current) {
476
- cb(model, data);
479
+ cb({ model, data });
477
480
  }
478
481
 
479
- onStepRef.current?.(data.time);
482
+ onStepRef.current?.({ time: data.time, model, data });
480
483
  }, -1);
481
484
 
482
485
  function ensureInterpolationBuffers(model: MujocoModel) {
@@ -515,7 +518,7 @@ export function MujocoSimProvider({
515
518
  }
516
519
  }
517
520
 
518
- configRef.current.onReset?.(model, data);
521
+ configRef.current.onReset?.({ model, data });
519
522
  mujoco.mj_forward(model, data);
520
523
 
521
524
  // Notify composable plugins (e.g. IkController)
@@ -43,7 +43,7 @@ export type ControllerComponent<TConfig> = React.FC<{
43
43
  * const MyController = createController<{ speed: number }>(
44
44
  * { name: 'my-controller', defaultConfig: { speed: 1.0 } },
45
45
  * function MyControllerImpl({ config }) {
46
- * useBeforePhysicsStep((_model, data) => {
46
+ * useBeforePhysicsStep(({ data }) => {
47
47
  * data.ctrl[0] = config.speed;
48
48
  * });
49
49
  * return null;
@@ -100,7 +100,7 @@ export function createController<TConfig>(
100
100
  * { name: 'useMyController', defaultConfig: { gain: 1.0 } },
101
101
  * function useMyControllerImpl(config) {
102
102
  * // config is MyConfig | null — hooks must be called unconditionally
103
- * useBeforePhysicsStep((_model, data) => {
103
+ * useBeforePhysicsStep(({ data }) => {
104
104
  * if (!config) return;
105
105
  * data.ctrl[0] = config.gain * Math.sin(data.time);
106
106
  * });
@@ -29,7 +29,7 @@ export function useBodyState(name: Bodies): BodyStateResult {
29
29
  bodyIdRef.current = findBodyByName(model, name);
30
30
  }, [name, status, mjModelRef]);
31
31
 
32
- useAfterPhysicsStep((_model, data) => {
32
+ useAfterPhysicsStep(({ data }) => {
33
33
  const bid = bodyIdRef.current;
34
34
  if (bid < 0) return;
35
35
 
@@ -60,7 +60,7 @@ export function useContacts(
60
60
  bodyResolvedRef.current = true;
61
61
  }, [bodyName, status, mjModelRef]);
62
62
 
63
- useAfterPhysicsStep((model, data) => {
63
+ useAfterPhysicsStep(({ model, data }) => {
64
64
  // Resolve body id lazily once model exists, to avoid missing the first ready frame.
65
65
  if (bodyName && !bodyResolvedRef.current) {
66
66
  bodyIdRef.current = findBodyByName(model, bodyName);
@@ -30,7 +30,7 @@ export function useCtrlNoise(config: CtrlNoiseConfig = {}) {
30
30
  configRef.current = config;
31
31
  const noiseRef = useRef<Float64Array | null>(null);
32
32
 
33
- useBeforePhysicsStep((_model, data) => {
33
+ useBeforePhysicsStep(({ data }) => {
34
34
  const cfg = configRef.current;
35
35
  if (cfg.enabled === false) return;
36
36
 
@@ -50,7 +50,7 @@ export function useGamepad(config: GamepadConfig) {
50
50
  }
51
51
  }, [config.axes, config.buttons, status, mjModelRef]);
52
52
 
53
- useBeforePhysicsStep((_model, data) => {
53
+ useBeforePhysicsStep(({ data }) => {
54
54
  const cfg = configRef.current;
55
55
  if (cfg.enabled === false) return;
56
56
 
@@ -13,7 +13,7 @@ import { useBeforePhysicsStep } from '../core/MujocoSimProvider';
13
13
  * hook (and DragInteraction) compose correctly — both add to a clean slate.
14
14
  */
15
15
  export function useGravityCompensation(enabled = true): void {
16
- useBeforePhysicsStep((model, data) => {
16
+ useBeforePhysicsStep(({ model, data }) => {
17
17
  if (!enabled) return;
18
18
  for (let i = 0; i < model.nv; i++) {
19
19
  data.qfrc_applied[i] += data.qfrc_bias[i];
@@ -10,7 +10,7 @@ import { createControllerHook } from '../core/createController';
10
10
  import { useMujocoContext, useBeforePhysicsStep } from '../core/MujocoSimProvider';
11
11
  import { GenericIK } from '../core/GenericIK';
12
12
  import { createContiguousControlGroup, findSiteByName, resolveControlGroup } from '../core/SceneLoader';
13
- import type { ControlGroupInfo, IkConfig, IkContextValue, IKSolveFn, MujocoData } from '../types';
13
+ import type { ControlGroupInfo, IkConfig, IkContextValue, IKSolveFn, IkSolveInput, MujocoData } from '../types';
14
14
 
15
15
  // Preallocated temp for syncGizmoToSite
16
16
  const _syncMat4 = new THREE.Matrix4();
@@ -84,16 +84,16 @@ export const useIkController = createControllerHook<IkConfig, IkContextValue>(
84
84
 
85
85
  // IK solve function
86
86
  const ikSolveFn = useCallback(
87
- (pos: THREE.Vector3, quat: THREE.Quaternion, currentQ: number[]): number[] | null => {
87
+ ({ position, quaternion, currentQ, context }: IkSolveInput): number[] | null => {
88
88
  if (!config) return null;
89
- if (config.ikSolveFn) return config.ikSolveFn(pos, quat, currentQ);
89
+ if (config.ikSolveFn) return config.ikSolveFn({ position, quaternion, currentQ, context });
90
90
  const model = mjModelRef.current;
91
91
  const data = mjDataRef.current;
92
92
  const controlGroup = controlGroupRef.current;
93
93
  if (!model || !data || !controlGroup || siteIdRef.current === -1) return null;
94
94
  return genericIkRef.current.solve(
95
95
  model, data, siteIdRef.current, controlGroup.qposAdr,
96
- pos, quat, currentQ,
96
+ position, quaternion, currentQ,
97
97
  { damping: config.damping, maxIterations: config.maxIterations },
98
98
  );
99
99
  },
@@ -128,7 +128,7 @@ export const useIkController = createControllerHook<IkConfig, IkContextValue>(
128
128
  });
129
129
 
130
130
  // IK solve in physics loop
131
- useBeforePhysicsStep((model, data) => {
131
+ useBeforePhysicsStep(({ model, data }) => {
132
132
  if (!config || !ikEnabledRef.current) {
133
133
  ikCalculatingRef.current = false;
134
134
  return;
@@ -142,13 +142,22 @@ export const useIkController = createControllerHook<IkConfig, IkContextValue>(
142
142
 
143
143
  const currentQ = Array.from(controlGroup.readQpos(data));
144
144
  const solution = config.ikSolveFn
145
- ? config.ikSolveFn(target.position, target.quaternion, currentQ, {
146
- model,
147
- data,
148
- siteId: siteIdRef.current,
149
- controlGroup,
145
+ ? config.ikSolveFn({
146
+ position: target.position,
147
+ quaternion: target.quaternion,
148
+ currentQ,
149
+ context: {
150
+ model,
151
+ data,
152
+ siteId: siteIdRef.current,
153
+ controlGroup,
154
+ },
150
155
  })
151
- : ikSolveFnRef.current(target.position, target.quaternion, currentQ);
156
+ : ikSolveFnRef.current({
157
+ position: target.position,
158
+ quaternion: target.quaternion,
159
+ currentQ,
160
+ });
152
161
  if (solution) {
153
162
  controlGroup.writeCtrl(data, solution);
154
163
  }
@@ -192,8 +201,8 @@ export const useIkController = createControllerHook<IkConfig, IkContextValue>(
192
201
  }, [mjDataRef]);
193
202
 
194
203
  const solveIK = useCallback(
195
- (pos: THREE.Vector3, quat: THREE.Quaternion, currentQ: number[]): number[] | null => {
196
- return ikSolveFnRef.current(pos, quat, currentQ);
204
+ (input: IkSolveInput): number[] | null => {
205
+ return ikSolveFnRef.current(input);
197
206
  },
198
207
  [],
199
208
  );
@@ -59,7 +59,7 @@ export function useJointState(name: Joints): JointStateResult {
59
59
  jointIdRef.current = -1;
60
60
  }, [name, status, mjModelRef]);
61
61
 
62
- useAfterPhysicsStep((_model, data) => {
62
+ useAfterPhysicsStep(({ data }) => {
63
63
  if (jointIdRef.current < 0) return;
64
64
  const qa = qposAdrRef.current;
65
65
  const da = dofAdrRef.current;
@@ -70,7 +70,7 @@ export function useKeyboardTeleop(config: KeyboardTeleopConfig) {
70
70
  }, []);
71
71
 
72
72
  // Apply bindings each physics frame
73
- useBeforePhysicsStep((_model, data) => {
73
+ useBeforePhysicsStep(({ data }) => {
74
74
  if (!enabledRef.current) return;
75
75
  const bindings = bindingsRef.current;
76
76
  const cache = actuatorCacheRef.current;
@@ -28,7 +28,7 @@ export function usePolicy(config: PolicyConfig) {
28
28
  configRef.current = config;
29
29
  isRunningRef.current = config.enabled ?? isRunningRef.current;
30
30
 
31
- useBeforePhysicsStep((model, data) => {
31
+ useBeforePhysicsStep(({ model, data }) => {
32
32
  if (!isRunningRef.current) return;
33
33
 
34
34
  const cfg = configRef.current;
@@ -39,7 +39,7 @@ export function useSensor(name: Sensors): SensorHandle {
39
39
  }, [name, status, mjModelRef]);
40
40
 
41
41
  // Update every frame after physics step
42
- useAfterPhysicsStep((_model, data) => {
42
+ useAfterPhysicsStep(({ data }) => {
43
43
  if (sensorIdRef.current < 0) return;
44
44
  const adr = sensorAdrRef.current;
45
45
  const dim = sensorDimRef.current;
@@ -8,7 +8,7 @@
8
8
  import { useCallback, useRef } from 'react';
9
9
  import { useFrame } from '@react-three/fiber';
10
10
  import { useMujocoContext, useBeforePhysicsStep } from '../core/MujocoSimProvider';
11
- import type { PlaybackState, TrajectoryFrame, TrajectoryInput } from '../types';
11
+ import type { PlaybackState, TrajectoryStateChangeInput, TrajectoryFrame, TrajectoryInput } from '../types';
12
12
 
13
13
  export interface TrajectoryPlayerOptions {
14
14
  fps?: number;
@@ -16,7 +16,7 @@ export interface TrajectoryPlayerOptions {
16
16
  loop?: boolean;
17
17
  mode?: 'kinematic' | 'physics';
18
18
  onComplete?: () => void;
19
- onStateChange?: (state: PlaybackState) => void;
19
+ onStateChange?: (input: TrajectoryStateChangeInput) => void;
20
20
  }
21
21
 
22
22
  /** Check if input is TrajectoryFrame[] (vs number[][]) */
@@ -74,7 +74,7 @@ export function useTrajectoryPlayer(
74
74
  const setState = useCallback((next: PlaybackState) => {
75
75
  if (stateRef.current === next) return;
76
76
  stateRef.current = next;
77
- optionsRef.current.onStateChange?.(next);
77
+ optionsRef.current.onStateChange?.({ state: next });
78
78
  }, []);
79
79
 
80
80
  const play = useCallback(() => {
@@ -181,7 +181,7 @@ export function useTrajectoryPlayer(
181
181
  });
182
182
 
183
183
  // --- Physics mode: set ctrl values each physics step ---
184
- useBeforePhysicsStep((model, data) => {
184
+ useBeforePhysicsStep(({ model, data }) => {
185
185
  if (stateRef.current !== 'playing') return;
186
186
  if ((optionsRef.current.mode ?? 'kinematic') !== 'physics') return;
187
187
 
@@ -22,7 +22,7 @@ export function useTrajectoryRecorder(options: RecorderOptions = {}) {
22
22
  const framesRef = useRef<TrajectoryFrame[]>([]);
23
23
  const fields = options.fields ?? ['qpos'];
24
24
 
25
- useAfterPhysicsStep((_model, data) => {
25
+ useAfterPhysicsStep(({ data }) => {
26
26
  if (!recordingRef.current) return;
27
27
 
28
28
  const frame: TrajectoryFrame = {
package/src/index.ts CHANGED
@@ -110,8 +110,14 @@ export type {
110
110
  IkConfig,
111
111
  IkContextValue,
112
112
  IKSolveFn,
113
+ IkSolveInput,
113
114
  // Callbacks
114
115
  PhysicsStepCallback,
116
+ PhysicsStepInput,
117
+ ResetCallbackInput,
118
+ ReadyCallbackInput,
119
+ StepCallbackInput,
120
+ SelectionCallbackInput,
115
121
  // State management
116
122
  StateSnapshot,
117
123
  // Model introspection
@@ -155,6 +161,7 @@ export type {
155
161
  // Component props
156
162
  BodyProps,
157
163
  IkGizmoProps,
164
+ IkGizmoDragInput,
158
165
  DragInteractionProps,
159
166
  DebugProps,
160
167
  SceneLightsProps,
@@ -175,7 +182,10 @@ export type {
175
182
  ScenarioMaterialConfig,
176
183
  SplatEnvironmentProps,
177
184
  VisualScenarioEffectsProps,
185
+ VisualScenarioMaterialFilterInput,
178
186
  TrajectoryPlayerProps,
187
+ TrajectoryFrameCallbackInput,
188
+ TrajectoryStateChangeInput,
179
189
  ContactListenerProps,
180
190
  // API
181
191
  MujocoSimAPI,