added sprite and draw
This commit is contained in:
parent
13169698cf
commit
c718d36fe2
@ -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)
|
||||
// }
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
13
crates/core/src/model/sprite.rs
Normal file
13
crates/core/src/model/sprite.rs
Normal file
@ -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.
|
||||
}
|
||||
@ -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,11 +24,21 @@ pub struct Gfx {
|
||||
pub layers: [ScrollableTileLayer; 4],
|
||||
pub hud: HudTileLayer,
|
||||
pub banks: [Option<TileBank>; 16],
|
||||
pub sprites: [Sprite; 512], // Nossa "OAM"
|
||||
}
|
||||
|
||||
impl Gfx {
|
||||
pub fn new(w: usize, h: usize) -> Self {
|
||||
const EMPTY_BANK: Option<TileBank> = 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 {
|
||||
@ -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::<crate::model::Tile>();
|
||||
total += std::mem::size_of::<ScrollableTileLayer>();
|
||||
}
|
||||
|
||||
// HUD
|
||||
total += self.hud.map.tiles.len() * std::mem::size_of::<crate::model::Tile>();
|
||||
total += std::mem::size_of::<HudTileLayer>();
|
||||
|
||||
// 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::<TileBank>();
|
||||
}
|
||||
}
|
||||
|
||||
// Sprites (OAM)
|
||||
total += self.sprites.len() * std::mem::size_of::<crate::model::Sprite>();
|
||||
|
||||
total
|
||||
}
|
||||
}
|
||||
|
||||
/// Faz blend em RGB565 por canal com saturação.
|
||||
|
||||
@ -72,7 +72,7 @@ impl 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 {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user