docs: add Phase 5-1 through 6-3 specs, plans, and Cargo.lock
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
147
docs/superpowers/specs/2026-03-25-phase5-2-rigidbody.md
Normal file
147
docs/superpowers/specs/2026-03-25-phase5-2-rigidbody.md
Normal file
@@ -0,0 +1,147 @@
|
||||
# Phase 5-2: Rigid Body Simulation — Design Spec
|
||||
|
||||
## Overview
|
||||
|
||||
`voltex_physics`에 리지드바디 시뮬레이션을 추가한다.
|
||||
Semi-implicit Euler 적분 + 임펄스 기반 충돌 응답으로, 물체가 떨어지고 충돌하여 튕기는 기본 물리를 구현한다.
|
||||
|
||||
## Scope
|
||||
|
||||
- RigidBody 컴포넌트 (질량, 속도, 반발 계수)
|
||||
- 중력 + Semi-implicit Euler 적분
|
||||
- 임펄스 기반 충돌 응답 (선형만)
|
||||
- 위치 보정 (penetration resolution)
|
||||
- physics_step 통합 함수
|
||||
|
||||
## Out of Scope
|
||||
|
||||
- 각속도/회전 물리 (관성 텐서 필요, 추후 추가)
|
||||
- 마찰 (Coulomb friction)
|
||||
- Sequential Impulse 솔버
|
||||
- 연속 충돌 감지 (CCD)
|
||||
- Sleep/Island 시스템
|
||||
|
||||
## Module Structure
|
||||
|
||||
기존 `voltex_physics`에 추가:
|
||||
|
||||
```
|
||||
crates/voltex_physics/src/
|
||||
├── (기존) collider.rs, contact.rs, narrow.rs, bvh.rs, collision.rs, lib.rs
|
||||
├── rigid_body.rs — RigidBody 컴포넌트, PhysicsConfig
|
||||
├── integrator.rs — Semi-implicit Euler 적분
|
||||
└── solver.rs — 임펄스 기반 충돌 응답 + 위치 보정 + physics_step
|
||||
```
|
||||
|
||||
## Types
|
||||
|
||||
### RigidBody (ECS 컴포넌트)
|
||||
|
||||
```rust
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct RigidBody {
|
||||
pub velocity: Vec3,
|
||||
pub angular_velocity: Vec3,
|
||||
pub mass: f32,
|
||||
pub restitution: f32,
|
||||
pub gravity_scale: f32,
|
||||
}
|
||||
```
|
||||
|
||||
- `mass == 0.0` → 정적 물체 (무한 질량, 중력 영향 없음, 움직이지 않음)
|
||||
- `mass > 0.0` → 동적 물체
|
||||
- `restitution` — 반발 계수 (0.0 = 완전 비탄성, 1.0 = 완전 탄성)
|
||||
- `gravity_scale` — 중력 배율 (기본 1.0)
|
||||
- `angular_velocity` — 필드만 존재, 이번 Phase에서는 적분하지 않음
|
||||
|
||||
**Methods:**
|
||||
- `dynamic(mass: f32) -> Self` — 동적 물체 생성 (velocity=0, restitution=0.3, gravity_scale=1.0)
|
||||
- `statik() -> Self` — 정적 물체 생성 (mass=0, velocity=0, restitution=0.3)
|
||||
- `inv_mass(&self) -> f32` — mass가 0이면 0.0, 아니면 1.0/mass
|
||||
- `is_static(&self) -> bool` — mass == 0.0
|
||||
|
||||
### PhysicsConfig
|
||||
|
||||
```rust
|
||||
pub struct PhysicsConfig {
|
||||
pub gravity: Vec3,
|
||||
pub fixed_dt: f32,
|
||||
}
|
||||
```
|
||||
|
||||
- `default()` → gravity = (0, -9.81, 0), fixed_dt = 1.0/60.0
|
||||
|
||||
## Functions
|
||||
|
||||
### integrate (integrator.rs)
|
||||
|
||||
```rust
|
||||
pub fn integrate(world: &mut World, config: &PhysicsConfig)
|
||||
```
|
||||
|
||||
Transform + RigidBody를 가진 동적 entity 순회:
|
||||
1. `velocity += gravity * gravity_scale * dt`
|
||||
2. `position += velocity * dt`
|
||||
|
||||
정적 물체(mass == 0.0)는 건너뜀.
|
||||
|
||||
### resolve_collisions (solver.rs)
|
||||
|
||||
```rust
|
||||
pub fn resolve_collisions(world: &mut World, contacts: &[ContactPoint])
|
||||
```
|
||||
|
||||
각 ContactPoint에 대해:
|
||||
1. 두 entity의 RigidBody 조회 (없으면 스킵)
|
||||
2. 상대 속도 계산: `v_rel = velocity_a - velocity_b`
|
||||
3. 법선 방향 성분: `v_rel_n = v_rel · normal`
|
||||
4. 분리 중이면 스킵: `v_rel_n > 0`
|
||||
5. 반발 계수: `e = min(restitution_a, restitution_b)`
|
||||
6. 임펄스 크기: `j = -(1 + e) * v_rel_n / (inv_mass_a + inv_mass_b)`
|
||||
7. 속도 업데이트: `v_a += j * normal * inv_mass_a`, `v_b -= j * normal * inv_mass_b`
|
||||
|
||||
**위치 보정 (Positional Correction):**
|
||||
- 침투 깊이(depth)에 비례하여 물체를 법선 방향으로 분리
|
||||
- Baumgarte stabilization: `correction = max(depth - slop, 0) * percent / (inv_mass_a + inv_mass_b)`
|
||||
- slop = 0.01 (작은 침투 허용), percent = 0.4 (보정 비율)
|
||||
- `pos_a -= correction * inv_mass_a * normal`, `pos_b += correction * inv_mass_b * normal`
|
||||
|
||||
### physics_step (solver.rs)
|
||||
|
||||
```rust
|
||||
pub fn physics_step(world: &mut World, config: &PhysicsConfig)
|
||||
```
|
||||
|
||||
1. `integrate(world, config)`
|
||||
2. `let contacts = detect_collisions(world)`
|
||||
3. `resolve_collisions(world, &contacts)`
|
||||
|
||||
## ECS Integration Notes
|
||||
|
||||
- RigidBody는 Transform + Collider와 함께 entity에 추가
|
||||
- 물리 스텝은 게임 루프에서 fixed_update 타이밍에 호출
|
||||
- detect_collisions는 Phase 5-1의 기존 함수 재사용
|
||||
|
||||
## Conventions
|
||||
|
||||
- 단위: SI (미터, 초, 킬로그램)
|
||||
- 중력: (0, -9.81, 0) — Y-up 좌표계
|
||||
- 법선: Phase 5-1과 동일 (entity_a → entity_b 방향)
|
||||
|
||||
## Test Plan
|
||||
|
||||
### rigid_body.rs
|
||||
- `dynamic()`: 기본값 확인, inv_mass 정확도
|
||||
- `statik()`: mass=0, inv_mass=0, is_static=true
|
||||
- `with_restitution`: 설정 반영
|
||||
|
||||
### integrator.rs
|
||||
- 중력 낙하: 1프레임 후 위치 = (0, -9.81/60/60 ≈ position 변화)
|
||||
- 정적 물체: integrate 후 위치 불변
|
||||
- 초기 속도: velocity가 있는 경우 position 변화 확인
|
||||
|
||||
### solver.rs
|
||||
- 두 동적 구체 정면 충돌: 속도 반전 확인
|
||||
- 동적 구체 vs 정적 바닥: 구체만 튕김, 바닥 불변
|
||||
- 위치 보정: 침투 깊이만큼 분리
|
||||
- physics_step E2E: 구체가 바닥 위에서 낙하 → 충돌 → 반발
|
||||
Reference in New Issue
Block a user