434 lines
8.2 KiB
Markdown
434 lines
8.2 KiB
Markdown
< [Voltar](chapter-3.md) | [Sumário](table-of-contens.md) | [Adiante](chapter-5.md) >
|
||
|
||
# 🎨 **Periférico GFX (Sistema Gráfico)**
|
||
|
||
## 1. Visão Geral
|
||
|
||
O periférico **GFX** é responsável pela geração de imagem no PROMETEU.
|
||
|
||
Ele modela um **hardware gráfico simples**, inspirado em consoles clássicos
|
||
(SNES, CPS-2, Neo-Geo), priorizando:
|
||
|
||
- determinismo
|
||
- baixo custo computacional
|
||
- didática
|
||
- portabilidade
|
||
|
||
O GFX **não é uma GPU moderna**.
|
||
Ele é um dispositivo explícito, baseado em:
|
||
|
||
- framebuffer
|
||
- tilemaps
|
||
- banks de tiles
|
||
- sprites por prioridade
|
||
- composição por ordem de desenho
|
||
|
||
---
|
||
|
||
## 2. Resolução e Framebuffer
|
||
|
||
### Resolução base
|
||
|
||
- **320 × 180 pixels**
|
||
- proporção próxima de 16:9
|
||
- escalável pelo host (nearest-neighbor)
|
||
|
||
### Formato de pixel
|
||
|
||
- **RGB565**
|
||
- 5 bits Red
|
||
- 6 bits Green
|
||
- 5 bits Blue
|
||
- sem canal alpha
|
||
|
||
Transparência é feita por **color key**.
|
||
|
||
---
|
||
|
||
## 3. Double Buffering
|
||
|
||
O GFX mantém dois buffers:
|
||
|
||
- **Back Buffer** — onde o frame é construído
|
||
- **Front Buffer** — onde o frame é exibido
|
||
|
||
Fluxo por frame:
|
||
|
||
1. O sistema desenha no back buffer
|
||
2. Chama `present()`
|
||
3. Buffers são trocados
|
||
4. O host exibe o front buffer
|
||
|
||
Isso garante:
|
||
|
||
- ausência de tearing
|
||
- sincronização clara por frame
|
||
- comportamento determinístico
|
||
|
||
---
|
||
|
||
## 4. Estrutura Gráfica do PROMETEU
|
||
|
||
O mundo gráfico é composto por:
|
||
|
||
- Até **16 Tile Banks**
|
||
- **4 Game Layers** (scrolláveis)
|
||
- **1 HUD Layer** (fixa, sempre por cima)
|
||
- Sprites com prioridade entre layers
|
||
|
||
### 4.1 Tile Banks
|
||
|
||
- Existem até **16 banks**
|
||
- Cada bank tem tamanho fixo de tile:
|
||
- 8×8, 16×16 ou 32×32
|
||
- Um bank é uma biblioteca de gráficos:
|
||
- ambiente
|
||
- personagens
|
||
- UI
|
||
- efeitos
|
||
|
||
### 4.2 Layers
|
||
|
||
- Existem:
|
||
- 4 Game Layers
|
||
- 1 HUD Layer
|
||
- Cada layer aponta para **um único bank**
|
||
- Sprites podem usar **qualquer bank**
|
||
- HUD:
|
||
- não scrolla
|
||
- prioridade máxima
|
||
- geralmente usa tiles 8×8
|
||
|
||
---
|
||
|
||
## 5. Modelo Interno de uma Game Layer
|
||
|
||
Uma Game Layer **não é um bitmap de pixels**.
|
||
Ela é composta por:
|
||
|
||
- Um **Tilemap lógico** (índices de tiles)
|
||
- Um **Cache de Borda** (janela de tiles visíveis)
|
||
- Um **Offset de Scroll**
|
||
|
||
### Estrutura:
|
||
|
||
- `bank_id`
|
||
- `tile_size`
|
||
- `tilemap` (matriz grande)
|
||
- `scroll_x`, `scroll_y`
|
||
- `cache_origin_x`, `cache_origin_y`
|
||
- `cache_tiles[w][h]`
|
||
|
||
---
|
||
|
||
## 6. Tilemap Lógico
|
||
|
||
O tilemap representa o mundo:
|
||
|
||
Cada célula contém:
|
||
|
||
- `tile_id`
|
||
- `flip_x`
|
||
- `flip_y`
|
||
- `priority` (opcional)
|
||
- `palette_id` (opcional)
|
||
|
||
O tilemap pode ser muito maior que a tela.
|
||
|
||
---
|
||
|
||
## 7. Cache de Borda (Tile Cache)
|
||
|
||
O cache é uma janela de tiles ao redor da câmera.
|
||
|
||
Exemplo:
|
||
|
||
- Tela: 320×180
|
||
- Tiles 16×16 → 20×12 visíveis
|
||
- Cache: 22×14 (margem de 1 tile)
|
||
|
||
Ele guarda os tiles já resolvidos a partir do tilemap.
|
||
|
||
---
|
||
|
||
## 8. Atualização do Cache
|
||
|
||
A cada frame:
|
||
|
||
1. Calcular:
|
||
- `tile_x = scroll_x / tile_size`
|
||
- `tile_y = scroll_y / tile_size`
|
||
- `offset_x = scroll_x % tile_size`
|
||
- `offset_y = scroll_y % tile_size`
|
||
|
||
2. Se `tile_x` mudou:
|
||
- Avança `cache_origin_x`
|
||
- Recarrega apenas a nova coluna
|
||
|
||
3. Se `tile_y` mudou:
|
||
- Avança `cache_origin_y`
|
||
- Recarrega apenas a nova linha
|
||
|
||
Somente **uma linha e/ou coluna** é atualizada por frame.
|
||
|
||
---
|
||
|
||
## 9. Cache como Ring Buffer
|
||
|
||
O cache é circular:
|
||
|
||
- Não move dados fisicamente
|
||
- Apenas move índices lógicos
|
||
|
||
Acesso:
|
||
- `real_x = (cache_origin_x + logical_x) % cache_width`
|
||
- `real_y = (cache_origin_y + logical_y) % cache_height`
|
||
|
||
---
|
||
|
||
## 10. Projeção para o Back Buffer
|
||
|
||
Para cada frame:
|
||
|
||
1. Para cada Game Layer, em ordem:
|
||
- Rasterizar tiles visíveis do cache
|
||
- Aplicar scroll, flip e transparência
|
||
- Escrever no back buffer
|
||
|
||
2. Desenhar sprites:
|
||
- Com prioridade entre layers
|
||
- Ordem de desenho define profundidade
|
||
|
||
3. Desenhar HUD layer por último
|
||
|
||
---
|
||
|
||
## 11. Ordem de Desenho e Prioridade
|
||
|
||
- Não existe Z-buffer
|
||
- Não existe sorting automático
|
||
- Quem desenha depois fica na frente
|
||
|
||
Ordem base:
|
||
|
||
1. Game Layer 0
|
||
2. Game Layer 1
|
||
3. Game Layer 2
|
||
4. Game Layer 3
|
||
5. Sprites (por prioridade entre layers)
|
||
6. HUD Layer
|
||
|
||
---
|
||
|
||
## 12. Transparência (Color Key)
|
||
|
||
- Um valor RGB565 é reservado como TRANSPARENT_KEY
|
||
- Pixels com essa cor não são desenhados
|
||
|
||
```
|
||
if src == TRANSPARENT_KEY:
|
||
skip
|
||
else:
|
||
draw
|
||
```
|
||
|
||
|
||
---
|
||
|
||
## 13. Color Math (Blending Discreto)
|
||
|
||
Inspirado no SNES.
|
||
|
||
Modos oficiais:
|
||
|
||
- `BLEND_NONE`
|
||
- `BLEND_HALF`
|
||
- `BLEND_HALF_PLUS`
|
||
- `BLEND_HALF_MINUS`
|
||
- `BLEND_FULL`
|
||
|
||
Sem alpha contínuo.
|
||
Sem blending arbitrário.
|
||
|
||
Tudo é:
|
||
|
||
- inteiro
|
||
- barato
|
||
- determinístico
|
||
|
||
---
|
||
|
||
## 14. Onde o Blend é Aplicado
|
||
|
||
- O blend ocorre durante o desenho
|
||
- O resultado vai direto para o back buffer
|
||
- Não existe composição posterior automática
|
||
|
||
---
|
||
|
||
## 15. O que o GFX NÃO suporta
|
||
|
||
Por design:
|
||
|
||
- Alpha contínuo
|
||
- RGBA framebuffer
|
||
- Shader
|
||
- Pipeline moderno de GPU
|
||
- HDR
|
||
- Gamma correction
|
||
|
||
---
|
||
|
||
## 16. Regra de Performance
|
||
|
||
- Layers:
|
||
- só atualizam borda ao cruzar tile
|
||
- nunca redesenham mundo inteiro
|
||
- Rasterização:
|
||
- sempre por frame, só área visível
|
||
- Sprites:
|
||
- sempre redesenhados por frame
|
||
|
||
---
|
||
|
||
## 17. PostFX Especial — Fade (Scene e HUD)
|
||
|
||
O PROMETEU suporta **fade gradual** como um PostFX especial, com dois controles
|
||
independentes:
|
||
|
||
- **Scene Fade**: afeta toda a cena (Game Layers 0–3 + Sprites)
|
||
- **HUD Fade**: afeta apenas o HUD Layer (sempre composto por último)
|
||
|
||
O fade é implementado sem alpha contínuo por pixel e sem floats.
|
||
Ele usa um **nível inteiro discreto** (0..31), que na prática produz um resultado
|
||
visual “quase contínuo” em 320×180 pixel art.
|
||
|
||
---
|
||
|
||
### 17.1 Representação do Fade
|
||
|
||
Cada fade é representado por:
|
||
|
||
- `fade_level: u8` no intervalo **[0..31]**
|
||
- `0` → totalmente substituído pela cor de fade
|
||
- `31` → totalmente visível (sem fade)
|
||
- `fade_color: RGB565`
|
||
- cor para a qual a imagem será misturada
|
||
|
||
Registradores:
|
||
|
||
- `SCENE_FADE_LEVEL` (0..31)
|
||
- `SCENE_FADE_COLOR` (RGB565)
|
||
- `HUD_FADE_LEVEL` (0..31)
|
||
- `HUD_FADE_COLOR` (RGB565)
|
||
|
||
Casos comuns:
|
||
|
||
- Fade-out: `fade_color = BLACK`
|
||
- Flash/teleporte: `fade_color = WHITE`
|
||
- Efeitos especiais: qualquer cor RGB565
|
||
|
||
---
|
||
|
||
### 17.2 Operação de Fade (Mistura com Cor Arbitrária)
|
||
|
||
Para cada pixel RGB565 `src` e cor de fade `fc`, o pixel final `dst` é calculado por canal.
|
||
|
||
1) Extrair componentes:
|
||
|
||
- `src_r5`, `src_g6`, `src_b5`
|
||
- `fc_r5`, `fc_g6`, `fc_b5`
|
||
|
||
2) Aplicar mistura inteira:
|
||
|
||
```
|
||
src_weight = fade_level // 0..31
|
||
fc_weight = 31 - fade_level
|
||
|
||
r5 = (src_r5 * src_weight + fc_r5 * fc_weight) / 31
|
||
g6 = (src_g6 * src_weight + fc_g6 * fc_weight) / 31
|
||
b5 = (src_b5 * src_weight + fc_b5 * fc_weight) / 31
|
||
```
|
||
|
||
|
||
- `src_r5`, `src_g6`, `src_b5`
|
||
- `fc_r5`, `fc_g6`, `fc_b5`
|
||
|
||
2) Aplicar mistura inteira:
|
||
|
||
src_weight = fade_level // 0..31
|
||
fc_weight = 31 - fade_level
|
||
|
||
r5 = (src_r5 * src_weight + fc_r5 * fc_weight) / 31
|
||
g6 = (src_g6 * src_weight + fc_g6 * fc_weight) / 31
|
||
b5 = (src_b5 * src_weight + fc_b5 * fc_weight) / 31
|
||
|
||
3) Reempacotar:
|
||
|
||
```
|
||
dst = pack_rgb565(r5, g6, b5)
|
||
```
|
||
|
||
|
||
Observações:
|
||
|
||
- Operação determinística
|
||
- Somente inteiros
|
||
- Pode ser otimizada via LUT
|
||
|
||
---
|
||
|
||
### 17.3 Ordem de Aplicação no Frame
|
||
|
||
A composição do frame segue esta ordem:
|
||
|
||
1. Rasterizar **Game Layers 0–3** → Back Buffer
|
||
2. Rasterizar **Sprites** conforme prioridade
|
||
3. (Opcional) Pipeline extra (Emission/Light/Glow etc.)
|
||
4. Aplicar **Scene Fade** usando:
|
||
- `SCENE_FADE_LEVEL`
|
||
- `SCENE_FADE_COLOR`
|
||
5. Rasterizar **HUD Layer**
|
||
6. Aplicar **HUD Fade** usando:
|
||
- `HUD_FADE_LEVEL`
|
||
- `HUD_FADE_COLOR`
|
||
7. `present()`
|
||
|
||
Regras:
|
||
|
||
- Scene Fade nunca afeta HUD
|
||
- HUD Fade nunca afeta a cena
|
||
|
||
---
|
||
|
||
### 17.4 Casos de Uso
|
||
|
||
- Troca de HUD:
|
||
- diminuir `HUD_FADE_LEVEL` até 0
|
||
- trocar HUD/tilemap
|
||
- aumentar `HUD_FADE_LEVEL` até 31
|
||
|
||
- Troca de área:
|
||
- diminuir `SCENE_FADE_LEVEL` até 0
|
||
- trocar cenário
|
||
- aumentar `SCENE_FADE_LEVEL` até 31
|
||
|
||
- Flash / dano / teleporte:
|
||
- usar `fade_color = WHITE` ou outra cor temática
|
||
|
||
---
|
||
|
||
## 18. Resumo
|
||
|
||
O GFX do PROMETEU é simples **por escolha**, não por limitação.
|
||
|
||
- Framebuffer RGB565 com double buffer
|
||
- Color key para transparência
|
||
- Blending discreto estilo SNES
|
||
- Até 16 tile banks
|
||
- 4 game layers + 1 HUD
|
||
- Layer = tilemap + cache + scroll
|
||
- Projeção rasterizada por frame
|
||
- Profundidade definida por ordem de desenho
|
||
|
||
< [Voltar](chapter-3.md) | [Sumário](table-of-contens.md) | [Adiante](chapter-5.md) > |