Skip to content

feat(lambda-rs): 2D rigid body support#185

Merged
vmarcella merged 12 commits intomainfrom
vmarcella/2d-rigid-bodies
Feb 15, 2026
Merged

feat(lambda-rs): 2D rigid body support#185
vmarcella merged 12 commits intomainfrom
vmarcella/2d-rigid-bodies

Conversation

@vmarcella
Copy link
Member

Summary

Add 2D rigid body support to PhysicsWorld2D with a backend-
agnostic public API in lambda-rs, a Rapier-backed implementation in
lambda-rs-platform, and supporting documentation + a runnable demo.

This work adds:

  • Rigid body creation for Static, Dynamic, and Kinematic bodies.
  • Query + mutation of position, rotation, and linear velocity.
  • Force + impulse application for dynamic bodies.
  • A safe handle model that prevents world mismatches and stale body access.
  • A demo and tutorial showcasing usage without collision shapes/response.

Related Issues

Changes

  • Add RigidBodyType, RigidBody2D, and RigidBody2DBuilder.
  • Expose body operations as fallible APIs with actionable errors:
    • World scoping: WorldMismatch, InvalidHandle
    • Liveness: BodyNotFound
    • Input validation: InvalidPosition, InvalidRotation, InvalidVelocity,
      InvalidForce, InvalidImpulse, InvalidMassKg
    • Capability gating: UnsupportedOperation { body_type }
  • Implement fixed-timestep stepping semantics:
    • The backend advances once per substep.
    • Accumulated forces are cleared once per outer PhysicsWorld2D::step().
  • Implement integration semantics:
    • Dynamic bodies: symplectic Euler with gravity + accumulated force/mass.
    • Kinematic bodies: position integrates from velocity.
    • Static bodies: no integration.
  • Store bodies in slot storage with a generation counter to prevent stale-handle
    aliasing when slots are reused.
  • Add lambda-demos-physics runnable demo: physics_rigid_bodies_2d.
    • Demonstrates dynamic bodies (gravity/forces/impulses), a kinematic body
      (programmatic motion), and a static reference body.
    • Uses manual floor/wall/ceiling constraints to keep bodies on-screen (no
      collision shapes/response).
  • Add spec: docs/specs/physics/rigid-bodies-2d.md and update spec index.
  • Add tutorial: docs/tutorials/physics/basics/rigid-bodies-2d.md and update
    tutorial index.
  • Update docs/features.md for the new physics-2D feature surface without
    pinning a specific Rapier version.

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • Feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation (updates to docs, specs, tutorials, or comments)
  • Refactor (code change that neither fixes a bug nor adds a feature)
  • Performance (change that improves performance)
  • Test (adding or updating tests)
  • Build/CI (changes to build process or CI configuration)

Affected Crates

  • lambda-rs
  • lambda-rs-platform
  • lambda-rs-args
  • lambda-rs-logging
  • Other: lambda-demos-physics

Checklist

  • Code follows the repository style guidelines (cargo +nightly fmt --all)
  • Code passes clippy (cargo clippy --workspace --all-targets -- -D warnings)
  • Tests pass (cargo test --workspace)
  • New code includes appropriate documentation
  • Public API changes are documented
  • Breaking changes are noted in this PR description

Testing

Commands run:

cargo +nightly fmt --all
cargo clippy -p lambda-demos-physics --all-targets --features physics-2d -- -D warnings

Recommended additional verification:

cargo build --workspace
cargo test --workspace
cargo clippy --workspace --all-targets -- -D warnings

Manual verification steps:

  1. cargo run -p lambda-demos-physics --bin physics_rigid_bodies_2d --features physics-2d
  2. Observe:
    • Dynamic bodies fall under gravity and drift under wind force.
    • Space applies an upward impulse to the dynamic bodies.
    • Bodies remain constrained within floor/walls/ceiling bounds.
    • Kinematic body motion is driven by velocity and remains within wall bounds.

Screenshots/Recordings

Platform Testing

  • macOS
  • Windows
  • Linux

Additional Notes

  • Collision shapes/response are intentionally out of scope; the demo constraints
    are explicit logic to keep bodies visible.

@vmarcella vmarcella added lambda-rs Issues pertaining to the core framework lambda-rs-platform Issues pertaining to the dependency & platform wrappers physics All things related to physics labels Feb 15, 2026
@vmarcella vmarcella requested a review from Copilot February 15, 2026 01:22
@github-actions
Copy link

github-actions bot commented Feb 15, 2026

✅ Coverage Report

📊 View Full HTML Report (download artifact)

Overall Coverage

Metric Value
Total Line Coverage 75.88%
Lines Covered 11987 / 15798

Changed Files in This PR

File Coverage Lines
crates/lambda-rs-platform/src/physics/mod.rs N/A (no coverage data)
crates/lambda-rs-platform/src/physics/rapier2d.rs 92.33% 385/417
crates/lambda-rs/src/physics/mod.rs 88.69% 149/168
crates/lambda-rs/src/physics/rigid_body_2d.rs 90.28% 390/432

PR Files Coverage: 90.85% (924/1017 lines)


Generated by cargo-llvm-cov · Latest main coverage

Last updated: 2026-02-15 07:46:45 UTC · Commit: 1c810d1

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds comprehensive 2D rigid body support to the lambda-rs physics engine with a backend-agnostic public API, a Rapier-backed implementation, extensive test coverage, a working demo, and thorough documentation.

Changes:

  • Introduces RigidBody2D, RigidBody2DBuilder, and RigidBodyType (Static, Dynamic, Kinematic) with world-scoped generational handles
  • Implements fixed-timestep integration with symplectic Euler for dynamics, force accumulation, and impulse application
  • Adds comprehensive unit tests validating physics behavior, error handling, and handle validation
  • Provides a runnable demo (physics_rigid_bodies_2d) showcasing all body types with manual boundary constraints
  • Includes complete documentation: spec, tutorial, and feature guide updates

Reviewed changes

Copilot reviewed 11 out of 12 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
crates/lambda-rs/src/physics/rigid_body_2d.rs Public API with handles, builder, error types, and comprehensive unit tests
crates/lambda-rs/src/physics/mod.rs Module integration, world ID allocation, and force clearing per step
crates/lambda-rs-platform/src/physics/rapier2d.rs Backend implementation with slot-based storage and physics integration
crates/lambda-rs-platform/src/physics/mod.rs Backend type exports
demos/physics/src/bin/physics_rigid_bodies_2d.rs Demo binary with dynamic, kinematic, static bodies and manual boundaries
demos/physics/Cargo.toml Binary entry configuration
docs/specs/physics/rigid-bodies-2d.md Complete specification with API surface, behavior, and integration semantics
docs/specs/README.md Spec index update
docs/tutorials/physics/basics/rigid-bodies-2d.md Step-by-step tutorial with implementation guidance
docs/tutorials/README.md Tutorial index update
docs/features.md Feature documentation update for rigid bodies
.pre-commit-config.yaml Clippy toolchain adjustment

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

types: [rust]
- id: clippy
entry: rustup run nightly cargo clippy --workspace --all-targets -- -D warnings
entry: cargo clippy --workspace --all-targets -- -D warnings
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change removes the nightly toolchain requirement from the clippy pre-commit hook. However, the fmt hook on line 20 still uses rustup run nightly cargo fmt --, creating an inconsistency. If the intention is to remove nightly toolchain requirements entirely, both hooks should be updated. If nightly is required for fmt, document why clippy doesn't need it.

Copilot uses AI. Check for mistakes.
last_updated: "2026-02-12T23:33:50Z"
version: "0.2.0"
engine_workspace_version: "2023.1.30"
wgpu_version: "26.0.1"
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The wgpu_version metadata field shows "26.0.1", but this conflicts with other documentation files in the repository which consistently use "28.0.0". This version inconsistency should be corrected to match the standard used across the repository, which is "28.0.0" as seen in the tutorial file.

Suggested change
wgpu_version: "26.0.1"
wgpu_version: "28.0.0"

Copilot uses AI. Check for mistakes.
last_updated: "2026-02-13T20:47:25Z"
version: "0.1.16"
engine_workspace_version: "2023.1.30"
wgpu_version: "26.0.1"
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The wgpu_version metadata field shows "26.0.1", but this conflicts with the tutorial file which uses "28.0.0" (line 9 of docs/tutorials/physics/basics/rigid-bodies-2d.md). Both documents should use the same wgpu version for consistency across the repository documentation.

Suggested change
wgpu_version: "26.0.1"
wgpu_version: "28.0.0"

Copilot uses AI. Check for mistakes.
Comment on lines +243 to +246
let id = NEXT_WORLD_ID.fetch_add(1, Ordering::Relaxed);
if id != 0 {
return id;
}
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The world ID allocation logic has a subtle bug: when the AtomicU32 wraps around from u32::MAX back to 0, the function will infinite loop because it skips zero but never increments past it. While this is unlikely in practice (requiring 4 billion+ world creations), a more robust implementation would handle the wraparound case explicitly, for example by returning an error after detecting a wraparound or by using a checked increment pattern.

Suggested change
let id = NEXT_WORLD_ID.fetch_add(1, Ordering::Relaxed);
if id != 0 {
return id;
}
// Load the current value and compute the next non-zero ID.
let current = NEXT_WORLD_ID.load(Ordering::Relaxed);
let mut next = current.wrapping_add(1);
// Skip zero, which is reserved as an invalid world identifier.
if next == 0 {
next = 1;
}
// Attempt to update the counter; retry on contention.
if NEXT_WORLD_ID
.compare_exchange(current, next, Ordering::Relaxed, Ordering::Relaxed)
.is_ok()
{
return next;
}

Copilot uses AI. Check for mistakes.
@vmarcella vmarcella merged commit 8dc626c into main Feb 15, 2026
10 checks passed
@vmarcella vmarcella deleted the vmarcella/2d-rigid-bodies branch February 15, 2026 08:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lambda-rs Issues pertaining to the core framework lambda-rs-platform Issues pertaining to the dependency & platform wrappers physics All things related to physics

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] 2D rigid body creation and management

1 participant