tile, layers and bank implementation
This commit is contained in:
parent
072b6133d5
commit
13169698cf
@ -1,32 +0,0 @@
|
||||
/// Cor simples em RGB565 (0bRRRRRGGGGGGBBBBB).
|
||||
/// - 5 bits Red
|
||||
/// - 6 bits Green
|
||||
/// - 5 bits Blue
|
||||
///
|
||||
/// Não há canal alpha.
|
||||
/// Transparência é tratada via Color Key ou Blend Mode.
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
pub struct Color(pub u16);
|
||||
|
||||
impl Color {
|
||||
/// Cria uma cor RGB565 a partir de componentes 8-bit.
|
||||
pub const fn rgb(r: u8, g: u8, b: u8) -> Self {
|
||||
let r5 = (r as u16 >> 3) & 0x1F;
|
||||
let g6 = (g as u16 >> 2) & 0x3F;
|
||||
let b5 = (b as u16 >> 3) & 0x1F;
|
||||
|
||||
Self((r5 << 11) | (g6 << 5) | b5)
|
||||
}
|
||||
|
||||
pub const fn gray_scale(c: u8) -> Self {
|
||||
Self::rgb(c, c, c)
|
||||
}
|
||||
|
||||
pub const fn from_raw(raw: u16) -> Self {
|
||||
Self(raw)
|
||||
}
|
||||
|
||||
pub const fn raw(self) -> u16 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,5 @@
|
||||
pub mod machine;
|
||||
pub mod peripherals;
|
||||
mod color;
|
||||
mod c_button;
|
||||
mod model;
|
||||
|
||||
pub use machine::Machine;
|
||||
pub use color::Color;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
use crate::peripherals::{BlendMode, Gfx, InputSignals, Pad, Touch};
|
||||
use crate::Color;
|
||||
use crate::model::Color;
|
||||
use crate::peripherals::{Gfx, InputSignals, Pad, Touch};
|
||||
|
||||
/// PROMETEU "hardware lógico" (v0).
|
||||
/// O Host alimenta INPUT SIGNALS e chama `step_frame()` em 60Hz.
|
||||
@ -44,20 +44,50 @@ impl Machine {
|
||||
|
||||
/// Lógica do frame (demo hardcoded por enquanto).
|
||||
pub fn tick(&mut self) {
|
||||
self.gfx.clear(Color::rgb(0x10, 0x10, 0x10));
|
||||
// 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)
|
||||
|
||||
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)
|
||||
};
|
||||
// Limpa a tela com um azul escuro "estilo console"
|
||||
self.gfx.clear(Color::rgb(0x20, 0x20, 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)
|
||||
// Simula o carregamento de um banco se estiver vazio (apenas para teste)
|
||||
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 tile_size = 8;
|
||||
let start_x = 8; // ID 1 está na segunda posição da primeira linha
|
||||
let start_y = 0;
|
||||
|
||||
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::WHITE;
|
||||
}
|
||||
}
|
||||
self.gfx.banks[0] = Some(test_bank);
|
||||
}
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
/// Final do frame: troca buffers.
|
||||
@ -65,9 +95,9 @@ impl Machine {
|
||||
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)
|
||||
}
|
||||
// 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)
|
||||
// }
|
||||
}
|
||||
|
||||
@ -1,21 +1,19 @@
|
||||
#[derive(Default, Clone, Copy, Debug)]
|
||||
pub struct CButton {
|
||||
pub struct Button {
|
||||
pub pressed: bool,
|
||||
pub released: bool,
|
||||
pub down: bool,
|
||||
pub hold_frames: u32,
|
||||
}
|
||||
|
||||
impl CButton {
|
||||
impl Button {
|
||||
pub fn begin_frame(&mut self, is_down_now: bool) {
|
||||
let was_down = self.down;
|
||||
self.down = is_down_now;
|
||||
|
||||
// Detecta transições
|
||||
self.pressed = !was_down && self.down;
|
||||
self.released = was_down && !self.down;
|
||||
|
||||
// Atualiza contador de frames
|
||||
if self.down {
|
||||
self.hold_frames = self.hold_frames.saturating_add(1);
|
||||
} else {
|
||||
59
crates/core/src/model/color.rs
Normal file
59
crates/core/src/model/color.rs
Normal file
@ -0,0 +1,59 @@
|
||||
/// Cor simples em RGB565 (0bRRRRRGGGGGGBBBBB).
|
||||
/// - 5 bits Red
|
||||
/// - 6 bits Green
|
||||
/// - 5 bits Blue
|
||||
///
|
||||
/// Não há canal alpha.
|
||||
/// Transparência é tratada via Color Key ou Blend Mode.
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
pub struct Color(pub u16);
|
||||
|
||||
impl Color {
|
||||
pub const BLACK: Self = Self::rgb(0, 0, 0); // 0x0000
|
||||
pub const WHITE: Self = Self::rgb(255, 255, 255); // 0xFFFF
|
||||
pub const RED: Self = Self::rgb(255, 0, 0); // 0xF800
|
||||
pub const GREEN: Self = Self::rgb(0, 255, 0); // 0x07E0
|
||||
pub const BLUE: Self = Self::rgb(0, 0, 255); // 0x001F
|
||||
pub const YELLOW: Self = Self::rgb(255, 255, 0); // 0xFFE0
|
||||
pub const CYAN: Self = Self::rgb(0, 255, 255); // 0x07FF
|
||||
pub const MAGENTA: Self = Self::rgb(255, 0, 255); // 0xF81F
|
||||
pub const COLOR_KEY: Self = Self::MAGENTA;
|
||||
|
||||
/// Extrai canais já na faixa nativa do RGB565:
|
||||
/// R: 0..31, G: 0..63, B: 0..31
|
||||
#[inline(always)]
|
||||
pub const fn unpack_to_native(px: u16) -> (u8, u8, u8) {
|
||||
let r = ((px >> 11) & 0x1F) as u8;
|
||||
let g = ((px >> 5) & 0x3F) as u8;
|
||||
let b = (px & 0x1F) as u8;
|
||||
(r, g, b)
|
||||
}
|
||||
|
||||
/// Incorpora canais já na faixa nativa do RGB565 em um pixel:
|
||||
/// R: 0..31, G: 0..63, B: 0..31
|
||||
#[inline(always)]
|
||||
pub const fn pack_from_native(r: u8, g: u8, b: u8) -> u16 {
|
||||
((r as u16 & 0x1F) << 11) | ((g as u16 & 0x3F) << 5) | (b as u16 & 0x1F)
|
||||
}
|
||||
|
||||
/// Cria uma cor RGB565 a partir de componentes 8-bit (0..255).
|
||||
pub const fn rgb(r: u8, g: u8, b: u8) -> Self {
|
||||
let r5 = (r as u16 >> 3) & 0x1F;
|
||||
let g6 = (g as u16 >> 2) & 0x3F;
|
||||
let b5 = (b as u16 >> 3) & 0x1F;
|
||||
|
||||
Self((r5 << 11) | (g6 << 5) | b5)
|
||||
}
|
||||
|
||||
pub const fn gray_scale(c: u8) -> Self {
|
||||
Self::rgb(c, c, c)
|
||||
}
|
||||
|
||||
pub const fn from_raw(raw: u16) -> Self {
|
||||
Self(raw)
|
||||
}
|
||||
|
||||
pub const fn raw(self) -> u16 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
11
crates/core/src/model/mod.rs
Normal file
11
crates/core/src/model/mod.rs
Normal file
@ -0,0 +1,11 @@
|
||||
mod color;
|
||||
mod button;
|
||||
mod tile;
|
||||
mod tile_layer;
|
||||
mod tile_bank;
|
||||
|
||||
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};
|
||||
7
crates/core/src/model/tile.rs
Normal file
7
crates/core/src/model/tile.rs
Normal file
@ -0,0 +1,7 @@
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct Tile {
|
||||
pub id: u16,
|
||||
pub flip_x: bool,
|
||||
pub flip_y: bool,
|
||||
pub palette_id: u8,
|
||||
}
|
||||
45
crates/core/src/model/tile_bank.rs
Normal file
45
crates/core/src/model/tile_bank.rs
Normal file
@ -0,0 +1,45 @@
|
||||
use crate::model::Color;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum TileSize {
|
||||
Size8 = 8,
|
||||
Size16 = 16,
|
||||
Size32 = 32,
|
||||
}
|
||||
|
||||
pub struct TileBank {
|
||||
pub tile_size: TileSize,
|
||||
pub width: usize, // em pixels
|
||||
pub height: usize, // em pixels
|
||||
pub pixels: Vec<Color>,
|
||||
}
|
||||
|
||||
impl TileBank {
|
||||
pub fn new(tile_size: TileSize, width: usize, height: usize) -> Self {
|
||||
Self {
|
||||
tile_size,
|
||||
width,
|
||||
height,
|
||||
pixels: vec![Color::BLACK; width * height],
|
||||
}
|
||||
}
|
||||
|
||||
/// 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;
|
||||
|
||||
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]
|
||||
} else {
|
||||
Color::BLACK
|
||||
}
|
||||
}
|
||||
}
|
||||
109
crates/core/src/model/tile_layer.rs
Normal file
109
crates/core/src/model/tile_layer.rs
Normal file
@ -0,0 +1,109 @@
|
||||
use crate::model::Tile;
|
||||
use crate::model::tile_bank::TileSize;
|
||||
use crate::model::TileSize::Size8;
|
||||
|
||||
pub struct TileMap {
|
||||
pub width: usize,
|
||||
pub height: usize,
|
||||
pub tiles: Vec<Tile>,
|
||||
}
|
||||
|
||||
impl TileMap {
|
||||
fn create(width: usize, height: usize) -> Self {
|
||||
Self {
|
||||
width,
|
||||
height,
|
||||
tiles: vec![Tile::default(); width * height],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_tile(&mut self, x: usize, y: usize, tile: Tile) {
|
||||
if x < self.width && y < self.height {
|
||||
self.tiles[y * self.width + x] = tile;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub struct TileLayer {
|
||||
pub bank_id: u8,
|
||||
pub tile_size: TileSize,
|
||||
pub map: TileMap,
|
||||
}
|
||||
|
||||
impl TileLayer {
|
||||
fn create(width: usize, height: usize, tile_size: TileSize) -> Self {
|
||||
Self {
|
||||
bank_id: 0,
|
||||
tile_size: tile_size,
|
||||
map: TileMap::create(width, height),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for TileLayer {
|
||||
type Target = TileMap;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.map
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::DerefMut for TileLayer {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.map
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ScrollableTileLayer {
|
||||
pub layer: TileLayer,
|
||||
pub scroll_x: i32,
|
||||
pub scroll_y: i32,
|
||||
}
|
||||
|
||||
impl ScrollableTileLayer {
|
||||
pub fn new(width: usize, height: usize, tile_size: TileSize) -> Self {
|
||||
Self {
|
||||
layer: TileLayer::create(width, height, tile_size),
|
||||
scroll_x: 0,
|
||||
scroll_y: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for ScrollableTileLayer {
|
||||
type Target = TileLayer;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.layer
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::DerefMut for ScrollableTileLayer {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.layer
|
||||
}
|
||||
}
|
||||
|
||||
pub struct HudTileLayer {
|
||||
pub layer: TileLayer,
|
||||
}
|
||||
|
||||
impl HudTileLayer {
|
||||
pub fn new(width: usize, height: usize) -> Self {
|
||||
Self {
|
||||
layer: TileLayer::create(width, height, Size8),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for HudTileLayer {
|
||||
type Target = TileLayer;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.layer
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::DerefMut for HudTileLayer {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.layer
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
use crate::color::Color;
|
||||
use crate::model::{Color, HudTileLayer, ScrollableTileLayer, TileBank, TileMap, TileSize};
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
pub enum BlendMode {
|
||||
@ -20,16 +20,30 @@ pub struct Gfx {
|
||||
h: usize,
|
||||
front: Vec<u16>,
|
||||
back: Vec<u16>,
|
||||
|
||||
pub layers: [ScrollableTileLayer; 4],
|
||||
pub hud: HudTileLayer,
|
||||
pub banks: [Option<TileBank>; 16],
|
||||
}
|
||||
|
||||
impl Gfx {
|
||||
impl Gfx {
|
||||
pub fn new(w: usize, h: usize) -> Self {
|
||||
const EMPTY_BANK: Option<TileBank> = None;
|
||||
|
||||
let len = w * h;
|
||||
Self {
|
||||
w,
|
||||
h,
|
||||
front: vec![0; len],
|
||||
back: vec![0; len],
|
||||
layers: [
|
||||
ScrollableTileLayer::new(64, 64, TileSize::Size16),
|
||||
ScrollableTileLayer::new(64, 64, TileSize::Size16),
|
||||
ScrollableTileLayer::new(64, 64, TileSize::Size16),
|
||||
ScrollableTileLayer::new(64, 64, TileSize::Size16),
|
||||
],
|
||||
hud: HudTileLayer::new(64, 32),
|
||||
banks: [EMPTY_BANK; 16],
|
||||
}
|
||||
}
|
||||
|
||||
@ -85,6 +99,82 @@ impl Gfx {
|
||||
pub fn present(&mut self) {
|
||||
std::mem::swap(&mut self.front, &mut self.back);
|
||||
}
|
||||
|
||||
/// Pipeline principal de renderização do frame.
|
||||
/// Segue a ordem de prioridade do manual (Capítulo 4.11).
|
||||
pub fn render_all(&mut self) {
|
||||
// 1. Layers de jogo (0 a 3)
|
||||
for i in 0..self.layers.len() {
|
||||
self.render_layer(i);
|
||||
}
|
||||
|
||||
// 2. Sprites (Ainda vamos implementar)
|
||||
|
||||
// 3. HUD (Sempre por cima)
|
||||
self.render_hud();
|
||||
}
|
||||
|
||||
pub fn render_layer(&mut self, layer_idx: usize) {
|
||||
if layer_idx >= self.layers.len() { return; }
|
||||
|
||||
let bank_id = self.layers[layer_idx].bank_id as usize;
|
||||
let scroll_x = self.layers[layer_idx].scroll_x;
|
||||
let scroll_y = self.layers[layer_idx].scroll_y;
|
||||
|
||||
let bank = match self.banks.get(bank_id) {
|
||||
Some(Some(b)) => b,
|
||||
_ => return,
|
||||
};
|
||||
|
||||
Self::draw_tile_map(&mut self.back, self.w, self.h, &self.layers[layer_idx].map, bank, scroll_x, scroll_y);
|
||||
}
|
||||
|
||||
/// Renderiza o HUD (sem scroll).
|
||||
pub fn render_hud(&mut self) {
|
||||
let bank_id = self.hud.bank_id as usize;
|
||||
let bank = match self.banks.get(bank_id) {
|
||||
Some(Some(b)) => b,
|
||||
_ => return,
|
||||
};
|
||||
|
||||
Self::draw_tile_map(&mut self.back, self.w, self.h, &self.hud.map, bank, 0, 0);
|
||||
}
|
||||
|
||||
fn draw_tile_map(
|
||||
back: &mut [u16],
|
||||
screen_w: usize,
|
||||
screen_h: usize,
|
||||
map: &TileMap,
|
||||
bank: &TileBank,
|
||||
scroll_x: i32,
|
||||
scroll_y: i32
|
||||
) {
|
||||
let size = bank.tile_size as usize;
|
||||
|
||||
for screen_y in 0..screen_h {
|
||||
for screen_x in 0..screen_w {
|
||||
let world_x = screen_x as i32 + scroll_x;
|
||||
let world_y = screen_y as i32 + scroll_y;
|
||||
|
||||
let tile_x = (world_x / size as i32) as usize;
|
||||
let tile_y = (world_y / size as i32) as usize;
|
||||
|
||||
if tile_x >= map.width || tile_y >= map.height { continue; }
|
||||
|
||||
let tile = map.tiles[tile_y * map.width + tile_x];
|
||||
if tile.id == 0 { continue; }
|
||||
|
||||
let local_x = (world_x % size as i32) as usize;
|
||||
let local_y = (world_y % size as i32) as usize;
|
||||
|
||||
let color = bank.get_pixel(tile.id, local_x, local_y);
|
||||
if color == Color::COLOR_KEY { continue; }
|
||||
|
||||
let idx = (screen_y * screen_w) + screen_x;
|
||||
back[idx] = color.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Faz blend em RGB565 por canal com saturação.
|
||||
@ -94,60 +184,45 @@ fn blend_rgb565(dst: u16, src: u16, mode: BlendMode) -> u16 {
|
||||
BlendMode::None => src,
|
||||
|
||||
BlendMode::Half => {
|
||||
let (dr, dg, db) = unpack_rgb565(dst);
|
||||
let (sr, sg, sb) = unpack_rgb565(src);
|
||||
let (dr, dg, db) = Color::unpack_to_native(dst);
|
||||
let (sr, sg, sb) = Color::unpack_to_native(src);
|
||||
let r = ((dr as u16 + sr as u16) >> 1) as u8;
|
||||
let g = ((dg as u16 + sg as u16) >> 1) as u8;
|
||||
let b = ((db as u16 + sb as u16) >> 1) as u8;
|
||||
pack_rgb565(r, g, b)
|
||||
Color::pack_from_native(r, g, b)
|
||||
}
|
||||
|
||||
BlendMode::HalfPlus => {
|
||||
let (dr, dg, db) = unpack_rgb565(dst);
|
||||
let (sr, sg, sb) = unpack_rgb565(src);
|
||||
let (dr, dg, db) = Color::unpack_to_native(dst);
|
||||
let (sr, sg, sb) = Color::unpack_to_native(src);
|
||||
|
||||
let r = (dr as u16 + ((sr as u16) >> 1)).min(31) as u8;
|
||||
let g = (dg as u16 + ((sg as u16) >> 1)).min(63) as u8;
|
||||
let b = (db as u16 + ((sb as u16) >> 1)).min(31) as u8;
|
||||
|
||||
pack_rgb565(r, g, b)
|
||||
Color::pack_from_native(r, g, b)
|
||||
}
|
||||
|
||||
BlendMode::HalfMinus => {
|
||||
let (dr, dg, db) = unpack_rgb565(dst);
|
||||
let (sr, sg, sb) = unpack_rgb565(src);
|
||||
let (dr, dg, db) = Color::unpack_to_native(dst);
|
||||
let (sr, sg, sb) = Color::unpack_to_native(src);
|
||||
|
||||
let r = (dr as i16 - ((sr as i16) >> 1)).max(0) as u8;
|
||||
let g = (dg as i16 - ((sg as i16) >> 1)).max(0) as u8;
|
||||
let b = (db as i16 - ((sb as i16) >> 1)).max(0) as u8;
|
||||
|
||||
pack_rgb565(r, g, b)
|
||||
Color::pack_from_native(r, g, b)
|
||||
}
|
||||
|
||||
BlendMode::Full => {
|
||||
let (dr, dg, db) = unpack_rgb565(dst);
|
||||
let (sr, sg, sb) = unpack_rgb565(src);
|
||||
let (dr, dg, db) = Color::unpack_to_native(dst);
|
||||
let (sr, sg, sb) = Color::unpack_to_native(src);
|
||||
|
||||
let r = (dr as u16 + sr as u16).min(31) as u8;
|
||||
let g = (dg as u16 + sg as u16).min(63) as u8;
|
||||
let b = (db as u16 + sb as u16).min(31) as u8;
|
||||
|
||||
pack_rgb565(r, g, b)
|
||||
Color::pack_from_native(r, g, b)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Extrai canais já na faixa nativa do RGB565:
|
||||
/// R: 0..31, G: 0..63, B: 0..31
|
||||
#[inline(always)]
|
||||
fn unpack_rgb565(px: u16) -> (u8, u8, u8) {
|
||||
let r = ((px >> 11) & 0x1F) as u8;
|
||||
let g = ((px >> 5) & 0x3F) as u8;
|
||||
let b = (px & 0x1F) as u8;
|
||||
(r, g, b)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn pack_rgb565(r: u8, g: u8, b: u8) -> u16 {
|
||||
((r as u16 & 0x1F) << 11) | ((g as u16 & 0x3F) << 5) | (b as u16 & 0x1F)
|
||||
}
|
||||
|
||||
@ -1,22 +1,22 @@
|
||||
use crate::c_button::CButton;
|
||||
use crate::model::Button;
|
||||
use crate::peripherals::input_signal::InputSignals;
|
||||
|
||||
#[derive(Default, Clone, Copy, Debug)]
|
||||
pub struct Pad {
|
||||
pub up: CButton,
|
||||
pub down: CButton,
|
||||
pub left: CButton,
|
||||
pub right: CButton,
|
||||
pub up: Button,
|
||||
pub down: Button,
|
||||
pub left: Button,
|
||||
pub right: Button,
|
||||
|
||||
pub a: CButton, // ps: square
|
||||
pub d: CButton, // ps: circle
|
||||
pub w: CButton, // ps: triangle
|
||||
pub s: CButton, // ps: cross
|
||||
pub q: CButton, // ps: R
|
||||
pub e: CButton, // ps: L
|
||||
pub a: Button, // ps: square
|
||||
pub d: Button, // ps: circle
|
||||
pub w: Button, // ps: triangle
|
||||
pub s: Button, // ps: cross
|
||||
pub q: Button, // ps: R
|
||||
pub e: Button, // ps: L
|
||||
|
||||
pub start: CButton,
|
||||
pub select: CButton,
|
||||
pub start: Button,
|
||||
pub select: Button,
|
||||
}
|
||||
|
||||
impl Pad {
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
use crate::c_button::CButton;
|
||||
use crate::model::Button;
|
||||
use crate::peripherals::input_signal::InputSignals;
|
||||
|
||||
#[derive(Default, Clone, Copy, Debug)]
|
||||
pub struct Touch {
|
||||
pub f: CButton,
|
||||
pub f: Button,
|
||||
pub x: i32,
|
||||
pub y: i32,
|
||||
}
|
||||
|
||||
@ -72,7 +72,7 @@ Isso garante:
|
||||
O mundo gráfico é composto por:
|
||||
|
||||
- Até **16 Tile Banks**
|
||||
- **4 Game Layers** (scrolláveis)
|
||||
- **4 Tile Layers** (scrolláveis)
|
||||
- **1 HUD Layer** (fixa, sempre por cima)
|
||||
- Sprites com prioridade entre layers
|
||||
|
||||
@ -90,7 +90,7 @@ O mundo gráfico é composto por:
|
||||
### 4.2 Layers
|
||||
|
||||
- Existem:
|
||||
- 4 Game Layers
|
||||
- 4 Tile Layers
|
||||
- 1 HUD Layer
|
||||
- Cada layer aponta para **um único bank**
|
||||
- Sprites podem usar **qualquer bank**
|
||||
@ -296,7 +296,7 @@ Por design:
|
||||
O PROMETEU suporta **fade gradual** como um PostFX especial, com dois controles
|
||||
independentes:
|
||||
|
||||
- **Scene Fade**: afeta toda a cena (Game Layers 0–3 + Sprites)
|
||||
- **Scene Fade**: afeta toda a cena (Tile 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.
|
||||
@ -371,7 +371,7 @@ Observações:
|
||||
|
||||
A composição do frame segue esta ordem:
|
||||
|
||||
1. Rasterizar **Game Layers 0–3** → Back Buffer
|
||||
1. Rasterizar **Tile Layers 0–3** → Back Buffer
|
||||
2. Rasterizar **Sprites** conforme prioridade
|
||||
3. (Opcional) Pipeline extra (Emission/Light/Glow etc.)
|
||||
4. Aplicar **Scene Fade** usando:
|
||||
@ -415,7 +415,7 @@ O GFX do PROMETEU é simples **por escolha**, não por limitação.
|
||||
- Color key para transparência
|
||||
- Blending discreto estilo SNES
|
||||
- Até 16 tile banks
|
||||
- 4 game layers + 1 HUD
|
||||
- 4 Tile Layers + 1 HUD
|
||||
- Layer = tilemap + cache + scroll
|
||||
- Projeção rasterizada por frame
|
||||
- Profundidade definida por ordem de desenho
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user