Skip to content

Commit 1c624de

Browse files
authored
feat(lambda-rs): Add 2D physics world for managing physics in 2D environments (#176)
## Summary Add an opt-in 2D physics world API (`PhysicsWorld2D`) to `lambda-rs`, backed by Rapier, and provide a minimal demo and tutorial that render a falling 2D quad while stepping an empty physics world. ## Related Issues - Resolves #118 ## Changes - Add `lambda::physics::PhysicsWorld2D` and `PhysicsWorld2DBuilder` behind the `physics-2d` feature, including validation and fixed-timestep stepping. - Add `lambda_platform::physics::rapier2d` backend wrapper and wire it behind `lambda-rs-platform/physics-2d`. - Add unit tests for builder validation and stepping an empty world. - Add a dedicated physics demos crate (`demos/physics`) and a minimal falling quad demo (`physics_falling_quad`) that defaults to `physics-2d` enabled. - Add a specification for the 2D physics world under `docs/specs/physics/`. - Update `docs/features.md` to document `physics-2d` feature flags and their dependency relationships. - Add a tutorial documenting how to build the falling quad demo from scratch. ## Type of Change - [ ] Bug fix (non-breaking change that fixes an issue) - [x] Feature (non-breaking change that adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [x] 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) - [x] Test (adding or updating tests) - [ ] Build/CI (changes to build process or CI configuration) ## Affected Crates - [x] `lambda-rs` - [x] `lambda-rs-platform` - [ ] `lambda-rs-args` - [ ] `lambda-rs-logging` - [x] Other: `demos/physics`, docs ## 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`) - [x] New code includes appropriate documentation - [x] Public API changes are documented - [ ] Breaking changes are noted in this PR description ## Testing **Commands run:** ```bash # Suggested minimum coverage for this PR: cargo test -p lambda-rs --features physics-2d # Demo: cargo run -p lambda-demos-physics --bin physics_falling_quad ``` **Manual verification steps (if applicable):** 1. Run `cargo run -p lambda-demos-physics --bin physics_falling_quad`. 2. Confirm a 2D quad falls under gravity and remains visible while the physics world steps each fixed tick. ## Screenshots/Recordings - N/A ## Platform Testing - [x] macOS - [ ] Windows - [ ] Linux ## Additional Notes
2 parents a3ce973 + e45973b commit 1c624de

File tree

17 files changed

+2371
-11
lines changed

17 files changed

+2371
-11
lines changed

Cargo.lock

Lines changed: 373 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ members = [
1111
"tools/lambda_audio",
1212
# Demo crates (not built by default; see `default-members`).
1313
"demos/audio",
14+
"demos/physics",
1415
"demos/render",
1516
"demos/minimal",
1617
]

crates/lambda-rs-platform/Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ symphonia = { version = "=0.5.5", optional = true, default-features = false, fea
2929
"wav",
3030
"pcm",
3131
] }
32+
rapier2d = { version = "=0.32.0", optional = true }
3233

3334
# Force windows crate to 0.62 to unify wgpu-hal and gpu-allocator dependencies.
3435
# Both crates support this version range, but Cargo may resolve to different
@@ -65,3 +66,8 @@ audio = ["audio-device", "audio-decode-wav", "audio-decode-vorbis"]
6566
audio-device = ["dep:cpal"]
6667
audio-decode-wav = ["dep:symphonia"]
6768
audio-decode-vorbis = ["dep:symphonia"]
69+
70+
# --------------------------------- PHYSICS -----------------------------------
71+
72+
# Umbrella features (disabled by default)
73+
physics-2d = ["dep:rapier2d"]

crates/lambda-rs-platform/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,6 @@ pub mod winit;
2323
feature = "audio-decode-vorbis"
2424
))]
2525
pub mod audio;
26+
27+
#[cfg(feature = "physics-2d")]
28+
pub mod physics;
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
//! Internal physics backends.
2+
//!
3+
//! This module contains physics backend implementations that support the
4+
//! higher-level `lambda-rs` physics APIs without exposing vendor types from
5+
//! `lambda-rs` itself.
6+
7+
pub mod rapier2d;
8+
9+
pub use rapier2d::PhysicsBackend2D;
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
//! Rapier-backed 2D physics backend.
2+
//!
3+
//! This module provides a minimal wrapper around `rapier2d` to support the
4+
//! higher-level `lambda-rs` physics APIs without exposing vendor types outside
5+
//! of the platform layer.
6+
7+
use rapier2d::prelude::*;
8+
9+
/// A 2D physics backend powered by `rapier2d`.
10+
///
11+
/// This type is an internal implementation detail used by `lambda-rs`.
12+
pub struct PhysicsBackend2D {
13+
gravity: Vector,
14+
integration_parameters: IntegrationParameters,
15+
islands: IslandManager,
16+
broad_phase: BroadPhaseBvh,
17+
narrow_phase: NarrowPhase,
18+
bodies: RigidBodySet,
19+
colliders: ColliderSet,
20+
impulse_joints: ImpulseJointSet,
21+
multibody_joints: MultibodyJointSet,
22+
ccd_solver: CCDSolver,
23+
pipeline: PhysicsPipeline,
24+
}
25+
26+
impl PhysicsBackend2D {
27+
/// Creates a new empty 2D physics backend.
28+
///
29+
/// # Arguments
30+
/// - `gravity`: The gravity vector in meters per second squared.
31+
/// - `timestep_seconds`: The fixed integration timestep in seconds.
32+
///
33+
/// # Returns
34+
/// Returns a new `PhysicsBackend2D` with no bodies, colliders, or joints.
35+
pub fn new(gravity: [f32; 2], timestep_seconds: f32) -> Self {
36+
let gravity_vector = Vector::new(gravity[0], gravity[1]);
37+
38+
let integration_parameters = IntegrationParameters {
39+
dt: timestep_seconds,
40+
..Default::default()
41+
};
42+
43+
return Self {
44+
gravity: gravity_vector,
45+
integration_parameters,
46+
islands: IslandManager::new(),
47+
broad_phase: BroadPhaseBvh::new(),
48+
narrow_phase: NarrowPhase::new(),
49+
bodies: RigidBodySet::new(),
50+
colliders: ColliderSet::new(),
51+
impulse_joints: ImpulseJointSet::new(),
52+
multibody_joints: MultibodyJointSet::new(),
53+
ccd_solver: CCDSolver::new(),
54+
pipeline: PhysicsPipeline::new(),
55+
};
56+
}
57+
58+
/// Returns the gravity vector used by this backend.
59+
///
60+
/// # Returns
61+
/// Returns the gravity vector in meters per second squared.
62+
pub fn gravity(&self) -> [f32; 2] {
63+
return [self.gravity.x, self.gravity.y];
64+
}
65+
66+
/// Returns the fixed integration timestep in seconds.
67+
///
68+
/// # Returns
69+
/// Returns the timestep used for each simulation step.
70+
pub fn timestep_seconds(&self) -> f32 {
71+
return self.integration_parameters.dt;
72+
}
73+
74+
/// Advances the simulation by one fixed timestep.
75+
///
76+
/// # Returns
77+
/// Returns `()` after applying integration and constraint solving for the
78+
/// configured timestep.
79+
pub fn step(&mut self) {
80+
return self.step_with_timestep_seconds(self.integration_parameters.dt);
81+
}
82+
83+
/// Advances the simulation by the given timestep.
84+
///
85+
/// # Arguments
86+
/// - `timestep_seconds`: The timestep used for this step.
87+
///
88+
/// # Returns
89+
/// Returns `()` after applying integration and constraint solving.
90+
pub fn step_with_timestep_seconds(&mut self, timestep_seconds: f32) {
91+
self.integration_parameters.dt = timestep_seconds;
92+
93+
self.pipeline.step(
94+
self.gravity,
95+
&self.integration_parameters,
96+
&mut self.islands,
97+
&mut self.broad_phase,
98+
&mut self.narrow_phase,
99+
&mut self.bodies,
100+
&mut self.colliders,
101+
&mut self.impulse_joints,
102+
&mut self.multibody_joints,
103+
&mut self.ccd_solver,
104+
&(),
105+
&(),
106+
);
107+
108+
return;
109+
}
110+
}

crates/lambda-rs/Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,11 @@ render-validation-encoder = []
8989
render-validation-instancing = []
9090
render-validation-render-targets = []
9191

92+
# --------------------------------- PHYSICS -----------------------------------
93+
94+
# Umbrella features (disabled by default)
95+
physics-2d = ["lambda-rs-platform/physics-2d"]
96+
9297

9398
# ---------------------------- PLATFORM DEPENDENCIES ---------------------------
9499

crates/lambda-rs/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ pub mod util;
2929
))]
3030
pub mod audio;
3131

32+
#[cfg(feature = "physics-2d")]
33+
pub mod physics;
34+
3235
/// The logging module provides a simple logging interface for Lambda
3336
/// applications.
3437
pub use logging;

0 commit comments

Comments
 (0)