Merge wasm readme for package
This commit is contained in:
@@ -42,13 +42,9 @@ jobs:
|
||||
|
||||
- name: Prepare README for dist
|
||||
run: |
|
||||
if [ -f wasm/README.npm.md ]; then
|
||||
mkdir -p wasm/dist
|
||||
cp wasm/README.npm.md wasm/dist/README.md
|
||||
echo "Copied wasm/README.npm.md -> wasm/dist/README.md"
|
||||
else
|
||||
echo "No wasm/README.npm.md found; skipping README copy"
|
||||
fi
|
||||
mkdir -p wasm/dist
|
||||
cp wasm/README.md wasm/dist/README.md
|
||||
|
||||
|
||||
- name: Sync version with tag (dist)
|
||||
run: |
|
||||
|
||||
+194
@@ -119,6 +119,192 @@ joint using `data.jnt('myjoint')`.
|
||||
|
||||
For more details and examples of how to use named access, please refer to the [named access tests](tests/bindings_test.ts#L1876-L2378) and [documentation](https://mujoco.readthedocs.io/en/stable/python.html#named-access).
|
||||
|
||||
## Usage Guide
|
||||
When interacting with MuJoCo objects through the WASM bindings, it's important to understand how data is accessed. Properties on objects like `MjModel` and `MjData` can expose data in two ways: by copy or by reference.
|
||||
|
||||
### Install
|
||||
```sh
|
||||
npm install mujoco
|
||||
```
|
||||
|
||||
```ts
|
||||
import loadMujoco from 'mujoco';
|
||||
|
||||
const mujoco = await loadMujoco();
|
||||
|
||||
const model = mujoco.MjModel.fromXMLString(`
|
||||
<mujoco>
|
||||
<worldbody>
|
||||
<geom type="sphere" size="0.1"/>
|
||||
</worldbody>
|
||||
</mujoco>
|
||||
`);
|
||||
|
||||
const data = new mujoco.MjData(model);
|
||||
mujoco.mj_step(model, data);
|
||||
```
|
||||
|
||||
### Copy vs. Reference
|
||||
|
||||
#### 1. By Copy (Value-based access)
|
||||
|
||||
Some properties return a copy of the data at the time of access. This is common for complex data structures that need to be marshalled from C++ to JavaScript.
|
||||
|
||||
A key example is `MjData.contact`. When you access `data.contact`, you get a new array containing the contacts at that specific moment in the simulation. If you step the simulation forward, this array will not be updated. You must access `data.contact` again to get the new contact information.
|
||||
|
||||
Example:
|
||||
```typescript
|
||||
// Gets contacts at the current time.
|
||||
const contacts = data.contact;
|
||||
|
||||
// Step the simulation
|
||||
mujoco.mj_step(model, data);
|
||||
|
||||
// `contacts` is now stale. To get the new contacts, you must access the property again:
|
||||
const newContacts = data.contact;
|
||||
```
|
||||
|
||||
#### 2. By Reference (View-based access)
|
||||
Many properties, especially large numerical arrays, return a live view directly into the WebAssembly memory. This is highly efficient as it avoids copying large amounts of data.
|
||||
|
||||
A key example is `MjData.qpos` (joint positions). When you get a reference to this array, it points directly to the simulation's state data. Any changes in the simulation (e.g., after a call to `mj_step`) will be immediately reflected in this array.
|
||||
|
||||
```typescript
|
||||
// `qpos` is a live view into the simulation state.
|
||||
const qpos = data.qpos;
|
||||
|
||||
console.log(qpos[0]); // Print initial position
|
||||
|
||||
// Step the simulation
|
||||
mujoco.mj_step(model, data);
|
||||
|
||||
// `qpos` is automatically updated.
|
||||
console.log(qpos[0]); // Print new position
|
||||
```
|
||||
|
||||
### Data Layout: Row-Major Matrices
|
||||
When a function from the MuJoCo C API returns a matrix (or needs a matrix as input), these are represented in the JavaScript bindings as flat, one-dimensional `TypedArray`'s. The elements are stored in row-major order.
|
||||
|
||||
For example, a 3x10 matrix will be returned as a flat array with 30 elements. The first 10 elements represent the first row, the next 10 represent the second row, and so on.
|
||||
|
||||
Example: Accessing an element at `(row, col)`
|
||||
```typescript
|
||||
// A 3x10 matrix stored as a flat array.
|
||||
const matrix: Float64Array = ...;
|
||||
const nRows = 3;
|
||||
const nCols = 10;
|
||||
|
||||
// To access the element at row `i` and column `j`:
|
||||
const element = matrix[i * nCols + j];
|
||||
```
|
||||
|
||||
### Working with Out Parameters
|
||||
Many functions in the MuJoCo C API use "out parameters" to return data. This means instead of returning a value, they write the result into one of the arguments passed to them by reference (using pointers). In our JavaScript bindings, you'll need to handle these cases specifically.
|
||||
|
||||
There are two main scenarios you'll encounter:
|
||||
|
||||
#### 1. Array-like Out Parameters
|
||||
When a function expects a pointer to a primitive type (like `mjtNum*` or `int*`) to write an array of values, you need to pre-allocate memory for the result on the JavaScript side. We provide helper classes for this: `mujoco.Uint8Buffer`, `mujoco.DoubleBuffer`, `mujoco.FloatBuffer`, and `mujoco.IntBuffer`.
|
||||
|
||||
Here's how to use them:
|
||||
|
||||
1. Create a buffer: Instantiate the appropriate buffer class with an initial array of the correct size (e.g., an array of zeros).
|
||||
2. Call the function: Pass the buffer instance to the function as the out parameter.
|
||||
3. Access the result: Use the `.getView()` method on the buffer to get a `TypedArray` view of the data written by the C++ function.
|
||||
4. Free the memory: When you are done with the buffer, you must call the `.delete()` method to free the underlying memory and prevent memory leaks.
|
||||
|
||||
Example: Rotating a vector
|
||||
|
||||
The function `mju_rotVecQuat` rotates a vector `vec` by a quaternion `quat` and stores the result in the `res` out parameter.
|
||||
|
||||
```typescript
|
||||
// Create a buffer to hold the 3D vector result.
|
||||
const res = new mujoco.DoubleBuffer([0, 0, 0]);
|
||||
|
||||
const vec = [1, 0, 0];
|
||||
const quat = [0.707, 0, 0, 0.707]; // 90-degree rotation around z-axis
|
||||
|
||||
try {
|
||||
// Call the function with the buffer as the out parameter.
|
||||
mujoco.mju_rotVecQuat(res, vec, quat);
|
||||
|
||||
// Get the result as a Float64Array.
|
||||
const resultView = res.getView();
|
||||
console.log(resultView); // Expected: approximately [0, 1, 0]
|
||||
|
||||
} finally {
|
||||
// IMPORTANT: Free the memory allocated for the buffer.
|
||||
res.delete();
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. Struct Out Parameters (e.g., mjvCamera*, mjvScene*)
|
||||
When a function modifies a struct passed by pointer, you should pass an instance of the corresponding JavaScript wrapper class. The underlying C++ struct will be modified in place.
|
||||
|
||||
Example: Updating a scene
|
||||
|
||||
The function `mjv_updateScene` populates an `mjvScene` object with information from `mjModel` and `mjData`.
|
||||
```typescript
|
||||
// Create instances of the necessary structs.
|
||||
const model = mujoco.MjModel.loadFromXML(xmlContent);
|
||||
const data = new mujoco.MjData(model);
|
||||
const scene = new mujoco.MjvScene(model, 1000);
|
||||
const option = new mujoco.MjvOption();
|
||||
const perturb = new mujoco.MjvPerturb();
|
||||
const camera = new mujoco.MjvCamera();
|
||||
|
||||
// ... (step simulation, etc.)
|
||||
|
||||
// Update the scene. The 'scene' object is modified by the function.
|
||||
mujoco.mjv_updateScene(
|
||||
model,
|
||||
data,
|
||||
option,
|
||||
perturb,
|
||||
camera,
|
||||
mujoco.mjtCatBit.mjCAT_ALL.value,
|
||||
scene
|
||||
);
|
||||
|
||||
console.log('Number of geoms in scene:', scene.ngeom);
|
||||
|
||||
// Remember to delete all created objects when they are no longer needed.
|
||||
scene.delete();
|
||||
camera.delete();
|
||||
perturb.delete();
|
||||
option.delete();
|
||||
data.delete();
|
||||
model.delete();
|
||||
```
|
||||
|
||||
As with buffers, you are responsible for managing the memory of these struct instances and must call `.delete()` on them when you are finished.
|
||||
|
||||
### Enums
|
||||
Access via `.value`:
|
||||
```javascript
|
||||
mujoco.mjtDisableBit.mjDSBL_CLAMPCTRL.value
|
||||
```
|
||||
|
||||
### Constants
|
||||
Scalar constants will be accessed the same way they are on python, simply:
|
||||
|
||||
```javascript
|
||||
mujoco.mjNEQDATA
|
||||
```
|
||||
|
||||
Due to Embind limitations, more complex constants that are not scalar, but are represented in more dimensions are exposed as functions. E.g to use `mujoco.mjFRAMESTRING` you will need to call a function:
|
||||
|
||||
```javascript
|
||||
mujoco.get_mjFRAMESTRING()
|
||||
```
|
||||
|
||||
This will return a javascript array representation of the values in MuJoCo `mjFRAMESTRING`.
|
||||
|
||||
### About assets
|
||||
The package is ESM (`type: module`) and ships TypeScript types.
|
||||
|
||||
Ensure your bundler or dev server serves the `.wasm` asset at runtime.
|
||||
|
||||
## Development
|
||||
|
||||
In order to change the bindings you will need to change the [`bindings.cc`](codegen/generated/bindings.cc)
|
||||
@@ -183,6 +369,14 @@ method to do this only works internally at Google, but it should be possible to
|
||||
replicate the experience with open-source tooling — community suggestions are
|
||||
welcome!
|
||||
|
||||
## Versioning
|
||||
Package versions follow the official MuJoCo release versions.
|
||||
For example:
|
||||
|
||||
| npm version | MuJoCo version |
|
||||
|-------------|----------------|
|
||||
| 3.5.0 | 3.5.0 |
|
||||
|
||||
## Future Work
|
||||
|
||||
1. **Bind all useful APIs.**
|
||||
|
||||
@@ -1,208 +0,0 @@
|
||||
# MuJoCo WASM bindings
|
||||
|
||||
Official WebAssembly (WASM) bindings for the MuJoCo physics engine, compiled from the MuJoCo C/C++ sources into WASM with JS glue (Emscripten + Embind). The package ships prebuilt ESM-ready JS/WASM artifacts and TypeScript types for immediate use.
|
||||
|
||||
**Important**:_These bindings are still a WIP_.
|
||||
|
||||
## Install
|
||||
```sh
|
||||
npm install mujoco
|
||||
```
|
||||
|
||||
## Quick start (ESM / TypeScript)
|
||||
|
||||
```ts
|
||||
import loadMujoco from 'mujoco';
|
||||
|
||||
const mujoco = await loadMujoco();
|
||||
|
||||
const model = mujoco.MjModel.fromXMLString(`
|
||||
<mujoco>
|
||||
<worldbody>
|
||||
<geom type="sphere" size="0.1"/>
|
||||
</worldbody>
|
||||
</mujoco>
|
||||
`);
|
||||
|
||||
const data = new mujoco.MjData(model);
|
||||
mujoco.mj_step(model, data);
|
||||
```
|
||||
|
||||
## Usage Guide
|
||||
When interacting with MuJoCo objects through the WASM bindings, it's important to understand how data is accessed. Properties on objects like `MjModel` and `MjData` can expose data in two ways: by copy or by reference.
|
||||
|
||||
### Copy vs. Reference
|
||||
|
||||
#### 1. By Copy (Value-based access)
|
||||
|
||||
Some properties return a copy of the data at the time of access. This is common for complex data structures that need to be marshalled from C++ to JavaScript.
|
||||
|
||||
A key example is `MjData.contact`. When you access `data.contact`, you get a new array containing the contacts at that specific moment in the simulation. If you step the simulation forward, this array will not be updated. You must access `data.contact` again to get the new contact information.
|
||||
|
||||
Example:
|
||||
```typescript
|
||||
// Gets contacts at the current time.
|
||||
const contacts = data.contact;
|
||||
|
||||
// Step the simulation
|
||||
mujoco.mj_step(model, data);
|
||||
|
||||
// `contacts` is now stale. To get the new contacts, you must access the property again:
|
||||
const newContacts = data.contact;
|
||||
```
|
||||
|
||||
#### 2. By Reference (View-based access)
|
||||
Many properties, especially large numerical arrays, return a live view directly into the WebAssembly memory. This is highly efficient as it avoids copying large amounts of data.
|
||||
|
||||
A key example is `MjData.qpos` (joint positions). When you get a reference to this array, it points directly to the simulation's state data. Any changes in the simulation (e.g., after a call to `mj_step`) will be immediately reflected in this array.
|
||||
|
||||
```typescript
|
||||
// `qpos` is a live view into the simulation state.
|
||||
const qpos = data.qpos;
|
||||
|
||||
console.log(qpos[0]); // Print initial position
|
||||
|
||||
// Step the simulation
|
||||
mujoco.mj_step(model, data);
|
||||
|
||||
// `qpos` is automatically updated.
|
||||
console.log(qpos[0]); // Print new position
|
||||
```
|
||||
|
||||
### Data Layout: Row-Major Matrices
|
||||
When a function from the MuJoCo C API returns a matrix (or needs a matrix as input), these are represented in the JavaScript bindings as flat, one-dimensional `TypedArray`'s. The elements are stored in row-major order.
|
||||
|
||||
For example, a 3x10 matrix will be returned as a flat array with 30 elements. The first 10 elements represent the first row, the next 10 represent the second row, and so on.
|
||||
|
||||
Example: Accessing an element at `(row, col)`
|
||||
```typescript
|
||||
// A 3x10 matrix stored as a flat array.
|
||||
const matrix: Float64Array = ...;
|
||||
const nRows = 3;
|
||||
const nCols = 10;
|
||||
|
||||
// To access the element at row `i` and column `j`:
|
||||
const element = matrix[i * nCols + j];
|
||||
```
|
||||
|
||||
### Working with Out Parameters
|
||||
Many functions in the MuJoCo C API use "out parameters" to return data. This means instead of returning a value, they write the result into one of the arguments passed to them by reference (using pointers). In our JavaScript bindings, you'll need to handle these cases specifically.
|
||||
|
||||
There are two main scenarios you'll encounter:
|
||||
|
||||
#### 1. Array-like Out Parameters
|
||||
When a function expects a pointer to a primitive type (like `mjtNum*` or `int*`) to write an array of values, you need to pre-allocate memory for the result on the JavaScript side. We provide helper classes for this: `mujoco.Uint8Buffer`, `mujoco.DoubleBuffer`, `mujoco.FloatBuffer`, and `mujoco.IntBuffer`.
|
||||
|
||||
Here's how to use them:
|
||||
|
||||
1. Create a buffer: Instantiate the appropriate buffer class with an initial array of the correct size (e.g., an array of zeros).
|
||||
2. Call the function: Pass the buffer instance to the function as the out parameter.
|
||||
3. Access the result: Use the `.getView()` method on the buffer to get a `TypedArray` view of the data written by the C++ function.
|
||||
4. Free the memory: When you are done with the buffer, you must call the `.delete()` method to free the underlying memory and prevent memory leaks.
|
||||
|
||||
Example: Rotating a vector
|
||||
|
||||
The function `mju_rotVecQuat` rotates a vector `vec` by a quaternion `quat` and stores the result in the `res` out parameter.
|
||||
|
||||
```typescript
|
||||
// Create a buffer to hold the 3D vector result.
|
||||
const res = new mujoco.DoubleBuffer([0, 0, 0]);
|
||||
|
||||
const vec = [1, 0, 0];
|
||||
const quat = [0.707, 0, 0, 0.707]; // 90-degree rotation around z-axis
|
||||
|
||||
try {
|
||||
// Call the function with the buffer as the out parameter.
|
||||
mujoco.mju_rotVecQuat(res, vec, quat);
|
||||
|
||||
// Get the result as a Float64Array.
|
||||
const resultView = res.getView();
|
||||
console.log(resultView); // Expected: approximately [0, 1, 0]
|
||||
|
||||
} finally {
|
||||
// IMPORTANT: Free the memory allocated for the buffer.
|
||||
res.delete();
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. Struct Out Parameters (e.g., mjvCamera*, mjvScene*)
|
||||
When a function modifies a struct passed by pointer, you should pass an instance of the corresponding JavaScript wrapper class. The underlying C++ struct will be modified in place.
|
||||
|
||||
Example: Updating a scene
|
||||
|
||||
The function `mjv_updateScene` populates an `mjvScene` object with information from `mjModel` and `mjData`.
|
||||
```typescript
|
||||
// Create instances of the necessary structs.
|
||||
const model = mujoco.MjModel.loadFromXML(xmlContent);
|
||||
const data = new mujoco.MjData(model);
|
||||
const scene = new mujoco.MjvScene(model, 1000);
|
||||
const option = new mujoco.MjvOption();
|
||||
const perturb = new mujoco.MjvPerturb();
|
||||
const camera = new mujoco.MjvCamera();
|
||||
|
||||
// ... (step simulation, etc.)
|
||||
|
||||
// Update the scene. The 'scene' object is modified by the function.
|
||||
mujoco.mjv_updateScene(
|
||||
model,
|
||||
data,
|
||||
option,
|
||||
perturb,
|
||||
camera,
|
||||
mujoco.mjtCatBit.mjCAT_ALL.value,
|
||||
scene
|
||||
);
|
||||
|
||||
console.log('Number of geoms in scene:', scene.ngeom);
|
||||
|
||||
// Remember to delete all created objects when they are no longer needed.
|
||||
scene.delete();
|
||||
camera.delete();
|
||||
perturb.delete();
|
||||
option.delete();
|
||||
data.delete();
|
||||
model.delete();
|
||||
```
|
||||
|
||||
As with buffers, you are responsible for managing the memory of these struct instances and must call `.delete()` on them when you are finished.
|
||||
|
||||
### Enums
|
||||
Access via `.value`:
|
||||
```javascript
|
||||
mujoco.mjtDisableBit.mjDSBL_CLAMPCTRL.value
|
||||
```
|
||||
|
||||
### Constants
|
||||
Scalar constants will be accessed the same way they are on python, simply:
|
||||
|
||||
```javascript
|
||||
mujoco.mjNEQDATA
|
||||
```
|
||||
|
||||
Due to Embind limitations, more complex constants that are not scalar, but are represented in more dimensions are exposed as functions. E.g to use `mujoco.mjFRAMESTRING` you will need to call a function:
|
||||
|
||||
```javascript
|
||||
mujoco.get_mjFRAMESTRING()
|
||||
```
|
||||
|
||||
This will return a javascript array representation of the values in MuJoCo `mjFRAMESTRING`.
|
||||
|
||||
## Notes
|
||||
The package is ESM (`type: module`) and ships TypeScript types.
|
||||
|
||||
Ensure your bundler or dev server serves the `.wasm` asset at runtime.
|
||||
|
||||
### Development
|
||||
For detailed build instructions see the repository [README](https://github.com/google-deepmind/mujoco/tree/main/wasm#mujoco-javascript-bindings) (Emscripten toolchain, Embind bindings, producing the .wasm artifact, and targets for Node and browser).
|
||||
|
||||
## Versioning
|
||||
Package versions follow the official MuJoCo release versions.
|
||||
For example:
|
||||
|
||||
| npm version | MuJoCo version |
|
||||
|-------------|----------------|
|
||||
| 3.5.0 | 3.5.0 |
|
||||
|
||||
---
|
||||
|
||||
For full engine documentation, see: https://mujoco.readthedocs.io
|
||||
Reference in New Issue
Block a user