# Phase 5-1: Collision Detection — Design Spec ## Overview `voltex_physics` crate에 충돌 감지 시스템을 구현한다. Broad phase (BVH) + Narrow phase (전용 함수) 구조로, Sphere와 Box 콜라이더를 지원한다. ## Dependencies - `voltex_math` — Vec3, Mat4, AABB(신규) - `voltex_ecs` — World, Entity, Transform, SparseSet ## Module Structure ``` crates/voltex_physics/ ├── Cargo.toml └── src/ ├── lib.rs — public exports ├── aabb.rs — AABB type (voltex_math에 추가) ├── collider.rs — Collider enum ├── contact.rs — ContactPoint ├── bvh.rs — BvhTree (broad phase) ├── narrow.rs — sphere_vs_sphere, sphere_vs_box, box_vs_box └── collision.rs — detect_collisions (ECS 통합) ``` 참고: AABB는 `voltex_math`에 추가한다 (스펙에 명시된 기본 수학 타입). ## Types ### AABB (voltex_math에 추가) ```rust #[derive(Debug, Clone, Copy)] pub struct AABB { pub min: Vec3, pub max: Vec3, } ``` **Methods:** - `new(min, max) -> Self` - `from_center_half_extents(center, half_extents) -> Self` - `center() -> Vec3` - `half_extents() -> Vec3` - `contains_point(point: Vec3) -> bool` - `intersects(other: &AABB) -> bool` - `merged(other: &AABB) -> AABB` — 두 AABB를 감싸는 최소 AABB - `surface_area() -> f32` — BVH SAH 비용 계산용 ### Collider (voltex_physics) ```rust #[derive(Debug, Clone, Copy)] pub enum Collider { Sphere { radius: f32 }, Box { half_extents: Vec3 }, } ``` - ECS 컴포넌트로 사용. 형상(shape)만 저장. - 위치는 같은 entity의 `Transform.position`에서 가져온다. - `aabb(&self, position: Vec3) -> AABB` — broad phase용 바운딩 박스 생성 ### ContactPoint (voltex_physics) ```rust #[derive(Debug, Clone, Copy)] pub struct ContactPoint { pub entity_a: Entity, pub entity_b: Entity, pub normal: Vec3, // A에서 B 방향 단위 법선 pub depth: f32, // 침투 깊이 (양수 = 겹침) pub point_on_a: Vec3, // A 표면의 접촉점 pub point_on_b: Vec3, // B 표면의 접촉점 } ``` ### BvhTree (voltex_physics) 바이너리 트리. 각 리프는 하나의 Entity + AABB. ```rust pub struct BvhTree { nodes: Vec, } enum BvhNode { Leaf { entity: Entity, aabb: AABB }, Internal { aabb: AABB, left: usize, right: usize }, } ``` **Methods:** - `build(entries: &[(Entity, AABB)]) -> Self` — 중앙값 분할(median split)로 구축. 가장 긴 축 기준 정렬 후 이분. - `query_pairs(&self) -> Vec<(Entity, Entity)>` — 트리 순회로 AABB가 겹치는 리프 쌍 반환. 매 프레임 rebuild한다 (동적 씬 대응). 최적화(incremental update)는 성능 문제 발생 시 추후 적용. ### Narrow Phase Functions (voltex_physics::narrow) 모든 함수는 위치(Vec3)와 형상 파라미터를 받아 `Option`를 반환한다. Entity 정보는 호출부에서 채운다. ```rust pub fn sphere_vs_sphere( pos_a: Vec3, radius_a: f32, pos_b: Vec3, radius_b: f32, ) -> Option<(Vec3, f32, Vec3, Vec3)> // (normal, depth, point_a, point_b) pub fn sphere_vs_box( sphere_pos: Vec3, radius: f32, box_pos: Vec3, half_extents: Vec3, ) -> Option<(Vec3, f32, Vec3, Vec3)> pub fn box_vs_box( pos_a: Vec3, half_a: Vec3, pos_b: Vec3, half_b: Vec3, ) -> Option<(Vec3, f32, Vec3, Vec3)> ``` **box_vs_box**: SAT (Separating Axis Theorem) 기반. 축 정렬(AABB vs AABB)만 지원한다. 회전된 OBB는 Convex Hull 추가 시 GJK/EPA로 대체. ### ECS Integration (voltex_physics::collision) ```rust pub fn detect_collisions(world: &World) -> Vec ``` 1. `Transform` + `Collider`를 가진 entity를 `query2`로 수집 2. 각 entity의 AABB 계산 (`collider.aabb(transform.position)`) 3. `BvhTree::build()` → `query_pairs()`로 broad phase 후보 추출 4. 각 후보 쌍에 대해 collider 타입 조합에 맞는 narrow phase 함수 호출 5. 접촉이 있으면 `ContactPoint`에 entity 정보를 채워 결과에 추가 ## Conventions - 법선(normal): entity_a에서 entity_b 방향 - 침투 깊이(depth): 양수 = 겹침, 0 이하 = 접촉 없음 - 좌표계: 기존 voltex_math 규약 (오른손 좌표계) - WGSL vec3 alignment 규칙은 AABB에 해당 없음 (GPU에 올리지 않음) ## Test Plan ### voltex_math (AABB) - `new`, `from_center_half_extents` 생성 - `center`, `half_extents` 계산 - `contains_point`: 내부/외부/경계 - `intersects`: 겹침/분리/접선 - `merged`: 두 AABB 합집합 - `surface_area`: 정확도 ### voltex_physics - **narrow::sphere_vs_sphere**: 겹침, 분리, 접선, 완전 포함 - **narrow::sphere_vs_box**: 면/모서리/꼭짓점 접촉, 분리, 내부 포함 - **narrow::box_vs_box**: 각 축 겹침, 분리, 접선 - **collider::aabb**: Sphere/Box의 AABB 생성 - **bvh::build**: 빈 입력, 단일, 다수 엔트리 - **bvh::query_pairs**: 겹치는 쌍 정확성, 분리된 쌍 미포함 - **collision::detect_collisions**: ECS 통합 E2E ## Out of Scope (Phase 5-1) - Capsule, Convex Hull 콜라이더 - GJK/EPA 알고리즘 - 회전된 박스(OBB) 충돌 - 리지드바디 시뮬레이션 (Phase 5-2) - 레이캐스팅 (Phase 5-3) - 연속 충돌 감지 (CCD)