added pallete system
This commit is contained in:
parent
a9345244e3
commit
48c6c6d7b0
@ -49,132 +49,99 @@ impl Machine {
|
||||
|
||||
/// Lógica do frame (demo hardcoded por enquanto).
|
||||
pub fn tick(&mut self) {
|
||||
// Limpa a tela com um azul escuro "estilo console"
|
||||
// Limpa a tela com Preto
|
||||
self.gfx.clear(Color::BLACK);
|
||||
|
||||
// Simula o carregamento de um banco se estiver vazio (apenas para teste)
|
||||
// SETUP BANCO 0 (Tiles 8x8)
|
||||
if self.gfx.banks[0].is_none() {
|
||||
let mut test_bank = crate::model::TileBank::new(crate::model::TileSize::Size8, 128, 128);
|
||||
// Pinta o primeiro tile (ID 1) de Branco para teste
|
||||
// Ele começa no pixel (8, 0) pois o ID 0 é o primeiro (0,0)
|
||||
let mut bank = crate::model::TileBank::new(crate::model::TileSize::Size8, 128, 128);
|
||||
// Define Cor na Paleta 0, Índice 1 = VERDE
|
||||
bank.palettes[0][1] = Color::GREEN.raw();
|
||||
|
||||
let tile_size = 8;
|
||||
let start_x = 8; // ID 1 está na segunda posição da primeira linha
|
||||
let start_y = 0;
|
||||
|
||||
let start_x = 8; // Tile ID 1
|
||||
for y in 0..tile_size {
|
||||
for x in 0..tile_size {
|
||||
let px = start_x + x;
|
||||
let py = start_y + y;
|
||||
let idx = py * 128 + px;
|
||||
test_bank.pixels[idx] = Color::GREEN;
|
||||
let idx = y * 128 + (start_x + x);
|
||||
bank.pixel_indices[idx] = 1; // Atribui o índice da paleta
|
||||
}
|
||||
}
|
||||
self.gfx.banks[0] = Some(test_bank);
|
||||
self.gfx.banks[0] = Some(bank);
|
||||
}
|
||||
|
||||
// Simula o carregamento de um banco se estiver vazio (apenas para teste)
|
||||
// SETUP BANCO 1 (Tiles 16x16)
|
||||
if self.gfx.banks[1].is_none() {
|
||||
let mut test_bank = crate::model::TileBank::new(crate::model::TileSize::Size16, 128, 128);
|
||||
// Pinta o primeiro tile (ID 1) de Branco para teste
|
||||
// Ele começa no pixel (16, 0) pois o ID 0 é o primeiro (0,0)
|
||||
let tile_size = 16;
|
||||
let start_x = 16; // ID 1 está na segunda posição da primeira linha
|
||||
let start_y = 0;
|
||||
let mut bank = crate::model::TileBank::new(crate::model::TileSize::Size16, 128, 128);
|
||||
// Define Cor na Paleta 0, Índice 1 = VERMELHO
|
||||
bank.palettes[0][1] = Color::RED.raw();
|
||||
|
||||
let tile_size = 16;
|
||||
let start_x = 16; // Tile ID 1
|
||||
for y in 0..tile_size {
|
||||
for x in 0..tile_size {
|
||||
let px = start_x + x;
|
||||
let py = start_y + y;
|
||||
let idx = py * 128 + px;
|
||||
test_bank.pixels[idx] = Color::RED;
|
||||
let idx = y * 128 + (start_x + x);
|
||||
bank.pixel_indices[idx] = 1;
|
||||
}
|
||||
}
|
||||
self.gfx.banks[1] = Some(test_bank);
|
||||
self.gfx.banks[1] = Some(bank);
|
||||
}
|
||||
|
||||
// Simula o carregamento de um banco se estiver vazio (apenas para teste)
|
||||
// SETUP BANCO 2 (Tiles 16x16 para Sprites)
|
||||
if self.gfx.banks[2].is_none() {
|
||||
let mut test_bank = crate::model::TileBank::new(crate::model::TileSize::Size16, 128, 128);
|
||||
// Pinta o primeiro tile (ID 1) de Branco para teste
|
||||
// Ele começa no pixel (16, 0) pois o ID 0 é o primeiro (0,0)
|
||||
let tile_size = 16;
|
||||
let start_x = 16; // ID 1 está na segunda posição da primeira linha
|
||||
let start_y = 0;
|
||||
let mut bank = crate::model::TileBank::new(crate::model::TileSize::Size16, 128, 128);
|
||||
// Define Cor na Paleta 0, Índice 1 = INDIGO (Roxo)
|
||||
bank.palettes[0][1] = Color::rgb(0x4B, 0x00, 0x82).raw();
|
||||
// Define Cor na Paleta 1, Índice 1 = AMARELO (para Palette Swap)
|
||||
bank.palettes[1][1] = Color::YELLOW.raw();
|
||||
|
||||
let tile_size = 16;
|
||||
let start_x = 16;
|
||||
for y in 0..tile_size {
|
||||
for x in 0..tile_size {
|
||||
let px = start_x + x;
|
||||
let py = start_y + y;
|
||||
let idx = py * 128 + px;
|
||||
test_bank.pixels[idx] = Color::INDIGO;
|
||||
let idx = y * 128 + (start_x + x);
|
||||
bank.pixel_indices[idx] = 1;
|
||||
}
|
||||
}
|
||||
self.gfx.banks[2] = Some(test_bank);
|
||||
self.gfx.banks[2] = Some(bank);
|
||||
}
|
||||
|
||||
// Coloca o Tile ID 1 no mapa do HUD (canto superior esquerdo)
|
||||
self.gfx.hud.map.set_tile(18, 6, crate::model::Tile { id: 1, ..Default::default() });
|
||||
// --- LÓGICA DE ESTADO ---
|
||||
|
||||
self.gfx.layers[0].bank_id = 1;
|
||||
self.gfx.layers[0].map.set_tile(8, 1, crate::model::Tile { id: 1, ..Default::default() });
|
||||
self.gfx.layers[0].map.set_tile(8, 2, crate::model::Tile { id: 1, ..Default::default() });
|
||||
self.gfx.layers[0].map.set_tile(8, 3, crate::model::Tile { id: 1, ..Default::default() });
|
||||
self.gfx.layers[0].map.set_tile(8, 4, crate::model::Tile { id: 1, ..Default::default() });
|
||||
self.gfx.layers[0].map.set_tile(8, 5, crate::model::Tile { id: 1, ..Default::default() });
|
||||
self.gfx.layers[1].bank_id = 1;
|
||||
self.gfx.layers[1].map.set_tile(9, 1, crate::model::Tile { id: 1, ..Default::default() });
|
||||
self.gfx.layers[1].map.set_tile(9, 2, crate::model::Tile { id: 1, ..Default::default() });
|
||||
self.gfx.layers[1].map.set_tile(9, 3, crate::model::Tile { id: 1, ..Default::default() });
|
||||
self.gfx.layers[1].map.set_tile(9, 4, crate::model::Tile { id: 1, ..Default::default() });
|
||||
self.gfx.layers[1].map.set_tile(9, 5, crate::model::Tile { id: 1, ..Default::default() });
|
||||
self.gfx.layers[2].bank_id = 1;
|
||||
self.gfx.layers[2].map.set_tile(10, 1, crate::model::Tile { id: 1, ..Default::default() });
|
||||
self.gfx.layers[2].map.set_tile(10, 2, crate::model::Tile { id: 1, ..Default::default() });
|
||||
self.gfx.layers[2].map.set_tile(10, 3, crate::model::Tile { id: 1, ..Default::default() });
|
||||
self.gfx.layers[2].map.set_tile(10, 4, crate::model::Tile { id: 1, ..Default::default() });
|
||||
self.gfx.layers[2].map.set_tile(10, 5, crate::model::Tile { id: 1, ..Default::default() });
|
||||
self.gfx.layers[3].bank_id = 1;
|
||||
self.gfx.layers[3].map.set_tile(11, 1, crate::model::Tile { id: 1, ..Default::default() });
|
||||
self.gfx.layers[3].map.set_tile(11, 2, crate::model::Tile { id: 1, ..Default::default() });
|
||||
self.gfx.layers[3].map.set_tile(11, 3, crate::model::Tile { id: 1, ..Default::default() });
|
||||
self.gfx.layers[3].map.set_tile(11, 4, crate::model::Tile { id: 1, ..Default::default() });
|
||||
self.gfx.layers[3].map.set_tile(11, 5, crate::model::Tile { id: 1, ..Default::default() });
|
||||
// HUD
|
||||
self.gfx.hud.map.set_tile(18, 6, crate::model::Tile { id: 1, palette_id: 0, ..Default::default() });
|
||||
|
||||
// Faz a cena pulsar entre claro e escuro a cada 2 segundos
|
||||
// Usamos uma conta matemática simples para gerar um valor de 0 a 31
|
||||
let pulse = (self.frame_index / 4) % 64;
|
||||
let level = if pulse > 31 { 63 - pulse } else { pulse };
|
||||
|
||||
self.gfx.scene_fade_level = level as u8;
|
||||
self.gfx.scene_fade_color = Color::BLACK;
|
||||
|
||||
// O HUD continuará 100% visível enquanto o resto escurece!
|
||||
self.gfx.hud_fade_level = 31;
|
||||
// Camadas de Jogo (usando Banco 1 - Vermelho)
|
||||
for i in 0..4 {
|
||||
self.gfx.layers[i].bank_id = 1;
|
||||
self.gfx.layers[i].map.set_tile(8 + i, 1 + i, crate::model::Tile { id: 1, palette_id: 0, ..Default::default() });
|
||||
}
|
||||
|
||||
// 512 Sprites (usando Banco 2 - Indigo e Amarelo)
|
||||
for i in 0..512 {
|
||||
let s = &mut self.gfx.sprites[i];
|
||||
s.active = true;
|
||||
|
||||
// Distribui os sprites entre os 3 bancos de teste que você criou
|
||||
s.bank_id = (i % 2) as u8 + 1;
|
||||
s.bank_id = 2;
|
||||
s.tile.id = 1;
|
||||
|
||||
// Cria um movimento caótico baseado no index do sprite e no frame_index
|
||||
// Palette Swap: pares Indigo (0), ímpares Amarelo (1)
|
||||
s.tile.palette_id = (i % 2) as u8;
|
||||
|
||||
let speed = (1 + (i % 3)) as u64;
|
||||
s.x = (((self.frame_index * speed) + (i as u64 * 10)) % 400) as i32 - 40;
|
||||
s.y = (((self.frame_index * speed) + (i as u64 * 4)) % 180) as i32;
|
||||
if i % 2 != 0 {
|
||||
s.y = 179 - s.y;
|
||||
}
|
||||
let y_base = (((self.frame_index * speed) + (i as u64 * 4)) % 180) as i32;
|
||||
s.y = if i % 2 == 0 { y_base } else { 179 - y_base };
|
||||
|
||||
// Distribui as prioridades de 0 a 4
|
||||
// Isso vai fazer alguns sprites passarem atrás do cenário e outros na frente
|
||||
s.priority = (i % 5) as u8;
|
||||
|
||||
// Efeito de flip alternado para testar a lógica de espelhamento
|
||||
s.flip_x = i % 2 == 0;
|
||||
s.flip_y = i % 3 == 0;
|
||||
}
|
||||
|
||||
// Post-FX Fade Pulsante
|
||||
let pulse = (self.frame_index / 4) % 64;
|
||||
let level = if pulse > 31 { 63 - pulse } else { pulse };
|
||||
self.gfx.scene_fade_level = level as u8;
|
||||
self.gfx.scene_fade_color = Color::BLACK;
|
||||
self.gfx.hud_fade_level = 31;
|
||||
}
|
||||
|
||||
/// Final do frame: troca buffers.
|
||||
|
||||
@ -11,7 +11,12 @@ pub struct TileBank {
|
||||
pub tile_size: TileSize,
|
||||
pub width: usize, // em pixels
|
||||
pub height: usize, // em pixels
|
||||
pub pixels: Vec<Color>,
|
||||
// pub pixels: Vec<Color>,
|
||||
|
||||
/// Agora guardamos índices de 0 a 15 (4 bits por pixel simulados em 8 bits)
|
||||
pub pixel_indices: Vec<u8>,
|
||||
/// 256 paletas, cada uma com 16 cores (RGB565 como u16)
|
||||
pub palettes: [[u16; 16]; 256],
|
||||
}
|
||||
|
||||
impl TileBank {
|
||||
@ -20,26 +25,38 @@ impl TileBank {
|
||||
tile_size,
|
||||
width,
|
||||
height,
|
||||
pixels: vec![Color::BLACK; width * height],
|
||||
pixel_indices: vec![0; width * height], // Índice 0 = Transparente
|
||||
palettes: [[0; 16]; 256],
|
||||
}
|
||||
}
|
||||
|
||||
/// Retorna a cor de um pixel específico dentro de um tile.
|
||||
/// tile_id: o índice do tile no banco
|
||||
/// local_x, local_y: a posição do pixel dentro do tile (0 até tile_size-1)
|
||||
pub fn get_pixel(&self, tile_id: u16, local_x: usize, local_y: usize) -> Color {
|
||||
let tile_size = self.tile_size as usize;
|
||||
let tiles_per_row = self.width / tile_size;
|
||||
let tile_x = (tile_id as usize % tiles_per_row) * tile_size;
|
||||
let tile_y = (tile_id as usize / tiles_per_row) * tile_size;
|
||||
pub fn get_pixel_index(&self, tile_id: u16, local_x: usize, local_y: usize) -> u8 {
|
||||
let size = self.tile_size as usize;
|
||||
let tiles_per_row = self.width / size;
|
||||
let tile_x = (tile_id as usize % tiles_per_row) * size;
|
||||
let tile_y = (tile_id as usize / tiles_per_row) * size;
|
||||
|
||||
let pixel_x = tile_x + local_x;
|
||||
let pixel_y = tile_y + local_y;
|
||||
|
||||
if pixel_x < self.width && pixel_y < self.height {
|
||||
self.pixels[pixel_y * self.width + pixel_x]
|
||||
self.pixel_indices[pixel_y * self.width + pixel_x]
|
||||
} else {
|
||||
Color::BLACK
|
||||
0 // Fora do banco = Transparente
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolve um índice de pixel para uma Color real usando uma paleta.
|
||||
pub fn resolve_color(&self, palette_id: u8, pixel_index: u8) -> Color {
|
||||
// Regra: Índice 0 é sempre transparente (usamos MAGENTA/COLOR_KEY como vácuo)
|
||||
if pixel_index == 0 {
|
||||
return Color::COLOR_KEY;
|
||||
}
|
||||
|
||||
let raw_color = self.palettes[palette_id as usize][pixel_index as usize];
|
||||
Color::from_raw(raw_color)
|
||||
}
|
||||
}
|
||||
@ -248,10 +248,16 @@ impl Gfx {
|
||||
let fetch_x = if tile.flip_x { size - 1 - local_x } else { local_x };
|
||||
let fetch_y = if tile.flip_y { size - 1 - local_y } else { local_y };
|
||||
|
||||
let color = bank.get_pixel(tile.id, fetch_x, fetch_y);
|
||||
if color == Color::COLOR_KEY { continue; }
|
||||
// 1. Pega o índice do pixel no banco
|
||||
let px_index = bank.get_pixel_index(tile.id, fetch_x, fetch_y);
|
||||
|
||||
back[world_y as usize * screen_w + world_x as usize] = color.0;
|
||||
// 2. Regra: Índice 0 é transparente
|
||||
if px_index == 0 { continue; }
|
||||
|
||||
// 3. Resolve a cor usando a paleta do tile
|
||||
let color = bank.resolve_color(tile.palette_id, px_index);
|
||||
|
||||
back[world_y as usize * screen_w + world_x as usize] = color.raw();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -275,40 +281,31 @@ impl Gfx {
|
||||
sprite: &Sprite,
|
||||
bank: &TileBank
|
||||
) {
|
||||
// ... (mesmo cálculo de bounds/clipping que já tínhamos) ...
|
||||
let size = bank.tile_size as usize;
|
||||
let sprite_w = size as i32;
|
||||
let sprite_h = size as i32;
|
||||
|
||||
// 1. Clipping Rápido: Se o sprite estiver totalmente fora, nem processamos
|
||||
if sprite.x <= -sprite_w || sprite.x >= screen_w as i32 ||
|
||||
sprite.y <= -sprite_h || sprite.y >= screen_h as i32 {
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. Calcular a área visível do sprite (Interseção com a tela)
|
||||
let start_x = sprite.x.max(0);
|
||||
let start_y = sprite.y.max(0);
|
||||
let end_x = (sprite.x + sprite_w).min(screen_w as i32);
|
||||
let end_y = (sprite.y + sprite_h).min(screen_h as i32);
|
||||
let end_x = (sprite.x + size as i32).min(screen_w as i32);
|
||||
let end_y = (sprite.y + size as i32).min(screen_h as i32);
|
||||
|
||||
// 3. Loop apenas sobre os pixels visíveis na tela
|
||||
for world_y in start_y..end_y {
|
||||
for world_x in start_x..end_x {
|
||||
|
||||
// Calcular a posição local dentro do tile considerando o scroll/posição do sprite
|
||||
let local_x = (world_x - sprite.x) as usize;
|
||||
let local_y = (world_y - sprite.y) as usize;
|
||||
|
||||
// Lógica de Flip (Espelhamento)
|
||||
let fetch_x = if sprite.flip_x { size - 1 - local_x } else { local_x };
|
||||
let fetch_y = if sprite.flip_y { size - 1 - local_y } else { local_y };
|
||||
|
||||
let color = bank.get_pixel(sprite.tile.id, fetch_x, fetch_y);
|
||||
// 1. Pega o índice
|
||||
let px_index = bank.get_pixel_index(sprite.tile.id, fetch_x, fetch_y);
|
||||
|
||||
if color == Color::COLOR_KEY { continue; }
|
||||
// 2. Transparência
|
||||
if px_index == 0 { continue; }
|
||||
|
||||
let idx = (world_y as usize * screen_w) + world_x as usize;
|
||||
back[idx] = color.0;
|
||||
// 3. Resolve cor via paleta (do tile dentro do sprite)
|
||||
let color = bank.resolve_color(sprite.tile.palette_id, px_index);
|
||||
|
||||
back[world_y as usize * screen_w + world_x as usize] = color.raw();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -334,34 +331,44 @@ impl Gfx {
|
||||
}
|
||||
}
|
||||
|
||||
/// Retorna o uso estimado de memória das estruturas gráficas em bytes.
|
||||
/// Isso ajuda a monitorar o "custo" do hardware simulado.
|
||||
/// Retorna o uso real de memória das estruturas gráficas em bytes.
|
||||
/// Reflete os buffers de índices, paletas e OAM conforme Capítulo 10.8.
|
||||
pub fn memory_usage_bytes(&self) -> usize {
|
||||
let mut total = 0;
|
||||
|
||||
// Buffers de tela (Front + Back)
|
||||
// 1. Framebuffers (Front + Back)
|
||||
// Cada um é Vec<u16>, ocupando 2 bytes por pixel.
|
||||
total += self.front.len() * 2;
|
||||
total += self.back.len() * 2;
|
||||
|
||||
// Layers (Tiles + Metadados)
|
||||
// 2. Tile Layers (4 Game Layers)
|
||||
for layer in &self.layers {
|
||||
total += layer.map.tiles.len() * std::mem::size_of::<crate::model::Tile>();
|
||||
// Tamanho da struct + os dados do mapa (Vec<Tile>)
|
||||
total += std::mem::size_of::<ScrollableTileLayer>();
|
||||
total += layer.map.tiles.len() * std::mem::size_of::<crate::model::Tile>();
|
||||
}
|
||||
|
||||
// HUD
|
||||
total += self.hud.map.tiles.len() * std::mem::size_of::<crate::model::Tile>();
|
||||
// 3. HUD Layer
|
||||
total += std::mem::size_of::<HudTileLayer>();
|
||||
total += self.hud.map.tiles.len() * std::mem::size_of::<crate::model::Tile>();
|
||||
|
||||
// Tile Banks (Apenas bancos carregados)
|
||||
// 4. Tile Banks (Assets e Paletas)
|
||||
for bank_opt in &self.banks {
|
||||
if let Some(bank) = bank_opt {
|
||||
total += bank.pixels.len() * 2; // RGB565 = 2 bytes
|
||||
total += std::mem::size_of::<TileBank>();
|
||||
|
||||
// Buffer de Índices (pixel_indices: Vec<u8>)
|
||||
// Cada pixel ocupa exatamente 1 byte.
|
||||
total += bank.pixel_indices.len();
|
||||
|
||||
// Tabela de Paletas (palettes: [[u16; 16]; 256])
|
||||
// 256 paletas * 16 cores * 2 bytes cada.
|
||||
total += 256 * 16 * 2;
|
||||
}
|
||||
}
|
||||
|
||||
// Sprites (OAM)
|
||||
// 5. Sprites (OAM)
|
||||
// Array fixo de 512 Sprites.
|
||||
total += self.sprites.len() * std::mem::size_of::<crate::model::Sprite>();
|
||||
|
||||
total
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user