Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion .github/workflows/rapier-ci-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Cargo doc
run: cargo doc --features parallel,simd-stable,serde-serialize,debug-render -p rapier3d -p rapier2d -p rapier3d-meshloader -p rapier3d-urdf
run: cargo doc --features parallel,simd-stable,serde-serialize,debug-render -p rapier3d -p rapier2d -p rapier3d-meshloader -p rapier3d-urdf && cargo doc -p mjcf-rs --features msh && cargo doc -p rapier3d-mjcf --features stl,wavefront,msh
build-native:
runs-on: ubuntu-latest
env:
Expand All @@ -37,6 +37,10 @@ jobs:
run: cargo clippy -p rapier-examples-2d --features parallel,simd-stable
- name: Clippy rapier3d
run: cargo clippy -p rapier-examples-3d --features parallel,simd-stable
- name: Clippy mjcf-rs
run: cargo clippy -p mjcf-rs --all-targets --features msh
- name: Clippy rapier3d-mjcf
run: cargo clippy -p rapier3d-mjcf --all-targets --features stl,wavefront,msh
- name: Build rapier2d
run: cargo build --verbose -p rapier2d;
- name: Build rapier3d
Expand All @@ -51,6 +55,10 @@ jobs:
run: cd crates/rapier3d; cargo build --verbose --features simd-stable --features parallel;
- name: Run tests
run: cargo test
- name: Test mjcf-rs
run: cargo test -p mjcf-rs --features msh
- name: Test rapier3d-mjcf
run: cargo test -p rapier3d-mjcf --features stl,wavefront,msh
- name: Check rapier_testbed2d
run: cargo check --verbose -p rapier_testbed2d;
- name: Check rapier_testbed3d
Expand Down
4 changes: 4 additions & 0 deletions .typos.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ extend-ignore-re = [
[default.extend-identifiers]
anc_color = "anc_color"
rady = "rady"
iit_softfoot = "iit_softfoot"
FoV = "FoV"
# Shepperd's method: named after S. W. Shepperd (quaternion extraction).
Shepperd = "Shepperd"

# Case insensitive, matches inside word.
[default.extend-words]
Expand Down
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
- Update `glamx` to 0.3.
- Updated `wide` version to `1` ([#902](https://github.com/dimforge/rapier/issues/902)).

## v0.31.0 (09 Jan. 2026)
## v0.32.0 (09 Jan. 2026)

### Modified

Expand Down
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ members = [
"examples3d-f64",
"crates/rapier3d-urdf",
"crates/rapier3d-meshloader",
"crates/mjcf-rs",
"crates/rapier3d-mjcf",
]
resolver = "2"

Expand Down
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,21 @@ The easiest way to get started with Rapier is to:
Their source code are available on the `examples2d/` and `examples3d/` directory.
3. Don't hesitate to ask for help on [Discord](https://discord.gg/vt9DJSW), or by opening an issue on GitHub.

## AI coding disclaimer and policy

AI coding is extensively used for the implementation and maintenance of the following crates: `mjcf-rs`, `rapier3d-mjcf`.

We actively use AI assistance (with human reviews) for the following tasks:
- Documentation generation.
- Changelogs generation.
- Tests generation.
- CI configuration and scripts.

We accept contributions involving AI coding as long as:
- They are verified to work properly by a human.
- The code quality is up to human-written code standards.
- Include non-regression tests whenever applicable (which itself can be AI-generated).

## Resources and discussions

- [Dimforge](https://dimforge.com): See all the open-source projects we are working on! Follow our announcements
Expand Down
282 changes: 282 additions & 0 deletions assets/3d/agility_cassie/cassie.xml

Large diffs are not rendered by default.

23 changes: 23 additions & 0 deletions assets/3d/agility_cassie/scene.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<mujoco model="cassie scene">
<include file="cassie.xml"/>

<statistic center="0 0 0.55" extent="1.1"/>

<visual>
<headlight diffuse="0.6 0.6 0.6" ambient="0.3 0.3 0.3" specular="0 0 0"/>
<rgba haze="0.15 0.25 0.35 1"/>
<global azimuth="150" elevation="-20"/>
</visual>

<asset>
<texture type="skybox" builtin="gradient" rgb1="0.3 0.5 0.7" rgb2="0 0 0" width="512" height="3072"/>
<texture type="2d" name="groundplane" builtin="checker" mark="edge" rgb1="0.2 0.3 0.4" rgb2="0.1 0.2 0.3"
markrgb="0.8 0.8 0.8" width="300" height="300"/>
<material name="groundplane" texture="groundplane" texuniform="true" texrepeat="5 5" reflectance="0.2"/>
</asset>

<worldbody>
<light pos="0 0 3" dir="0 0 -1" directional="false"/>
<geom name="floor" size="0 0 .125" type="plane" material="groundplane" conaffinity="15" condim="3"/>
</worldbody>
</mujoco>
32 changes: 32 additions & 0 deletions crates/mjcf-rs/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
[package]
name = "mjcf-rs"
version = "0.1.0"
authors = ["Sébastien Crozet <sebcrozet@dimforge.com>"]
description = "Pure-Rust parser for the MuJoCo MJCF XML format."
documentation = "https://docs.rs/mjcf-rs"
homepage = "https://rapier.rs"
repository = "https://github.com/dimforge/rapier"
readme = "README.md"
categories = [
"parser-implementations",
"science",
"simulation",
]
keywords = ["mjcf", "mujoco", "robotics", "parser", "xml"]
license = "Apache-2.0"
edition = "2024"

[features]
default = []
## Enable parsing MuJoCo's custom binary `.msh` mesh format.
msh = ["dep:byteorder"]

[dependencies]
roxmltree = "0.20"
thiserror = "2"
log = "0.4"
byteorder = { version = "1", optional = true }
glamx = { workspace = true, features = ["std", "f64"] }

[dev-dependencies]
tempfile = "3"
22 changes: 22 additions & 0 deletions crates/mjcf-rs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
## MJCF parser for Rust

> **Disclaimer.** Most of this crate — source, tests, and documentation — was
> produced by an AI coding assistant working iteratively from MJCF reference
> scenes (primarily the [MuJoCo Menagerie](https://github.com/google-deepmind/mujoco_menagerie)),
> under human direction and review.

`mjcf-rs` is a pure-Rust parser for the [MuJoCo XML format (MJCF)](https://mujoco.readthedocs.io/en/stable/XMLreference.html).

It produces a typed AST that mirrors MJCF 1:1 in element names, with all
preprocessing (`<include>` resolution, `<default>` class inheritance, angle
unit normalization) already done — so consumers don't need to revisit the
spec for any of those.

This crate has **no dependency** on `rapier3d` or any physics engine. Pair
it with [`rapier3d-mjcf`](../rapier3d-mjcf) to actually simulate MJCF
models.

### Status

See [`rapier3d-mjcf`'s feature matrix](../rapier3d-mjcf/README.md) for an
up-to-date, phase-by-phase list of what's supported.
146 changes: 146 additions & 0 deletions crates/mjcf-rs/src/assets.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
//! `<asset>` library: meshes, hfields, textures, materials.

use crate::Pose;

/// Container for all assets defined in a model (and its includes).
#[derive(Default, Clone, Debug)]
pub struct Assets {
/// `<mesh>` definitions, keyed by name.
pub meshes: Vec<Mesh>,
/// `<hfield>` definitions, keyed by name.
pub hfields: Vec<Hfield>,
/// `<texture>` definitions (recorded only — not loaded).
pub textures: Vec<Texture>,
/// `<material>` definitions (recorded only).
pub materials: Vec<Material>,
}

impl Assets {
/// Looks up a `<mesh>` by name.
pub fn mesh(&self, name: &str) -> Option<&Mesh> {
self.meshes.iter().find(|m| m.name.as_deref() == Some(name))
}

/// Looks up a `<hfield>` by name.
pub fn hfield(&self, name: &str) -> Option<&Hfield> {
self.hfields
.iter()
.find(|h| h.name.as_deref() == Some(name))
}

/// Looks up a `<material>` by name.
pub fn material(&self, name: &str) -> Option<&Material> {
self.materials
.iter()
.find(|m| m.name.as_deref() == Some(name))
}

/// Looks up a `<texture>` by name.
pub fn texture(&self, name: &str) -> Option<&Texture> {
self.textures
.iter()
.find(|t| t.name.as_deref() == Some(name))
}
}

/// `<mesh>` asset. Either references a file or carries inline vertex/face data.
#[derive(Default, Clone, Debug)]
pub struct Mesh {
/// Asset name.
pub name: Option<String>,
/// Class for default lookup.
pub class: Option<String>,
/// Mesh file path (relative to `meshdir` / model dir).
pub file: Option<String>,
/// Per-axis scale (default `[1, 1, 1]`).
pub scale: [f64; 3],
/// Reference pose applied to the source mesh before use.
pub refpose: Pose,
/// Inline vertex array (alternative to `file`). Flat `[x0, y0, z0, x1, …]`.
pub inline_vertices: Option<Vec<f64>>,
/// Inline normals. Optional even when `inline_vertices` is set.
pub inline_normals: Option<Vec<f64>>,
/// Inline triangle indices.
pub inline_faces: Option<Vec<u32>>,
/// `inertia` attribute: `"shell"`, `"convex"` (default), or `"exact"`.
pub inertia: MeshInertia,
/// Maximum number of vertices when computing the convex hull.
pub max_hull_vert: Option<u32>,
/// `smoothnormal` (visualisation only — recorded).
pub smoothnormal: f64,
}

/// `<mesh inertia>` selector.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
pub enum MeshInertia {
/// Surface integral.
Shell,
/// Convex-hull interior (default).
#[default]
Convex,
/// Tetrahedral integration over all triangles.
Exact,
/// Treat as a point mass at the geometric centre.
Legacy,
}

/// `<hfield>` heightfield asset.
#[derive(Default, Clone, Debug)]
pub struct Hfield {
/// Asset name.
pub name: Option<String>,
/// Class.
pub class: Option<String>,
/// Number of grid rows.
pub nrow: u32,
/// Number of grid columns.
pub ncol: u32,
/// `(radius_x, radius_y, elevation_z, base_z)`.
pub size: [f64; 4],
/// File path (PNG / `.hfield`).
pub file: Option<String>,
/// Inline elevation data (`nrow * ncol` values, row-major).
pub elevation: Option<Vec<f64>>,
}

/// `<texture>` asset (recorded only).
#[derive(Default, Clone, Debug)]
pub struct Texture {
/// Asset name.
pub name: Option<String>,
/// Asset class.
pub class: Option<String>,
/// Texture type (`2d`, `cube`, `skybox`).
pub type_: Option<String>,
/// File path.
pub file: Option<String>,
/// `builtin` (`"none"`, `"gradient"`, `"checker"`, `"flat"`).
pub builtin: Option<String>,
/// Primary RGB.
pub rgb1: Option<[f64; 3]>,
/// Secondary RGB.
pub rgb2: Option<[f64; 3]>,
}

/// `<material>` asset (recorded only).
#[derive(Default, Clone, Debug)]
pub struct Material {
/// Asset name.
pub name: Option<String>,
/// Asset class.
pub class: Option<String>,
/// Texture reference.
pub texture: Option<String>,
/// `rgba`.
pub rgba: Option<[f64; 4]>,
/// `emission`.
pub emission: f64,
/// `specular`.
pub specular: f64,
/// `shininess`.
pub shininess: f64,
/// `roughness`.
pub roughness: f64,
/// `metallic`.
pub metallic: f64,
}
Loading
Loading