# Asset - 에셋 관리 서브시스템

**"파일에서 GPU로"**를 담당하는 에셋 로딩 및 캐싱 계층입니다.

## 📦 모듈 구성

### `manager.rs`
- **`AssetManager`**: 에셋의 최상위 관리자
- 텍스처 로딩 및 캐싱
- GPU 리소스 생명주기 관리
- Engine이 직접 소유하는 유일 인스턴스

```rust
let asset_manager = AssetManager::new(
    PathBuf::from("assets/"),
    device,
    queue,
);

let texture = asset_manager.load_texture("sprites/player.png");
```

## 🎯 설계 철학

### 유일 인스턴스 (Singleton-like)
`AssetManager`는 Engine이 단 하나만 소유하며, 모든 에셋 로딩을 중앙화합니다.

```
Engine
└── AssetManager (1개)
    ├── texture_cache: HashMap<String, Arc<Texture>>
    ├── device: Arc<Device>
    ├── queue: Arc<Queue>
    └── placeholder: Arc<Texture> (로딩 실패 시 대체)
```

### 캐싱 전략
한 번 로드된 텍스처는 메모리에 캐시되어 중복 로딩을 방지합니다.

```rust
// 첫 번째 호출: 파일에서 로드
let texture1 = asset_manager.load_texture("sprite.png");

// 두 번째 호출: 캐시에서 반환 (빠름!)
let texture2 = asset_manager.load_texture("sprite.png");

// Arc::clone으로 공유 (복사 아님)
assert!(Arc::ptr_eq(&texture1, &texture2));
```

### 에러 복원력
로딩 실패 시 자동으로 플레이스홀더 텍스처(마젠타)를 반환하여 크래시 방지합니다.

```rust
// 파일이 없어도 게임은 계속됨 (경고만 출력)
let texture = asset_manager.load_texture("missing.png");
// → 마젠타 색상의 플레이스홀더 반환
```

## 📊 로딩 흐름

### 텍스처 로딩
```
AssetManager::load_texture("sprite.png")
├── 캐시 확인
│   ├── 있음 → Arc::clone() 반환 ✅
│   └── 없음 ↓
├── 파일 시스템
│   └── asset_root/sprite.png 읽기
├── GPU 업로드
│   ├── render::load_texture_from_file()
│   ├── image::open() (PNG/JPEG 디코딩)
│   ├── device.create_texture()
│   └── queue.write_texture()
├── 캐시 저장
│   └── HashMap<String, Arc<Texture>>
└── 성공 → Arc<Texture> 반환
    실패 → placeholder 반환 ⚠️
```

## 🔗 관련 계층

- **`runtime/`**: `Engine`이 `AssetManager` 소유
- **`render/`**: 텍스처 로딩 함수(`load_texture_from_file`) 제공
- **`entity/components/`**: `Sprite` 컴포넌트가 텍스처 경로 참조

## 🚀 사용 예시

### AssetManager 생성 (Engine 내부)
```rust
let device = Arc::new(render_system.device().clone());
let queue = Arc::new(render_system.queue().clone());

let asset_manager = AssetManager::new(
    PathBuf::from("assets/"),
    device,
    queue,
);
```

### 텍스처 로딩 (게임 코드)
```rust
// 상대 경로 사용 (asset_root 기준)
let player_texture = asset_manager.load_texture("sprites/player.png");
let bg_texture = asset_manager.load_texture("backgrounds/level1.png");

// Arc<Texture>이므로 복사 비용 없음
let shared_texture = Arc::clone(&player_texture);
```

### Sprite와 함께 사용
```rust
use moltrun_core::entity::components::Sprite;

let entity_id = world.create_entity();
if let Some(entity) = world.get_entity_mut(entity_id) {
    // Sprite는 텍스처 경로만 저장
    entity.add_component(Sprite::with_texture("sprites/enemy.png"));
}

// 렌더링 시 AssetManager가 실제 텍스처 제공
render_system.render(&world, &mut asset_manager)?;
```

### 에셋 루트 경로
```rust
// 현재 에셋 루트 확인
let root = asset_manager.asset_root();
println!("Assets: {}", root.display());
```

## 💡 확장 가능성

현재는 텍스처만 지원하지만, 향후 확장 가능:

- **오디오**: `load_sound(path)` - 사운드 파일 로딩
- **폰트**: `load_font(path)` - 텍스트 렌더링용 폰트
- **데이터**: `load_json(path)` - 게임 설정 파일
- **셰이더**: `load_shader(path)` - 커스텀 셰이더

## ⚠️ 주의사항

### Arc<Texture> 공유
```rust
// ✅ 올바름: Arc::clone으로 공유
let texture = asset_manager.load_texture("sprite.png");
sprites.push(Arc::clone(&texture));

// ❌ 불필요: 이미 Arc이므로 추가 Arc 불필요
let texture = Arc::new(asset_manager.load_texture("sprite.png"));
```

### 경로 표기
```rust
// ✅ 올바름: 상대 경로 (asset_root 기준)
asset_manager.load_texture("sprites/player.png");

// ❌ 잘못됨: 절대 경로 사용 금지
asset_manager.load_texture("/Users/name/assets/sprites/player.png");
```

### 초기화 순서
```rust
// ✅ 올바름: RenderSystem 먼저 초기화
render_system.initialize(window).await?;
let asset_manager = AssetManager::new(/* device, queue from render_system */);

// ❌ 불가능: Device/Queue 없이 생성 불가
let asset_manager = AssetManager::new(/* ??? */);
```
