From c718d36fe24b74a9ec333c57f5beecc80d2139d2 Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Tue, 13 Jan 2026 07:04:14 +0000 Subject: [PATCH] added sprite and draw --- crates/core/src/machine.rs | 33 +++----- crates/core/src/model/mod.rs | 2 + crates/core/src/model/sprite.rs | 13 ++++ crates/core/src/peripherals/gfx.rs | 118 ++++++++++++++++++++++++++++- crates/host_desktop/src/main.rs | 18 ++++- 5 files changed, 154 insertions(+), 30 deletions(-) create mode 100644 crates/core/src/model/sprite.rs diff --git a/crates/core/src/machine.rs b/crates/core/src/machine.rs index 10cfc656..c753b01d 100644 --- a/crates/core/src/machine.rs +++ b/crates/core/src/machine.rs @@ -28,6 +28,7 @@ impl Machine { pub fn step_frame(&mut self, signals: &InputSignals) { self.begin_frame(signals); self.tick(); // futuro: executa cartucho/VM + self.gfx.render_all(); // A máquina executa o pipeline gráfico (Ação física) self.end_frame(); // present/housekeeping } @@ -44,22 +45,6 @@ impl Machine { /// Lógica do frame (demo hardcoded por enquanto). pub fn tick(&mut self) { - // self.gfx.clear(Color::rgb(0x10, 0x10, 0x10)); - // - // let (x, y) = self.demo_pos(); - // - // let color = if self.pad.a.down || self.touch.f.down { - // Color::rgb(0x00, 0xFF, 0x00) - // } else { - // Color::rgb(0xFF, 0x40, 0x40) - // }; - // - // // game - // self.gfx.fill_rect(x, y, 20, 20, color); - // // smoke - // self.gfx.fill_rect_blend(140, 0, 40, 180, Color::gray_scale(0x88), BlendMode::Half) - - // Limpa a tela com um azul escuro "estilo console" self.gfx.clear(Color::rgb(0x20, 0x20, 0x40)); @@ -86,18 +71,18 @@ impl Machine { // Coloca o Tile ID 1 no mapa do HUD (canto superior esquerdo) self.gfx.hud.map.set_tile(0, 0, crate::model::Tile { id: 1, ..Default::default() }); - // Executa o pipeline gráfico - self.gfx.render_all(); + for i in 0..10 { + let s = &mut self.gfx.sprites[i]; + s.active = true; + s.bank_id = 0; + s.tile.id = 1; + s.x = ((self.frame_index + (i as u64 * 20)) % 320) as i32; + s.y = (i as i32 * 15) + 20; + } } /// Final do frame: troca buffers. pub fn end_frame(&mut self) { self.gfx.present(); } - - // fn demo_pos(&self) -> (i32, i32) { - // let x = (self.frame_index % 300) as i32; - // let y = (self.pad.a.hold_frames % 160) as i32; - // (x, y) - // } } diff --git a/crates/core/src/model/mod.rs b/crates/core/src/model/mod.rs index ee0d4cc3..45834c1c 100644 --- a/crates/core/src/model/mod.rs +++ b/crates/core/src/model/mod.rs @@ -3,9 +3,11 @@ mod button; mod tile; mod tile_layer; mod tile_bank; +mod sprite; pub use button::Button; pub use color::Color; pub use tile::Tile; pub use tile_bank::{TileBank, TileSize}; pub use tile_layer::{HudTileLayer, ScrollableTileLayer, TileMap}; +pub use sprite::Sprite; diff --git a/crates/core/src/model/sprite.rs b/crates/core/src/model/sprite.rs new file mode 100644 index 00000000..bca952fc --- /dev/null +++ b/crates/core/src/model/sprite.rs @@ -0,0 +1,13 @@ +use crate::model::Tile; + +#[derive(Clone, Copy, Debug, Default)] +pub struct Sprite { + pub tile: Tile, + pub x: i32, + pub y: i32, + pub bank_id: u8, + pub active: bool, + pub flip_x: bool, + pub flip_y: bool, + // No futuro: priority, palette override, etc. +} \ No newline at end of file diff --git a/crates/core/src/peripherals/gfx.rs b/crates/core/src/peripherals/gfx.rs index f37a31c4..6a1f0817 100644 --- a/crates/core/src/peripherals/gfx.rs +++ b/crates/core/src/peripherals/gfx.rs @@ -1,4 +1,4 @@ -use crate::model::{Color, HudTileLayer, ScrollableTileLayer, TileBank, TileMap, TileSize}; +use crate::model::{Color, HudTileLayer, ScrollableTileLayer, Sprite, TileBank, TileMap, TileSize}; #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] pub enum BlendMode { @@ -24,12 +24,22 @@ pub struct Gfx { pub layers: [ScrollableTileLayer; 4], pub hud: HudTileLayer, pub banks: [Option; 16], + pub sprites: [Sprite; 512], // Nossa "OAM" } -impl Gfx { +impl Gfx { pub fn new(w: usize, h: usize) -> Self { const EMPTY_BANK: Option = None; - + const EMPTY_SPRITE: Sprite = Sprite { + tile: crate::model::Tile { id: 0, flip_x: false, flip_y: false, palette_id: 0 }, + x: 0, + y: 0, + bank_id: 0, + active: false, + flip_x: false, + flip_y: false, + }; + let len = w * h; Self { w, @@ -44,6 +54,7 @@ impl Gfx { ], hud: HudTileLayer::new(64, 32), banks: [EMPTY_BANK; 16], + sprites: [EMPTY_SPRITE; 512], } } @@ -108,7 +119,9 @@ impl Gfx { self.render_layer(i); } - // 2. Sprites (Ainda vamos implementar) + // 2. Sprites + self.render_sprites(); + // 3. HUD (Sempre por cima) self.render_hud(); @@ -175,6 +188,103 @@ impl Gfx { } } } + + fn render_sprites(&mut self) { + for sprite in self.sprites.iter() { + if !sprite.active { continue; } + + let bank = match self.banks.get(sprite.bank_id as usize) { + Some(Some(b)) => b, + _ => continue, + }; + + Self::draw_sprite_pixel_by_pixel( + &mut self.back, + self.w, + self.h, + sprite, + bank + ); + } + } + + fn draw_sprite_pixel_by_pixel( + back: &mut [u16], + screen_w: usize, + screen_h: usize, + sprite: &Sprite, + bank: &TileBank + ) { + 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); + + // 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); + + if color == Color::COLOR_KEY { continue; } + + let idx = (world_y as usize * screen_w) + world_x as usize; + back[idx] = color.0; + } + } + } + + /// Retorna o uso estimado de memória das estruturas gráficas em bytes. + /// Isso ajuda a monitorar o "custo" do hardware simulado. + pub fn memory_usage_bytes(&self) -> usize { + let mut total = 0; + + // Buffers de tela (Front + Back) + total += self.front.len() * 2; + total += self.back.len() * 2; + + // Layers (Tiles + Metadados) + for layer in &self.layers { + total += layer.map.tiles.len() * std::mem::size_of::(); + total += std::mem::size_of::(); + } + + // HUD + total += self.hud.map.tiles.len() * std::mem::size_of::(); + total += std::mem::size_of::(); + + // Tile Banks (Apenas bancos carregados) + 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::(); + } + } + + // Sprites (OAM) + total += self.sprites.len() * std::mem::size_of::(); + + total + } } /// Faz blend em RGB565 por canal com saturação. diff --git a/crates/host_desktop/src/main.rs b/crates/host_desktop/src/main.rs index 080d5814..bcedf0b4 100644 --- a/crates/host_desktop/src/main.rs +++ b/crates/host_desktop/src/main.rs @@ -69,10 +69,10 @@ impl PrometeuApp { } } -impl ApplicationHandler for PrometeuApp { +impl ApplicationHandler for PrometeuApp { fn resumed(&mut self, event_loop: &ActiveEventLoop) { let attrs = WindowAttributes::default() - .with_title("PROMETEU - host_desktop") + .with_title(format!("PROMETEU - host_desktop | GFX Mem: {:.2} KB | Frame: {}", 0.0, 0)) .with_inner_size(LogicalSize::new(960.0, 540.0)) .with_min_inner_size(LogicalSize::new(320.0, 180.0)); @@ -188,6 +188,20 @@ impl ApplicationHandler for PrometeuApp { // executa 1 frame do PROMETEU self.machine.step_frame(&self.input_signals); + // temp: atualiza o título da janela a cada 1 segundo (60 frames) + if self.machine.frame_index % 60 == 0 { + if let Some(window) = self.window { + let usage_bytes = self.machine.gfx.memory_usage_bytes(); + let kb = usage_bytes as f64 / 1024.0; + + let title = format!( + "PROMETEU - host_desktop | GFX Mem: {:.2} KB | Frame: {}", + kb, + self.machine.frame_index); + window.set_title(&title); + } + } + // pede redraw self.request_redraw(); } else {