mujoco-react 8.5.0 → 8.6.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
@@ -306,10 +306,30 @@ const gripperIk = useIkController({
306
306
 
307
307
  ## Type-Safe Resource Names
308
308
 
309
- Use TypeScript module augmentation to get autocomplete and type checking for actuator, sensor, body, joint, site, geom, and keyframe names:
309
+ Use the Vite plugin to generate TanStack-style declaration merging for actuator, sensor, body, joint, site, geom, and keyframe names:
310
310
 
311
311
  ```ts
312
- // e.g. in src/mujoco-register.d.ts
312
+ // vite.config.ts
313
+ import { defineConfig } from "vite";
314
+ import { mujocoReact } from "mujoco-react/vite";
315
+
316
+ export default defineConfig({
317
+ plugins: [
318
+ mujocoReact({
319
+ models: "models/panda/scene.xml",
320
+ }),
321
+ ],
322
+ });
323
+ ```
324
+
325
+ The plugin writes `src/mujoco-register.gen.d.ts` during dev and build. Commit that generated file:
326
+
327
+ ```ts
328
+ // src/mujoco-register.gen.d.ts
329
+ // Auto-generated by mujoco-react. Do not edit.
330
+
331
+ import "mujoco-react";
332
+
313
333
  declare module "mujoco-react" {
314
334
  interface Register {
315
335
  actuators: "joint1" | "joint2" | "joint3" | "gripper";
@@ -319,7 +339,13 @@ declare module "mujoco-react" {
319
339
  }
320
340
  ```
321
341
 
322
- Once declared, hooks like `useCtrl`, `useSensor`, `useBodyState`, and API methods like `setCtrl`, `applyForce`, `getSensorData` will only accept the declared names. When no `Register` augmentation is provided, all names fall back to `string`.
342
+ Once generated, hooks like `useCtrl`, `useSensor`, `useBodyState`, and API methods like `setCtrl`, `applyForce`, `getSensorData` will only accept the declared names. When no `Register` augmentation is present, names fall back to `string`.
343
+
344
+ Non-Vite projects can generate the same file with:
345
+
346
+ ```bash
347
+ npx mujoco-react codegen models/panda/scene.xml
348
+ ```
323
349
 
324
350
  ## Loading Models
325
351
 
@@ -344,7 +370,8 @@ const custom: SceneConfig = {
344
370
  ```ts
345
371
  interface SceneConfig {
346
372
  src: string; // Base URL for model files
347
- sceneFile: string; // Entry XML file, e.g. "scene.xml"
373
+ sceneFile: string; // Entry XML/URDF file, e.g. "scene.xml"
374
+ files?: File[]; // Local files for browser upload workflows
348
375
  sceneObjects?: SceneObject[]; // Objects injected into scene XML at load time
349
376
  homeJoints?: number[]; // Initial joint positions
350
377
  xmlPatches?: XmlPatch[]; // Patches applied to XML files during loading
@@ -352,6 +379,30 @@ interface SceneConfig {
352
379
  }
353
380
  ```
354
381
 
382
+ ### Local Files and URDF
383
+
384
+ Load browser-selected MJCF or URDF files directly. Folder uploads preserve `webkitRelativePath`, and flat uploads fall back to matching referenced mesh/texture assets by basename:
385
+
386
+ ```tsx
387
+ function ModelUpload() {
388
+ const sim = useMujoco();
389
+
390
+ return (
391
+ <input
392
+ type="file"
393
+ multiple
394
+ // @ts-expect-error Chromium/WebKit folder uploads
395
+ webkitdirectory=""
396
+ onChange={(event) => {
397
+ if (sim.isReady && event.currentTarget.files) {
398
+ sim.api.loadFromFiles(event.currentTarget.files);
399
+ }
400
+ }}
401
+ />
402
+ );
403
+ }
404
+ ```
405
+
355
406
  ### Adding Objects to Any Scene
356
407
 
357
408
  ```tsx
@@ -552,6 +603,10 @@ Renders tendons as tube geometry from wrap paths.
552
603
 
553
604
  Renders deformable flex bodies from `flexvert_xpos`.
554
605
 
606
+ ### `<InstancedGeomRenderer />`
607
+
608
+ Opt-in renderer for repeated compatible geoms. It batches matching geom shape/material signatures into Three.js `InstancedMesh` objects and syncs instance transforms from `data.geom_xpos` / `data.geom_xmat`.
609
+
555
610
  ### `<ContactListener />`
556
611
 
557
612
  Component wrapper for contact events:
@@ -861,6 +916,9 @@ The full API object available via `ref` or `useMujoco()` (when `isReady`):
861
916
  |--------|-------------|
862
917
  | `setGravity(g)` | Set gravity vector |
863
918
  | `setTimestep(dt)` | Set timestep |
919
+ | `addBody(body)` | Add a `SceneObject` and recompile the scene |
920
+ | `removeBody(name)` | Remove a generated `SceneObject` and recompile |
921
+ | `recompile(patches?)` | Recompile current scene, optionally appending XML patches |
864
922
  | `setBodyMass(name, mass)` | Domain randomization |
865
923
  | `setGeomFriction(name, friction)` | Domain randomization |
866
924
  | `setGeomSize(name, size)` | Domain randomization |
@@ -878,6 +936,7 @@ The full API object available via `ref` or `useMujoco()` (when `isReady`):
878
936
  | Method | Description |
879
937
  |--------|-------------|
880
938
  | `loadScene(newConfig)` | Runtime model swap |
939
+ | `loadFromFiles(files, options?)` | Load MJCF/URDF from a browser `FileList` |
881
940
 
882
941
  ## Guides
883
942
 
@@ -916,13 +975,7 @@ Features planned but not yet implemented:
916
975
 
917
976
  | Feature | Priority | Description |
918
977
  |---------|----------|-------------|
919
- | **User-uploaded model loading** | P2 | `loadFromFiles(FileList)` -- detect meshdir, write to VFS |
920
- | **URDF loading** | P2 | Load URDF models via MuJoCo's built-in URDF compiler |
921
- | **XML mutation / recompile** | P1 | `addBody()`, `removeBody()`, `recompile()` for runtime XML editing |
922
- | **Physics interpolation** | P1 | Smooth rendering between physics ticks for very high refresh displays |
923
- | **Instanced geom rendering** | P2 | `<InstancedGeomRenderer />` for particle/granular sims |
924
978
  | **Web Worker physics** | P2 | Run `mj_step` off main thread via SharedArrayBuffer |
925
- | **Register codegen** | P2 | CLI to auto-generate `Register` type augmentation from MJCF XML |
926
979
 
927
980
  ### WASM Limitations (@mujoco/mujoco)
928
981
 
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ process.argv.splice(2, 0, 'codegen');
3
+ await import('./mujoco-react.mjs');
@@ -0,0 +1,86 @@
1
+ #!/usr/bin/env node
2
+ import { watch } from 'node:fs';
3
+ import path from 'node:path';
4
+ import { generateMujocoRegister } from '../dist/vite.js';
5
+
6
+ const usage = `
7
+ Usage:
8
+ mujoco-react codegen <scene.xml> [...more.xml] [--out src/mujoco-register.gen.d.ts] [--watch]
9
+
10
+ Vite users usually do not need this command. Prefer:
11
+
12
+ import { mujocoReact } from "mujoco-react/vite";
13
+
14
+ export default defineConfig({
15
+ plugins: [mujocoReact({ models: "models/panda/scene.xml" })],
16
+ });
17
+ `;
18
+
19
+ const args = process.argv.slice(2);
20
+ const command = args[0];
21
+
22
+ if (!command || command === '--help' || command === '-h') {
23
+ console.log(usage.trim());
24
+ process.exit(command ? 0 : 1);
25
+ }
26
+
27
+ if (command !== 'codegen') {
28
+ console.error(`Unknown command: ${command}`);
29
+ console.error(usage.trim());
30
+ process.exit(1);
31
+ }
32
+
33
+ const commandArgs = args.slice(1);
34
+ const out = valueAfter(commandArgs, '--out') ?? 'src/mujoco-register.gen.d.ts';
35
+ const moduleName = valueAfter(commandArgs, '--module') ?? 'mujoco-react';
36
+ const shouldWatch = commandArgs.includes('--watch');
37
+ const models = commandArgs.filter((arg, index) => {
38
+ if (arg.startsWith('--')) return false;
39
+ const previous = commandArgs[index - 1];
40
+ return previous !== '--out' && previous !== '--module';
41
+ });
42
+
43
+ if (!models.length) {
44
+ console.error(usage.trim());
45
+ process.exit(1);
46
+ }
47
+
48
+ let watchedFiles = [];
49
+ await generate();
50
+
51
+ if (shouldWatch) {
52
+ console.log('[mujoco-react] watching model files...');
53
+ let timer;
54
+ const refreshWatchers = () => {
55
+ for (const file of watchedFiles) {
56
+ watch(file, { persistent: true }, () => {
57
+ clearTimeout(timer);
58
+ timer = setTimeout(() => {
59
+ generate().catch((error) => {
60
+ console.error('[mujoco-react] register generation failed');
61
+ console.error(error);
62
+ });
63
+ }, 50);
64
+ });
65
+ }
66
+ };
67
+ refreshWatchers();
68
+ }
69
+
70
+ async function generate() {
71
+ const result = await generateMujocoRegister({
72
+ models,
73
+ out,
74
+ moduleName,
75
+ root: process.cwd(),
76
+ });
77
+ watchedFiles = result.files;
78
+ const total = Object.values(result.counts).reduce((sum, count) => sum + count, 0);
79
+ console.log(`[mujoco-react] generated ${path.relative(process.cwd(), result.out)} (${total} names)`);
80
+ }
81
+
82
+ function valueAfter(values, flag) {
83
+ const index = values.indexOf(flag);
84
+ if (index === -1) return undefined;
85
+ return values[index + 1];
86
+ }
package/dist/index.d.ts CHANGED
@@ -275,11 +275,22 @@ interface XmlPatch {
275
275
  injectAfter?: string;
276
276
  replace?: [string, string];
277
277
  }
278
+ type LocalMujocoFile = File;
279
+ interface LoadFromFilesOptions {
280
+ /** Entry MJCF/URDF file. Inferred from scene.xml, model.xml, robot.xml, or the first XML/URDF file when omitted. */
281
+ sceneFile?: string;
282
+ homeJoints?: number[];
283
+ xmlPatches?: XmlPatch[];
284
+ sceneObjects?: SceneObject[];
285
+ onReset?: (model: MujocoModel, data: MujocoData) => void;
286
+ }
278
287
  interface SceneConfig {
279
288
  /** Base URL for fetching model files. The loader fetches `src + sceneFile` and follows dependencies. */
280
289
  src: string;
281
- /** Entry MJCF XML file name, e.g. 'scene.xml'. */
290
+ /** Entry MJCF XML or URDF file name, e.g. 'scene.xml' or 'robot.urdf'. */
282
291
  sceneFile: string;
292
+ /** Browser-selected files for local MJCF/URDF loading. Preserves webkitRelativePath when available. */
293
+ files?: readonly LocalMujocoFile[];
283
294
  sceneObjects?: SceneObject[];
284
295
  homeJoints?: number[];
285
296
  xmlPatches?: XmlPatch[];
@@ -610,6 +621,10 @@ interface MujocoSimAPI {
610
621
  getKeyframeNames(): string[];
611
622
  getKeyframeCount(): number;
612
623
  loadScene(newConfig: SceneConfig): Promise<void>;
624
+ loadFromFiles(files: FileList | readonly LocalMujocoFile[], options?: LoadFromFilesOptions): Promise<void>;
625
+ addBody(body: SceneObject): Promise<void>;
626
+ removeBody(name: Bodies): Promise<void>;
627
+ recompile(patches?: XmlPatch[]): Promise<void>;
613
628
  getCanvasSnapshot(width?: number, height?: number, mimeType?: string): string;
614
629
  project2DTo3D(x: number, y: number, cameraPos: THREE.Vector3, lookAt: THREE.Vector3): {
615
630
  point: THREE.Vector3;
@@ -633,6 +648,7 @@ type MujocoCanvasProps = Omit<CanvasProps, 'onError'> & {
633
648
  substeps?: number;
634
649
  paused?: boolean;
635
650
  speed?: number;
651
+ interpolate?: boolean;
636
652
  };
637
653
  interface SitePositionResult {
638
654
  position: React__default.RefObject<THREE.Vector3>;
@@ -744,6 +760,8 @@ interface MujocoPhysicsProps {
744
760
  paused?: boolean;
745
761
  /** Simulation speed multiplier. */
746
762
  speed?: number;
763
+ /** Interpolate rendered body poses between fixed physics steps. */
764
+ interpolate?: boolean;
747
765
  children: React.ReactNode;
748
766
  }
749
767
  /**
@@ -811,9 +829,10 @@ interface MujocoSimProviderProps {
811
829
  substeps?: number;
812
830
  paused?: boolean;
813
831
  speed?: number;
832
+ interpolate?: boolean;
814
833
  children: React.ReactNode;
815
834
  }
816
- declare function MujocoSimProvider({ mujoco, config, apiRef: externalApiRef, onReady, onError, onStep, onSelection, gravity, timestep, substeps, paused, speed, children, }: MujocoSimProviderProps): react_jsx_runtime.JSX.Element;
835
+ declare function MujocoSimProvider({ mujoco, config, apiRef: externalApiRef, onReady, onError, onStep, onSelection, gravity, timestep, substeps, paused, speed, interpolate, children, }: MujocoSimProviderProps): react_jsx_runtime.JSX.Element;
817
836
 
818
837
  /**
819
838
  * @license
@@ -1043,6 +1062,20 @@ declare function TendonRenderer(props: Omit<ThreeElements['group'], 'ref'>): rea
1043
1062
  */
1044
1063
  declare function FlexRenderer(props: Omit<ThreeElements['group'], 'ref'>): react_jsx_runtime.JSX.Element | null;
1045
1064
 
1065
+ interface InstancedGeomRendererProps extends Omit<ThreeElements['group'], 'ref'> {
1066
+ /** Only render geoms from this MuJoCo geom group. */
1067
+ geomGroup?: number;
1068
+ /** Predicate for selecting geoms. */
1069
+ filter?: (geom: GeomInfo) => boolean;
1070
+ /** Optional material override for every instanced batch. */
1071
+ material?: THREE.Material;
1072
+ /** Hide or show the instanced batches. */
1073
+ visible?: boolean;
1074
+ castShadow?: boolean;
1075
+ receiveShadow?: boolean;
1076
+ }
1077
+ declare function InstancedGeomRenderer({ geomGroup, filter, material, visible, castShadow, receiveShadow, ...groupProps }?: InstancedGeomRendererProps): react_jsx_runtime.JSX.Element | null;
1078
+
1046
1079
  /**
1047
1080
  * @license
1048
1081
  * SPDX-License-Identifier: Apache-2.0
@@ -1450,4 +1483,4 @@ interface CameraAnimationAPI {
1450
1483
  */
1451
1484
  declare function useCameraAnimation(): CameraAnimationAPI;
1452
1485
 
1453
- export { type ActuatedJointInfo, type ActuatorInfo, type Actuators, type Bodies, Body, type BodyInfo, type BodyProps, type BodyStateResult, type CameraAnimationAPI, type ContactInfo, ContactListener, type ContactListenerProps, ContactMarkers, type ControlGroupInfo, type ControlGroupSelector, type ControlJointInfo, type ControllerComponent, type ControllerOptions, type CtrlHandle, Debug, type DebugProps, DragInteraction, type DragInteractionProps, FlexRenderer, type GeomInfo, type Geoms, type IKSolveFn, type IkConfig, type IkContextValue, IkGizmo, type IkGizmoProps, type JointInfo, type JointStateResult, type Joints, type KeyBinding, type KeyboardTeleopConfig, type Keyframes, type ModelOptions, MujocoCanvas, type MujocoCanvasProps, type MujocoContact, type MujocoContactArray, type MujocoContextValue, type MujocoData, type MujocoLoader, type MujocoLoaderOptions, type MujocoModel, type MujocoModule, MujocoPhysics, type MujocoPhysicsProps, MujocoProvider, type MujocoProviderProps, type MujocoSimAPI, MujocoSimProvider, type MujocoWasmVariant, type ObservationConfig, type ObservationHandle, type ObservationLayoutItem, type ObservationOutput, type ObservationResult, type PhysicsConfig, type PhysicsStepCallback, type PlaybackState, type PolicyConfig, type RayHit, type Register, type ResourceSelector, type SceneConfig, SceneLights, type SceneLightsProps, type SceneMarker, type SceneObject, type SensorHandle, type SensorInfo, type SensorResult, type Sensors, type SiteInfo, type SitePositionResult, type Sites, type StateSnapshot, TendonRenderer, type TrajectoryData, type TrajectoryFrame, type TrajectoryInput, TrajectoryPlayer, type TrajectoryPlayerProps, type XmlPatch, buildObservation, createContiguousControlGroup, createController, createControllerHook, findActuatorByName, findBodyByName, findGeomByName, findJointByName, findKeyframeByName, findSensorByName, findSiteByName, findTendonByName, getActuatedJoints, getContact, getControlMap, getName, loadScene, resolveControlGroup, useActuators, useAfterPhysicsStep, useBeforePhysicsStep, useBodyMeshes, useBodyState, useCameraAnimation, useContactEvents, useContacts, useCtrl, useCtrlNoise, useGamepad, useGravityCompensation, useIkController, useJointState, useKeyboardTeleop, useMujoco, useMujocoWasm, useObservation, usePolicy, useSceneLights, useSelectionHighlight, useSensor, useSensors, useSitePosition, useTrajectoryPlayer, useTrajectoryRecorder, useVideoRecorder };
1486
+ export { type ActuatedJointInfo, type ActuatorInfo, type Actuators, type Bodies, Body, type BodyInfo, type BodyProps, type BodyStateResult, type CameraAnimationAPI, type ContactInfo, ContactListener, type ContactListenerProps, ContactMarkers, type ControlGroupInfo, type ControlGroupSelector, type ControlJointInfo, type ControllerComponent, type ControllerOptions, type CtrlHandle, Debug, type DebugProps, DragInteraction, type DragInteractionProps, FlexRenderer, type GeomInfo, type Geoms, type IKSolveFn, type IkConfig, type IkContextValue, IkGizmo, type IkGizmoProps, InstancedGeomRenderer, type JointInfo, type JointStateResult, type Joints, type KeyBinding, type KeyboardTeleopConfig, type Keyframes, type ModelOptions, MujocoCanvas, type MujocoCanvasProps, type MujocoContact, type MujocoContactArray, type MujocoContextValue, type MujocoData, type MujocoLoader, type MujocoLoaderOptions, type MujocoModel, type MujocoModule, MujocoPhysics, type MujocoPhysicsProps, MujocoProvider, type MujocoProviderProps, type MujocoSimAPI, MujocoSimProvider, type MujocoWasmVariant, type ObservationConfig, type ObservationHandle, type ObservationLayoutItem, type ObservationOutput, type ObservationResult, type PhysicsConfig, type PhysicsStepCallback, type PlaybackState, type PolicyConfig, type RayHit, type Register, type ResourceSelector, type SceneConfig, SceneLights, type SceneLightsProps, type SceneMarker, type SceneObject, type SensorHandle, type SensorInfo, type SensorResult, type Sensors, type SiteInfo, type SitePositionResult, type Sites, type StateSnapshot, TendonRenderer, type TrajectoryData, type TrajectoryFrame, type TrajectoryInput, TrajectoryPlayer, type TrajectoryPlayerProps, type XmlPatch, buildObservation, createContiguousControlGroup, createController, createControllerHook, findActuatorByName, findBodyByName, findGeomByName, findJointByName, findKeyframeByName, findSensorByName, findSiteByName, findTendonByName, getActuatedJoints, getContact, getControlMap, getName, loadScene, resolveControlGroup, useActuators, useAfterPhysicsStep, useBeforePhysicsStep, useBodyMeshes, useBodyState, useCameraAnimation, useContactEvents, useContacts, useCtrl, useCtrlNoise, useGamepad, useGravityCompensation, useIkController, useJointState, useKeyboardTeleop, useMujoco, useMujocoWasm, useObservation, usePolicy, useSceneLights, useSelectionHighlight, useSensor, useSensors, useSitePosition, useTrajectoryPlayer, useTrajectoryRecorder, useVideoRecorder };