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/README.md CHANGED
@@ -154,7 +154,7 @@ import { ScenarioLighting, VisualScenarioEffects } from "mujoco-react";
154
154
  <MujocoCanvas config={sceneConfig}>
155
155
  <VisualScenarioEffects
156
156
  scenario={scenario}
157
- materialFilter={(object) => object.name.startsWith("prop_")}
157
+ materialFilter={({ object }) => object.name.startsWith("prop_")}
158
158
  />
159
159
  <ScenarioLighting preset={scenario.lighting} />
160
160
  </MujocoCanvas>;
@@ -216,7 +216,7 @@ function Scene() {
216
216
  import { useBeforePhysicsStep } from "mujoco-react";
217
217
 
218
218
  function MyController() {
219
- useBeforePhysicsStep((_model, data) => {
219
+ useBeforePhysicsStep(({ data }) => {
220
220
  data.ctrl[0] = Math.sin(data.time);
221
221
  });
222
222
 
@@ -284,7 +284,7 @@ import type { ControlGroupInfo } from "mujoco-react";
284
284
  function HoldTcpPose() {
285
285
  const armRef = useRef<ControlGroupInfo | null>(null);
286
286
 
287
- useBeforePhysicsStep((model, data) => {
287
+ useBeforePhysicsStep(({ model, data }) => {
288
288
  armRef.current ??= resolveControlGroup(model, { siteName: RobotSites.franka.tcp });
289
289
  if (!armRef.current) return;
290
290
 
@@ -305,7 +305,7 @@ Build policy-ready observation vectors from common MuJoCo state without hard-cod
305
305
  import { buildObservation, useBeforePhysicsStep } from "mujoco-react";
306
306
 
307
307
  function PolicyDriver() {
308
- useBeforePhysicsStep((model, data) => {
308
+ useBeforePhysicsStep(({ model, data }) => {
309
309
  const obs = buildObservation(model, data, {
310
310
  qpos: true,
311
311
  qvel: true,
@@ -361,7 +361,7 @@ function useWebSocketControls(url: string) {
361
361
  }, [url]);
362
362
 
363
363
  // Apply incoming actuator controls each physics step.
364
- useBeforePhysicsStep((model, data) => {
364
+ useBeforePhysicsStep(({ model, data }) => {
365
365
  const ctrl = latestCtrlRef.current;
366
366
  if (!ctrl) return;
367
367
  for (let i = 0; i < Math.min(ctrl.length, model.nu); i++) {
@@ -370,7 +370,7 @@ function useWebSocketControls(url: string) {
370
370
  });
371
371
 
372
372
  // Send simulation feedback back after physics.
373
- useAfterPhysicsStep((model, data) => {
373
+ useAfterPhysicsStep(({ data }) => {
374
374
  const ws = wsRef.current;
375
375
  if (!ws || ws.readyState !== WebSocket.OPEN) return;
376
376
 
@@ -433,8 +433,8 @@ The built-in `useIkController()` uses Damped Least-Squares. Pass `ikSolveFn` to
433
433
  import { RobotSites } from "mujoco-react";
434
434
  import type { IKSolveFn } from "mujoco-react";
435
435
 
436
- const myIK: IKSolveFn = (pos, quat, currentQ) => {
437
- return myAnalyticalSolver(pos, currentQ); // return joint angles or null
436
+ const myIK: IKSolveFn = ({ position, currentQ }) => {
437
+ return myAnalyticalSolver(position, currentQ); // return joint angles or null
438
438
  };
439
439
 
440
440
  const ik = useIkController({ siteName: RobotSites.franka.tcp, ikSolveFn: myIK });
@@ -526,7 +526,7 @@ interface SceneConfig {
526
526
  sceneObjects?: SceneObject[]; // Objects injected into scene XML at load time
527
527
  homeJoints?: number[]; // Initial joint positions
528
528
  xmlPatches?: XmlPatch[]; // Patches applied to XML files during loading
529
- onReset?: (model, data) => void; // Called during reset after mj_resetData
529
+ onReset?: ({ model, data }) => void; // Called during reset after mj_resetData
530
530
  }
531
531
  ```
532
532
 
@@ -636,10 +636,10 @@ Thin wrapper around R3F `<Canvas>`. Accepts all R3F Canvas props plus:
636
636
  | Prop | Type | Description |
637
637
  |------|------|-------------|
638
638
  | `config` | `SceneConfig` | **Required.** Scene/robot configuration |
639
- | `onReady` | `(api: MujocoSimAPI) => void` | Fires when model is loaded |
639
+ | `onReady` | `({ api }) => void` | Fires when model is loaded |
640
640
  | `onError` | `(error: Error) => void` | Fires on scene load failure |
641
- | `onStep` | `(time: number) => void` | Called each physics step |
642
- | `onSelection` | `(bodyId: number, name: string) => void` | Called on double-click |
641
+ | `onStep` | `({ time, model, data }) => void` | Called each physics step |
642
+ | `onSelection` | `({ bodyId, name }) => void` | Called on double-click |
643
643
  | `gravity` | `[number, number, number]` | Override model gravity |
644
644
  | `timestep` | `number` | Override model.opt.timestep |
645
645
  | `substeps` | `number` | mj_step calls per frame |
@@ -664,10 +664,10 @@ Physics provider for use inside your own R3F `<Canvas>`. Same physics props as `
664
664
  | Prop | Type | Description |
665
665
  |------|------|-------------|
666
666
  | `config` | `SceneConfig` | **Required.** Scene/robot configuration |
667
- | `onReady` | `(api: MujocoSimAPI) => void` | Fires when model is loaded |
667
+ | `onReady` | `({ api }) => void` | Fires when model is loaded |
668
668
  | `onError` | `(error: Error) => void` | Fires on scene load failure |
669
- | `onStep` | `(time: number) => void` | Called each physics step |
670
- | `onSelection` | `(bodyId: number, name: string) => void` | Called on double-click |
669
+ | `onStep` | `({ time, model, data }) => void` | Called each physics step |
670
+ | `onSelection` | `({ bodyId, name }) => void` | Called on double-click |
671
671
  | `gravity` | `[number, number, number]` | Override model gravity |
672
672
  | `timestep` | `number` | Override model.opt.timestep |
673
673
  | `substeps` | `number` | mj_step calls per frame |
@@ -715,7 +715,7 @@ drei PivotControls gizmo that tracks a MuJoCo site and drives IK on drag. Requir
715
715
  | `controller` | `IkContextValue` | **required** | Controller from `useIkController()` |
716
716
  | `siteName` | `string?` | controller's site | MuJoCo site to track |
717
717
  | `scale` | `number?` | `0.18` | Gizmo handle scale |
718
- | `onDrag` | `(pos, quat) => void` | -- | Custom drag handler (disables auto-IK) |
718
+ | `onDrag` | `({ position, quaternion }) => void` | -- | Custom drag handler (disables auto-IK) |
719
719
 
720
720
  ### `<DragInteraction />`
721
721
 
@@ -826,7 +826,7 @@ if (mujoco) {
826
826
  Run logic **before** `mj_step` each frame. Write to `data.ctrl`, apply forces, drive automation.
827
827
 
828
828
  ```tsx
829
- useBeforePhysicsStep((model, data) => {
829
+ useBeforePhysicsStep(({ data }) => {
830
830
  data.ctrl[0] = Math.sin(data.time);
831
831
  });
832
832
  ```
@@ -341,7 +341,7 @@ function applyScenarioMaterials(scene, scenario, snapshots, materialFilter) {
341
341
  for (const material of normalizeMaterials(object.material)) {
342
342
  const mutable = getMutableScenarioMaterial(material);
343
343
  if (!mutable) continue;
344
- if (materialFilter && !materialFilter(object, material)) continue;
344
+ if (materialFilter && !materialFilter({ object, material })) continue;
345
345
  if (!snapshots.has(material)) {
346
346
  snapshots.set(material, {
347
347
  color: mutable.color.clone(),
@@ -396,5 +396,5 @@ function clamp01(value) {
396
396
  */
397
397
 
398
398
  export { ScenarioLighting, SplatEnvironment, VisualScenarioEffects, createPairedSplatEnvironment, createSparkSplatViewerUrl, createSplatEnvironmentUserData, getScenarioBackground, getScenarioCameraPosition, useSplatEnvironment, useVisualScenarioEffects, withSplatEnvironment };
399
- //# sourceMappingURL=chunk-SEWQULWO.js.map
400
- //# sourceMappingURL=chunk-SEWQULWO.js.map
399
+ //# sourceMappingURL=chunk-33CV6HSV.js.map
400
+ //# sourceMappingURL=chunk-33CV6HSV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/VisualScenario.tsx"],"names":[],"mappings":"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","file":"chunk-33CV6HSV.js","sourcesContent":["/**\n * @license\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { useThree } from '@react-three/fiber';\nimport type { ThreeElements } from '@react-three/fiber';\nimport type { ReactNode } from 'react';\nimport { useEffect, useMemo } from 'react';\nimport * as THREE from 'three';\nimport type {\n PairedSplatEnvironmentConfig,\n ScenarioMaterialConfig,\n SceneConfig,\n SplatCollisionProxyConfig,\n SplatEnvironmentMetadata,\n SplatEnvironmentMetadataInput,\n SplatFormat,\n SplatRendererKind,\n SplatSceneInput,\n ScenarioLightingPreset,\n ScenarioLightingProps,\n SplatEnvironmentProps,\n VisualScenarioConfig,\n VisualScenarioEffectsProps,\n} from '../types';\n\nconst DEFAULT_BACKGROUND = '#181a1f';\n\nexport function ScenarioLighting({\n preset = 'studio',\n castShadow = true,\n intensity = 1,\n}: ScenarioLightingProps) {\n if (preset === 'warehouse') {\n return (\n <>\n <ambientLight intensity={0.18 * intensity} />\n <directionalLight\n position={[3.5, -2, 5]}\n intensity={2.2 * intensity}\n castShadow={castShadow}\n />\n <directionalLight position={[-2, 1.5, 2.5]} intensity={0.25 * intensity} />\n </>\n );\n }\n\n if (preset === 'low-light') {\n return (\n <>\n <ambientLight intensity={0.08 * intensity} />\n <directionalLight\n position={[2, -2, 3]}\n intensity={0.75 * intensity}\n castShadow={castShadow}\n />\n <pointLight position={[-0.5, -0.8, 1.3]} intensity={0.6 * intensity} />\n </>\n );\n }\n\n if (preset === 'splat') {\n return (\n <>\n <ambientLight intensity={0.42 * intensity} />\n <directionalLight\n position={[1.8, -2.4, 3.5]}\n intensity={1.2 * intensity}\n castShadow={castShadow}\n />\n <pointLight position={[0.4, 0.2, 1.4]} intensity={0.35 * intensity} />\n </>\n );\n }\n\n return (\n <>\n <ambientLight intensity={0.35 * intensity} />\n <directionalLight\n position={[2.5, -3, 4]}\n intensity={1.6 * intensity}\n castShadow={castShadow}\n />\n </>\n );\n}\n\nexport function getScenarioBackground(\n preset: ScenarioLightingPreset | undefined,\n fallback = DEFAULT_BACKGROUND\n) {\n if (preset === 'warehouse') return '#20242b';\n if (preset === 'low-light') return '#0f1115';\n if (preset === 'splat') return '#1b1f24';\n return fallback;\n}\n\nexport function getScenarioCameraPosition(\n basePosition: readonly [number, number, number],\n scenario?: Pick<VisualScenarioConfig, 'camera'>\n): [number, number, number] {\n const [x, y, z] = basePosition;\n const jitter = scenario?.camera?.jitter ?? 0;\n\n return [\n Number((x + jitter * 0.6).toFixed(3)),\n Number((y - jitter * 0.4).toFixed(3)),\n Number((z + jitter * 0.25).toFixed(3)),\n ];\n}\n\nexport function VisualScenarioEffects(props: VisualScenarioEffectsProps) {\n useVisualScenarioEffects(props);\n return null;\n}\n\nexport function useVisualScenarioEffects({\n scenario,\n enabled = true,\n applyBackground = true,\n applyFog = true,\n applyRenderer = true,\n applyMaterials = true,\n background,\n fogNear,\n fogFar,\n materialFilter,\n}: VisualScenarioEffectsProps) {\n const { gl, scene, invalidate } = useThree();\n\n useEffect(() => {\n if (!enabled || !scenario) {\n return undefined;\n }\n\n const previousExposure = gl.toneMappingExposure;\n const previousBackground = scene.background;\n const previousFog = scene.fog;\n const materialSnapshots = new Map<\n THREE.Material,\n {\n color?: THREE.Color;\n roughness?: number;\n metalness?: number;\n }\n >();\n\n if (applyRenderer) {\n gl.toneMappingExposure = scenario.camera?.exposure ?? 1;\n }\n\n if (applyBackground) {\n scene.background = new THREE.Color(\n background ?? getScenarioBackground(scenario.lighting)\n );\n }\n\n if (applyFog) {\n scene.fog = createScenarioFog(scenario, background, fogNear, fogFar);\n }\n\n if (applyMaterials && scenario.materials) {\n applyScenarioMaterials(scene, scenario, materialSnapshots, materialFilter);\n }\n\n invalidate();\n\n return () => {\n gl.toneMappingExposure = previousExposure;\n scene.background = previousBackground;\n scene.fog = previousFog;\n\n for (const [material, snapshot] of materialSnapshots) {\n const mutable = getMutableScenarioMaterial(material);\n if (!mutable) continue;\n if (snapshot.color) mutable.color.copy(snapshot.color);\n if (typeof snapshot.roughness === 'number') {\n mutable.roughness = snapshot.roughness;\n }\n if (typeof snapshot.metalness === 'number') {\n mutable.metalness = snapshot.metalness;\n }\n mutable.needsUpdate = true;\n }\n\n invalidate();\n };\n }, [\n applyBackground,\n applyFog,\n applyMaterials,\n applyRenderer,\n background,\n enabled,\n fogFar,\n fogNear,\n gl,\n invalidate,\n materialFilter,\n scenario,\n scene,\n ]);\n}\n\n/**\n * Renderer-agnostic Gaussian splat environment boundary.\n *\n * This component intentionally does not import a specific 3DGS renderer. Pass a\n * Spark/GaussianSplats3D object as `children` once the app chooses a renderer,\n * and pass MuJoCo/MJCF collision proxy visuals via `collisionProxy`.\n */\nexport function SplatEnvironment({\n environment,\n scenario,\n renderer,\n src,\n format,\n collisionProxy,\n collisionProxyMetadata,\n children,\n showPlaceholder = true,\n ...groupProps\n}: SplatEnvironmentProps) {\n const metadata = useSplatEnvironment({\n environment,\n scenario,\n renderer,\n src,\n format,\n collisionProxy: collisionProxyMetadata,\n });\n const existingUserData =\n typeof groupProps.userData === 'object' && groupProps.userData !== null\n ? groupProps.userData\n : {};\n\n return (\n <group\n {...groupProps}\n userData={{\n ...existingUserData,\n ...metadata.userData,\n }}\n >\n {children}\n {children || !showPlaceholder ? null : <SplatPlaceholder />}\n {collisionProxy}\n </group>\n );\n}\n\nexport function useSplatEnvironment({\n environment,\n scenario,\n renderer,\n src,\n format,\n collisionProxy,\n}: SplatEnvironmentMetadataInput): SplatEnvironmentMetadata {\n const scenarioEnvironment = useMemo(\n () =>\n environment ??\n (scenario\n ? createPairedSplatEnvironment(scenario, { renderer })\n : undefined),\n [environment, renderer, scenario]\n );\n const resolvedSrc = src ?? scenarioEnvironment?.splat.src ?? scenario?.splat?.src;\n const resolvedFormat =\n format ??\n scenarioEnvironment?.splat.format ??\n scenario?.splat?.format ??\n 'spz';\n const resolvedCollisionProxy =\n collisionProxy ??\n scenarioEnvironment?.collisionProxy ??\n scenario?.splat?.collisionProxy ??\n undefined;\n\n return useMemo(\n () => ({\n src: resolvedSrc,\n format: resolvedFormat,\n collisionProxy: resolvedCollisionProxy,\n userData: createSplatEnvironmentUserData({\n environment: scenarioEnvironment,\n src: resolvedSrc,\n format: resolvedFormat,\n collisionProxy: resolvedCollisionProxy,\n }),\n }),\n [scenarioEnvironment, resolvedSrc, resolvedFormat, resolvedCollisionProxy]\n );\n}\n\n/**\n * Convert a generic visual scenario splat block into a paired visual/physics\n * environment config. Returns undefined until both the splat asset and MJCF\n * collision proxy are present.\n */\nexport function createPairedSplatEnvironment(\n scenario: Pick<VisualScenarioConfig, 'id' | 'label' | 'environment' | 'splat'>,\n options: {\n id?: string;\n label?: string;\n description?: string;\n renderer?: SplatRendererKind;\n } = {}\n): PairedSplatEnvironmentConfig | undefined {\n const splat = scenario.splat;\n const collisionProxy = splat?.collisionProxy;\n\n if (!splat?.enabled || !splat.src || !collisionProxy?.xmlPath) {\n return undefined;\n }\n\n return {\n id: options.id ?? scenario.id ?? 'splat-environment',\n label: options.label ?? scenario.label ?? 'Gaussian splat environment',\n description:\n options.description ??\n (scenario.environment\n ? `Visual ${scenario.environment} splat paired with MJCF collision proxy.`\n : undefined),\n splat: {\n src: splat.src,\n format: splat.format ?? 'spz',\n renderer: options.renderer,\n },\n collisionProxy: {\n ...collisionProxy,\n xmlPath: collisionProxy.xmlPath,\n },\n };\n}\n\nfunction isPairedSplatEnvironment(input: SplatSceneInput): input is PairedSplatEnvironmentConfig {\n return !!input && 'collisionProxy' in input && 'splat' in input;\n}\n\nfunction sceneRelativePath(sceneConfig: SceneConfig, path: string): string {\n const src = sceneConfig.src;\n if (!src) return path;\n\n const base = src.endsWith('/') ? src : src + '/';\n if (path.startsWith(base)) return path.slice(base.length);\n return path;\n}\n\nfunction uniquePaths(paths: readonly string[]): string[] {\n const seen = new Set<string>();\n const result: string[] = [];\n for (const path of paths) {\n if (seen.has(path)) continue;\n seen.add(path);\n result.push(path);\n }\n return result;\n}\n\n/**\n * Compose a MuJoCo scene config with a paired splat collision proxy.\n *\n * This keeps the common hybrid setup declarative:\n * robot XML remains `sceneFile`, the `.spz` remains a visual-only layer, and\n * the paired MJCF collision proxy is added to `environmentFiles`.\n */\nexport function withSplatEnvironment(\n sceneConfig: SceneConfig,\n input: SplatSceneInput,\n options: { renderer?: SplatRendererKind } = {}\n): SceneConfig {\n const environment = isPairedSplatEnvironment(input)\n ? input\n : input\n ? createPairedSplatEnvironment(input, options)\n : undefined;\n const xmlPath = environment?.collisionProxy.xmlPath;\n if (!xmlPath) return sceneConfig;\n\n return {\n ...sceneConfig,\n environmentFiles: uniquePaths([\n ...(sceneConfig.environmentFiles ?? []),\n sceneRelativePath(sceneConfig, xmlPath),\n ]),\n };\n}\n\nexport function createSplatEnvironmentUserData({\n environment,\n src,\n format = 'spz',\n collisionProxy,\n}: {\n environment?: PairedSplatEnvironmentConfig;\n src?: string;\n format?: SplatFormat;\n collisionProxy?: SplatCollisionProxyConfig;\n}) {\n return {\n role: 'splat-environment',\n environmentId: environment?.id,\n environmentLabel: environment?.label,\n splatSrc: src,\n splatFormat: format,\n splatRenderer: environment?.splat.renderer,\n collisionProxyStatus: collisionProxy?.status ?? 'missing',\n collisionProxyXmlPath: collisionProxy?.xmlPath,\n collisionProxyPrimitives: collisionProxy?.primitives ?? [],\n };\n}\n\nexport function createSparkSplatViewerUrl({\n viewerUrl,\n splatSrc,\n}: {\n viewerUrl: string;\n splatSrc: string;\n}) {\n const url = new URL(viewerUrl, 'http://mujoco-react.local');\n url.searchParams.set('splat', splatSrc);\n return viewerUrl.startsWith('http') ? url.toString() : `${url.pathname}${url.search}`;\n}\n\nfunction SplatPlaceholder() {\n return (\n <group>\n <mesh position={[0, 0, 1.2]}>\n <boxGeometry args={[2.4, 2.4, 2.4]} />\n <meshBasicMaterial\n color=\"#8b8b8b\"\n transparent\n opacity={0.06}\n wireframe\n side={THREE.DoubleSide}\n />\n </mesh>\n </group>\n );\n}\n\nfunction createScenarioFog(\n scenario: VisualScenarioConfig,\n background: THREE.ColorRepresentation | undefined,\n fogNear: number | undefined,\n fogFar: number | undefined\n) {\n if (scenario.lighting === 'low-light') {\n return new THREE.Fog(\n background ?? getScenarioBackground(scenario.lighting),\n fogNear ?? 2.5,\n fogFar ?? 9\n );\n }\n\n if (scenario.lighting === 'warehouse') {\n return new THREE.Fog(\n background ?? getScenarioBackground(scenario.lighting),\n fogNear ?? 5,\n fogFar ?? 16\n );\n }\n\n return null;\n}\n\nfunction applyScenarioMaterials(\n scene: THREE.Scene,\n scenario: VisualScenarioConfig,\n snapshots: Map<\n THREE.Material,\n {\n color?: THREE.Color;\n roughness?: number;\n metalness?: number;\n }\n >,\n materialFilter: VisualScenarioEffectsProps['materialFilter']\n) {\n const materials = scenario.materials;\n if (!materials) return;\n\n scene.traverse((object) => {\n if (!(object instanceof THREE.Mesh)) {\n return;\n }\n\n for (const material of normalizeMaterials(object.material)) {\n const mutable = getMutableScenarioMaterial(material);\n if (!mutable) continue;\n if (materialFilter && !materialFilter({ object, material })) continue;\n\n if (!snapshots.has(material)) {\n snapshots.set(material, {\n color: mutable.color.clone(),\n roughness: mutable.roughness,\n metalness: mutable.metalness,\n });\n }\n\n applyScenarioMaterial(mutable, object, scenario, materials);\n }\n });\n}\n\nfunction applyScenarioMaterial(\n material: THREE.MeshStandardMaterial | THREE.MeshPhysicalMaterial,\n object: THREE.Object3D,\n scenario: VisualScenarioConfig,\n materials: ScenarioMaterialConfig\n) {\n const seed = scenario.seed ?? 0;\n const objectKey = `${scenario.id ?? 'scenario'}:${object.name}:${material.name}:${seed}`;\n const variation = hashToUnitInterval(objectKey);\n\n if (materials.randomizeObjectColors) {\n material.color.setHSL(variation, 0.38, 0.42);\n }\n\n if (materials.randomizeTableMaterial) {\n material.roughness = clamp01(\n materials.roughness ?? 0.35 + variation * 0.45\n );\n material.metalness = clamp01(\n materials.metalness ?? variation * 0.12\n );\n }\n\n material.needsUpdate = true;\n}\n\nfunction normalizeMaterials(\n material: THREE.Material | THREE.Material[]\n): THREE.Material[] {\n return Array.isArray(material) ? material : [material];\n}\n\nfunction getMutableScenarioMaterial(\n material: THREE.Material\n): THREE.MeshStandardMaterial | THREE.MeshPhysicalMaterial | null {\n if (\n material instanceof THREE.MeshStandardMaterial ||\n material instanceof THREE.MeshPhysicalMaterial\n ) {\n return material;\n }\n\n return null;\n}\n\nfunction hashToUnitInterval(value: string) {\n let hash = 2166136261;\n for (let index = 0; index < value.length; index += 1) {\n hash ^= value.charCodeAt(index);\n hash = Math.imul(hash, 16777619);\n }\n return (hash >>> 0) / 4294967295;\n}\n\nfunction clamp01(value: number) {\n return Math.max(0, Math.min(1, value));\n}\n\nexport type SplatCollisionProxy = ReactNode | ThreeElements['group'];\n"]}
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { M as MujocoContextValue, a as MujocoCanvasProps, b as MujocoSimAPI, S as SceneConfig, c as MujocoModule, P as PhysicsStepCallback, d as MujocoModel, e as MujocoData, C as ControlGroupInfo, A as ActuatedJointInfo, f as ControlGroupSelector, O as ObservationConfig, g as ObservationResult, I as IkConfig, h as IkContextValue, B as BodyProps, i as IkGizmoProps, D as DragInteractionProps, j as SceneLightsProps, k as ScenarioLightingProps, l as SplatEnvironmentProps, V as VisualScenarioEffectsProps, m as VisualScenarioConfig, n as SplatRendererKind, o as PairedSplatEnvironmentConfig, p as SplatFormat, q as SplatCollisionProxyConfig, r as SplatCollisionPrimitive, s as ScenarioLightingPreset, t as SplatEnvironmentMetadataInput, u as SplatEnvironmentMetadata, v as SplatSceneInput, w as DebugProps, G as GeomInfo, x as ContactListenerProps, T as TrajectoryPlayerProps, y as ActuatorInfo, z as Sites, E as SitePositionResult, F as Sensors, H as SensorHandle, J as SensorInfo, K as Joints, L as JointStateResult, N as Bodies, Q as BodyStateResult, R as Actuators, U as CtrlHandle, W as ContactInfo, X as KeyboardTeleopConfig, Y as PolicyConfig, Z as PolicyVector, _ as ObservationHandle, $ as TrajectoryInput, a0 as PlaybackState, a1 as TrajectoryFrame } from './types-BmneHLBM.js';
3
- export { a2 as BodyInfo, a3 as ControlJointInfo, a4 as Geoms, a5 as IKSolveFn, a6 as JointInfo, a7 as KeyBinding, a8 as Keyframes, a9 as ModelOptions, aa as MujocoContact, ab as MujocoContactArray, ac as ObservationLayoutItem, ad as ObservationOutput, ae as PhysicsConfig, af as PolicyActionInput, ag as PolicyInferenceInput, ah as PolicyObservationInput, ai as RayHit, aj as Register, ak as RegisteredRobotMap, al as ResourceSelector, am as RobotActuators, an as RobotBodies, ao as RobotGeoms, ap as RobotJoints, aq as RobotKeyframes, ar as RobotResource, as as RobotResources, at as RobotSensors, au as RobotSites, av as Robots, aw as ScenarioCameraConfig, ax as ScenarioMaterialConfig, ay as SceneMarker, az as SceneObject, aA as SensorResult, aB as SiteInfo, aC as SplatAssetConfig, aD as SplatScenarioConfig, aE as StateSnapshot, aF as TrajectoryData, aG as XmlPatch, aH as getContact, aI as registerRobotResources } from './types-BmneHLBM.js';
2
+ import { M as MujocoContextValue, a as MujocoCanvasProps, b as MujocoSimAPI, S as SceneConfig, R as ReadyCallbackInput, c as StepCallbackInput, d as SelectionCallbackInput, e as MujocoModule, P as PhysicsStepCallback, f as MujocoModel, g as MujocoData, C as ControlGroupInfo, A as ActuatedJointInfo, h as ControlGroupSelector, O as ObservationConfig, i as ObservationResult, I as IkConfig, j as IkContextValue, B as BodyProps, k as IkGizmoProps, D as DragInteractionProps, l as SceneLightsProps, m as ScenarioLightingProps, n as SplatEnvironmentProps, V as VisualScenarioEffectsProps, o as VisualScenarioConfig, p as SplatRendererKind, q as PairedSplatEnvironmentConfig, r as SplatFormat, s as SplatCollisionProxyConfig, t as SplatCollisionPrimitive, u as ScenarioLightingPreset, v as SplatEnvironmentMetadataInput, w as SplatEnvironmentMetadata, x as SplatSceneInput, y as DebugProps, G as GeomInfo, z as ContactListenerProps, T as TrajectoryPlayerProps, E as ActuatorInfo, F as Sites, H as SitePositionResult, J as Sensors, K as SensorHandle, L as SensorInfo, N as Joints, Q as JointStateResult, U as Bodies, W as BodyStateResult, X as Actuators, Y as CtrlHandle, Z as ContactInfo, _ as KeyboardTeleopConfig, $ as PolicyConfig, a0 as PolicyVector, a1 as ObservationHandle, a2 as TrajectoryInput, a3 as TrajectoryStateChangeInput, a4 as PlaybackState, a5 as TrajectoryFrame } from './types-izZlUweI.js';
3
+ export { a6 as BodyInfo, a7 as ControlJointInfo, a8 as Geoms, a9 as IKSolveFn, aa as IkGizmoDragInput, ab as IkSolveInput, ac as JointInfo, ad as KeyBinding, ae as Keyframes, af as ModelOptions, ag as MujocoContact, ah as MujocoContactArray, ai as ObservationLayoutItem, aj as ObservationOutput, ak as PhysicsConfig, al as PhysicsStepInput, am as PolicyActionInput, an as PolicyInferenceInput, ao as PolicyObservationInput, ap as RayHit, aq as Register, ar as RegisteredRobotMap, as as ResetCallbackInput, at as ResourceSelector, au as RobotActuators, av as RobotBodies, aw as RobotGeoms, ax as RobotJoints, ay as RobotKeyframes, az as RobotResource, aA as RobotResources, aB as RobotSensors, aC as RobotSites, aD as Robots, aE as ScenarioCameraConfig, aF as ScenarioMaterialConfig, aG as SceneMarker, aH as SceneObject, aI as SensorResult, aJ as SiteInfo, aK as SplatAssetConfig, aL as SplatScenarioConfig, aM as StateSnapshot, aN as TrajectoryData, aO as TrajectoryFrameCallbackInput, aP as VisualScenarioMaterialFilterInput, aQ as XmlPatch, aR as getContact, aS as registerRobotResources } from './types-izZlUweI.js';
4
4
  import * as React$1 from 'react';
5
5
  import React__default from 'react';
6
6
  import { ThreeElements } from '@react-three/fiber';
@@ -56,13 +56,13 @@ interface MujocoPhysicsProps {
56
56
  /** Scene/robot configuration. */
57
57
  config: SceneConfig;
58
58
  /** Fires when model is loaded and API is ready. */
59
- onReady?: (api: MujocoSimAPI) => void;
59
+ onReady?: (input: ReadyCallbackInput) => void;
60
60
  /** Fires on scene load failure. */
61
61
  onError?: (error: Error) => void;
62
62
  /** Called each physics step. */
63
- onStep?: (time: number) => void;
63
+ onStep?: (input: StepCallbackInput) => void;
64
64
  /** Called on body double-click selection. */
65
- onSelection?: (bodyId: number, name: string) => void;
65
+ onSelection?: (input: SelectionCallbackInput) => void;
66
66
  /** Override model gravity. */
67
67
  gravity?: [number, number, number];
68
68
  /** Override model.opt.timestep. */
@@ -133,10 +133,10 @@ interface MujocoSimProviderProps {
133
133
  mujoco: MujocoModule;
134
134
  config: SceneConfig;
135
135
  apiRef?: React.ForwardedRef<MujocoSimAPI>;
136
- onReady?: (api: MujocoSimAPI) => void;
136
+ onReady?: (input: ReadyCallbackInput) => void;
137
137
  onError?: (error: Error) => void;
138
- onStep?: (time: number) => void;
139
- onSelection?: (bodyId: number, name: string) => void;
138
+ onStep?: (input: StepCallbackInput) => void;
139
+ onSelection?: (input: SelectionCallbackInput) => void;
140
140
  gravity?: [number, number, number];
141
141
  timestep?: number;
142
142
  substeps?: number;
@@ -243,7 +243,7 @@ type ControllerComponent<TConfig> = React.FC<{
243
243
  * const MyController = createController<{ speed: number }>(
244
244
  * { name: 'my-controller', defaultConfig: { speed: 1.0 } },
245
245
  * function MyControllerImpl({ config }) {
246
- * useBeforePhysicsStep((_model, data) => {
246
+ * useBeforePhysicsStep(({ data }) => {
247
247
  * data.ctrl[0] = config.speed;
248
248
  * });
249
249
  * return null;
@@ -270,7 +270,7 @@ declare function createController<TConfig>(options: ControllerOptions<TConfig>,
270
270
  * { name: 'useMyController', defaultConfig: { gain: 1.0 } },
271
271
  * function useMyControllerImpl(config) {
272
272
  * // config is MyConfig | null — hooks must be called unconditionally
273
- * useBeforePhysicsStep((_model, data) => {
273
+ * useBeforePhysicsStep(({ data }) => {
274
274
  * if (!config) return;
275
275
  * data.ctrl[0] = config.gain * Math.sin(data.time);
276
276
  * });
@@ -312,7 +312,7 @@ declare function Body({ name, type, size, position, rgba, mass, freejoint, frict
312
312
  * - `controller` — IkContextValue from `useIkController()`.
313
313
  * - `siteName` — MuJoCo site to track. Defaults to the controller's configured site.
314
314
  * - `scale` — Gizmo handle scale. Default: 0.18.
315
- * - `onDrag` — Custom drag callback `(pos, quat) => void`.
315
+ * - `onDrag` — Custom drag callback `({ position, quaternion }) => void`.
316
316
  * When omitted, dragging enables IK and writes to the IK target.
317
317
  * When provided, the consumer handles what happens during drag.
318
318
  */
@@ -655,7 +655,7 @@ interface TrajectoryPlayerOptions {
655
655
  loop?: boolean;
656
656
  mode?: 'kinematic' | 'physics';
657
657
  onComplete?: () => void;
658
- onStateChange?: (state: PlaybackState) => void;
658
+ onStateChange?: (input: TrajectoryStateChangeInput) => void;
659
659
  }
660
660
  /**
661
661
  * Play back a trajectory, overriding simulation state.
@@ -896,4 +896,4 @@ interface CameraAnimationAPI {
896
896
  */
897
897
  declare function useCameraAnimation(): CameraAnimationAPI;
898
898
 
899
- export { ActuatedJointInfo, ActuatorInfo, Actuators, Bodies, Body, BodyProps, BodyStateResult, type CameraAnimationAPI, ContactInfo, ContactListener, ContactListenerProps, ContactMarkers, ControlGroupInfo, ControlGroupSelector, type ControllerComponent, type ControllerOptions, CtrlHandle, Debug, DebugProps, DragInteraction, DragInteractionProps, FlexRenderer, type FrameCaptureAPI, type FrameCaptureBlobResult, type FrameCaptureOptions, type FrameCaptureResult, type FrameCaptureStatus, type FrameCaptureTarget, type FrameCaptureTargetRef, GeomInfo, IkConfig, IkContextValue, IkGizmo, IkGizmoProps, InstancedGeomRenderer, JointStateResult, Joints, KeyboardTeleopConfig, MujocoCanvas, MujocoCanvasProps, MujocoContextValue, MujocoData, type MujocoLoader, type MujocoLoaderOptions, MujocoModel, MujocoModule, MujocoPhysics, type MujocoPhysicsProps, MujocoProvider, type MujocoProviderProps, MujocoSimAPI, MujocoSimProvider, type MujocoWasmVariant, ObservationConfig, ObservationHandle, ObservationResult, PairedSplatEnvironmentConfig, PhysicsStepCallback, PlaybackState, PolicyConfig, PolicyVector, ScenarioLighting, ScenarioLightingPreset, ScenarioLightingProps, SceneConfig, SceneLights, SceneLightsProps, SensorHandle, SensorInfo, Sensors, SitePositionResult, Sites, SplatCollisionPrimitive, SplatCollisionProxyConfig, SplatEnvironment, SplatEnvironmentMetadata, SplatEnvironmentMetadataInput, SplatEnvironmentProps, SplatFormat, SplatRendererKind, SplatSceneInput, TendonRenderer, TrajectoryFrame, TrajectoryInput, TrajectoryPlayer, TrajectoryPlayerProps, VisualScenarioConfig, VisualScenarioEffects, VisualScenarioEffectsProps, buildObservation, captureFrame, captureFrameBlob, createContiguousControlGroup, createController, createControllerHook, createPairedSplatEnvironment, createSparkSplatViewerUrl, createSplatEnvironmentUserData, findActuatorByName, findBodyByName, findGeomByName, findJointByName, findKeyframeByName, findSensorByName, findSiteByName, findTendonByName, getActuatedJoints, getControlMap, getName, getScenarioBackground, getScenarioCameraPosition, loadScene, resolveControlGroup, useActuators, useAfterPhysicsStep, useBeforePhysicsStep, useBodyMeshes, useBodyState, useCameraAnimation, useContactEvents, useContacts, useCtrl, useCtrlNoise, useFrameCapture, useGamepad, useGravityCompensation, useIkController, useJointState, useKeyboardTeleop, useMujoco, useMujocoWasm, useObservation, usePolicy, useSceneLights, useSelectionHighlight, useSensor, useSensors, useSitePosition, useSplatEnvironment, useTrajectoryPlayer, useTrajectoryRecorder, useVideoRecorder, useVisualScenarioEffects, withSplatEnvironment };
899
+ export { ActuatedJointInfo, ActuatorInfo, Actuators, Bodies, Body, BodyProps, BodyStateResult, type CameraAnimationAPI, ContactInfo, ContactListener, ContactListenerProps, ContactMarkers, ControlGroupInfo, ControlGroupSelector, type ControllerComponent, type ControllerOptions, CtrlHandle, Debug, DebugProps, DragInteraction, DragInteractionProps, FlexRenderer, type FrameCaptureAPI, type FrameCaptureBlobResult, type FrameCaptureOptions, type FrameCaptureResult, type FrameCaptureStatus, type FrameCaptureTarget, type FrameCaptureTargetRef, GeomInfo, IkConfig, IkContextValue, IkGizmo, IkGizmoProps, InstancedGeomRenderer, JointStateResult, Joints, KeyboardTeleopConfig, MujocoCanvas, MujocoCanvasProps, MujocoContextValue, MujocoData, type MujocoLoader, type MujocoLoaderOptions, MujocoModel, MujocoModule, MujocoPhysics, type MujocoPhysicsProps, MujocoProvider, type MujocoProviderProps, MujocoSimAPI, MujocoSimProvider, type MujocoWasmVariant, ObservationConfig, ObservationHandle, ObservationResult, PairedSplatEnvironmentConfig, PhysicsStepCallback, PlaybackState, PolicyConfig, PolicyVector, ReadyCallbackInput, ScenarioLighting, ScenarioLightingPreset, ScenarioLightingProps, SceneConfig, SceneLights, SceneLightsProps, SelectionCallbackInput, SensorHandle, SensorInfo, Sensors, SitePositionResult, Sites, SplatCollisionPrimitive, SplatCollisionProxyConfig, SplatEnvironment, SplatEnvironmentMetadata, SplatEnvironmentMetadataInput, SplatEnvironmentProps, SplatFormat, SplatRendererKind, SplatSceneInput, StepCallbackInput, TendonRenderer, TrajectoryFrame, TrajectoryInput, TrajectoryPlayer, TrajectoryPlayerProps, TrajectoryStateChangeInput, VisualScenarioConfig, VisualScenarioEffects, VisualScenarioEffectsProps, buildObservation, captureFrame, captureFrameBlob, createContiguousControlGroup, createController, createControllerHook, createPairedSplatEnvironment, createSparkSplatViewerUrl, createSplatEnvironmentUserData, findActuatorByName, findBodyByName, findGeomByName, findJointByName, findKeyframeByName, findSensorByName, findSiteByName, findTendonByName, getActuatedJoints, getControlMap, getName, getScenarioBackground, getScenarioCameraPosition, loadScene, resolveControlGroup, useActuators, useAfterPhysicsStep, useBeforePhysicsStep, useBodyMeshes, useBodyState, useCameraAnimation, useContactEvents, useContacts, useCtrl, useCtrlNoise, useFrameCapture, useGamepad, useGravityCompensation, useIkController, useJointState, useKeyboardTeleop, useMujoco, useMujocoWasm, useObservation, usePolicy, useSceneLights, useSelectionHighlight, useSensor, useSensors, useSitePosition, useSplatEnvironment, useTrajectoryPlayer, useTrajectoryRecorder, useVideoRecorder, useVisualScenarioEffects, withSplatEnvironment };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- export { ScenarioLighting, SplatEnvironment, VisualScenarioEffects, createPairedSplatEnvironment, createSparkSplatViewerUrl, createSplatEnvironmentUserData, getScenarioBackground, getScenarioCameraPosition, useSplatEnvironment, useVisualScenarioEffects, withSplatEnvironment } from './chunk-SEWQULWO.js';
1
+ export { ScenarioLighting, SplatEnvironment, VisualScenarioEffects, createPairedSplatEnvironment, createSparkSplatViewerUrl, createSplatEnvironmentUserData, getScenarioBackground, getScenarioCameraPosition, useSplatEnvironment, useVisualScenarioEffects, withSplatEnvironment } from './chunk-33CV6HSV.js';
2
2
  import loadMujoco from '@mujoco/mujoco';
3
3
  import defaultMujocoWasmUrl from '@mujoco/mujoco/mujoco.wasm?url';
4
4
  import { createContext, forwardRef, useEffect, useContext, useState, useRef, useCallback, useMemo, useLayoutEffect } from 'react';
@@ -1285,7 +1285,7 @@ function SceneRenderer(props) {
1285
1285
  const model = mjModelRef.current;
1286
1286
  if (model && bodyID < model.nbody && onSelectionRef.current) {
1287
1287
  const name = getName(model, model.name_bodyadr[bodyID]);
1288
- onSelectionRef.current(bodyID, name);
1288
+ onSelectionRef.current({ bodyId: bodyID, name });
1289
1289
  }
1290
1290
  }
1291
1291
  }
@@ -1420,7 +1420,7 @@ function useBeforePhysicsStep(callback) {
1420
1420
  const callbackRef = useRef(callback);
1421
1421
  callbackRef.current = callback;
1422
1422
  useEffect(() => {
1423
- const wrapped = (model, data) => callbackRef.current(model, data);
1423
+ const wrapped = (input) => callbackRef.current(input);
1424
1424
  beforeStepCallbacks.current.add(wrapped);
1425
1425
  return () => {
1426
1426
  beforeStepCallbacks.current.delete(wrapped);
@@ -1432,7 +1432,7 @@ function useAfterPhysicsStep(callback) {
1432
1432
  const callbackRef = useRef(callback);
1433
1433
  callbackRef.current = callback;
1434
1434
  useEffect(() => {
1435
- const wrapped = (model, data) => callbackRef.current(model, data);
1435
+ const wrapped = (input) => callbackRef.current(input);
1436
1436
  afterStepCallbacks.current.add(wrapped);
1437
1437
  return () => {
1438
1438
  afterStepCallbacks.current.delete(wrapped);
@@ -1576,7 +1576,7 @@ function MujocoSimProvider({
1576
1576
  useEffect(() => {
1577
1577
  if (status === "ready") {
1578
1578
  const api2 = apiRef.current;
1579
- if (onReady) onReady(api2);
1579
+ if (onReady) onReady({ api: api2 });
1580
1580
  if (externalApiRef) {
1581
1581
  if (typeof externalApiRef === "function") {
1582
1582
  externalApiRef(api2);
@@ -1596,7 +1596,7 @@ function MujocoSimProvider({
1596
1596
  data.qfrc_applied[i] = 0;
1597
1597
  }
1598
1598
  for (const cb of beforeStepCallbacks.current) {
1599
- cb(model, data);
1599
+ cb({ model, data });
1600
1600
  }
1601
1601
  const numSubsteps = substepsRef.current;
1602
1602
  if (!interpolateRef.current) {
@@ -1647,14 +1647,14 @@ function MujocoSimProvider({
1647
1647
  interpolationStateRef.current.alpha = Math.min(Math.max(physicsAccumulatorRef.current / stepDt, 0), 1);
1648
1648
  interpolationStateRef.current.valid = true;
1649
1649
  if (!stepped) {
1650
- onStepRef.current?.(data.time);
1650
+ onStepRef.current?.({ time: data.time, model, data });
1651
1651
  return;
1652
1652
  }
1653
1653
  }
1654
1654
  for (const cb of afterStepCallbacks.current) {
1655
- cb(model, data);
1655
+ cb({ model, data });
1656
1656
  }
1657
- onStepRef.current?.(data.time);
1657
+ onStepRef.current?.({ time: data.time, model, data });
1658
1658
  }, -1);
1659
1659
  function ensureInterpolationBuffers(model) {
1660
1660
  const state = interpolationStateRef.current;
@@ -1685,7 +1685,7 @@ function MujocoSimProvider({
1685
1685
  }
1686
1686
  }
1687
1687
  }
1688
- configRef.current.onReset?.(model, data);
1688
+ configRef.current.onReset?.({ model, data });
1689
1689
  mujoco.mj_forward(model, data);
1690
1690
  for (const cb of resetCallbacks.current) {
1691
1691
  cb();
@@ -2841,9 +2841,9 @@ var useIkController = createControllerHook(
2841
2841
  }
2842
2842
  }, [config?.siteName, config?.numJoints, config?.joints, config?.actuators, status, mjModelRef, mjDataRef, config]);
2843
2843
  const ikSolveFn = useCallback(
2844
- (pos, quat, currentQ) => {
2844
+ ({ position, quaternion, currentQ, context }) => {
2845
2845
  if (!config) return null;
2846
- if (config.ikSolveFn) return config.ikSolveFn(pos, quat, currentQ);
2846
+ if (config.ikSolveFn) return config.ikSolveFn({ position, quaternion, currentQ, context });
2847
2847
  const model = mjModelRef.current;
2848
2848
  const data = mjDataRef.current;
2849
2849
  const controlGroup = controlGroupRef.current;
@@ -2853,8 +2853,8 @@ var useIkController = createControllerHook(
2853
2853
  data,
2854
2854
  siteIdRef.current,
2855
2855
  controlGroup.qposAdr,
2856
- pos,
2857
- quat,
2856
+ position,
2857
+ quaternion,
2858
2858
  currentQ,
2859
2859
  { damping: config.damping, maxIterations: config.maxIterations }
2860
2860
  );
@@ -2883,7 +2883,7 @@ var useIkController = createControllerHook(
2883
2883
  target.quaternion.slerpQuaternions(ga.startRot, ga.endRot, ease);
2884
2884
  if (t >= 1) ga.active = false;
2885
2885
  });
2886
- useBeforePhysicsStep((model, data) => {
2886
+ useBeforePhysicsStep(({ model, data }) => {
2887
2887
  if (!config || !ikEnabledRef.current) {
2888
2888
  ikCalculatingRef.current = false;
2889
2889
  return;
@@ -2894,12 +2894,21 @@ var useIkController = createControllerHook(
2894
2894
  const controlGroup = controlGroupRef.current;
2895
2895
  if (!controlGroup) return;
2896
2896
  const currentQ = Array.from(controlGroup.readQpos(data));
2897
- const solution = config.ikSolveFn ? config.ikSolveFn(target.position, target.quaternion, currentQ, {
2898
- model,
2899
- data,
2900
- siteId: siteIdRef.current,
2901
- controlGroup
2902
- }) : ikSolveFnRef.current(target.position, target.quaternion, currentQ);
2897
+ const solution = config.ikSolveFn ? config.ikSolveFn({
2898
+ position: target.position,
2899
+ quaternion: target.quaternion,
2900
+ currentQ,
2901
+ context: {
2902
+ model,
2903
+ data,
2904
+ siteId: siteIdRef.current,
2905
+ controlGroup
2906
+ }
2907
+ }) : ikSolveFnRef.current({
2908
+ position: target.position,
2909
+ quaternion: target.quaternion,
2910
+ currentQ
2911
+ });
2903
2912
  if (solution) {
2904
2913
  controlGroup.writeCtrl(data, solution);
2905
2914
  }
@@ -2938,8 +2947,8 @@ var useIkController = createControllerHook(
2938
2947
  if (data && target) syncGizmoToSite(data, siteIdRef.current, target);
2939
2948
  }, [mjDataRef]);
2940
2949
  const solveIK = useCallback(
2941
- (pos, quat, currentQ) => {
2942
- return ikSolveFnRef.current(pos, quat, currentQ);
2950
+ (input) => {
2951
+ return ikSolveFnRef.current(input);
2943
2952
  },
2944
2953
  []
2945
2954
  );
@@ -3153,7 +3162,7 @@ function IkGizmo({ controller, siteName, scale = 0.18, onDrag }) {
3153
3162
  onDrag: (_l, _dl, world) => {
3154
3163
  world.decompose(_pos, _quat, _scale);
3155
3164
  if (onDrag) {
3156
- onDrag(_pos.clone(), _quat.clone());
3165
+ onDrag({ position: _pos.clone(), quaternion: _quat.clone() });
3157
3166
  } else {
3158
3167
  const target = ikTargetRef.current;
3159
3168
  if (target) {
@@ -3327,7 +3336,7 @@ function DragInteraction({
3327
3336
  window.removeEventListener("pointercancel", onPointerUp);
3328
3337
  };
3329
3338
  }, [gl, camera, scene, controls, mjDataRef]);
3330
- useBeforePhysicsStep((model, data) => {
3339
+ useBeforePhysicsStep(({ model, data }) => {
3331
3340
  if (!draggingRef.current || bodyIdRef.current <= 0) return;
3332
3341
  const bid = bodyIdRef.current;
3333
3342
  const mujoco = mujocoRef.current;
@@ -4118,7 +4127,7 @@ function useContacts(bodyName, callback) {
4118
4127
  bodyIdRef.current = findBodyByName(model, bodyName);
4119
4128
  bodyResolvedRef.current = true;
4120
4129
  }, [bodyName, status, mjModelRef]);
4121
- useAfterPhysicsStep((model, data) => {
4130
+ useAfterPhysicsStep(({ model, data }) => {
4122
4131
  if (bodyName && !bodyResolvedRef.current) {
4123
4132
  bodyIdRef.current = findBodyByName(model, bodyName);
4124
4133
  bodyResolvedRef.current = true;
@@ -4224,7 +4233,7 @@ function useTrajectoryPlayer(trajectory, options = {}) {
4224
4233
  const setState = useCallback((next) => {
4225
4234
  if (stateRef.current === next) return;
4226
4235
  stateRef.current = next;
4227
- optionsRef.current.onStateChange?.(next);
4236
+ optionsRef.current.onStateChange?.({ state: next });
4228
4237
  }, []);
4229
4238
  const play = useCallback(() => {
4230
4239
  const traj = trajectoryRef.current;
@@ -4306,7 +4315,7 @@ function useTrajectoryPlayer(trajectory, options = {}) {
4306
4315
  }
4307
4316
  }
4308
4317
  });
4309
- useBeforePhysicsStep((model, data) => {
4318
+ useBeforePhysicsStep(({ model, data }) => {
4310
4319
  if (stateRef.current !== "playing") return;
4311
4320
  if ((optionsRef.current.mode ?? "kinematic") !== "physics") return;
4312
4321
  const traj = trajectoryRef.current;
@@ -4391,7 +4400,10 @@ function TrajectoryPlayer({
4391
4400
  const currentFrame = player.frame;
4392
4401
  if (currentFrame !== lastReportedFrameRef.current && player.playing) {
4393
4402
  lastReportedFrameRef.current = currentFrame;
4394
- onFrameRef.current(currentFrame);
4403
+ onFrameRef.current({
4404
+ frameIndex: currentFrame,
4405
+ frame: trajectory[currentFrame]
4406
+ });
4395
4407
  }
4396
4408
  });
4397
4409
  return null;
@@ -4465,7 +4477,7 @@ function useSitePosition(siteName) {
4465
4477
 
4466
4478
  // src/hooks/useGravityCompensation.ts
4467
4479
  function useGravityCompensation(enabled = true) {
4468
- useBeforePhysicsStep((model, data) => {
4480
+ useBeforePhysicsStep(({ model, data }) => {
4469
4481
  if (!enabled) return;
4470
4482
  for (let i = 0; i < model.nv; i++) {
4471
4483
  data.qfrc_applied[i] += data.qfrc_bias[i];
@@ -4492,7 +4504,7 @@ function useSensor(name) {
4492
4504
  }
4493
4505
  sensorIdRef.current = -1;
4494
4506
  }, [name, status, mjModelRef]);
4495
- useAfterPhysicsStep((_model, data) => {
4507
+ useAfterPhysicsStep(({ data }) => {
4496
4508
  if (sensorIdRef.current < 0) return;
4497
4509
  const adr = sensorAdrRef.current;
4498
4510
  const dim = sensorDimRef.current;
@@ -4589,7 +4601,7 @@ function useJointState(name) {
4589
4601
  }
4590
4602
  jointIdRef.current = -1;
4591
4603
  }, [name, status, mjModelRef]);
4592
- useAfterPhysicsStep((_model, data) => {
4604
+ useAfterPhysicsStep(({ data }) => {
4593
4605
  if (jointIdRef.current < 0) return;
4594
4606
  const qa = qposAdrRef.current;
4595
4607
  const da = dofAdrRef.current;
@@ -4619,7 +4631,7 @@ function useBodyState(name) {
4619
4631
  if (!model || status !== "ready") return;
4620
4632
  bodyIdRef.current = findBodyByName(model, name);
4621
4633
  }, [name, status, mjModelRef]);
4622
- useAfterPhysicsStep((_model, data) => {
4634
+ useAfterPhysicsStep(({ data }) => {
4623
4635
  const bid = bodyIdRef.current;
4624
4636
  if (bid < 0) return;
4625
4637
  const i3 = bid * 3;
@@ -4715,7 +4727,7 @@ function useKeyboardTeleop(config) {
4715
4727
  window.removeEventListener("keyup", onKeyUp);
4716
4728
  };
4717
4729
  }, []);
4718
- useBeforePhysicsStep((_model, data) => {
4730
+ useBeforePhysicsStep(({ data }) => {
4719
4731
  if (!enabledRef.current) return;
4720
4732
  const bindings = bindingsRef.current;
4721
4733
  const cache = actuatorCacheRef.current;
@@ -4743,7 +4755,7 @@ function usePolicy(config) {
4743
4755
  const configRef = useRef(config);
4744
4756
  configRef.current = config;
4745
4757
  isRunningRef.current = config.enabled ?? isRunningRef.current;
4746
- useBeforePhysicsStep((model, data) => {
4758
+ useBeforePhysicsStep(({ model, data }) => {
4747
4759
  if (!isRunningRef.current) return;
4748
4760
  const cfg = configRef.current;
4749
4761
  model.opt?.timestep ?? 2e-3;
@@ -4800,7 +4812,7 @@ function useTrajectoryRecorder(options = {}) {
4800
4812
  const recordingRef = useRef(false);
4801
4813
  const framesRef = useRef([]);
4802
4814
  const fields = options.fields ?? ["qpos"];
4803
- useAfterPhysicsStep((_model, data) => {
4815
+ useAfterPhysicsStep(({ data }) => {
4804
4816
  if (!recordingRef.current) return;
4805
4817
  const frame = {
4806
4818
  time: data.time,
@@ -4889,7 +4901,7 @@ function useGamepad(config) {
4889
4901
  buttonCacheRef.current.set(Number(idx), findActuatorByName(model, name));
4890
4902
  }
4891
4903
  }, [config.axes, config.buttons, status, mjModelRef]);
4892
- useBeforePhysicsStep((_model, data) => {
4904
+ useBeforePhysicsStep(({ data }) => {
4893
4905
  const cfg = configRef.current;
4894
4906
  if (cfg.enabled === false) return;
4895
4907
  const gamepads = navigator.getGamepads?.();
@@ -5080,7 +5092,7 @@ function useCtrlNoise(config = {}) {
5080
5092
  const configRef = useRef(config);
5081
5093
  configRef.current = config;
5082
5094
  const noiseRef = useRef(null);
5083
- useBeforePhysicsStep((_model, data) => {
5095
+ useBeforePhysicsStep(({ data }) => {
5084
5096
  const cfg = configRef.current;
5085
5097
  if (cfg.enabled === false) return;
5086
5098
  const rate = cfg.rate ?? 0.01;