Skip to content

Commit b0b05ee

Browse files
committed
[add] tutorial for the demo.
1 parent 6a3b507 commit b0b05ee

File tree

2 files changed

+286
-3
lines changed

2 files changed

+286
-3
lines changed

docs/tutorials/README.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ title: "Tutorials Index"
33
document_id: "tutorials-index-2025-10-17"
44
status: "living"
55
created: "2025-10-17T00:20:00Z"
6-
last_updated: "2026-02-07T00:00:00Z"
7-
version: "0.8.0"
6+
last_updated: "2026-02-13T00:00:00Z"
7+
version: "0.9.0"
88
engine_workspace_version: "2023.1.30"
99
wgpu_version: "28.0.0"
1010
shader_backend_default: "naga"
1111
winit_version: "0.29.10"
12-
repo_commit: "4b0c5abf6743788596177b3c10c3214db20ad6b1"
12+
repo_commit: "6a3b507eedddc39f568ed73cfadf34011d57b9a3"
1313
owners: ["lambda-sh"]
1414
reviewers: ["engine", "rendering"]
1515
tags: ["index", "tutorials", "docs"]
@@ -44,11 +44,13 @@ Browse all tutorials under `rendering/`.
4444
### Basics
4545

4646
- Physics 2D: Falling Quad (Kinematic) — [physics/basics/falling-quad-kinematic.md](physics/basics/falling-quad-kinematic.md)
47+
- Physics 2D: Rigid Bodies (No Collisions) — [physics/basics/rigid-bodies-2d.md](physics/basics/rigid-bodies-2d.md)
4748

4849
Browse all tutorials under `physics/`.
4950

5051
Changelog
5152

53+
- 0.9.0 (2026-02-13): Add rigid bodies physics tutorial.
5254
- 0.8.0 (2026-02-07): Add physics tutorial section and first physics demo.
5355
- 0.7.1 (2026-02-07): Group tutorials by feature area in the index.
5456
- 0.7.0 (2026-01-05): Rename push constants tutorial to immediates for wgpu v28; update metadata.
Lines changed: 281 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,281 @@
1+
---
2+
title: "Physics 2D: Rigid Bodies (No Collisions)"
3+
document_id: "physics-rigid-bodies-2d-no-collisions-2026-02-13"
4+
status: "draft"
5+
created: "2026-02-13T00:00:00Z"
6+
last_updated: "2026-02-13T00:00:00Z"
7+
version: "0.1.0"
8+
engine_workspace_version: "2023.1.30"
9+
wgpu_version: "28.0.0"
10+
shader_backend_default: "naga"
11+
winit_version: "0.29.10"
12+
repo_commit: "6a3b507eedddc39f568ed73cfadf34011d57b9a3"
13+
owners: ["lambda-sh"]
14+
reviewers: ["engine", "rendering"]
15+
tags: ["tutorial", "physics", "2d", "rigid-bodies", "fixed-timestep", "uniform-buffers"]
16+
---
17+
18+
## Overview <a name="overview"></a>
19+
20+
This tutorial builds a render demo that showcases 2D rigid bodies in
21+
`PhysicsWorld2D`. The simulation does not define collision shapes, so bodies do
22+
not collide. Instead, simple boundary rules clamp and bounce bodies within the
23+
viewport to keep the demo visible.
24+
25+
Reference implementation: `demos/physics/src/bin/physics_rigid_bodies_2d.rs`.
26+
27+
## Table of Contents
28+
29+
- [Overview](#overview)
30+
- [Goals](#goals)
31+
- [Prerequisites](#prerequisites)
32+
- [Requirements and Constraints](#requirements-and-constraints)
33+
- [Data Flow](#data-flow)
34+
- [Implementation Steps](#implementation-steps)
35+
- [Step 1 — Add a Demo Binary Entry](#step-1)
36+
- [Step 2 — Define Shader and Uniform Contract](#step-2)
37+
- [Step 3 — Define Component State](#step-3)
38+
- [Step 4 — Create the Physics World and Bodies](#step-4)
39+
- [Step 5 — Build GPU Resources and Per-Body Uniform Buffers](#step-5)
40+
- [Step 6 — Implement Fixed-Timestep Stepping and Controls](#step-6)
41+
- [Step 7 — Write Uniforms and Render Bodies](#step-7)
42+
- [Validation](#validation)
43+
- [Notes](#notes)
44+
- [Conclusion](#conclusion)
45+
- [Exercises](#exercises)
46+
- [Changelog](#changelog)
47+
48+
## Goals <a name="goals"></a>
49+
50+
- Create three rigid body types:
51+
- Dynamic bodies (gravity, forces, impulses).
52+
- Kinematic body (user-provided velocity and rotation).
53+
- Static body (fixed reference).
54+
- Step physics with a fixed timestep accumulator.
55+
- Apply forces and impulses to dynamic bodies.
56+
- Query position and rotation each frame and render via uniform buffers.
57+
58+
## Prerequisites <a name="prerequisites"></a>
59+
60+
- The workspace builds: `cargo build --workspace`.
61+
- The physics demo crate builds: `cargo build -p lambda-demos-physics`.
62+
63+
## Requirements and Constraints <a name="requirements-and-constraints"></a>
64+
65+
- The demo MUST enable `lambda-rs` feature `physics-2d`.
66+
- The update loop MUST step the simulation using a fixed timestep accumulator.
67+
Rationale: reduces variance across machines.
68+
- The demo MUST NOT rely on collision shapes, collision detection, or collision
69+
response. Boundary behavior MUST be implemented in user code.
70+
- Uniform structs in Rust MUST match shader uniform blocks in size and
71+
alignment.
72+
73+
## Data Flow <a name="data-flow"></a>
74+
75+
- Fixed ticks apply:
76+
- A constant wind force (dynamic bodies).
77+
- A pending impulse on input (dynamic bodies).
78+
- A kinematic rotation update and kinematic velocity (kinematic body).
79+
- One `PhysicsWorld2D::step()`.
80+
- Manual boundary bounce and clamp logic (dynamic and kinematic bodies).
81+
- Each render frame queries rigid body transforms and writes them to per-body
82+
uniform buffers used by the vertex shader.
83+
84+
ASCII diagram
85+
86+
```
87+
Variable frame time
88+
89+
90+
Accumulator (seconds)
91+
│ while >= fixed_dt
92+
93+
Fixed tick:
94+
├─ apply_force / apply_impulse (dynamic)
95+
├─ set_rotation (kinematic)
96+
├─ PhysicsWorld2D::step()
97+
└─ boundary clamp + bounce (user code)
98+
99+
100+
Per-frame:
101+
├─ query body position/rotation
102+
├─ write per-body uniforms
103+
└─ draw the same quad mesh per body
104+
```
105+
106+
## Implementation Steps <a name="implementation-steps"></a>
107+
108+
### Step 1 — Add a Demo Binary Entry <a name="step-1"></a>
109+
110+
Add a new binary to the physics demos crate.
111+
112+
Update `demos/physics/Cargo.toml`:
113+
114+
```toml
115+
[[bin]]
116+
name = "physics_rigid_bodies_2d"
117+
path = "src/bin/physics_rigid_bodies_2d.rs"
118+
required-features = ["physics-2d"]
119+
```
120+
121+
After this step, `cargo build -p lambda-demos-physics` SHOULD still succeed.
122+
123+
### Step 2 — Define Shader and Uniform Contract <a name="step-2"></a>
124+
125+
Define a vertex shader that:
126+
127+
- Rotates a quad in 2D using a uniform rotation in radians.
128+
- Translates the quad by a uniform `(x, y)` offset.
129+
- Applies a per-body tint color for readability.
130+
131+
Define a matching Rust uniform:
132+
133+
```rust
134+
#[repr(C)]
135+
#[derive(Debug, Clone, Copy)]
136+
pub struct QuadGlobalsUniform {
137+
pub offset_rotation: [f32; 4],
138+
pub tint: [f32; 4],
139+
}
140+
```
141+
142+
After this step, the shader and uniform contract represent a complete per-body
143+
transform and color payload.
144+
145+
### Step 3 — Define Component State <a name="step-3"></a>
146+
147+
Define a component that stores:
148+
149+
- A `PhysicsWorld2D`.
150+
- A fixed timestep accumulator.
151+
- `RigidBody2D` handles for each body in the demo.
152+
- Basic input state (for an impulse trigger).
153+
- GPU resources: shaders, mesh, pipeline, render pass.
154+
- One uniform buffer and bind group per rigid body.
155+
156+
After this step, the demo has a concrete place to store both simulation state
157+
and render state.
158+
159+
### Step 4 — Create the Physics World and Bodies <a name="step-4"></a>
160+
161+
Construct the physics world using `PhysicsWorld2DBuilder`, then create four
162+
bodies:
163+
164+
- Two dynamic bodies with different masses.
165+
- One kinematic body with an initial velocity.
166+
- One static body as a fixed reference.
167+
168+
Example:
169+
170+
```rust
171+
let mut physics_world = PhysicsWorld2DBuilder::new()
172+
.with_gravity(0.0, -1.6)
173+
.build()
174+
.expect("Failed to create PhysicsWorld2D");
175+
176+
let dynamic_light_body = RigidBody2DBuilder::new(RigidBodyType::Dynamic)
177+
.with_position(-0.35, 0.75)
178+
.with_dynamic_mass_kg(0.5)
179+
.build(&mut physics_world)
180+
.expect("Failed to create dynamic body (light)");
181+
```
182+
183+
After this step, the demo has simulation objects whose state can be queried and
184+
stepped.
185+
186+
### Step 5 — Build GPU Resources and Per-Body Uniform Buffers <a name="step-5"></a>
187+
188+
In `on_attach`:
189+
190+
- Build a quad mesh.
191+
- Build a uniform bind group layout.
192+
- For each rigid body:
193+
- Query initial position and rotation.
194+
- Create a CPU-visible uniform buffer with `QuadGlobalsUniform`.
195+
- Create a bind group for that buffer.
196+
- Build a render pipeline using the shared layout and shared mesh buffer.
197+
198+
After this step, each body has a uniform buffer and bind group that can be
199+
updated independently while sharing a single mesh and pipeline.
200+
201+
### Step 6 — Implement Fixed-Timestep Stepping and Controls <a name="step-6"></a>
202+
203+
Implement:
204+
205+
- Keyboard handling that sets an impulse flag when Space is pressed.
206+
- A fixed timestep loop in `on_update`:
207+
- Apply a constant wind force to dynamic bodies each tick.
208+
- Apply an upward impulse when the impulse flag is set.
209+
- Increment and set a kinematic rotation value each tick.
210+
- Call `PhysicsWorld2D::step()`.
211+
- Apply manual boundary bounce and clamp logic:
212+
- Floor and ceiling bounce for dynamic bodies.
213+
- Left and right wall bounce for dynamic bodies.
214+
- Left and right wall clamp (and velocity flip) for the kinematic body.
215+
216+
After this step, the demo shows steady movement regardless of frame rate, and
217+
input can inject instantaneous velocity changes into the dynamic bodies.
218+
219+
### Step 7 — Write Uniforms and Render Bodies <a name="step-7"></a>
220+
221+
In `on_render`:
222+
223+
- For each body:
224+
- Query position and rotation from the physics world.
225+
- Write `QuadGlobalsUniform` to the corresponding uniform buffer.
226+
- Record draw commands:
227+
- Begin a render pass, set the pipeline, set viewport/scissor, bind the shared
228+
vertex buffer.
229+
- For each body, set its bind group and draw the quad.
230+
231+
After this step, the rendered quads track the simulated bodies each frame.
232+
233+
## Validation <a name="validation"></a>
234+
235+
Build and run:
236+
237+
```bash
238+
cargo run -p lambda-demos-physics --bin physics_rigid_bodies_2d
239+
```
240+
241+
Expected behavior:
242+
243+
- Two dynamic bodies fall under gravity and drift from a constant wind force.
244+
- Dynamic bodies bounce off the floor, ceiling, and side walls.
245+
- The kinematic body moves horizontally, rotates continuously, and clamps at
246+
the walls.
247+
- The static body remains fixed.
248+
- Pressing Space applies an upward impulse to both dynamic bodies.
249+
250+
## Notes <a name="notes"></a>
251+
252+
- This demo intentionally does not use collision shapes. Any “bouncing” behavior
253+
is implemented by clamping positions and mutating velocities in user code.
254+
- `apply_force` and `apply_impulse` are intended for dynamic bodies. Calls on
255+
static or kinematic bodies SHOULD return an error.
256+
- Fixed timestep stepping is implemented with an accumulator. The demo MUST NOT
257+
advance simulation directly from variable frame deltas.
258+
- The rotation shown in the demo is explicitly set each tick. Angular dynamics
259+
are not required for this tutorial.
260+
261+
## Conclusion <a name="conclusion"></a>
262+
263+
This tutorial demonstrates how to create and step 2D rigid bodies using
264+
`PhysicsWorld2D` and render them by writing per-body uniform buffers. The demo
265+
uses dynamic, kinematic, and static bodies and applies forces and impulses to
266+
validate basic rigid body integration without relying on collision shapes.
267+
268+
## Exercises <a name="exercises"></a>
269+
270+
- Add a configurable drag force that reduces dynamic body velocity over time.
271+
- Replace constant wind with a time-varying sinusoidal force.
272+
- Add a toggle key that enables or disables gravity at runtime.
273+
- Render a simple velocity indicator (for example, a line in the direction of
274+
velocity) using additional geometry.
275+
- Spawn N dynamic bodies at startup and randomize initial positions and masses.
276+
- Apply a force proportional to body mass and observe acceleration differences.
277+
- Add a soft “camera” offset uniform and pan the view with arrow keys.
278+
279+
## Changelog <a name="changelog"></a>
280+
281+
- 0.1.0 (2026-02-13): Initial tutorial for `physics_rigid_bodies_2d`.

0 commit comments

Comments
 (0)