< [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) >