basic drawing window stuffs

This commit is contained in:
dani 2023-07-21 08:31:52 +00:00
parent d75c8f483e
commit 40d4ba4574
8 changed files with 184 additions and 33 deletions

View File

@ -12,9 +12,9 @@ glam = "0.24.0"
ascii = "1.1.0" ascii = "1.1.0"
gilrs = "0.10.2" gilrs = "0.10.2"
cpal = "0.15.2" cpal = "0.15.2"
winit = "0.28.6"
[dependencies.skunk2d] softbuffer = "0.3.0"
path = "../skunk2d" gif = "0.12.0"
[profile.release-dani] [profile.release-dani]
inherits = "release" inherits = "release"

View File

@ -0,0 +1,23 @@
use crate::{Gsa, MAX_SPRITES, TILE_SIZE};
use glam::IVec2;
use softbuffer::Buffer;
pub(crate) fn render_to_window(target: &mut Buffer, gsa: &Gsa, window_size: IVec2) {
for (y, row) in target.chunks_exact_mut(window_size.x as usize).enumerate() {
let y = y as i32;
for x in 0..window_size.x {
let mut p = 0;
for sprite in &gsa.sprites {
if sprite.tile > 0
&& x >= sprite.pos.x
&& y >= sprite.pos.y
&& x < sprite.pos.x + TILE_SIZE as i32
&& y < sprite.pos.y + TILE_SIZE as i32
{
p = 1;
}
}
row[x as usize] = gsa.palette[p].to_u32();
}
}
}

View File

@ -22,12 +22,14 @@
mod buttons; mod buttons;
mod gsa; mod gsa;
mod gsa_render_to_screen;
mod input; mod input;
mod rgb; mod rgb;
mod run; mod run;
mod sprite; mod sprite;
mod state; mod state;
mod tilemap; mod tilemap;
mod tileset;
pub use crate::buttons::Buttons; pub use crate::buttons::Buttons;
pub use crate::gsa::Gsa; pub use crate::gsa::Gsa;
@ -40,8 +42,23 @@ pub use crate::tilemap::Tilemap;
/// Amount of sprites in [Gsa::sprites] /// Amount of sprites in [Gsa::sprites]
pub const MAX_SPRITES: usize = 0xff; pub const MAX_SPRITES: usize = 0xff;
/// Screen Width in pixels
pub const SCREEN_WIDTH: usize = 304;
/// Screen Height in pixels
pub const SCREEN_HEIGHT: usize = 176;
/// X and y dimensions of maps in [Gsa::maps] /// X and y dimensions of maps in [Gsa::maps]
pub const TILEMAP_MAX_SIZE: usize = 1024; pub const TILEMAP_MAX_SIZE: usize = 1024;
/// Width and height (in tiles) of tileset
pub const TILESET_SIZE: usize = 0x100;
/// Width and height of a tile
pub const TILE_SIZE: usize = 16;
/// Width and height of a tile in half-tile mode
pub const HALF_TILE_SIZE: usize = 8;
/// Amount of tile maps in [Gsa::maps] /// Amount of tile maps in [Gsa::maps]
pub const MAX_TILEMAPS: usize = 4; pub const MAX_TILEMAPS: usize = 4;

View File

@ -8,3 +8,9 @@ pub struct Rgb {
/// Blue component 0-255 /// Blue component 0-255
pub b: u8, pub b: u8,
} }
impl Rgb {
pub(crate) fn to_u32(self) -> u32 {
(self.r as u32) | ((self.g as u32) << 8) | ((self.b as u32) << 16)
}
}

View File

@ -1,7 +1,16 @@
use crate::gsa_render_to_screen::render_to_window;
use crate::state::State; use crate::state::State;
use crate::{Gsa, Rgb, Sprite, MAX_SPRITES}; use crate::tileset::load_tileset;
use crate::{Gsa, Rgb, Sprite, MAX_SPRITES, SCREEN_HEIGHT, SCREEN_WIDTH};
use gilrs::Gilrs; use gilrs::Gilrs;
use skunk2d::Image8; use glam::IVec2;
use std::cmp::min;
use std::num::NonZeroU32;
use std::time::{Duration, Instant};
use winit::dpi::LogicalSize;
use winit::event::{Event, VirtualKeyCode, WindowEvent};
use winit::event_loop::{ControlFlow, EventLoop};
use winit::window::WindowBuilder;
/// Creates main function, includes gfx.gif, and calls run /// Creates main function, includes gfx.gif, and calls run
/// ///
@ -25,17 +34,7 @@ pub fn run<TGame: 'static>(
update_fn: fn(game: &mut TGame, gsa: &mut Gsa), update_fn: fn(game: &mut TGame, gsa: &mut Gsa),
image_data: &[u8], image_data: &[u8],
) { ) {
let tileset = Image8::load_data(image_data); let (tileset, palette) = load_tileset(image_data);
let pal = Image8::load_data_palette(image_data);
let mut palette = [Rgb { r: 0, g: 0, b: 0 }; 256];
for i in 0..pal.len() / 3 {
palette[i] = Rgb {
r: pal[i * 3 + 2],
g: pal[i * 3 + 1],
b: pal[i * 3],
};
}
let mut gsa = Gsa { let mut gsa = Gsa {
sprites: [Sprite::default(); MAX_SPRITES], sprites: [Sprite::default(); MAX_SPRITES],
@ -44,8 +43,8 @@ pub fn run<TGame: 'static>(
font: 0x1010, font: 0x1010,
input: Default::default(), input: Default::default(),
}; };
let game = init_fn(&mut gsa); let mut game = init_fn(&mut gsa);
let state = State::<TGame> { /*let state = State::<TGame> {
gsa, gsa,
game, game,
tileset, tileset,
@ -53,5 +52,83 @@ pub fn run<TGame: 'static>(
first: true, first: true,
gilrs: Gilrs::new().unwrap(), gilrs: Gilrs::new().unwrap(),
}; };
skunk2d::run_with::<Image8, State<TGame>>(304, 176, 60, state);
*/
let event_loop = EventLoop::new();
let size = LogicalSize::new(SCREEN_WIDTH as u32, SCREEN_HEIGHT as u32);
let window = WindowBuilder::new()
.with_title("Game Skunk Advance v0.1")
.with_inner_size(size)
.with_min_inner_size(size)
.build(&event_loop)
.unwrap();
let context = unsafe { softbuffer::Context::new(&window) }.unwrap();
let mut surface = unsafe { softbuffer::Surface::new(&context, &window) }.unwrap();
let mut frames_since_last_second = 0;
let mut last_second = Instant::now();
let mut last_frame = last_second;
let target_frame_duration = Duration::from_secs(1).div_f64(6000.0);
let mut scale = 1usize;
let mut off_x = 0usize;
let mut off_y = 0usize;
event_loop.run(move |event, _, control_flow| {
let scale = &mut scale;
let off_x = &mut off_x;
let off_y = &mut off_y;
if Instant::now() - last_second > Duration::from_secs(1) {
println!("fps: {}", frames_since_last_second);
frames_since_last_second = 0;
last_second += Duration::from_secs(1);
}
match event {
Event::WindowEvent {
event: WindowEvent::KeyboardInput { input, .. },
..
} => match input.virtual_keycode {
Some(VirtualKeyCode::Escape) => {
*control_flow = ControlFlow::Exit;
}
_ => {}
},
Event::MainEventsCleared {} => {
if Instant::now() - last_frame >= target_frame_duration {
update_fn(&mut game, &mut gsa);
let size = window.inner_size();
surface
.resize(
NonZeroU32::new(size.width).unwrap(),
NonZeroU32::new(size.height).unwrap(),
)
.unwrap();
*scale = min(
size.width / SCREEN_WIDTH as u32,
size.height / SCREEN_HEIGHT as u32,
) as usize;
*off_x = (size.width as usize - SCREEN_WIDTH * *scale) / 2;
*off_y = (size.height as usize - SCREEN_HEIGHT * *scale) / 2;
let mut buf = surface.buffer_mut().unwrap();
render_to_window(
&mut buf,
&gsa,
IVec2 {
x: size.width as i32,
y: size.height as i32,
},
);
buf.present().unwrap();
frames_since_last_second += 1;
last_frame += target_frame_duration;
}
}
_ => {}
}
});
} }

View File

@ -2,24 +2,19 @@ use crate::buttons::Buttons;
use crate::{Gsa, TILEMAP_MAX_SIZE}; use crate::{Gsa, TILEMAP_MAX_SIZE};
use gilrs::{Button, EventType, Gilrs}; use gilrs::{Button, EventType, Gilrs};
use glam::IVec2; use glam::IVec2;
use skunk2d::{Event, IRect, Image8, WindowState};
use std::rc::Rc; use std::rc::Rc;
pub struct State<TGame> { pub struct State<TGame> {
pub(crate) gsa: Gsa, pub(crate) gsa: Gsa,
pub(crate) game: TGame, pub(crate) game: TGame,
pub(crate) update_fn: fn(game: &mut TGame, gsa: &mut Gsa), pub(crate) update_fn: fn(game: &mut TGame, gsa: &mut Gsa),
pub(crate) tileset: Rc<Image8>, pub(crate) tileset: Vec<u8>,
pub(crate) first: bool, pub(crate) first: bool,
pub(crate) gilrs: Gilrs, pub(crate) gilrs: Gilrs,
} }
impl<TGame> skunk2d::Game<Image8> for State<TGame> { impl<TGame> State<TGame> {
fn update(&mut self, window_state: &mut WindowState) { fn update(&mut self) {
if self.first {
window_state.toggle_fullscreen();
self.first = false;
}
let mut new_buttons = self.gsa.input.down; let mut new_buttons = self.gsa.input.down;
while let Some(event) = self.gilrs.next_event() { while let Some(event) = self.gilrs.next_event() {
match event { match event {
@ -72,6 +67,7 @@ impl<TGame> skunk2d::Game<Image8> for State<TGame> {
}; };
//todo: don't if not updated... how check? <_< //todo: don't if not updated... how check? <_<
/*
for i in 0..=255 { for i in 0..=255 {
window_state.set_palette( window_state.set_palette(
i, i,
@ -80,13 +76,12 @@ impl<TGame> skunk2d::Game<Image8> for State<TGame> {
self.gsa.palette[i as usize].b, self.gsa.palette[i as usize].b,
); );
} }
*/
(self.update_fn)(&mut self.game, &mut self.gsa); (self.update_fn)(&mut self.game, &mut self.gsa);
} }
fn on_event(&mut self, _window_state: &mut WindowState, _event: Event) {} fn draw(&self) {
//target.clear();
fn draw(&self, target: &mut Image8) {
target.clear();
for map in &self.gsa.maps { for map in &self.gsa.maps {
let tcmult = if map.half_tile { 2 } else { 1 }; let tcmult = if map.half_tile { 2 } else { 1 };
@ -103,6 +98,7 @@ impl<TGame> skunk2d::Game<Image8> for State<TGame> {
if tile > 0 { if tile > 0 {
let ty = tile / 0x100; let ty = tile / 0x100;
let tx = tile % 0x100; let tx = tile % 0x100;
/*
target.draw_image_partial( target.draw_image_partial(
IVec2 { IVec2 {
x: x * tilesize - map.scroll.x, x: x * tilesize - map.scroll.x,
@ -120,6 +116,8 @@ impl<TGame> skunk2d::Game<Image8> for State<TGame> {
}, },
}, },
) )
*/
} }
} }
} }
@ -129,6 +127,7 @@ impl<TGame> skunk2d::Game<Image8> for State<TGame> {
if sprite.tile > 0 { if sprite.tile > 0 {
let ty = sprite.tile / 0x100; let ty = sprite.tile / 0x100;
let tx = sprite.tile % 0x100; let tx = sprite.tile % 0x100;
/*
target.draw_image_partial( target.draw_image_partial(
sprite.pos, sprite.pos,
&self.tileset, &self.tileset,
@ -140,6 +139,8 @@ impl<TGame> skunk2d::Game<Image8> for State<TGame> {
size: IVec2 { x: 16, y: 16 }, size: IVec2 { x: 16, y: 16 },
}, },
); );
*/
} }
} }
} }

View File

@ -4,7 +4,7 @@ use glam::IVec2;
/// Tilemap which will be rendered on screen /// Tilemap which will be rendered on screen
pub struct Tilemap { pub struct Tilemap {
/// Tiles in idx, accessible via \[x\]\[y\], x and y 0..[TILEMAP_MAX_SIZE] /// Tiles in idx, accessible via \[x\]\[y\], x and y 0..[TILEMAP_MAX_SIZE]
pub tiles: Box<[[u16; TILEMAP_MAX_SIZE]; TILEMAP_MAX_SIZE]>, pub tiles: Vec<Vec<u16>>,
/// Camera scroll (negative draw offset) for rendering /// Camera scroll (negative draw offset) for rendering
pub scroll: IVec2, pub scroll: IVec2,
/// Are tiles indices half-tile indices? /// Are tiles indices half-tile indices?
@ -13,8 +13,10 @@ pub struct Tilemap {
impl Default for Tilemap { impl Default for Tilemap {
fn default() -> Self { fn default() -> Self {
let row = vec![0u16; TILEMAP_MAX_SIZE];
let tiles = vec![row; TILEMAP_MAX_SIZE];
Self { Self {
tiles: [[0u16; TILEMAP_MAX_SIZE]; TILEMAP_MAX_SIZE].into(), tiles,
scroll: IVec2::ZERO, scroll: IVec2::ZERO,
half_tile: false, half_tile: false,
} }

25
src/tileset.rs Normal file
View File

@ -0,0 +1,25 @@
use crate::{Rgb, TILESET_SIZE, TILE_SIZE};
pub(crate) fn load_tileset(data: &[u8]) -> (Vec<u8>, [Rgb; 256]) {
let mut options = gif::DecodeOptions::new();
options.set_color_output(gif::ColorOutput::Indexed);
let mut decoder = options.read_info(data).unwrap();
assert_eq!(decoder.width(), (TILESET_SIZE * TILE_SIZE) as u16);
assert_eq!(decoder.height(), (TILESET_SIZE * TILE_SIZE) as u16);
decoder.next_frame_info().unwrap();
let mut tileset = Vec::new();
tileset.resize(TILESET_SIZE * TILE_SIZE * TILESET_SIZE * TILE_SIZE, 0);
decoder.read_into_buffer(tileset.as_mut_slice()).unwrap();
let pal = decoder.palette().unwrap();
let mut palette = [Rgb { r: 0, g: 0, b: 0 }; 256];
for i in 0..pal.len() / 3 {
palette[i] = Rgb {
r: pal[i * 3 + 2],
g: pal[i * 3 + 1],
b: pal[i * 3],
};
}
(tileset, palette)
}