diff --git a/.github/workflows/bevy-rapier.yml b/.github/workflows/bevy-rapier.yml index 81364b50e..af846771a 100644 --- a/.github/workflows/bevy-rapier.yml +++ b/.github/workflows/bevy-rapier.yml @@ -60,11 +60,16 @@ jobs: - uses: Swatinem/rust-cache@v2 with: workspaces: bindings/bevy_rapier + save-if: ${{ github.ref == 'refs/heads/master' }} - run: sudo apt update && sudo apt-get install pkg-config libx11-dev libasound2-dev libudev-dev libwayland-dev libxkbcommon-dev - name: Cargo doc run: cargo doc --no-deps - test: + clippy: runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + dim: [2, 3] env: RUSTFLAGS: -D warnings steps: @@ -75,26 +80,77 @@ jobs: - uses: Swatinem/rust-cache@v2 with: workspaces: bindings/bevy_rapier + key: clippy-${{ matrix.dim }}d + save-if: ${{ github.ref == 'refs/heads/master' }} + - run: sudo apt update && sudo apt-get install pkg-config libx11-dev libasound2-dev libudev-dev libwayland-dev libxkbcommon-dev + - name: Clippy + run: cargo clippy --verbose -p bevy_rapier${{ matrix.dim }}d --examples + - name: Clippy (debug-render, simd, serde) + run: cargo clippy --verbose -p bevy_rapier${{ matrix.dim }}d --features debug-render-${{ matrix.dim }}d,simd8,serde-serialize,picking-backend + test: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + dim: [2, 3] + env: + RUSTFLAGS: -D warnings + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - uses: Swatinem/rust-cache@v2 + with: + workspaces: bindings/bevy_rapier + key: test-${{ matrix.dim }}d + save-if: ${{ github.ref == 'refs/heads/master' }} + - run: sudo apt update && sudo apt-get install pkg-config libx11-dev libasound2-dev libudev-dev libwayland-dev libxkbcommon-dev + - name: Test + run: cargo test --verbose -p bevy_rapier${{ matrix.dim }}d + # Builds Bevy with its default features, so it gets a job of its own. + ambiguity-detection: + runs-on: ubuntu-latest + env: + RUSTFLAGS: -D warnings + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - uses: Swatinem/rust-cache@v2 + with: + workspaces: bindings/bevy_rapier + save-if: ${{ github.ref == 'refs/heads/master' }} - run: sudo apt update && sudo apt-get install pkg-config libx11-dev libasound2-dev libudev-dev libwayland-dev libxkbcommon-dev - - run: cargo install cargo-all-features - - name: Clippy for bevy_rapier2d - run: cargo clippy --verbose -p bevy_rapier2d --examples - - name: Clippy for bevy_rapier3d - run: cargo clippy --verbose -p bevy_rapier3d --examples - - name: Clippy for bevy_rapier2d (debug-render, simd, serde) - run: cargo clippy --verbose -p bevy_rapier2d --features debug-render-2d,simd8,serde-serialize,picking-backend - - name: Clippy for bevy_rapier3d (debug-render, simd, serde) - run: cargo clippy --verbose -p bevy_rapier3d --features debug-render-3d,simd8,serde-serialize,picking-backend - - name: Test for bevy_rapier2d - run: cargo test --verbose -p bevy_rapier2d - - name: Test for bevy_rapier3d - run: cargo test --verbose -p bevy_rapier3d - name: Test for no conflicting system order ambiguity run: cargo run --bin ambiguity_detection + # Each chunk checks a quarter of the (crate, feature set) pairs. The chunks share one cache, + # saved by the first one. + check-all-features: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + chunk: [1, 2, 3, 4] + env: + RUSTFLAGS: -D warnings + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - uses: taiki-e/install-action@v2 + with: + tool: cargo-all-features + - uses: Swatinem/rust-cache@v2 + with: + workspaces: bindings/bevy_rapier + shared-key: bevy-rapier-all-features + save-if: ${{ github.ref == 'refs/heads/master' && matrix.chunk == 1 }} + - run: sudo apt update && sudo apt-get install pkg-config libx11-dev libasound2-dev libudev-dev libwayland-dev libxkbcommon-dev - name: Check all features individually - run: cargo check-all-features + run: cargo check-all-features --n-chunks 4 --chunk ${{ matrix.chunk }} --chunk-granularity feature test-wasm: runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + dim: [2, 3] env: RUSTFLAGS: -D warnings steps: @@ -106,10 +162,10 @@ jobs: - uses: Swatinem/rust-cache@v2 with: workspaces: bindings/bevy_rapier - - name: Clippy bevy_rapier2d - run: cd bevy_rapier2d && cargo clippy --verbose --features bevy/webgl2 --target wasm32-unknown-unknown - - name: Clippy bevy_rapier3d - run: cd bevy_rapier3d && cargo clippy --verbose --features bevy/webgl2 --target wasm32-unknown-unknown + key: wasm-${{ matrix.dim }}d + save-if: ${{ github.ref == 'refs/heads/master' }} + - name: Clippy + run: cd bevy_rapier${{ matrix.dim }}d && cargo clippy --verbose --features bevy/webgl2 --target wasm32-unknown-unknown dependencies: runs-on: ubuntu-latest steps: @@ -118,25 +174,25 @@ jobs: uses: EmbarkStudios/cargo-deny-action@v2 with: manifest-path: bindings/bevy_rapier/Cargo.toml - # Checks the Bevy documentation snippets compile against the in-tree bevy_rapier. Both - # snippet crates are standalone workspaces sharing one target directory. + # Checks the Bevy documentation snippets compile against the in-tree bevy_rapier. Each + # snippet crate is a standalone workspace. website-bevy-snippets: runs-on: ubuntu-latest - env: - CARGO_TARGET_DIR: ${{ github.workspace }}/website/docs-examples/target + strategy: + fail-fast: false + matrix: + dim: [2, 3] defaults: run: - working-directory: website/docs-examples + working-directory: website/docs-examples/${{ matrix.dim }}d/bevy steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 with: - workspaces: | - website/docs-examples/2d/bevy -> ../../target - website/docs-examples/3d/bevy -> ../../target + workspaces: website/docs-examples/${{ matrix.dim }}d/bevy + key: snippets-${{ matrix.dim }}d + save-if: ${{ github.ref == 'refs/heads/master' }} - run: sudo apt update && sudo apt-get install pkg-config libx11-dev libasound2-dev libudev-dev libwayland-dev libxkbcommon-dev - - name: Check the 2D snippets - run: cd 2d/bevy && cargo check --examples - - name: Check the 3D snippets - run: cd 3d/bevy && cargo check --examples + - name: Check the snippets + run: cargo check --examples diff --git a/CHANGELOG.md b/CHANGELOG.md index 2169713c5..14d71b10a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,45 +1,72 @@ -## Unreleased +## v0.36.0 (24 September 2026) ### Added -- `Multibody::remove_dof_coupling`, `Multibody::retain_dof_couplings` and - `Multibody::clear_dof_couplings` to remove DoF couplings. -- `rapier3d-urdf`: `UrdfLink::urdf_link_index` and `UrdfJoint::urdf_joint_index` (and related) map the - loaded links and joints to their URDF counterparts, even when empty links are squeezed. -- `rapier3d-mjcf`: `MjcfContactHooks::is_excluded` and `MjcfContactHooks::pair_override` to query the - contact rules of a model. +- Soft bodies: `SoftBodySet`, `SoftBody` and `SoftBodyBuilder` (ropes, cloth, trimeshes, volumes…), + simulated together with rigid bodies, contacts and joints; also on `PhysicsWorld`. +- Soft-body materials with plasticity, tearing and cutting (`SoftBodySet::tear`/`cut`); torn-off + pieces become soft bodies and report a `SoftBodyTearEvent`. +- Soft-body clusters (`SoftBodySet::add_cluster`): rigid proxies that joints and colliders attach to. +- Deformable colliders: `ColliderSet::insert_deformable` binds a `DEFORMABLE` trimesh (3D) or + polyline (2D) to a soft body through a `SoftMeshBinding`. +- Experimental FEM soft-body solver behind the new `fem` feature (`SoftBodySolver::Fem`). +- `IntegrationParameters::soft_bodies`, soft-body debug-render modes, and soft bodies in snapshots. +- `RigidBody(Builder)::additional_pgs_iterations`: extra PGS iterations per substep for a body's island. +- `DebugRenderMode::PSEUDO_NORMALS` draws the pseudo-normals of trimeshes and polylines. +- `ColliderBuilder::polyline_with_flags`, `RigidBodyHandle::is_invalid`, and a public + `CoefficientCombineRule::combine`. +- `PhysicsWorld::detect_collisions` (collision detection without stepping) and + `PhysicsWorld::remove_body_with_colliders`. +- `CCDSolver::invalidate_fixed_targets_cache`, for users calling `CCDSolver::solve_continuous` directly. +- `Multibody::remove_dof_coupling`, `retain_dof_couplings` and `clear_dof_couplings`. +- `rapier3d-urdf`: `UrdfLink::urdf_link_index` and `UrdfJoint::urdf_joint_index` (and + `merged_urdf_joint_indices`) map loaded links and joints to their URDF counterparts. +- `rapier3d-mjcf`: `MjcfContactHooks::is_excluded` and `pair_override` query a model's contact rules. +- Testbed: drag bodies with the mouse, a "Debug" tab for the debug-render settings, and a + "Smooth mesh colliders" option (3D). +- New C bindings (`bindings/c`): 2D/3D, f32/f64, a C11 header with C++ helpers, and a CMake package. ### Modified +- Update to parry 0.31: query results now report the hit sub-shape (e.g. the triangle of a trimesh). +- `PhysicsPipeline::step`, `RigidBodySet::remove`, `ColliderSet::remove`, `DebugRenderPipeline::render` + and `CCDSolver::solve_continuous` take the `SoftBodySet`. +- `EventHandler` has a new required method `handle_soft_body_tear_event`, and + `ChannelEventCollector::new` takes a sender for these events. +- `ContactPair::manifolds` and `solver_clusters` moved to `ContactPair::contacts`: use + `pair.manifolds()` or `pair.rigid()`. +- The rigid-contact fields of `ContactModificationContext` moved behind `ctx.rigid_mut()`. +- `SolverFlags::COMPUTE_IMPULSES` is renamed `COMPUTE_RIGID_IMPULSES`. +- `RigidBodyType::SoftFrame`: the rigid proxies of soft bodies, counted as dynamic by `is_dynamic`. +- `DebugRenderObject::SoftBody` variant, and new fields in `IntegrationParameters`, + `DebugRenderStyle` and `PhysicsWorld` (`soft_bodies`, `collision_pipeline`). +- `CCDSolver::solve_continuous` no longer takes `scene_changed`, and + `RigidBodyCcd::is_moving_fast` also takes the body's mass properties. +- Snapshots serialized with previous versions can't be loaded anymore. +- The `std` feature of `rapier2d` and `rapier2d-f64` now enables `parry2d/spade`. - The C, Python, and JavaScript/TypeScript bindings moved to the `bindings/` directory. -- The `bevy_rapier2d` and `bevy_rapier3d` plugins now live in this repository, in `bindings/bevy_rapier`, and - build against the in-tree rapier crates. +- The `bevy_rapier2d` and `bevy_rapier3d` plugins now live in this repository, in `bindings/bevy_rapier`. +- Testbed: rigid-body axes are shown by default and polylines are drawn as thick lines (2D) or tubes (3D). ### Fixed -- `CollisionPipeline::step` no longer trips a debug assertion when a moving body touches another - collider. -- A zero-length step no longer corrupts the simulation: it only applies the user changes and runs - the collision detection. +- The CCD no longer sweeps against a stale list of fixed colliders after they were added, removed or + moved during a step where no body needed CCD. +- `KinematicCharacterController::solve_character_collision_impulses` now pushes every dynamic body + touched by a compound character, not only the last one. +- A step with `dt == 0` no longer produces NaNs: it only applies user changes and detects collisions. +- `CollisionPipeline::step` no longer trips a debug assertion when a moving body touches a collider. - Enabling or disabling an impulse joint now updates the islands of its bodies. -- Multibody DoF couplings and disabled self-contacts are no longer lost when multibodies merge or split. -- `PhysicsPipeline::counters` now fills the contact pair, contact, and constraint counts. -- `rapier3d-urdf`: the fixed children of a squeezed empty link now stay rigidly attached together, and - the joint taking over the removed link's joint keeps its original pivot. -- 2D soft-body cluster proxies now follow counterclockwise rotations of their particles. -- The rigid colliders attached to soft-body cluster proxies no longer lag one step behind their proxy. -- The broad-phase AABBs of soft-body surfaces now match their end-of-step geometry. -- When the CCD splits a step, the end-of-step soft-body motion margin and soft-CCD AABBs now cover the - full next step instead of the last CCD pass. -- The CCD now also splits the first step of a fast body at its impact. -- The soft-body contact history driving the extra substeps now counts steps instead of CCD passes. -- The CCD fast-body check now divides the applied forces by the body's mass, so heavy bodies under - gravity are no longer flagged as fast. - -### Modified - -- The soft-body motion margin now only pads deformable colliders, not the rigid colliders of clusters. -- `RigidBodyCcd::is_moving_fast` now takes the mass-properties along with the forces. +- Multibody DoF couplings are no longer lost when multibodies merge or split, and disabled + self-contacts are no longer re-enabled when they merge. +- `PhysicsPipeline::counters` now fills the contact pair, contact and constraint counts. +- With `max_ccd_substeps > 1`: end-of-step AABBs cover the full next step, the step splitter uses + the current velocity, and heavy bodies under gravity are no longer flagged as fast. +- `SpringCoefficients` no longer divides by zero when `damping_ratio` is zero. +- `rapier3d-urdf`: `` angles are now composed as fixed-axis roll-pitch-yaw, as specified. +- `rapier3d-urdf`: the fixed children of a squeezed empty link stay rigidly attached, and the joint + taking over the removed link's joint keeps its pivot. +- Testbed (3D): `camera_rotation` and `camera_fwd_dir` return the actual camera orientation. ## v0.35.3 (28 August 2026) diff --git a/Cargo.toml b/Cargo.toml index df0682c40..400747f82 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,7 +52,7 @@ exclude = ["bindings/typescript", "bindings/bevy_rapier"] resolver = "2" [workspace.package] -version = "0.35.3" +version = "0.36.0" authors = ["Sébastien Crozet "] homepage = "https://rapier.rs" repository = "https://github.com/dimforge/rapier" @@ -131,18 +131,18 @@ getrandom = { version = "0.2", features = ["js"] } wasm-bindgen = "0.2" # Internal crates (paths are relative to the workspace root) -rapier2d = { version = "0.35.2", path = "crates/rapier2d" } -rapier2d-f64 = { version = "0.35.2", path = "crates/rapier2d-f64" } -rapier3d = { version = "0.35.2", path = "crates/rapier3d" } -rapier3d-f64 = { version = "0.35.2", path = "crates/rapier3d-f64" } -rapier_testbed2d = { version = "0.35.2", path = "crates/rapier_testbed2d" } -rapier_testbed2d-f64 = { version = "0.35.2", path = "crates/rapier_testbed2d-f64" } -rapier_testbed3d = { version = "0.35.2", path = "crates/rapier_testbed3d" } -rapier_testbed3d-f64 = { version = "0.35.2", path = "crates/rapier_testbed3d-f64" } -rapier3d-urdf = { version = "0.35.2", path = "crates/rapier3d-urdf" } -rapier3d-meshloader = { version = "0.35.2", path = "crates/rapier3d-meshloader", default-features = false } -mjcf-rs = { version = "0.35.2", path = "crates/mjcf-rs" } -rapier3d-mjcf = { version = "0.35.2", path = "crates/rapier3d-mjcf" } +rapier2d = { version = "0.36.0", path = "crates/rapier2d" } +rapier2d-f64 = { version = "0.36.0", path = "crates/rapier2d-f64" } +rapier3d = { version = "0.36.0", path = "crates/rapier3d" } +rapier3d-f64 = { version = "0.36.0", path = "crates/rapier3d-f64" } +rapier_testbed2d = { version = "0.36.0", path = "crates/rapier_testbed2d" } +rapier_testbed2d-f64 = { version = "0.36.0", path = "crates/rapier_testbed2d-f64" } +rapier_testbed3d = { version = "0.36.0", path = "crates/rapier_testbed3d" } +rapier_testbed3d-f64 = { version = "0.36.0", path = "crates/rapier_testbed3d-f64" } +rapier3d-urdf = { version = "0.36.0", path = "crates/rapier3d-urdf" } +rapier3d-meshloader = { version = "0.36.0", path = "crates/rapier3d-meshloader", default-features = false } +mjcf-rs = { version = "0.36.0", path = "crates/mjcf-rs" } +rapier3d-mjcf = { version = "0.36.0", path = "crates/rapier3d-mjcf" } # Dev bincode = "1" diff --git a/bindings/bevy_rapier/Cargo.toml b/bindings/bevy_rapier/Cargo.toml index c18673319..6115c922b 100644 --- a/bindings/bevy_rapier/Cargo.toml +++ b/bindings/bevy_rapier/Cargo.toml @@ -4,11 +4,11 @@ resolver = "2" [workspace.dependencies] # Build against the rapier crates of this repository so bevy_rapier always tracks the in-tree engine. -rapier2d = { version = "0.35.3", path = "../../crates/rapier2d" } -rapier3d = { version = "0.35.3", path = "../../crates/rapier3d" } -rapier3d-urdf = { version = "0.35.3", path = "../../crates/rapier3d-urdf" } -rapier3d-mjcf = { version = "0.35.3", path = "../../crates/rapier3d-mjcf" } -rapier3d-meshloader = { version = "0.35.3", path = "../../crates/rapier3d-meshloader", default-features = false } +rapier2d = { version = "0.36.0", path = "../../crates/rapier2d" } +rapier3d = { version = "0.36.0", path = "../../crates/rapier3d" } +rapier3d-urdf = { version = "0.36.0", path = "../../crates/rapier3d-urdf" } +rapier3d-mjcf = { version = "0.36.0", path = "../../crates/rapier3d-mjcf" } +rapier3d-meshloader = { version = "0.36.0", path = "../../crates/rapier3d-meshloader", default-features = false } [profile.dev] # Use slightly better optimization by default, as examples otherwise seem laggy. diff --git a/bindings/c/VERSION b/bindings/c/VERSION index aee1388f5..2c359a6bc 100644 --- a/bindings/c/VERSION +++ b/bindings/c/VERSION @@ -1 +1 @@ -0.35.3+c.2 +0.36.0+c.0 diff --git a/bindings/python/docs/changelog.rst b/bindings/python/docs/changelog.rst index ebb79ab37..f16dceb2a 100644 --- a/bindings/python/docs/changelog.rst +++ b/bindings/python/docs/changelog.rst @@ -7,75 +7,57 @@ release cadence. The authoritative changelog is the Cargo `Rapier repository `_. -Unreleased ----------- - -**Small fixes.** ``DebugRenderStyle.copy`` returns a detached style; -``BroadPhasePairEvent.is_added`` / ``is_removed`` give the event kind (the -``added`` constructor shadows the ``added`` flag on instances); the -``QueryPipeline.project_point`` stub lists ``max_dist``. - -**Soft bodies.** ``PhysicsWorld.add_soft_body`` inserts a deformable body -(``SoftBody.rope`` / ``cloth`` / ``cuboid`` / ``sphere`` / ``trimesh`` / -``volumetric`` generators, ``SoftBodyBuilder`` setters, ``SoftBodyMaterial``), -simulated together with the rigid bodies, contacts and joints; ``world.soft_bodies`` -is the ``SoftBodySet``. Particles can be pinned, attached to rigid bodies, driven -kinematically or pushed by forces and impulses; clusters -(``add_soft_body_cluster``) give a set of particles a rigid proxy that joints and -colliders attach to; deformable colliders (``insert_deformable``) bind a triangle -mesh to a cluster. Tearing and cutting (``tear_soft_body``, ``cut_soft_body``, -material thresholds) split pieces off into soft bodies of their own and report a -``SoftBodyTearEvent`` (``EventHandler.handle_soft_body_tear_event``, -``ChannelEventCollector.drain_soft_body_tear_events``). See :doc:`api/soft_bodies`. - -Related changes: ``RigidBodyType.SOFT_FRAME`` marks the proxies of soft-body -clusters (``RigidBody.is_soft_frame`` / ``soft_body`` / ``soft_cluster``); -``RigidBody.additional_pgs_iterations``; ``IntegrationParameters.soft_bodies`` -(``SoftBodiesSettings``); ``RigidBodySet.remove``, ``ColliderSet.remove``, -``PhysicsPipeline.step`` and ``DebugRenderPipeline.render`` / ``render_to_arrays`` -take an optional ``soft_bodies`` argument; ``DebugRenderMode.SOFT_BODIES`` and -related modes; snapshots include the soft bodies; ``SolverFlags.COMPUTE_RIGID_IMPULSES`` -is the engine's new name for ``COMPUTE_IMPULSES``. - -**Rigid bodies and colliders.** ``RigidBody.set_next_kinematic_translation`` / -``set_next_kinematic_rotation`` / ``set_next_kinematic_position`` drive position-based -kinematic bodies; ``RigidBody.allow_fast_rotation`` (and the matching builder method and -factory keyword) lifts the angular speed cap. ``Collider.position_wrt_parent`` / -``translation_wrt_parent`` / ``rotation_wrt_parent`` read and move a collider on its body. -New shapes: ``Collider.segment`` / ``polyline`` / ``voxelized_mesh`` and their -``SharedShape`` siblings, with ``FillMode`` selecting a hollow or solid voxelization. Every -``Collider`` shape factory now accepts the ``ColliderBuilder`` keyword arguments, and the -mesh-based ones accept nested lists as well as NumPy arrays. - -Fixes: ``SharedShape.heightfield`` / ``Collider.heightfield`` read row-major (C-ordered) -arrays correctly (rows along Z, columns along X; previously the heights were scrambled) and -accept ``float64`` arrays and nested lists. ``InteractionGroups.test_mode`` reads back as -``AND`` instead of ``DEFAULT`` (``DEFAULT`` and ``ONLY_DYNAMIC`` are now deprecated aliases -of ``AND``). The enums are hashable, and their stubs no longer claim they are -``enum.IntEnum`` subclasses. ``ColliderSet.remove`` takes ``wake_up`` (``wake_parent`` still -works but is deprecated) and ``RigidBody.add_gravitational_force`` takes ``wake_up``. The -force and torque docstrings now state that user forces persist until -``reset_forces`` / ``reset_torques``. - -Soft-body additions: the FEM solver is always built in (``SoftBodySolver``, -``SoftBodyBuilder.solver`` / ``solver=``, ``SoftBody.solver``, ``SoftFemParameters`` on -``SoftBodiesSettings.fem``); ``SoftBody.volumetric_with`` with ``VolumeMeshParameters`` and -``MeshEnclosure``; ``to_trimesh()`` on ``Ball``, ``Cuboid``, ``Capsule``, ``Cylinder``, -``Cone``, ``ConvexPolyhedron``, ``HeightField`` and ``Voxels``; -``SoftBody.set_cluster_shape_matching_target``, ``set_edge_tear_resistance``, -``set_cell_tear_resistance`` and ``set_particle_damaged``; ``SoftBody.particle_positions`` and -``particle_velocities`` are writable. ``SoftBody.material`` and -``IntegrationParameters.soft_bodies`` (with ``recovery`` and ``fem``) are now live views, with a -``copy()`` for a detached copy; assigning them still works. A view of a removed soft body raises -``InvalidHandle``; ``RigidBodySet.remove`` and ``ColliderSet.remove`` raise ``ValueError`` for a -soft-body proxy or collision mesh when ``soft_bodies`` is left out; the soft-body index arrays -(pinned particles, elements, cluster particles, torn edges and cells) accept any memory layout and -integer dtype, where a sliced array used to be silently ignored. - -**Breaking — ``SpringCoefficients`` keywords and defaults.** Its constructor takes ``natural_frequency`` and -``damping_ratio`` (``stiffness`` and ``damping`` remain as deprecated aliases, for the keywords and -the properties), and its defaults are now those of ``SpringCoefficients.contact_defaults()`` -(``30.0`` Hz and ``10.0``, instead of a damping ratio of ``5.0``). +0.36.0 (24 September 2026) +-------------------------- + +**Added** + +- Soft bodies: ``PhysicsWorld.add_soft_body``, ``SoftBodySet``, ``SoftBody`` generators + (``rope``, ``cloth``, ``trimesh``, ``volumetric``…), ``SoftBodyBuilder``, ``SoftBodyMaterial``. +- Soft-body tearing and cutting (``tear_soft_body``, ``cut_soft_body``), reported as + ``SoftBodyTearEvent`` (``EventHandler.handle_soft_body_tear_event``). +- Soft-body clusters (``add_soft_body_cluster``) and deformable colliders (``insert_deformable``). +- The FEM soft-body solver is always built in (``SoftBodySolver``, ``SoftFemParameters``). +- ``RigidBodyType.SOFT_FRAME``, ``RigidBody.additional_pgs_iterations``, + ``IntegrationParameters.soft_bodies``; snapshots include the soft bodies. +- ``PhysicsWorld.detect_collisions()``, ``PhysicsWorld.quarantine`` and ``build_features()``. +- ``RigidBody.set_next_kinematic_*`` and ``allow_fast_rotation``; ``Collider.*_wrt_parent``. +- ``Collider.segment`` / ``polyline`` / ``voxelized_mesh`` (with ``FillMode``), and ``to_trimesh()`` + on the primitive shapes. +- ``NarrowPhase.contact_pairs_with``; solver contacts readable and editable from hooks + (``set_solver_contact``, ``set_tangent_velocity``); ``QueryPipeline.project_point(max_dist=)``. +- PD/PID controller gain properties and the world-space ``Wheel`` state. +- Live ``MultibodyJoint`` views and ``Multibody.apply_displacements``. +- MJCF actuators, keyframes and contact rules (``MjcfRobotHandles.contact_hooks``). +- ``IntegrationParameters.warmstart_joints`` / ``friction_in_bias_pass``; deterministic + ``sin``, ``cos``, ``exp``, ``sqrt``… in ``rapier3d.math``. +- The wheels are built with rapier's ``profiler`` feature, so the ``Counters`` timings are measured. +- Testbed: ``set_world`` accepts a ``PhysicsWorld``, soft-body rendering and examples. + +**Modified** + +- **Breaking:** ``SpringCoefficients`` takes ``natural_frequency`` / ``damping_ratio`` (``stiffness`` + / ``damping`` are deprecated aliases) and its default damping ratio is ``10.0`` instead of ``5.0``. +- ``RigidBodySet.remove``, ``ColliderSet.remove``, ``PhysicsPipeline.step`` and + ``DebugRenderPipeline.render`` take an optional ``soft_bodies`` argument. +- ``SolverFlags.COMPUTE_IMPULSES`` is renamed ``COMPUTE_RIGID_IMPULSES``. +- Deprecated: ``FrictionModel.COEFFICIENT`` (now ``SIMPLIFIED``), ``ColliderSet.remove(wake_parent=)`` + (now ``wake_up``), and the ``InteractionGroups`` test modes ``DEFAULT`` / ``ONLY_DYNAMIC``. +- The enums are hashable; the type stubs cover the whole package and ``Vec3`` / ``Point3`` are read-only. + +**Fixed** + +- Heightfields read row-major arrays correctly (the heights were scrambled). +- ``update_query_pipeline`` no longer consumes the collider changes, which broke the next ``step()``. +- Using a view of a removed object raises ``InvalidHandle`` instead of a ``PanicException``. +- Reading the world from a hook, or using it from another thread, no longer panics. +- ``len(multibody_joints)`` counts joints, scalar PID gains apply to every axis, and + ``ContactData.contact_id`` is the point index (it was always 0). +- ``update_vehicle`` and ``solve_character_collision_impulses`` honor the ``QueryFilter`` predicate. +- ``PhysicsWorld.clear()`` also removes the soft bodies. + +0.35.1 (08 August 2026) +----------------------- **Breaking — repackaged as ``rapier3d``.** The single ``rapier`` umbrella package (with ``rapier.dim3`` submodules) has been replaced by the @@ -103,97 +85,3 @@ reports it. A worker count gives the world its own pool; ``None`` goes back to rayon's global pool (one worker per logical CPU, shared by every world) and ``1`` runs everything inline on the calling thread. Results are bit-identical whatever the worker count. - -**Controllers.** ``Wheel.center`` / ``suspension`` / ``axle`` give the -world-space state of a vehicle wheel after the last update. -``PdController`` and ``PidController`` expose their gains as ``lin_kp`` / -``ang_kp`` / ``lin_kd`` / ``ang_kd`` (plus ``lin_ki`` / ``ang_ki`` and the -read-only ``lin_integral`` / ``ang_integral`` on the PID), and -``rigid_body_correction`` takes an optional ``target_vels`` -(``RigidBodyVelocity``). ``CharacterCollision.hit`` aliases ``toi``. -Fixes: scalar gains (``PidController(Kp=60.0)``) are applied to every axis -instead of raising ``TypeError``; the ``PidController.axes`` setter was -exposed as ``axes_attr``; ``update_vehicle`` and -``solve_character_collision_impulses`` now honor the ``QueryFilter`` -predicate (evaluated once per collider before the update) and -``update_vehicle`` always excludes the chassis colliders; -``KinematicCharacterController(snap_to_ground=None)`` (and -``autostep=None``) disables the feature; out-of-range wheel indices raise -``IndexError`` instead of ``TypeError``. The unused ``bodies`` / -``colliders`` arguments of ``move_shape`` and ``character_pos`` of -``solve_character_collision_impulses`` accept ``None``; passing sets other -than the query pipeline's now raises ``ValueError``. - -**Joints.** ``multibody_joints[handle]`` returns a live -:class:`~rapier3d.MultibodyJoint` (``data``, ``kinematic``, ``coords``, -``link_id``, ``multibody``); modifying its ``data`` wakes its bodies up and -changing its locked axes raises ``ValueError``. -:meth:`~rapier3d.Multibody.apply_displacements` applies the result of -``inverse_kinematics_for_link``, which now takes a ``joint_can_move`` -callback. ``PrismaticJointBuilder.motor``, the ``RopeJointBuilder`` motor -methods and ``RopeJoint.motor`` / ``set_motor_*``. The joint builders accept -``softness=`` as a keyword argument. Fixed: ``len(multibody_joints)`` now -counts joints (like iteration) instead of multibodies; -``InverseKinematicsOption`` shows its ``constrained_axes`` in its repr. - -**Loaders.** MJCF actuators, keyframes and contact rules: -``MjcfRobotHandles.actuators`` (:class:`~rapier3d.loaders.mjcf.MjcfActuatorHandle`), -``apply_controls``, ``apply_keyframe``, ``keyframe_controls``, -``keyframe_names`` and ``contact_hooks`` (a -:class:`~rapier3d.loaders.mjcf.MjcfContactHooks` that runs natively when -assigned to ``PhysicsWorld.physics_hooks``); ``MjcfRobot.gravity`` and -``keyframe_names``; ``MjcfMultibodyOptions.SKIP_JOINT_SPRINGS``. The -``Mjcf``/``UrdfMultibodyOptions`` flags support ``in``. Fixed: the repr of -``MjcfModel`` no longer shows ``Some(...)``, the URDF handles have a repr, -and the ``load_from_path`` docs list the supported formats. - -**Scene queries, contacts and hooks.** ``QueryPipeline.project_point`` takes a -``max_dist``. ``NarrowPhase.contact_pairs_with`` / ``intersection_pairs_with`` -list the pairs of one collider. ``ContactManifoldData.solver_contacts`` and -``solver_contact_world_points`` expose the solver contacts; -``SolverContact.tangent_velocity`` and -``ContactModificationContext.set_solver_contact`` / ``set_tangent_velocity`` -modify them from a hook. ``PairFilterContext`` / ``ContactModificationContext`` -expose the ``colliders`` / ``bodies`` being stepped, and event handlers receive -them (and the ``SoftBodySet``) instead of ``None``: during a callback, they and -the views into them can be read (not modified). ``QueryFilter``'s current groups -and predicate are read through ``interaction_groups`` / ``predicate_fn`` (the -former getters were shadowed by the builder methods). -``DebugRenderPipeline.style`` is now the same live object on every access. - -Fixes: ``PhysicsWorld.update_query_pipeline`` / ``QueryPipeline.update`` no -longer consume the collider changes, which made the next ``step()`` panic or -miss them; the query pipeline is up to date after every step, so -``auto_update_query`` now has no effect. Reading the world from a hook or event -handler no longer raises a ``PanicException`` (running a query or stepping from -there raises ``RuntimeError``). A hooks or event-handler object may omit any of its -methods. ``DebugLineCollector.lines()`` returns the documented ``(N, 2, 3)`` -array. ``ContactData.contact_id`` is the index of the point in its manifold -(it was always 0). ``ShapeCastHit`` documents its frames (``witness1`` / -``normal1`` on the hit collider in world space, ``witness2`` / ``normal2`` on -the cast shape in its local space). ``ContactPair``, ``ContactForceEvent``, -``ContactManifoldData`` and ``SolverContact`` have a ``repr``. - -**World and pipelines.** ``PhysicsWorld.quarantine`` / ``PhysicsPipeline.quarantine`` -report the objects neutralized by the last step because their state became non-finite -(:class:`~rapier3d.Quarantine`). ``PhysicsWorld.detect_collisions()`` updates the contacts -and the scene queries without stepping, with the world's new ``collision_pipeline``. -``PhysicsPipeline.counters`` is now a live view (``counters.disable()`` / ``enable()`` -act on the pipeline), and the wheels are built with rapier's ``profiler`` feature so -its timings are measured. ``build_features()`` (:class:`~rapier3d.BuildFeatures`) -reports the Cargo profile and the optional features of the loaded extension. -``IntegrationParameters.warmstart_joints`` and ``friction_in_bias_pass``; -``FrictionModel.SIMPLIFIED`` is the new name of ``COEFFICIENT`` (kept as a deprecated -alias). ``rapier3d.math`` gains ``sin``, ``cos``, ``tan``, ``asin``, ``acos``, ``atan``, -``atan2``, ``exp``, ``log``, ``pow`` and ``sqrt``, computed by Rapier's math backend -(cross-platform deterministic with the ``determinism`` feature). ``SoftBody.is_enabled`` -is writable. The testbed's ``set_world`` accepts a ``PhysicsWorld``. - -**Fixes.** ``PhysicsWorld.clear()`` also removes the soft bodies and resets the -pipelines. Using a rigid-body, collider, impulse-joint or multibody view after its object -was removed raises ``InvalidHandle`` instead of a ``PanicException``. A world and its sets -can be used from any thread: using them from another thread no longer panics, and -modifying them (or reading them outside of its callbacks) while another thread steps the -world raises ``RuntimeError``. The type stubs now -cover ``PhysicsWorld``, the pipelines, the counters and the whole package -(``__init__.pyi``), and ``Vec3`` / ``Point3`` coordinates are read-only. diff --git a/bindings/python/rapier-testbed/pyproject.toml b/bindings/python/rapier-testbed/pyproject.toml index bf14fbaef..d4d640f0d 100644 --- a/bindings/python/rapier-testbed/pyproject.toml +++ b/bindings/python/rapier-testbed/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "rapier-testbed" -version = "0.35.2" +version = "0.36.0" description = "Panda3D-based visual testbed and example gallery for the Rapier Python bindings." readme = "README.md" license = { text = "Apache-2.0" } diff --git a/bindings/typescript/CHANGELOG.md b/bindings/typescript/CHANGELOG.md index 6fbb00289..8ce07fb46 100644 --- a/bindings/typescript/CHANGELOG.md +++ b/bindings/typescript/CHANGELOG.md @@ -1,68 +1,40 @@ -## Unreleased +## 0.21.0 (24 September 2026) ### Breaking changes -- `World.removeCollider`, `RigidBodySet.remove` and `ColliderSet.remove` take the world's - `SoftBodySet`: removing a rigid body that is a soft-body cluster proxy removes its cluster, - and removing a deformable collider drops its mesh from its soft body. `World` passes its own - set, so only direct users of the sets are affected. -- `PhysicsPipeline.step`, `DebugRenderPipeline.render`, `SerializationPipeline.serializeAll` - and `RigidBodySet.remove` take the `SoftBodySet` as an extra argument, right after the - `ColliderSet`. +- `PhysicsPipeline.step`, `DebugRenderPipeline.render`, `SerializationPipeline.serializeAll` and + `RigidBodySet.remove` take the `SoftBodySet` right after the `ColliderSet`. +- `ColliderSet.remove` takes the `SoftBodySet` right after the `RigidBodySet`. - The `World` constructor takes the raw soft-body set right after the raw collider set. -- `RigidBodyType` gained the `SoftFrame` variant: the proxies a soft body creates for itself - and its clusters. `RigidBody.isSoftFrame()` tells them apart. +- `RigidBodyType.SoftFrame`: the rigid proxies of soft bodies and their clusters (`RigidBody.isSoftFrame()`). ### Added -- `SoftBodyDesc.setOriented` and `SoftBody.isMeshOriented`: whether the shape of a soft body's - collision surface carries the `ORIENTED` flag. Left unset, a closed surface is oriented (it - encloses solid matter, so nothing is held inside it); `setOriented(false)` makes it a shell - whose inner side holds the bodies inside it. A deformable collider follows its own shape's flag. -- Soft bodies: `World.createSoftBody(desc)` inserts a deformable body made of particles linked - by edges, bending constraints and cells, simulated together with the rigid bodies, contacts - and joints. `SoftBodyDesc` offers the generators `rope`, `cloth`, `clothTube`, - `clothAnisotropic`, `cuboid`, `sphere`, `trimesh` and `volumetric` in 3D, and `rope`, - `polyline`, `polygon`, `disk`, `grid` and `volumetric` in 2D, plus setters for pinned - particles, masses, the material, the cell model (`SoftBodyCellModel`), volume preservation, - shape matching, self contacts, the particle radius and the surface collider template. -- `SoftBodyMaterial`: the softness of each constraint kind, the elastic parameters of the cells - (Young's modulus, Poisson's ratio), plasticity (`plasticYield`, `plasticCreep`, - `edgePlasticYield`…) and tearing thresholds (`tearStrain`, `tearForce`, `minPiece`). -- `SoftBody`: particle positions, velocities, masses and pinning; attachments to rigid bodies - (`attachParticle`); forces and impulses (`addForce`, `applyImpulseAtPoint`, - `applyRadialImpulse`…); the edges, cells, dihedrals and boundary of the body with their - stresses; the material; volume preservation; the hidden `rootBody()`; clusters - (`clusterProxy`, `setClusterPinned`, `setClusterKinematicTarget`…) and collision meshes - (`meshVertices`, `meshIndices`). -- Tearing and cutting: `World.tearSoftBody`, `World.cutSoftBody` and `SoftBody.tearEdge` / - `tearCell`; the pieces a tear disconnects become soft bodies of their own. Tears report a - `SoftBodyTearEvent`, also drained from an `EventQueue` with `drainSoftBodyTearEvents`. -- Clusters: `World.addSoftBodyCluster` creates a rigid proxy over some particles that joints - (`World.createImpulseJoint`) and colliders can attach to; `World.removeSoftBodyCluster`. -- Deformable colliders: `World.createDeformableCollider(desc, binding, proxy)` binds a - triangle mesh (3D) or polyline (2D) built with the `DEFORMABLE` flag to a cluster, through a - `SoftMeshBinding` (`direct`, `directByPosition`, `skinned`). `Polyline` and - `ColliderDesc.polyline` take a `PolylineFlags` argument; `Collider.isDeformable()` and - `Collider.softBody()` identify such colliders. -- `IntegrationParameters.softBodiesResweepStrain`, `softBodiesMaxExtraSubsteps` and - `softBodiesContactStiffening` tune the soft-body solver. -- `RigidBody.additionalPgsIterations()` / `setAdditionalPgsIterations` and - `RigidBodyDesc.setAdditionalPgsIterations`: extra internal PGS iterations per substep for the - island component of a body. -- `RigidBody.softBody()` and `RigidBody.softCluster()` identify the soft body and cluster a - proxy stands for. -- Snapshots (`World.takeSnapshot` / `restoreSnapshot`) include the soft bodies. -- `PolylineFlags.ORIENTED` (2D): one-sided polylines whose contact normals are clamped to - the outward side. -- `CompoundFlags.FIX_INTERNAL_EDGES`: the seams between the parts of a compound (or of a - convex decomposition) no longer catch sliding bodies. Taken by `Compound`, - `ColliderDesc.compound` and `ColliderDesc.convexDecomposition`. -- The testbeds gained soft-body demos ("soft bodies" in 2D and 3D, "soft tearing" in 3D). +- Soft bodies: `World.createSoftBody(SoftBodyDesc)`, `World.softBodies` (`SoftBodySet`), `SoftBody`, + `SoftBodyMaterial`, and the `rope`, `cloth`, `volumetric`… generators of `SoftBodyDesc`. +- Two soft-body solvers, selected with `SoftBodySolver` (`Constraints`, `Fem`); the npm builds enable `fem`. +- Soft-body plasticity, tearing and cutting: `World.tearSoftBody`, `World.cutSoftBody`, and + `SoftBodyTearEvent`s drained with `EventQueue.drainSoftBodyTearEvents`. +- Soft-body clusters (`World.addSoftBodyCluster`), rigid proxies that joints and colliders attach to. +- Deformable colliders: `World.createDeformableCollider` binds a `DEFORMABLE` trimesh (3D) or + polyline (2D) to a soft body through a `SoftMeshBinding`. +- Soft-body tuning in `IntegrationParameters` (`softBodies*` fields, `softBodiesRecovery`). +- `RigidBody.softBody()` / `softCluster()`; snapshots include the soft bodies. +- `RigidBody(Desc).setAdditionalPgsIterations`: extra PGS iterations per substep for a body's island. +- `PolylineFlags` (2D `ORIENTED`, `DEFORMABLE`), `CompoundFlags.FIX_INTERNAL_EDGES`, and + `TriMeshFlags.DEFORMABLE` / `FIX_INTERNAL_EDGES_TWO_SIDED`; `Polyline.flags`, `Compound.flags`. +- Soft-body demos in the 2D and 3D testbeds. ### Fixed -- The shape-cast docs now state the frame (world or local) of each witness point and normal. +- `EventQueue.clear()` now also discards the pending contact-force events. +- Docs: shape-cast witness frames, world-space `CharacterCollision` witnesses, predictive distance default. + +### Modified + +- Update to Rapier 0.36.0 and parry 0.31, see + [rapier's changelog](https://github.com/dimforge/rapier/blob/master/CHANGELOG.md#v0360-24-september-2026). +- The bindings moved to the `bindings/typescript` directory of the rapier repository. ## 0.20.0 (08 August 2026) diff --git a/bindings/typescript/builds/prepare_builds/templates/Cargo.toml.tera b/bindings/typescript/builds/prepare_builds/templates/Cargo.toml.tera index 9da07f96e..a129d244f 100644 --- a/bindings/typescript/builds/prepare_builds/templates/Cargo.toml.tera +++ b/bindings/typescript/builds/prepare_builds/templates/Cargo.toml.tera @@ -1,6 +1,6 @@ [package] name = "dimforge_{{ js_package_name }}" # Can't be named rapier{{ dimension }}d which conflicts with the dependency. -version = "0.20.0" +version = "0.21.0" authors = ["Sébastien Crozet "] description = "{{ dimension }}-dimensional physics engine in Rust - official JS bindings." documentation = "https://rapier.rs/rustdoc/rapier{{ dimension }}d/index.html" @@ -30,7 +30,7 @@ rust.unexpected_cfgs = { level = "warn", check-cfg = [ # `rapier{2,3}d` is patched to the in-repo crate (see the workspace # `[patch.crates-io]` in ../../Cargo.toml); this version requirement must stay # semver-compatible with that crate's version for the patch to take effect. -rapier{{ dimension }}d = { version = "0.35.0", features = [ +rapier{{ dimension }}d = { version = "0.36.0", features = [ "serde-serialize", "debug-render", "profiler", diff --git a/crates/rapier3d-urdf/CHANGELOG.md b/crates/rapier3d-urdf/CHANGELOG.md index adbf660e3..20857f084 100644 --- a/crates/rapier3d-urdf/CHANGELOG.md +++ b/crates/rapier3d-urdf/CHANGELOG.md @@ -1,6 +1,6 @@ -## Unreleased +## v0.36.0 (24 September 2026) -### Fix +### Fixed - URDF `` angles are now composed as fixed-axis roll-pitch-yaw (`Rz(yaw) * Ry(pitch) * Rx(roll)`), as the URDF specification defines. They diff --git a/crates/rapier_testbed2d-f64/Cargo.toml b/crates/rapier_testbed2d-f64/Cargo.toml index d16041ac5..16c53a8df 100644 --- a/crates/rapier_testbed2d-f64/Cargo.toml +++ b/crates/rapier_testbed2d-f64/Cargo.toml @@ -63,5 +63,5 @@ env_logger.workspace = true [dependencies.rapier] package = "rapier2d-f64" path = "../rapier2d-f64" -version = "0.35.2" +version = "0.36.0" features = ["serde-serialize", "debug-render", "profiler"] diff --git a/crates/rapier_testbed2d/Cargo.toml b/crates/rapier_testbed2d/Cargo.toml index 480bd9792..ef438a46e 100644 --- a/crates/rapier_testbed2d/Cargo.toml +++ b/crates/rapier_testbed2d/Cargo.toml @@ -63,5 +63,5 @@ env_logger.workspace = true [dependencies.rapier] package = "rapier2d" path = "../rapier2d" -version = "0.35.2" +version = "0.36.0" features = ["serde-serialize", "debug-render", "profiler"] diff --git a/crates/rapier_testbed3d-f64/Cargo.toml b/crates/rapier_testbed3d-f64/Cargo.toml index ca00b73f6..ae814a649 100644 --- a/crates/rapier_testbed3d-f64/Cargo.toml +++ b/crates/rapier_testbed3d-f64/Cargo.toml @@ -63,5 +63,5 @@ env_logger.workspace = true [dependencies.rapier] package = "rapier3d-f64" path = "../rapier3d-f64" -version = "0.35.2" +version = "0.36.0" features = ["serde-serialize", "debug-render", "profiler"] diff --git a/crates/rapier_testbed3d/Cargo.toml b/crates/rapier_testbed3d/Cargo.toml index 997f96db8..76b61287e 100644 --- a/crates/rapier_testbed3d/Cargo.toml +++ b/crates/rapier_testbed3d/Cargo.toml @@ -63,5 +63,5 @@ env_logger.workspace = true [dependencies.rapier] package = "rapier3d" path = "../rapier3d" -version = "0.35.2" +version = "0.36.0" features = ["serde-serialize", "debug-render", "profiler"] diff --git a/website/src/userGuideVariants.js b/website/src/userGuideVariants.js index c8c2460ec..71ec9bd3d 100644 --- a/website/src/userGuideVariants.js +++ b/website/src/userGuideVariants.js @@ -4,10 +4,10 @@ // Each variant is a folder of `docs/user_guides`, generated from the templates by `generate_user_guides.sh` // (except "All", which is the templates folder itself, showing every flavour with its color code). const variants = [ - {id: 'rust', dir: 'rust', label: 'Rust', version: '0.35'}, - {id: 'c', dir: 'c', label: 'C', version: '0.35'}, - {id: 'js', dir: 'javascript', label: 'JavaScript', version: '0.17'}, - {id: 'python', dir: 'python', label: 'Python', version: '0.35'}, + {id: 'rust', dir: 'rust', label: 'Rust', version: '0.36'}, + {id: 'c', dir: 'c', label: 'C', version: '0.36'}, + {id: 'js', dir: 'javascript', label: 'JavaScript', version: '0.21'}, + {id: 'python', dir: 'python', label: 'Python', version: '0.36'}, {id: 'bevy', dir: 'bevy_plugin', label: 'Bevy plugin', version: '0.37'}, {id: 'all', dir: 'templates', label: 'All'}, ];