docs: add Phase 8-4 editor UI status, spec, and deferred items
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -62,6 +62,16 @@
|
||||
- **raycast_all (다중 hit)** — 가장 가까운 hit만 반환.
|
||||
- **BVH 조기 종료 최적화** — 모든 리프 검사 후 최소 t 선택. front-to-back 순회 미구현.
|
||||
|
||||
## Phase 8-4
|
||||
|
||||
- **씬 뷰포트** — 3D 렌더러 임베드 미구현.
|
||||
- **엔티티 인스펙터** — ECS 컴포넌트 편집 미구현.
|
||||
- **에셋 브라우저** — 파일 시스템 탐색 미구현.
|
||||
- **텍스트 입력** — 키보드 → 문자열 입력 미구현.
|
||||
- **스크롤, 드래그앤드롭** — 미구현.
|
||||
- **도킹, 탭, 윈도우 드래그** — 미구현.
|
||||
- **TTF 폰트** — 비트맵 고정폭만. 가변 크기 미지원.
|
||||
|
||||
## Phase 8-3
|
||||
|
||||
- **핫 리로드** — 파일 변경 감지 + Lua state 재로드 미구현.
|
||||
|
||||
@@ -125,6 +125,14 @@
|
||||
- voltex_ai: A* pathfinding on triangle graph (center-point path)
|
||||
- voltex_ai: Steering behaviors (seek, flee, arrive, wander, follow_path)
|
||||
|
||||
### Phase 8-4: Immediate Mode UI (Editor Foundation)
|
||||
- voltex_editor: FontAtlas (8x12 비트맵 ASCII, 코드 생성)
|
||||
- voltex_editor: DrawList (정점/인덱스/커맨드), DrawVertex (Unorm8x4 color)
|
||||
- voltex_editor: UiContext (IMGUI 상태, hot/active, 커서 레이아웃)
|
||||
- voltex_editor: Widgets (text, button, slider, checkbox, panel)
|
||||
- voltex_editor: UiRenderer (wgpu 2D pipeline, alpha blending, orthographic)
|
||||
- examples/editor_demo (IMGUI 위젯 데모)
|
||||
|
||||
### Phase 8-3: Lua Scripting
|
||||
- voltex_script: Lua 5.4 내장 빌드 (cc crate)
|
||||
- voltex_script: Lua C API FFI 바인딩
|
||||
@@ -151,10 +159,11 @@ crates/
|
||||
├── voltex_audio — AudioClip, WAV parser, mixing, WASAPI backend, AudioSystem, MixGroup, spatial
|
||||
├── voltex_ai — NavMesh, A* pathfinding, steering behaviors
|
||||
├── voltex_net — UDP packets, NetServer, NetClient
|
||||
└── voltex_script — Lua 5.4 FFI, LuaState, scripting bindings
|
||||
├── voltex_script — Lua 5.4 FFI, LuaState, scripting bindings
|
||||
└── voltex_editor — IMGUI framework, UiRenderer, widgets
|
||||
```
|
||||
|
||||
## 테스트: 245개 전부 통과
|
||||
## 테스트: 255개 전부 통과
|
||||
|
||||
- voltex_asset: 14
|
||||
- voltex_audio: 35 (audio_clip 2 + wav 5 + mixing 11 + audio_system 2 + spatial 8 + mix_group 7)
|
||||
@@ -165,9 +174,10 @@ crates/
|
||||
- voltex_ai: 15 (navmesh 4 + pathfinding 5 + steering 6)
|
||||
- voltex_net: 8 (packet 7 + integration 1)
|
||||
- voltex_script: 9 (state 8 + bindings 1)
|
||||
- voltex_editor: 10 (font 2 + draw_list 3 + widgets 3 + layout 1 + renderer 1)
|
||||
- voltex_renderer: 33 (20 + SSGI 3 + RT 3 + bloom 3 + tonemap 4)
|
||||
|
||||
## Examples (11개)
|
||||
## Examples (12개)
|
||||
|
||||
- triangle — Phase 1 삼각형
|
||||
- model_viewer — OBJ 큐브 + Blinn-Phong
|
||||
@@ -180,8 +190,9 @@ crates/
|
||||
- ibl_demo — Normal map + IBL
|
||||
- audio_demo — 사인파 오디오 재생
|
||||
- deferred_demo — 디퍼드 렌더링 + 다중 포인트 라이트
|
||||
- editor_demo — IMGUI 위젯 데모
|
||||
|
||||
## 다음: Phase 8-4 (에디터) — Stretch Goal
|
||||
## 전체 완료!
|
||||
|
||||
스펙 참조: `docs/superpowers/specs/2026-03-24-voltex-engine-design.md`
|
||||
|
||||
|
||||
215
docs/superpowers/specs/2026-03-25-phase8-4-editor.md
Normal file
215
docs/superpowers/specs/2026-03-25-phase8-4-editor.md
Normal file
@@ -0,0 +1,215 @@
|
||||
# Phase 8-4: Immediate Mode UI — Design Spec
|
||||
|
||||
## Overview
|
||||
|
||||
`voltex_editor` crate를 신규 생성한다. Immediate Mode UI 프레임워크를 구현하여 에디터의 기반을 마련한다.
|
||||
|
||||
## Scope
|
||||
|
||||
- 비트맵 폰트 아틀라스 (8x12 고정폭 ASCII, 코드 생성)
|
||||
- DrawList (정점/인덱스/커맨드 버퍼)
|
||||
- UiContext (IMGUI 상태, hot/active 위젯)
|
||||
- 위젯: text, button, slider, checkbox, begin_panel/end_panel
|
||||
- 커서 기반 자동 레이아웃
|
||||
- wgpu UI 렌더 파이프라인 (2D, alpha blending)
|
||||
- editor_demo 예제
|
||||
|
||||
## Out of Scope
|
||||
|
||||
- 씬 뷰포트 (3D 임베드)
|
||||
- 엔티티 인스펙터 / 에셋 브라우저
|
||||
- 텍스트 입력 (키보드 → 문자열)
|
||||
- 스크롤, 드래그앤드롭
|
||||
- TTF 파싱 / 가변 크기 폰트
|
||||
- 도킹, 탭, 윈도우 드래그
|
||||
|
||||
## Module Structure
|
||||
|
||||
```
|
||||
crates/voltex_editor/
|
||||
├── Cargo.toml
|
||||
└── src/
|
||||
├── lib.rs
|
||||
├── font.rs — FontAtlas (비트맵 생성 + GPU 텍스처)
|
||||
├── draw_list.rs — DrawVertex, DrawCommand, DrawList
|
||||
├── ui_context.rs — UiContext (IMGUI 상태)
|
||||
├── widgets.rs — text, button, slider, checkbox, panel
|
||||
├── layout.rs — 커서 기반 레이아웃
|
||||
└── renderer.rs — UI 렌더 파이프라인 (wgpu 셰이더)
|
||||
```
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `voltex_math` — Vec2
|
||||
- `wgpu`, `bytemuck` — GPU 렌더링
|
||||
|
||||
## Types
|
||||
|
||||
### FontAtlas
|
||||
|
||||
```rust
|
||||
pub struct FontAtlas {
|
||||
pub width: u32, // 아틀라스 텍스처 너비
|
||||
pub height: u32,
|
||||
pub glyph_width: u32, // 8
|
||||
pub glyph_height: u32, // 12
|
||||
pub pixels: Vec<u8>, // R8 grayscale
|
||||
}
|
||||
```
|
||||
|
||||
- `generate() -> Self` — 하드코딩된 8x12 비트맵 데이터로 ASCII 32~126 생성
|
||||
- `glyph_uv(ch: char) -> (f32, f32, f32, f32)` — (u0, v0, u1, v1) UV 좌표
|
||||
|
||||
아틀라스 레이아웃: 16열 x 6행 = 96 글리프. 텍스처 크기: 128x72 (16*8 x 6*12).
|
||||
|
||||
### DrawVertex
|
||||
|
||||
```rust
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Pod, Zeroable)]
|
||||
pub struct DrawVertex {
|
||||
pub position: [f32; 2],
|
||||
pub uv: [f32; 2],
|
||||
pub color: [u8; 4], // RGBA
|
||||
}
|
||||
```
|
||||
|
||||
### DrawCommand
|
||||
|
||||
```rust
|
||||
pub struct DrawCommand {
|
||||
pub index_offset: u32,
|
||||
pub index_count: u32,
|
||||
pub clip_rect: [f32; 4], // x, y, w, h (미사용, 추후)
|
||||
}
|
||||
```
|
||||
|
||||
### DrawList
|
||||
|
||||
```rust
|
||||
pub struct DrawList {
|
||||
pub vertices: Vec<DrawVertex>,
|
||||
pub indices: Vec<u16>,
|
||||
pub commands: Vec<DrawCommand>,
|
||||
}
|
||||
```
|
||||
|
||||
- `clear()` — 매 프레임 리셋
|
||||
- `add_rect(x, y, w, h, color)` — 색상 사각형
|
||||
- `add_rect_uv(x, y, w, h, u0, v0, u1, v1, color)` — 텍스처 사각형
|
||||
- `add_text(font, text, x, y, color)` — 문자열 (글리프별 쿼드)
|
||||
|
||||
### UiContext
|
||||
|
||||
```rust
|
||||
pub struct UiContext {
|
||||
// Widget state
|
||||
pub hot: Option<u64>,
|
||||
pub active: Option<u64>,
|
||||
|
||||
// Draw output
|
||||
pub draw_list: DrawList,
|
||||
|
||||
// Layout cursor
|
||||
pub cursor_x: f32,
|
||||
pub cursor_y: f32,
|
||||
pub indent: f32,
|
||||
pub line_height: f32,
|
||||
|
||||
// Input (매 프레임 외부에서 설정)
|
||||
pub mouse_x: f32,
|
||||
pub mouse_y: f32,
|
||||
pub mouse_down: bool,
|
||||
pub mouse_clicked: bool,
|
||||
pub mouse_released: bool,
|
||||
|
||||
// Screen
|
||||
pub screen_width: f32,
|
||||
pub screen_height: f32,
|
||||
|
||||
// Font
|
||||
pub font: FontAtlas,
|
||||
|
||||
// Internal
|
||||
id_counter: u64,
|
||||
}
|
||||
```
|
||||
|
||||
- `new(screen_w, screen_h) -> Self`
|
||||
- `begin_frame(mouse_x, mouse_y, mouse_down)` — 프레임 시작, draw_list 클리어
|
||||
- `end_frame()` — draw_list 확정
|
||||
- `gen_id() -> u64` — 위젯별 고유 ID (호출 순서 기반)
|
||||
|
||||
### Widget Functions (UiContext methods)
|
||||
|
||||
```rust
|
||||
impl UiContext {
|
||||
pub fn text(&mut self, text: &str)
|
||||
pub fn button(&mut self, label: &str) -> bool
|
||||
pub fn slider(&mut self, label: &str, value: f32, min: f32, max: f32) -> f32
|
||||
pub fn checkbox(&mut self, label: &str, checked: bool) -> bool
|
||||
pub fn begin_panel(&mut self, title: &str, x: f32, y: f32, w: f32, h: f32)
|
||||
pub fn end_panel(&mut self)
|
||||
}
|
||||
```
|
||||
|
||||
**button 로직:**
|
||||
1. ID 생성
|
||||
2. 사각형 그리기 (배경색 = hot이면 밝게, active면 더 밝게)
|
||||
3. 텍스트 그리기
|
||||
4. 마우스가 사각형 안이면 hot 설정
|
||||
5. hot + mouse_clicked이면 active 설정
|
||||
6. active + mouse_released이면 → true 반환 (클릭됨)
|
||||
|
||||
**slider 로직:**
|
||||
1. 배경 바 그리기
|
||||
2. 현재 값 위치에 핸들 그리기
|
||||
3. active이면 마우스 X로 값 업데이트
|
||||
4. 변경된 값 반환
|
||||
|
||||
### UI Render Pipeline
|
||||
|
||||
```wgsl
|
||||
// ui_shader.wgsl
|
||||
struct UiUniform {
|
||||
projection: mat4x4<f32>, // orthographic screen projection
|
||||
};
|
||||
|
||||
@group(0) @binding(0) var<uniform> ui: UiUniform;
|
||||
@group(0) @binding(1) var t_atlas: texture_2d<f32>;
|
||||
@group(0) @binding(2) var s_atlas: sampler;
|
||||
|
||||
@vertex
|
||||
fn vs_main(@location(0) pos: vec2<f32>, @location(1) uv: vec2<f32>, @location(2) color: vec4<u32>) -> ... {
|
||||
// Transform by orthographic projection
|
||||
// Pass UV and color
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fs_main(...) -> @location(0) vec4<f32> {
|
||||
let tex = textureSample(t_atlas, s_atlas, uv);
|
||||
return vec4(color.rgb * tex.r, color.a * tex.r); // font alpha from texture
|
||||
}
|
||||
```
|
||||
|
||||
프로젝션: `orthographic(0, screen_w, screen_h, 0, -1, 1)` — 좌상단 원점.
|
||||
|
||||
비텍스처 사각형(배경, 버튼)은 UV=(0,0) 영역(아틀라스의 화이트 픽셀)을 사용하거나, 셰이더에서 UV=(0,0)일 때 색상만 사용.
|
||||
|
||||
## Test Plan
|
||||
|
||||
### font.rs
|
||||
- generate(): 아틀라스 크기 확인 (128x72)
|
||||
- glyph_uv('A'): UV 범위 확인
|
||||
|
||||
### draw_list.rs
|
||||
- add_rect: 정점 4개 + 인덱스 6개 추가
|
||||
- add_text: 글자 수 * 4 정점
|
||||
- clear: 비어있음
|
||||
|
||||
### ui_context.rs
|
||||
- button: mouse over → hot, click → returns true
|
||||
- slider: value 변경 확인
|
||||
|
||||
### 통합
|
||||
- editor_demo 예제: 텍스트, 버튼, 슬라이더가 화면에 표시
|
||||
Reference in New Issue
Block a user