Refactor: tray to solidify rendering API thing in preparation for extra renderers/drawers

main
Luke D. Jones 2023-07-27 19:23:21 +12:00
parent d9ae0c1f8e
commit 5651c58fcc
29 changed files with 774 additions and 431 deletions

15
Cargo.lock generated
View File

@ -171,7 +171,7 @@ dependencies = [
[[package]]
name = "golem"
version = "0.2.0"
source = "git+https://github.com/flukejones/golem/#64f44879e06a33e836e737caee564e29315d554e"
source = "git+https://github.com/flukejones/golem/#be66ac90add9739a71af2fe37967201e12e99ce6"
dependencies = [
"bytemuck",
"glow",
@ -407,6 +407,7 @@ name = "render-traits"
version = "1.0.0"
dependencies = [
"gameplay",
"golem",
]
[[package]]
@ -438,7 +439,7 @@ dependencies = [
[[package]]
name = "sdl2"
version = "0.35.2"
source = "git+https://github.com/Rust-SDL2/rust-sdl2#8613c502a6081ef1a218ad1d6aecf7c6bd7cf65c"
source = "git+https://github.com/Rust-SDL2/rust-sdl2#9b167586bd3938da9187fc1e84b96afe71742ad5"
dependencies = [
"bitflags",
"lazy_static",
@ -449,7 +450,7 @@ dependencies = [
[[package]]
name = "sdl2-sys"
version = "0.35.2"
source = "git+https://github.com/Rust-SDL2/rust-sdl2#8613c502a6081ef1a218ad1d6aecf7c6bd7cf65c"
source = "git+https://github.com/Rust-SDL2/rust-sdl2#9b167586bd3938da9187fc1e84b96afe71742ad5"
dependencies = [
"cfg-if",
"cmake",
@ -459,18 +460,18 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.175"
version = "1.0.176"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d25439cd7397d044e2748a6fe2432b5e85db703d6d097bd014b3c0ad1ebff0b"
checksum = "76dc28c9523c5d70816e393136b86d48909cfb27cecaa902d338c19ed47164dc"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.175"
version = "1.0.176"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b23f7ade6f110613c0d63858ddb8b94c1041f550eab58a16b371bdf2c9c80ab4"
checksum = "a4e7b8c5dc823e3b90651ff1d3808419cd14e5ad76de04feaf37da114e7a306f"
dependencies = [
"proc-macro2",
"quote",

View File

@ -2,7 +2,7 @@ mod text;
use crate::text::*;
use gamestate_traits::{
GameMode, GameTraits, MachinationTrait, MusTrack, PixelBuf, Scancode, TICRATE,
GameMode, GameTraits, MachinationTrait, MusTrack, PixelBuffer, RenderTarget, Scancode, TICRATE,
};
use hud_util::{load_char_patches, HUDString, HUD_STRING};
use wad::{
@ -40,6 +40,25 @@ impl Finale {
count: 0,
}
}
fn draw_pixels(&mut self, pixels: &mut impl PixelBuffer) {
let f = (pixels.height() / 200) as i32;
self.screen_width = pixels.width() as i32;
self.screen_height = pixels.height() as i32;
let pal = &self.palette;
for sx in (0..self.screen_width).step_by(64) {
for sy in (0..self.screen_height).step_by(64) {
for (y, col) in self.bg_flat.data.chunks(64).enumerate() {
for (x, c) in col.iter().enumerate() {
let c = &pal.0[*c as usize];
pixels.set_pixel(sx as usize + x, sy as usize + y, (c.r, c.g, c.b, 255));
}
}
}
}
self.text.draw_pixels(6 * f, 6 * f, self, pixels);
}
}
impl MachinationTrait for Finale {
@ -134,22 +153,18 @@ impl MachinationTrait for Finale {
&self.palette
}
fn draw(&mut self, buffer: &mut PixelBuf) {
let f = (buffer.height() / 200) as i32;
self.screen_width = buffer.width() as i32;
self.screen_height = buffer.height() as i32;
let pal = &self.palette;
for sx in (0..self.screen_width).step_by(64) {
for sy in (0..self.screen_height).step_by(64) {
for (y, col) in self.bg_flat.data.chunks(64).enumerate() {
for (x, c) in col.iter().enumerate() {
let c = &pal.0[*c as usize];
buffer.set_pixel(sx as usize + x, sy as usize + y, c.r, c.g, c.b, 255);
}
}
fn draw(&mut self, buffer: &mut RenderTarget) {
match buffer.render_type() {
gamestate_traits::RenderType::Software => {
let pixels = unsafe { buffer.software_unchecked() };
self.draw_pixels(pixels);
}
gamestate_traits::RenderType::SoftOpenGL => {
let pixels = unsafe { buffer.soft_opengl_unchecked() };
self.draw_pixels(pixels);
}
gamestate_traits::RenderType::OpenGL => todo!(),
gamestate_traits::RenderType::Vulkan => todo!(),
}
self.text.draw(6 * f, 6 * f, self, buffer);
}
}

View File

@ -7,8 +7,9 @@ use gamestate_traits::{
surface,
video::{Window, WindowContext},
},
PixelBuf,
RenderTarget,
};
use render_traits::{PixelBuffer, RenderType};
use crate::shaders::{
self, basic::Basic, cgwg_crt::Cgwgcrt, lottes_crt::LottesCRT, Drawer, Shaders,
@ -68,7 +69,7 @@ impl<'c> Blitter<'c> {
if let Some(post_process) = post_process {
info!("Using a post-process shader");
post_process.set_tex_filter().unwrap();
// post_process.set_tex_filter().unwrap();
shader = Some(Shader {
window,
shader: post_process,
@ -91,30 +92,33 @@ impl<'c> Blitter<'c> {
}
}
pub fn blit(&mut self, render_buffer: &mut PixelBuf) {
pub fn blit(&mut self, render_buffer: &mut RenderTarget) {
if let Some(shader) = &mut self.shader {
// shader.shader.clear();
let size = (render_buffer.width(), render_buffer.height());
shader
.shader
.set_image_data(render_buffer.read_pixels_mut(), size);
shader.shader.draw().unwrap();
shader.window.gl_swap_window();
if matches!(render_buffer.render_type(), RenderType::SoftOpenGL) {
let render_buffer = unsafe { render_buffer.soft_opengl_unchecked() };
// shader.shader.clear();
render_buffer.copy_softbuf_to_gl_texture();
shader.shader.draw(render_buffer.gl_texture()).unwrap();
shader.window.gl_swap_window();
}
} else if let Some(soft) = &mut self.soft {
let w = render_buffer.width();
let h = render_buffer.height();
let surf = surface::Surface::from_data(
render_buffer.read_pixels_mut(),
w,
h,
3 * w,
pixels::PixelFormatEnum::RGB24,
)
.unwrap()
.as_texture(&soft.tex_creator)
.unwrap();
soft.canvas.copy(&surf, None, Some(self.crop_rect)).unwrap();
soft.canvas.present();
let w = render_buffer.width() as u32;
let h = render_buffer.height() as u32;
if matches!(render_buffer.render_type(), RenderType::Software) {
let render_buffer = unsafe { render_buffer.software_unchecked() };
let surf = surface::Surface::from_data(
render_buffer.read_softbuf_pixels(),
w,
h,
4 * w,
pixels::PixelFormatEnum::RGBA32,
)
.unwrap()
.as_texture(&soft.tex_creator)
.unwrap();
soft.canvas.copy(&surf, None, Some(self.crop_rect)).unwrap();
soft.canvas.present();
}
}
}
}

View File

@ -19,13 +19,13 @@ use input::Input;
use intermission_doom::Intermission;
use menu_doom::MenuDoom;
use render_soft::SoftwareRenderer;
use render_traits::{PixelBuf, PlayRenderer};
use render_traits::{PixelBuffer, PlayRenderer, RenderTarget, RenderType};
use sound_traits::SoundAction;
use statusbar_doom::Statusbar;
use wad::lumps::{WadFlat, WadPatch};
use crate::{
blit::Blitter, cheats::Cheats, test_funcs::*, timestep::TimeStep, wipe::Wipe, CLIOptions,
blit::Blitter, cheats::Cheats, shaders::Shaders, timestep::TimeStep, wipe::Wipe, CLIOptions,
};
/// Never returns until `game.running` is set to false
@ -55,9 +55,24 @@ pub fn d_doom_loop(
matches!(options.verbose, log::LevelFilter::Debug),
);
let mut render_type = RenderType::Software;
if let Some(shader) = options.shader {
if !matches!(shader, Shaders::None) {
render_type = RenderType::SoftOpenGL;
}
};
info!("Using {render_type:?}");
let mut timestep = TimeStep::new();
let mut render_buffer = PixelBuf::new(screen_width as u32, screen_height as u32, true);
let mut render_buffer2 = PixelBuf::new(screen_width as u32, screen_height as u32, true);
let mut render_buffer = RenderTarget::new(screen_width, screen_height, &gl_ctx, render_type);
let mut render_buffer2 = RenderTarget::new(screen_width, screen_height, &gl_ctx, render_type);
if matches!(render_type, RenderType::SoftOpenGL) {
let buf = unsafe { render_buffer.soft_opengl_unchecked() };
buf.set_gl_filter().unwrap();
let buf = unsafe { render_buffer.soft_opengl_unchecked() };
buf.set_gl_filter().unwrap();
}
let mut blitter = Blitter::new(options.shader, &gl_ctx, window);
@ -140,29 +155,29 @@ pub fn d_doom_loop(
&mut timestep,
);
if options.palette_test {
palette_test(pal_num, &mut game, &mut render_buffer);
}
// if options.palette_test {
// palette_test(pal_num, &mut game, &mut render_buffer);
// }
if let Some(name) = options.image_test.clone() {
image_test(&name.to_ascii_uppercase(), &game, &mut render_buffer);
}
if let Some(images) = &images {
patch_select_test(&images[image_num], &game, &mut render_buffer);
}
if let Some(flats) = &flats {
flat_select_test(&flats[flat_num], &game, &mut render_buffer);
}
if let Some(sprites) = &sprites {
patch_select_test(&sprites[sprite_num], &game, &mut render_buffer);
}
if options.texture_test {
texture_select_test(
game.pic_data.borrow_mut().get_texture(tex_num),
&game,
&mut render_buffer,
);
}
// if let Some(name) = options.image_test.clone() {
// image_test(&name.to_ascii_uppercase(), &game, &mut render_buffer);
// }
// if let Some(images) = &images {
// patch_select_test(&images[image_num], &game, &mut render_buffer);
// }
// if let Some(flats) = &flats {
// flat_select_test(&flats[flat_num], &game, &mut render_buffer);
// }
// if let Some(sprites) = &sprites {
// patch_select_test(&sprites[sprite_num], &game, &mut render_buffer);
// }
// if options.texture_test {
// texture_select_test(
// game.pic_data.borrow_mut().get_texture(tex_num),
// &game,
// &mut render_buffer,
// );
// }
blitter.blit(&mut render_buffer);
@ -215,7 +230,7 @@ pub fn d_doom_loop(
Ok(())
}
fn draw_title(game: &mut Game, draw_buf: &mut PixelBuf) {
fn draw_title_pixels(game: &mut Game, draw_buf: &mut impl PixelBuffer) {
let mut xtmp = 0;
let mut ytmp = 0;
let f = draw_buf.height() / 200;
@ -227,10 +242,7 @@ fn draw_title(game: &mut Game, draw_buf: &mut PixelBuf) {
draw_buf.set_pixel(
(xtmp - n as i32) as usize, // - (image.left_offset as i32),
(ytmp + c.y_offset * f as i32) as usize, // - image.top_offset as i32 - 30,
colour.r,
colour.g,
colour.b,
255,
(colour.r, colour.g, colour.b, 255),
);
ytmp += 1;
}
@ -244,6 +256,21 @@ fn draw_title(game: &mut Game, draw_buf: &mut PixelBuf) {
}
}
fn draw_title(game: &mut Game, draw_buf: &mut RenderTarget) {
match draw_buf.render_type() {
gamestate_traits::RenderType::Software => {
let pixels = unsafe { draw_buf.software_unchecked() };
draw_title_pixels(game, pixels);
}
gamestate_traits::RenderType::SoftOpenGL => {
let pixels = unsafe { draw_buf.soft_opengl_unchecked() };
draw_title_pixels(game, pixels);
}
gamestate_traits::RenderType::OpenGL => todo!(),
gamestate_traits::RenderType::Vulkan => todo!(),
}
}
/// Does a bunch of stuff in Doom...
/// `pixels` is the buffer that is always drawn, so drawing in to `pixels2` then flipping
/// ensures the buffer is drawn. But if we draw in to `pixels2` and don't flip, we can
@ -261,8 +288,8 @@ fn d_display(
impl MachinationTrait,
>,
game: &mut Game,
disp_buf: &mut PixelBuf, // Display from this buffer
draw_buf: &mut PixelBuf, // Draw to this buffer
disp_buf: &mut RenderTarget, // Display from this buffer
draw_buf: &mut RenderTarget, // Draw to this buffer
blitter: &mut Blitter,
timestep: &mut TimeStep,
) {
@ -288,16 +315,27 @@ fn d_display(
}
}
}
// TODO: HU_Drawer();
// Fake crosshair
draw_buf.set_pixel(
disp_buf.width() as usize / 2,
disp_buf.height() as usize / 2,
200,
14,
14,
255,
);
match draw_buf.render_type() {
gamestate_traits::RenderType::Software => {
let pixels = unsafe { draw_buf.software_unchecked() };
pixels.set_pixel(
disp_buf.width() / 2,
disp_buf.height() / 2,
(200, 14, 14, 255),
);
}
gamestate_traits::RenderType::SoftOpenGL => {
let pixels = unsafe { draw_buf.soft_opengl_unchecked() };
pixels.set_pixel(
disp_buf.width() / 2,
disp_buf.height() / 2,
(200, 14, 14, 255),
);
}
gamestate_traits::RenderType::OpenGL => todo!(),
gamestate_traits::RenderType::Vulkan => todo!(),
}
}
match game.gamestate {
@ -317,6 +355,7 @@ fn d_display(
}
// // menus go directly to the screen
// draw_buf.clear();
menu.draw(draw_buf); // menu is drawn even on top of everything
// net update does i/o and buildcmds...
// TODO: NetUpdate(); // send out any new accumulation

View File

@ -3,7 +3,7 @@ mod cheats;
mod config;
mod d_main;
mod shaders;
mod test_funcs;
// mod test_funcs;
mod timestep;
mod wipe;

View File

@ -9,9 +9,8 @@ pub struct Basic {
_quad: [f32; 16],
indices: [u32; 6],
shader: ShaderProgram,
projection: Mat4,
look_at: Mat4,
texture: Texture,
_projection: Mat4,
_look_at: Mat4,
vb: VertexBuffer,
eb: ElementBuffer,
}
@ -73,9 +72,8 @@ impl Basic {
_quad: GL_QUAD,
indices: GL_QUAD_INDICES,
shader,
projection,
look_at,
texture: Texture::new(ctx).unwrap(),
_projection: projection,
_look_at: look_at,
vb,
eb,
}
@ -83,19 +81,9 @@ impl Basic {
}
impl Drawer for Basic {
fn set_tex_filter(&self) -> Result<(), GolemError> {
self.texture.set_minification(TextureFilter::Nearest)?;
self.texture.set_magnification(TextureFilter::Nearest)
}
fn set_image_data(&mut self, input: &[u8], input_size: (u32, u32)) {
self.texture
.set_image(Some(input), input_size.0, input_size.1, ColorFormat::RGB);
}
fn draw(&mut self) -> Result<(), GolemError> {
fn draw(&mut self, texture: &Texture) -> Result<(), GolemError> {
let bind_point = std::num::NonZeroU32::new(1).unwrap();
self.texture.set_active(bind_point);
texture.set_active(bind_point);
// self.ctx.clear();
unsafe {
self.shader.draw(

View File

@ -25,7 +25,6 @@ pub struct Cgwgcrt {
crt_height: u32,
projection: Mat4,
look_at: Mat4,
texture: Texture,
vb: VertexBuffer,
eb: ElementBuffer,
}
@ -97,7 +96,6 @@ impl Cgwgcrt {
crt_height,
projection,
look_at,
texture: Texture::new(ctx).unwrap(),
vb,
eb,
}
@ -105,20 +103,10 @@ impl Cgwgcrt {
}
impl Drawer for Cgwgcrt {
fn set_tex_filter(&self) -> Result<(), GolemError> {
self.texture.set_minification(TextureFilter::Nearest)?;
self.texture.set_magnification(TextureFilter::Nearest)
}
fn set_image_data(&mut self, input: &[u8], input_size: (u32, u32)) {
self.texture
.set_image(Some(input), input_size.0, input_size.1, ColorFormat::RGB);
}
fn draw(&mut self) -> Result<(), GolemError> {
fn draw(&mut self, texture: &Texture) -> Result<(), GolemError> {
// Set the image to use
let bind_point = std::num::NonZeroU32::new(1).unwrap();
self.texture.set_active(bind_point);
texture.set_active(bind_point);
self.crt_shader.bind();
self.crt_shader.prepare_draw(&self.vb, &self.eb)?;
@ -140,7 +128,7 @@ impl Drawer for Cgwgcrt {
self.crt_shader.set_uniform(
"inputSize",
UniformValue::Vector2([self.texture.width() as f32, self.texture.height() as f32]),
UniformValue::Vector2([texture.width() as f32, texture.height() as f32]),
)?;
self.crt_shader.set_uniform(
@ -149,7 +137,7 @@ impl Drawer for Cgwgcrt {
)?;
self.crt_shader.set_uniform(
"textureSize",
UniformValue::Vector2([self.texture.width() as f32, self.texture.height() as f32]),
UniformValue::Vector2([texture.width() as f32, texture.height() as f32]),
)?;
self.crt_shader

View File

@ -11,7 +11,6 @@ pub struct LottesCRT {
crt_shader: ShaderProgram,
projection: Mat4,
look_at: Mat4,
texture: Texture,
vb: VertexBuffer,
eb: ElementBuffer,
}
@ -77,7 +76,6 @@ impl LottesCRT {
crt_shader: shader,
projection,
look_at,
texture: Texture::new(ctx).unwrap(),
vb,
eb,
}
@ -85,17 +83,7 @@ impl LottesCRT {
}
impl Drawer for LottesCRT {
fn set_tex_filter(&self) -> Result<(), GolemError> {
self.texture.set_minification(TextureFilter::Nearest)?;
self.texture.set_magnification(TextureFilter::Linear)
}
fn set_image_data(&mut self, input: &[u8], input_size: (u32, u32)) {
self.texture
.set_image(Some(input), input_size.0, input_size.1, ColorFormat::RGB);
}
fn draw(&mut self) -> Result<(), GolemError> {
fn draw(&mut self, texture: &Texture) -> Result<(), GolemError> {
self.crt_shader.bind();
self.crt_shader.prepare_draw(&self.vb, &self.eb)?;
@ -117,13 +105,13 @@ impl Drawer for LottesCRT {
// CRT settings
self.crt_shader.set_uniform(
"color_texture_sz",
UniformValue::Vector2([self.texture.width() as f32, self.texture.height() as f32]),
UniformValue::Vector2([texture.width() as f32, texture.height() as f32]),
)?;
// size of color texture rounded up to power of 2
self.crt_shader.set_uniform(
"color_texture_pow2_sz",
UniformValue::Vector2([self.texture.width() as f32, self.texture.height() as f32]),
UniformValue::Vector2([texture.width() as f32, texture.height() as f32]),
)?;
// Hardness of scanline.
@ -162,7 +150,7 @@ impl Drawer for LottesCRT {
.set_uniform("cornersmooth", UniformValue::Float(70.0))?; // 70.0 to 170.0
let bind_point = std::num::NonZeroU32::new(1).unwrap();
self.texture.set_active(bind_point);
texture.set_active(bind_point);
unsafe {
self.crt_shader

View File

@ -11,7 +11,6 @@ pub struct LottesCRT {
crt_shader: ShaderProgram,
_projection: Mat4,
_look_at: Mat4,
texture: Texture,
vb: VertexBuffer,
eb: ElementBuffer,
}
@ -115,17 +114,12 @@ impl LottesCRT {
vb.set_data(&GL_QUAD);
eb.set_data(&GL_QUAD_INDICES);
let texture = Texture::new(ctx).unwrap();
let bind_point = std::num::NonZeroU32::new(1).unwrap();
texture.set_active(bind_point);
Self {
_quad: GL_QUAD,
indices: GL_QUAD_INDICES,
crt_shader: shader,
_projection: projection,
_look_at: look_at,
texture,
vb,
eb,
}
@ -133,24 +127,16 @@ impl LottesCRT {
}
impl Drawer for LottesCRT {
fn set_tex_filter(&self) -> Result<(), GolemError> {
self.texture.set_minification(TextureFilter::Linear)?;
self.texture.set_magnification(TextureFilter::Linear)
}
fn set_image_data(&mut self, input: &[u8], input_size: (u32, u32)) {
self.texture
.set_image(Some(input), input_size.0, input_size.1, ColorFormat::RGB);
}
fn draw(&mut self) -> Result<(), GolemError> {
self.crt_shader.bind();
fn draw(&mut self, texture: &Texture) -> Result<(), GolemError> {
// Set the image to use
let bind_point = std::num::NonZeroU32::new(1).unwrap();
texture.set_active(bind_point);
// CRT settings
self.crt_shader
.set_uniform(
"color_texture_sz",
UniformValue::Vector2([self.texture.width() as f32, self.texture.height() as f32]),
UniformValue::Vector2([texture.width() as f32, texture.height() as f32]),
)
.unwrap();
@ -158,7 +144,7 @@ impl Drawer for LottesCRT {
self.crt_shader
.set_uniform(
"color_texture_pow2_sz",
UniformValue::Vector2([self.texture.width() as f32, self.texture.height() as f32]),
UniformValue::Vector2([texture.width() as f32, texture.height() as f32]),
)
.unwrap();

View File

@ -1,4 +1,4 @@
use golem::GolemError;
use golem::{GolemError, Texture};
use serde::{Deserialize, Serialize};
use std::str::FromStr;
@ -48,15 +48,5 @@ const GL_QUAD: [f32; 16] = [
const GL_QUAD_INDICES: [u32; 6] = [0, 1, 2, 2, 3, 0];
pub trait Drawer {
fn clear(&self, ctx: &golem::Context) {
ctx.set_clear_color(0.0, 0.0, 0.0, 1.0);
ctx.clear();
}
fn set_tex_filter(&self) -> Result<(), GolemError>;
/// The input buffer/image of Doom
fn set_image_data(&mut self, input: &[u8], input_size: (u32, u32));
fn draw(&mut self) -> Result<(), GolemError>;
fn draw(&mut self, texture: &Texture) -> Result<(), GolemError>;
}

View File

@ -3,10 +3,10 @@
use gameplay::WallPic;
use gamestate::Game;
use render_traits::PixelBuf;
use render_traits::RenderTarget;
use wad::lumps::{WadFlat, WadPalette, WadPatch};
pub(crate) fn palette_test(pal_num: usize, game: &mut Game, pixels: &mut PixelBuf) {
pub(crate) fn palette_test(pal_num: usize, game: &mut Game, pixels: &mut RenderTarget) {
let height = pixels.height();
let row_count: i32 = 16;
@ -18,7 +18,7 @@ pub(crate) fn palette_test(pal_num: usize, game: &mut Game, pixels: &mut PixelBu
let pals: Vec<WadPalette> = game.wad_data.playpal_iter().collect();
for (i, c) in pals[pal_num].0.iter().enumerate() {
pixels.set_pixel(
pixels.set_softbuf_pixel(
(i as i32 % row_count * block_size + x_start) as usize,
(i as i32 / row_count * block_size + y_start) as usize,
c.r,
@ -29,7 +29,7 @@ pub(crate) fn palette_test(pal_num: usize, game: &mut Game, pixels: &mut PixelBu
}
}
pub(crate) fn image_test(name: &str, game: &Game, pixels: &mut PixelBuf) {
pub(crate) fn image_test(name: &str, game: &Game, pixels: &mut RenderTarget) {
let lump = game.wad_data.get_lump(name).unwrap();
let image = WadPatch::from_lump(lump);
let pals: Vec<WadPalette> = game.wad_data.playpal_iter().collect();
@ -42,7 +42,7 @@ pub(crate) fn image_test(name: &str, game: &Game, pixels: &mut PixelBuf) {
for (y, p) in c.pixels.iter().enumerate() {
let colour = pals[0].0[*p];
pixels.set_pixel(
pixels.set_softbuf_pixel(
(xs + x) as usize, // - (image.left_offset as i32),
(ys + y as i32 + c.y_offset) as usize, // - image.top_offset as i32 - 30,
colour.r,
@ -57,7 +57,7 @@ pub(crate) fn image_test(name: &str, game: &Game, pixels: &mut PixelBuf) {
}
}
pub(crate) fn patch_select_test(image: &WadPatch, game: &Game, pixels: &mut PixelBuf) {
pub(crate) fn patch_select_test(image: &WadPatch, game: &Game, pixels: &mut RenderTarget) {
let pals: Vec<WadPalette> = game.wad_data.playpal_iter().collect();
let xs = ((pixels.width() - image.width as u32) / 2) as i32;
@ -67,7 +67,7 @@ pub(crate) fn patch_select_test(image: &WadPatch, game: &Game, pixels: &mut Pixe
for c in image.columns.iter() {
for (y, p) in c.pixels.iter().enumerate() {
let colour = pals[0].0[*p];
pixels.set_pixel(
pixels.set_softbuf_pixel(
(xs + x) as usize, // - (image.left_offset as i32),
(ys + y as i32 + c.y_offset) as usize, // - image.top_offset as i32 - 30,
colour.r,
@ -82,7 +82,7 @@ pub(crate) fn patch_select_test(image: &WadPatch, game: &Game, pixels: &mut Pixe
}
}
pub(crate) fn texture_select_test(texture: &WallPic, game: &Game, pixels: &mut PixelBuf) {
pub(crate) fn texture_select_test(texture: &WallPic, game: &Game, pixels: &mut RenderTarget) {
let width = texture.data.len() as u32;
let height = texture.data[0].len() as u32;
let pals: Vec<WadPalette> = game.wad_data.playpal_iter().collect();
@ -97,7 +97,7 @@ pub(crate) fn texture_select_test(texture: &WallPic, game: &Game, pixels: &mut P
continue;
}
let colour = pal[*idx];
pixels.set_pixel(
pixels.set_softbuf_pixel(
(xs + x_pos as i32) as usize,
(ys + y_pos as i32) as usize,
colour.r,
@ -109,7 +109,7 @@ pub(crate) fn texture_select_test(texture: &WallPic, game: &Game, pixels: &mut P
}
}
pub(crate) fn flat_select_test(flat: &WadFlat, game: &Game, pixels: &mut PixelBuf) {
pub(crate) fn flat_select_test(flat: &WadFlat, game: &Game, pixels: &mut RenderTarget) {
let pals: Vec<WadPalette> = game.wad_data.playpal_iter().collect();
let xs = ((pixels.width() - 64) / 2) as i32;
@ -122,7 +122,7 @@ pub(crate) fn flat_select_test(flat: &WadFlat, game: &Game, pixels: &mut PixelBu
continue;
}
let colour = pal[*px as usize];
pixels.set_pixel(
pixels.set_softbuf_pixel(
(xs + x as i32) as usize,
(ys + y as i32) as usize,
colour.r,

View File

@ -1,5 +1,6 @@
use gameplay::m_random;
use gamestate_traits::PixelBuf;
use gamestate_traits::RenderTarget;
use render_traits::PixelBuffer;
#[derive(Debug)]
pub(crate) struct Wipe {
@ -26,10 +27,10 @@ impl Wipe {
Self { y, height, width }
}
pub(crate) fn do_melt(
fn do_melt_pixels(
&mut self,
disp_buf: &mut PixelBuf, // Display from this buffer
draw_buf: &mut PixelBuf, // Draw to this buffer
disp_buf: &mut impl PixelBuffer, // Display from this buffer
draw_buf: &mut impl PixelBuffer, // Draw to this buffer
) -> bool {
let mut done = true;
let f = (disp_buf.height() / 200) as i32;
@ -50,20 +51,41 @@ impl Wipe {
let mut y = self.y[x] as usize;
for _ in (0..dy).rev() {
let px = draw_buf.read_pixel(x, y);
disp_buf.set_pixel(x, y, px.0, px.1, px.2, px.3);
let px = draw_buf.read_softbuf_pixel(x, y);
disp_buf.set_pixel(x, y, (px.0, px.1, px.2, px.3));
y += 1;
}
self.y[x] += dy;
for c in 0..=self.height - self.y[x] - dy {
let y = self.height - c - dy;
let px = disp_buf.read_pixel(x, y as usize);
disp_buf.set_pixel(x, (self.height - c) as usize, px.0, px.1, px.2, px.3);
let px = disp_buf.read_softbuf_pixel(x, y as usize);
disp_buf.set_pixel(x, (self.height - c) as usize, (px.0, px.1, px.2, px.3));
}
done = false;
}
}
done
}
pub(crate) fn do_melt(
&mut self,
disp_buf: &mut RenderTarget, // Display from this buffer
draw_buf: &mut RenderTarget, // Draw to this buffer
) -> bool {
match disp_buf.render_type() {
render_traits::RenderType::Software => {
let disp_buf = unsafe { disp_buf.software_unchecked() };
let draw_buf = unsafe { draw_buf.software_unchecked() };
self.do_melt_pixels(disp_buf, draw_buf)
}
render_traits::RenderType::SoftOpenGL => {
let disp_buf = unsafe { disp_buf.soft_opengl_unchecked() };
let draw_buf = unsafe { draw_buf.soft_opengl_unchecked() };
self.do_melt_pixels(disp_buf, draw_buf)
}
render_traits::RenderType::OpenGL => todo!(),
render_traits::RenderType::Vulkan => todo!(),
}
}
}

View File

@ -7,7 +7,7 @@ pub use gameplay::{
m_random, AmmoType, Card, GameMode, PlayerStatus, Skill, WBPlayerStruct, WBStartStruct,
WeaponType, TICRATE, WEAPON_INFO,
};
pub use render_traits::PixelBuf;
pub use render_traits::{PixelBuffer, RenderTarget, RenderType};
pub use sdl2::{self, keyboard::Scancode};
pub use sound_traits::{MusTrack, SfxName};
@ -96,12 +96,13 @@ pub trait MachinationTrait {
fn get_palette(&self) -> &WadPalette;
/// Draw this Machination to the `PixelBuf`.
fn draw(&mut self, buffer: &mut PixelBuf);
fn draw(&mut self, buffer: &mut RenderTarget);
/// Free method, requires `get_palette()` to be implemented
fn draw_patch(&self, patch: &WadPatch, x: i32, y: i32, pixels: &mut PixelBuf) {
fn draw_patch_pixels(&self, patch: &WadPatch, x: i32, y: i32, pixels: &mut impl PixelBuffer) {
let mut xtmp = 0;
let mut ytmp = 0;
let f = pixels.height() / 200;
for c in patch.columns.iter() {
for n in 0..f {
@ -111,10 +112,7 @@ pub trait MachinationTrait {
pixels.set_pixel(
(x + xtmp - n as i32) as usize, // - (image.left_offset as i32),
(y + ytmp + c.y_offset * f as i32) as usize, // - image.top_offset as i32 - 30,
colour.r,
colour.g,
colour.b,
255,
(colour.r, colour.g, colour.b, 255),
);
ytmp += 1;
}

View File

@ -1,5 +1,5 @@
use crate::MachinationTrait;
use render_traits::PixelBuf;
use render_traits::{PixelBuffer, RenderTarget};
use std::mem::MaybeUninit;
use wad::{
lumps::{WadPatch, WAD_PATCH},
@ -33,16 +33,16 @@ pub fn get_st_key_sprites(wad: &WadData) -> [WadPatch; 6] {
unsafe { keys.map(|n| n.assume_init()) }
}
pub fn draw_num(
pub fn draw_num_pixels(
p: u32,
mut x: i32,
y: i32,
pad: usize,
nums: &[WadPatch],
drawer: &impl MachinationTrait,
buffer: &mut PixelBuf,
pixels: &mut impl PixelBuffer,
) -> i32 {
let f = (buffer.height() / 200) as i32;
let f = (pixels.height() / 200) as i32;
let width = nums[0].width as i32 * f;
let digits: Vec<u32> = p
.to_string()
@ -53,14 +53,38 @@ pub fn draw_num(
for n in digits.iter().rev() {
x -= width;
let num = &nums[*n as usize];
drawer.draw_patch(num, x, y, buffer);
drawer.draw_patch_pixels(num, x, y, pixels);
}
if digits.len() <= pad {
for _ in 0..=pad - digits.len() {
x -= width;
drawer.draw_patch(&nums[0], x, y, buffer);
drawer.draw_patch_pixels(&nums[0], x, y, pixels);
}
}
x
}
pub fn draw_num(
p: u32,
x: i32,
y: i32,
pad: usize,
nums: &[WadPatch],
drawer: &impl MachinationTrait,
buffer: &mut RenderTarget,
) -> i32 {
// TODO: remove duplicated functionality
match buffer.render_type() {
render_traits::RenderType::Software => {
let pixels = unsafe { buffer.software_unchecked() };
draw_num_pixels(p, x, y, pad, nums, drawer, pixels)
}
render_traits::RenderType::SoftOpenGL => {
let pixels = unsafe { buffer.soft_opengl_unchecked() };
draw_num_pixels(p, x, y, pad, nums, drawer, pixels)
}
render_traits::RenderType::OpenGL => todo!(),
render_traits::RenderType::Vulkan => todo!(),
}
}

View File

@ -1,4 +1,4 @@
use gamestate_traits::{GameTraits, MachinationTrait, PixelBuf, Scancode, TICRATE};
use gamestate_traits::{GameTraits, MachinationTrait, RenderTarget, Scancode, TICRATE};
use hud_util::{load_char_patches, HUDString, HUD_STRING};
use wad::{lumps::WadPalette, WadData};
@ -62,8 +62,8 @@ impl Messages {
}
}
pub fn draw_wrapped(&self, machination: &impl MachinationTrait, pixels: &mut PixelBuf) {
let f = (pixels.height() / 200) as i32;
pub fn draw_wrapped(&self, machination: &impl MachinationTrait, buffer: &mut RenderTarget) {
let f = (buffer.height() / 200) as i32;
let x = 10;
let mut y = 2;
@ -80,7 +80,7 @@ impl Messages {
continue;
}
self.lines[pos].draw(x, y, machination, pixels);
self.lines[pos].draw(x, y, machination, buffer);
y += self.lines[pos].line_height() * f + 1;
if pos == self.current {
@ -127,7 +127,7 @@ impl MachinationTrait for Messages {
&self.palette
}
fn draw(&mut self, buffer: &mut PixelBuf) {
fn draw(&mut self, buffer: &mut RenderTarget) {
self.screen_width = buffer.width() as i32;
self.screen_height = buffer.height() as i32;
self.draw_wrapped(self, buffer);

View File

@ -1,4 +1,4 @@
use gamestate_traits::{MachinationTrait, PixelBuf};
use gamestate_traits::{MachinationTrait, PixelBuffer, RenderTarget, RenderType};
use log::warn;
use wad::{
lumps::{WadPatch, WAD_PATCH},
@ -112,12 +112,12 @@ impl HUDString {
self.data.clear();
}
pub fn draw(
pub fn draw_pixels(
&self,
mut x: i32,
mut y: i32,
machination: &impl MachinationTrait,
pixels: &mut PixelBuf,
pixels: &mut impl PixelBuffer,
) -> Option<()> {
let f = (pixels.height() / 200) as i32;
let width = pixels.width() as i32;
@ -160,7 +160,7 @@ impl HUDString {
return None;
}
machination.draw_patch(
machination.draw_patch_pixels(
patch,
x,
y + self.line_height * f - patch.height as i32 * f,
@ -170,6 +170,28 @@ impl HUDString {
}
Some(())
}
pub fn draw(
&self,
x: i32,
y: i32,
machination: &impl MachinationTrait,
pixels: &mut RenderTarget,
) -> Option<()> {
match pixels.render_type() {
RenderType::Software => {
let pixels = unsafe { pixels.software_unchecked() };
self.draw_pixels(x, y, machination, pixels);
}
RenderType::SoftOpenGL => {
let pixels = unsafe { pixels.soft_opengl_unchecked() };
self.draw_pixels(x, y, machination, pixels);
}
RenderType::OpenGL => todo!(),
RenderType::Vulkan => todo!(),
}
Some(())
}
}
#[cfg(test)]

View File

@ -5,8 +5,8 @@ use crate::defs::{
};
use gameplay::{m_random, TICRATE};
use gamestate_traits::{
GameMode, GameTraits, MachinationTrait, MusTrack, PixelBuf, Scancode, WBPlayerStruct,
WBStartStruct,
GameMode, GameTraits, MachinationTrait, MusTrack, PixelBuffer, RenderTarget, Scancode,
WBPlayerStruct, WBStartStruct,
};
use log::warn;
use wad::{
@ -241,22 +241,41 @@ impl Intermission {
}
}
fn draw_animated_bg(&self, scale: i32, buffer: &mut PixelBuf) {
fn draw_animated_bg_pixels(&self, scale: i32, pixels: &mut impl PixelBuffer) {
if self.mode == GameMode::Commercial || self.level_info.epsd > 2 {
return;
}
for anim in self.animations[self.level_info.epsd as usize].iter() {
if anim.counter >= 0 {
self.draw_patch(
self.draw_patch_pixels(
&anim.patches[anim.counter as usize],
anim.location.0 * scale,
anim.location.1 * scale,
buffer,
pixels,
);
}
}
}
// fn draw_animated_bg(&self, scale: i32, buffer: &mut RenderTarget) {
// if self.mode == GameMode::Commercial || self.level_info.epsd > 2 {
// return;
// }
// match buffer.render_type() {
// gamestate_traits::RenderType::Software => {
// let pixels = unsafe { buffer.software_unchecked() };
// self.draw_animated_bg_pixels(scale, pixels);
// }
// gamestate_traits::RenderType::SoftOpenGL => {
// let pixels = unsafe { buffer.soft_opengl_unchecked() };
// self.draw_animated_bg_pixels(scale, pixels);
// }
// gamestate_traits::RenderType::OpenGL => todo!(),
// gamestate_traits::RenderType::Vulkan => todo!(),
// }
// }
}
impl MachinationTrait for Intermission {
@ -329,20 +348,41 @@ impl MachinationTrait for Intermission {
&self.palette
}
fn draw(&mut self, buffer: &mut PixelBuf) {
fn draw(&mut self, buffer: &mut RenderTarget) {
let scale = (buffer.height() / 200) as i32;
// TODO: stats and next are two different screens.
match self.state {
State::StatCount => {
self.draw_stats(scale, buffer);
match buffer.render_type() {
gamestate_traits::RenderType::Software => {
let pixels = unsafe { buffer.software_unchecked() };
match self.state {
State::StatCount => {
self.draw_stats_pixels(scale, pixels);
}
State::NextLoc => {
self.draw_next_loc_pixels(scale, pixels);
}
State::None => {
self.draw_no_state(scale, pixels);
}
}
}
State::NextLoc => {
self.draw_next_loc(scale, buffer);
}
State::None => {
self.draw_no_state(scale, buffer);
gamestate_traits::RenderType::SoftOpenGL => {
let pixels = unsafe { buffer.soft_opengl_unchecked() };
match self.state {
State::StatCount => {
self.draw_stats_pixels(scale, pixels);
}
State::NextLoc => {
self.draw_next_loc_pixels(scale, pixels);
}
State::None => {
self.draw_no_state(scale, pixels);
}
}
}
gamestate_traits::RenderType::OpenGL => todo!(),
gamestate_traits::RenderType::Vulkan => todo!(),
}
}
}

View File

@ -1,5 +1,5 @@
use crate::{Intermission, State, MAP_POINTS, SHOW_NEXT_LOC_DELAY, TICRATE, TITLE_Y};
use gamestate_traits::{GameMode, MachinationTrait, PixelBuf};
use gamestate_traits::{GameMode, MachinationTrait, PixelBuffer};
use wad::lumps::WadPatch;
impl Intermission {
@ -26,7 +26,7 @@ impl Intermission {
lv: usize,
patch: &WadPatch,
scale: i32,
buffer: &mut PixelBuf,
pixels: &mut impl PixelBuffer,
) {
let ep = self.level_info.epsd as usize;
let point = MAP_POINTS[ep][lv];
@ -34,13 +34,13 @@ impl Intermission {
let x = point.0 - patch.left_offset as i32;
let y = point.1 - patch.top_offset as i32;
self.draw_patch(patch, x * scale, y * scale, buffer);
self.draw_patch_pixels(patch, x * scale, y * scale, pixels);
}
pub(super) fn draw_enter_level(&self, scale: i32, buffer: &mut PixelBuf) {
pub(super) fn draw_enter_level_pixels(&self, scale: i32, buffer: &mut impl PixelBuffer) {
let half = buffer.width() as i32 / 2;
let mut y = TITLE_Y * scale;
self.draw_patch(
self.draw_patch_pixels(
&self.patches.enter,
half - self.patches.enter.width as i32 * scale / 2,
y,
@ -48,13 +48,13 @@ impl Intermission {
);
y += (5 * self.patches.enter.height as i32 * scale) / 4;
let patch = self.get_enter_level_name();
self.draw_patch(patch, half - patch.width as i32 * scale / 2, y, buffer);
self.draw_patch_pixels(patch, half - patch.width as i32 * scale / 2, y, buffer);
}
pub(super) fn draw_next_loc(&self, scale: i32, buffer: &mut PixelBuf) {
pub(super) fn draw_next_loc_pixels(&self, scale: i32, buffer: &mut impl PixelBuffer) {
// Background
self.draw_patch(self.get_bg(), 0, 0, buffer);
self.draw_animated_bg(scale, buffer);
self.draw_patch_pixels(self.get_bg(), 0, 0, buffer);
self.draw_animated_bg_pixels(scale, buffer);
// Location stuff only for episodes 1-3
if self.mode != GameMode::Commercial && self.level_info.epsd <= 2 {
@ -79,7 +79,7 @@ impl Intermission {
}
if self.mode != GameMode::Commercial || self.level_info.next != 30 {
self.draw_enter_level(scale, buffer);
self.draw_enter_level_pixels(scale, buffer);
}
}
}

View File

@ -1,11 +1,11 @@
use crate::{Intermission, State};
use gamestate_traits::{GameTraits, PixelBuf};
use gamestate_traits::{GameTraits, PixelBuffer};
use log::info;
impl Intermission {
pub(super) fn draw_no_state(&mut self, scale: i32, buffer: &mut PixelBuf) {
pub(super) fn draw_no_state(&mut self, scale: i32, pixels: &mut impl PixelBuffer) {
self.pointer_on = true;
self.draw_next_loc(scale, buffer);
self.draw_next_loc_pixels(scale, pixels);
}
pub(super) fn init_no_state(&mut self) {

View File

@ -1,5 +1,5 @@
use crate::{Intermission, State, SHOW_NEXT_LOC_DELAY, TICRATE, TITLE_Y};
use gamestate_traits::{util::draw_num, GameMode, MachinationTrait, PixelBuf};
use gamestate_traits::{util::draw_num_pixels, GameMode, MachinationTrait, PixelBuffer};
const SCREEN_HEIGHT: i32 = 200;
@ -30,36 +30,36 @@ impl Intermission {
}
}
pub(super) fn draw_level_finish(&self, scale: i32, buffer: &mut PixelBuf) {
let half = buffer.width() as i32 / 2;
pub(super) fn draw_level_finish_pixels(&self, scale: i32, pixels: &mut impl PixelBuffer) {
let half = pixels.width() as i32 / 2;
let mut y = TITLE_Y * scale;
self.draw_patch(
self.draw_patch_pixels(
&self.patches.finish,
half - self.patches.enter.width as i32 * scale / 2,
y,
buffer,
pixels,
);
y += (5 * self.patches.finish.height as i32) / 4 * scale;
let patch = self.get_this_level_name();
self.draw_patch(patch, half - patch.width as i32 * scale / 2, y, buffer);
self.draw_patch_pixels(patch, half - patch.width as i32 * scale / 2, y, pixels);
}
fn draw_percent(&self, p: u32, x: i32, y: i32, buffer: &mut PixelBuf) {
self.draw_patch(&self.patches.percent, x, y, buffer);
draw_num(p, x, y, 0, &self.patches.nums, self, buffer);
fn draw_percent(&self, p: u32, x: i32, y: i32, pixels: &mut impl PixelBuffer) {
self.draw_patch_pixels(&self.patches.percent, x, y, pixels);
draw_num_pixels(p, x, y, 0, &self.patches.nums, self, pixels);
}
fn draw_time(&self, t: u32, mut x: i32, y: i32, scale: i32, buffer: &mut PixelBuf) {
fn draw_time(&self, t: u32, mut x: i32, y: i32, scale: i32, buffer: &mut impl PixelBuffer) {
let mut div = 1;
if t <= 61 * 59 {
loop {
let n = (t / div) % 60;
x = draw_num(n, x, y, 1, &self.patches.nums, self, buffer)
x = draw_num_pixels(n, x, y, 1, &self.patches.nums, self, buffer)
- self.patches.colon.width as i32 * scale;
div *= 60;
if div == 60 || t / div != 0 {
self.draw_patch(&self.patches.colon, x, y, buffer);
self.draw_patch_pixels(&self.patches.colon, x, y, buffer);
}
if t / div == 0 {
@ -69,7 +69,7 @@ impl Intermission {
}
}
pub(super) fn draw_stats(&mut self, scale: i32, buffer: &mut PixelBuf) {
pub(super) fn draw_stats_pixels(&mut self, scale: i32, buffer: &mut impl PixelBuffer) {
let width = buffer.width() as i32;
let stats_x = SP_STATSX * scale;
let stats_y = SP_STATSY * scale;
@ -77,12 +77,12 @@ impl Intermission {
let time_y = SP_TIMEY * scale;
// Background
self.draw_patch(self.get_bg(), 0, 0, buffer);
self.draw_animated_bg(scale, buffer);
self.draw_level_finish(scale, buffer);
self.draw_patch_pixels(self.get_bg(), 0, 0, buffer);
self.draw_animated_bg_pixels(scale, buffer);
self.draw_level_finish_pixels(scale, buffer);
let mut lh = (3 * self.patches.nums[0].height / 2) as i32;
self.draw_patch(&self.patches.kills, stats_x, stats_y, buffer);
self.draw_patch_pixels(&self.patches.kills, stats_x, stats_y, buffer);
self.draw_percent(
self.player_info.skills as u32,
width - stats_x,
@ -91,7 +91,7 @@ impl Intermission {
);
lh += lh;
self.draw_patch(&self.patches.items, stats_x, stats_y + lh, buffer);
self.draw_patch_pixels(&self.patches.items, stats_x, stats_y + lh, buffer);
self.draw_percent(
self.player_info.sitems as u32,
width - stats_x,
@ -100,7 +100,7 @@ impl Intermission {
);
lh += lh;
self.draw_patch(&self.patches.sp_secret, stats_x, stats_y + lh, buffer);
self.draw_patch_pixels(&self.patches.sp_secret, stats_x, stats_y + lh, buffer);
self.draw_percent(
self.player_info.ssecret as u32,
width - stats_x,
@ -108,7 +108,7 @@ impl Intermission {
buffer,
);
self.draw_patch(&self.patches.time, time_x, time_y, buffer);
self.draw_patch_pixels(&self.patches.time, time_x, time_y, buffer);
self.draw_time(
self.player_info.stime / TICRATE as u32,
width / 2 - time_x,
@ -118,7 +118,7 @@ impl Intermission {
);
if self.level_info.epsd < 3 {
self.draw_patch(&self.patches.par, width / 2 + time_x, time_y, buffer);
self.draw_patch_pixels(&self.patches.par, width / 2 + time_x, time_y, buffer);
self.draw_time(
self.level_info.partime as u32,
width - time_x,

View File

@ -3,7 +3,9 @@
//! with the rest of the game it ends up being fairly generic - you could make this
//! fully generic with a little work, or use it as the basis for a different menu.
use gamestate_traits::{GameMode, GameTraits, MachinationTrait, PixelBuf, Scancode, Skill};
use gamestate_traits::{
GameMode, GameTraits, MachinationTrait, PixelBuffer, RenderTarget, Scancode, Skill,
};
use sound_traits::SfxName;
use std::collections::HashMap;
use wad::{
@ -331,6 +333,34 @@ impl MenuDoom {
.get(name)
.unwrap_or_else(|| panic!("{name} not in cache"))
}
fn draw_pixels(&mut self, pixels: &mut impl PixelBuffer) {
let f = (pixels.height() / 200) as i32;
if self.active || self.in_help {
let active = &self.menus[self.current_menu as usize];
// Titles
for item in active.titles.iter() {
self.draw_patch_pixels(self.get_patch(&item.patch), item.x * f, item.y * f, pixels);
}
// sub-items
let x = active.x * f;
let mut y = active.y * f;
for item in active.items.iter() {
self.draw_patch_pixels(self.get_patch(&item.patch), x, y, pixels);
y += LINEHEIGHT * f;
}
// SKULL
let y = active.y * f - 5 + active.last_on as i32 * LINEHEIGHT * f;
self.draw_patch_pixels(
self.get_patch(SKULLS[self.which_skull]),
x + -(32 * f),
y,
pixels,
);
}
}
}
fn sel_new_game(menu: &mut MenuDoom, _: i32, game: &mut dyn GameTraits) {
@ -498,31 +528,18 @@ impl MachinationTrait for MenuDoom {
&self.palette
}
fn draw(&mut self, buffer: &mut PixelBuf) {
let f = (buffer.height() / 200) as i32;
if self.active || self.in_help {
let active = &self.menus[self.current_menu as usize];
// Titles
for item in active.titles.iter() {
self.draw_patch(self.get_patch(&item.patch), item.x * f, item.y * f, buffer);
fn draw(&mut self, buffer: &mut RenderTarget) {
match buffer.render_type() {
gamestate_traits::RenderType::Software => {
let pixels = unsafe { buffer.software_unchecked() };
self.draw_pixels(pixels)
}
// sub-items
let x = active.x * f;
let mut y = active.y * f;
for item in active.items.iter() {
self.draw_patch(self.get_patch(&item.patch), x, y, buffer);
y += LINEHEIGHT * f;
gamestate_traits::RenderType::SoftOpenGL => {
let pixels = unsafe { buffer.soft_opengl_unchecked() };
self.draw_pixels(pixels)
}
// SKULL
let y = active.y * f - 5 + active.last_on as i32 * LINEHEIGHT * f;
self.draw_patch(
self.get_patch(SKULLS[self.which_skull]),
x + -(32 * f),
y,
buffer,
);
gamestate_traits::RenderType::OpenGL => todo!(),
gamestate_traits::RenderType::Vulkan => todo!(),
}
}
}

View File

@ -10,7 +10,7 @@ use gameplay::{
SubSector, IS_SSECTOR_MASK,
};
use glam::Vec2;
use render_traits::{PixelBuf, PlayRenderer};
use render_traits::{PixelBuffer, PlayRenderer, RenderTarget};
use std::{
cell::RefCell,
f32::consts::{FRAC_PI_2, FRAC_PI_4, PI},
@ -69,26 +69,55 @@ pub struct SoftwareRenderer {
}
impl PlayRenderer for SoftwareRenderer {
fn render_player_view(&mut self, player: &Player, level: &Level, pixels: &mut PixelBuf) {
fn render_player_view(&mut self, player: &Player, level: &Level, pixels: &mut RenderTarget) {
let map = &level.map_data;
self.clear(player, pixels.width() as f32);
// TODO: netupdate
let mut count = 0;
self.checked_sectors.clear();
// TODO: pull duplicate functionality out to a function
match pixels.render_type() {
render_traits::RenderType::Software => {
let pixels = unsafe { pixels.software_unchecked() };
self.clear(player, pixels.width() as f32);
// TODO: netupdate
let mut count = 0;
self.checked_sectors.clear();
self.texture_data
.borrow_mut()
.set_fixed_lightscale(player.fixedcolormap as usize);
self.texture_data.borrow_mut().set_player_palette(player);
self.texture_data
.borrow_mut()
.set_fixed_lightscale(player.fixedcolormap as usize);
self.texture_data.borrow_mut().set_player_palette(player);
self.render_bsp_node(map, player, map.start_node(), pixels, &mut count);
trace!("BSP traversals for render: {count}");
// TODO: netupdate again
self.draw_planes(player, pixels);
// TODO: netupdate again
self.draw_masked(player, pixels);
// TODO: netupdate again
self.render_bsp_node(map, player, map.start_node(), pixels, &mut count);
trace!("BSP traversals for render: {count}");
// TODO: netupdate again
self.draw_planes(player, pixels);
// TODO: netupdate again
self.draw_masked(player, pixels);
// TODO: netupdate again
}
render_traits::RenderType::SoftOpenGL => {
let pixels = unsafe { pixels.soft_opengl_unchecked() };
self.clear(player, pixels.width() as f32);
// TODO: netupdate
let mut count = 0;
self.checked_sectors.clear();
self.texture_data
.borrow_mut()
.set_fixed_lightscale(player.fixedcolormap as usize);
self.texture_data.borrow_mut().set_player_palette(player);
self.render_bsp_node(map, player, map.start_node(), pixels, &mut count);
trace!("BSP traversals for render: {count}");
// TODO: netupdate again
self.draw_planes(player, pixels);
// TODO: netupdate again
self.draw_masked(player, pixels);
// TODO: netupdate again
}
_ => {
panic!("Not a valid renderer for software mode")
}
}
}
}
@ -125,7 +154,7 @@ impl SoftwareRenderer {
}
/// Doom function name `R_DrawPlanes`
fn draw_planes(&mut self, player: &Player, pixels: &mut PixelBuf) {
fn draw_planes(&mut self, player: &Player, pixels: &mut impl PixelBuffer) {
let mobj = unsafe { player.mobj_unchecked() };
let view_angle = mobj.angle;
@ -179,7 +208,7 @@ impl SoftwareRenderer {
plane.baseyscale = baseyscale;
plane.view_angle = view_angle;
let mut span_start = vec![0.0; pixels.width() as usize];
let mut span_start = vec![0.0; pixels.width()];
for x in plane.minx as i32..=plane.maxx as i32 {
let mut step = x - 1;
if step < 0 {
@ -209,7 +238,7 @@ impl SoftwareRenderer {
player: &Player,
seg: &'a Segment,
front_sector: &'a Sector,
pixels: &mut PixelBuf,
pixels: &mut impl PixelBuffer,
) {
let mobj = unsafe { player.mobj_unchecked() };
// reject orthogonal back sides
@ -308,7 +337,7 @@ impl SoftwareRenderer {
map: &MapData,
player: &Player,
subsect: &SubSector,
pixels: &mut PixelBuf,
pixels: &mut impl PixelBuffer,
) {
let skynum = self.texture_data.borrow().sky_num();
// TODO: planes for floor & ceiling
@ -335,7 +364,7 @@ impl SoftwareRenderer {
let front_sector = &subsect.sector;
self.add_sprites(player, front_sector, pixels.width());
self.add_sprites(player, front_sector, pixels.width() as u32);
for i in subsect.start_seg..subsect.start_seg + subsect.seg_count {
let seg = &map.segments()[i as usize];
@ -366,7 +395,7 @@ impl SoftwareRenderer {
last: f32,
seg: &Segment,
object: &Player,
pixels: &mut PixelBuf,
pixels: &mut impl PixelBuffer,
) {
let mut next;
@ -469,7 +498,7 @@ impl SoftwareRenderer {
last: f32,
seg: &Segment,
object: &Player,
pixels: &mut PixelBuf,
pixels: &mut impl PixelBuffer,
) {
// Find the first range that touches the range
// (adjacent pixels are touching).
@ -555,7 +584,7 @@ impl SoftwareRenderer {
map: &MapData,
player: &Player,
node_id: u16,
pixels: &mut PixelBuf,
pixels: &mut impl PixelBuffer,
count: &mut usize,
) {
*count += 1;

View File

@ -3,7 +3,7 @@ use std::f32::consts::FRAC_PI_2;
use crate::utilities::screen_to_x_view;
use gameplay::{Angle, FlatPic, PicData};
use glam::Vec2;
use render_traits::PixelBuf;
use render_traits::PixelBuffer;
use super::defs::Visplane;
@ -203,7 +203,7 @@ pub fn make_spans(
plane: &Visplane,
span_start: &mut [f32],
texture_data: &PicData,
pixels: &mut PixelBuf,
pixels: &mut impl PixelBuffer,
) {
// TODO: t1, y, is causing a glitch
while t1 < t2 && t1 <= b1 {
@ -257,7 +257,7 @@ fn map_plane(
extra_light: i32,
plane: &Visplane,
texture_data: &PicData,
pixels: &mut PixelBuf,
pixels: &mut impl PixelBuffer,
) {
let planeheight = (plane.height - viewz).abs();
// TODO: maybe cache?
@ -324,7 +324,7 @@ impl<'a> DrawSpan<'a> {
}
}
fn draw(&mut self, textures: &PicData, pixels: &mut PixelBuf) {
fn draw(&mut self, textures: &PicData, pixels: &mut impl PixelBuffer) {
let pal = textures.palette();
// for s in self.ds_x1.round() as i32..=self.ds_x2.round() as i32 {
for s in self.ds_x1 as i32..=self.ds_x2 as i32 {
@ -341,7 +341,7 @@ impl<'a> DrawSpan<'a> {
let px = self.colourmap[self.texture.data[x][y] as usize];
let c = pal[px];
pixels.set_pixel(s as usize, self.ds_y as usize, c.r, c.g, c.b, 255);
pixels.set_pixel(s as usize, self.ds_y as usize, (c.r, c.g, c.b, 255));
self.ds_xfrac += self.ds_xstep;
self.ds_yfrac += self.ds_ystep;

View File

@ -1,6 +1,6 @@
use crate::utilities::screen_to_x_view;
use gameplay::{Angle, LineDefFlags, PicData, Player, Segment};
use render_traits::PixelBuf;
use render_traits::PixelBuffer;
use std::{cell::RefCell, f32::consts::FRAC_PI_2, ptr::NonNull, rc::Rc};
use crate::utilities::{point_to_dist, scale_from_view_angle};
@ -120,7 +120,7 @@ impl SegRender {
seg: &Segment,
player: &Player,
rdata: &mut RenderData,
pixels: &mut PixelBuf,
pixels: &mut impl PixelBuffer,
) {
// Keep original Doom behaviour here
if rdata.drawsegs.len() >= MAXDRAWSEGS {
@ -470,7 +470,7 @@ impl SegRender {
seg: &Segment,
view_height: f32,
rdata: &mut RenderData,
pixels: &mut PixelBuf,
pixels: &mut impl PixelBuffer,
) {
// R_RenderSegLoop
let mut yl: f32;
@ -687,7 +687,12 @@ impl<'a> DrawColumn<'a> {
/// will always have constant z depth.
/// Thus a special case loop for very fast rendering can
/// be used. It has also been used with Wolfenstein 3D.
pub fn draw_column(&mut self, textures: &PicData, doubled: bool, pixels: &mut PixelBuf) {
pub fn draw_column(
&mut self,
textures: &PicData,
doubled: bool,
pixels: &mut impl PixelBuffer,
) {
let pal = textures.palette();
let mut frac =
self.dc_texturemid + (self.yl - pixels.height() as f32 / 2.0) * self.fracstep + 0.1;
@ -708,7 +713,7 @@ impl<'a> DrawColumn<'a> {
let px = self.colourmap[self.texture_column[select as usize]];
let c = pal[px];
pixels.set_pixel(self.dc_x as usize, n as usize, c.r, c.g, c.b, 255);
pixels.set_pixel(self.dc_x as usize, n as usize, (c.r, c.g, c.b, 255));
frac += self.fracstep;
}

View File

@ -8,7 +8,7 @@ use gameplay::{
PspDef, Sector,
};
use glam::Vec2;
use render_traits::PixelBuf;
use render_traits::PixelBuffer;
use super::{bsp::SoftwareRenderer, defs::DrawSeg};
@ -255,7 +255,7 @@ impl SoftwareRenderer {
vis: &VisSprite,
clip_bottom: &[i32],
clip_top: &[i32],
pixels: &mut PixelBuf,
pixels: &mut impl PixelBuffer,
) {
let naff = self.texture_data.clone(); // Need to separate lifetimes
let texture_data = naff.borrow();
@ -311,9 +311,9 @@ impl SoftwareRenderer {
}
/// Doom function name `R_DrawSprite`
fn draw_sprite(&mut self, player: &Player, vis: &VisSprite, pixels: &mut PixelBuf) {
let mut clip_bottom = vec![-2i32; pixels.width() as usize];
let mut clip_top = vec![-2i32; pixels.width() as usize];
fn draw_sprite(&mut self, player: &Player, vis: &VisSprite, pixels: &mut impl PixelBuffer) {
let mut clip_bottom = vec![-2i32; pixels.width()];
let mut clip_top = vec![-2i32; pixels.width()];
// Breaking liftime to enable this loop
let segs = unsafe { &*(&self.r_data.drawsegs as *const Vec<DrawSeg>) };
@ -391,7 +391,7 @@ impl SoftwareRenderer {
self.draw_vissprite(vis, &clip_bottom, &clip_top, pixels);
}
fn draw_player_sprites(&mut self, player: &Player, pixels: &mut PixelBuf) {
fn draw_player_sprites(&mut self, player: &Player, pixels: &mut impl PixelBuffer) {
if let Some(mobj) = player.mobj() {
let light = unsafe { (*mobj.subsector).sector.lightlevel };
let light = (light >> 4) + player.extralight;
@ -409,7 +409,7 @@ impl SoftwareRenderer {
sprite: &PspDef,
light: usize,
flags: u32,
pixels: &mut PixelBuf,
pixels: &mut impl PixelBuffer,
) {
let f = pixels.height() / 200;
let pspriteiscale = 0.99 / f as f32;
@ -463,12 +463,12 @@ impl SoftwareRenderer {
vis.start_frac += vis.x_iscale * (vis.x1 - x1) as f32;
}
let clip_bottom = vec![0i32; pixels.width() as usize];
let clip_top = vec![pixels.height() as i32; pixels.width() as usize];
let clip_bottom = vec![0i32; pixels.width()];
let clip_top = vec![pixels.height() as i32; pixels.width()];
self.draw_vissprite(&vis, &clip_top, &clip_bottom, pixels)
}
pub(crate) fn draw_masked(&mut self, player: &Player, pixels: &mut PixelBuf) {
pub(crate) fn draw_masked(&mut self, player: &Player, pixels: &mut impl PixelBuffer) {
// Sort only the vissprites used
self.vissprites[..self.next_vissprite].sort();
// Need to break lifetime as a chain function call needs &mut on a separate item
@ -494,7 +494,7 @@ impl SoftwareRenderer {
ds: &DrawSeg,
x1: i32,
x2: i32,
pixels: &mut PixelBuf,
pixels: &mut impl PixelBuffer,
) {
let seg = unsafe { ds.curline.as_ref() };
let frontsector = seg.frontsector.clone();
@ -608,7 +608,7 @@ fn draw_masked_column(
yl: i32,
yh: i32,
textures: &PicData,
pixels: &mut PixelBuf,
pixels: &mut impl PixelBuffer,
) {
let pal = &textures.palette();
let mut frac = dc_texturemid + 0.5 + (yl as f32 - (pixels.height() / 2) as f32) * fracstep;
@ -627,7 +627,7 @@ fn draw_masked_column(
let px = colourmap[texture_column[select]];
let c = pal[px];
pixels.set_pixel(dc_x as usize, n as usize, c.r, c.g, c.b, 255);
pixels.set_pixel(dc_x as usize, n as usize, (c.r, c.g, c.b, 255));
frac += fracstep;
}
}

View File

@ -7,3 +7,4 @@ build = "../../build.rs"
[dependencies]
gameplay.workspace = true
golem.workspace = true

View File

@ -2,108 +2,278 @@
//! and a generic `PlayRenderer` for rendering the players view of the level.
use gameplay::{Level, Player};
use golem::{ColorFormat, Context, GolemError, Texture, TextureFilter};
const CHANNELS: usize = 3;
const CHANNELS: usize = 4;
/// A structure holding display data
pub struct PixelBuf {
width: u32,
height: u32,
is_software: bool,
/// Total length is width * height * CHANNELS, where CHANNELS is RGB bytes
software_buffer: Vec<u8>,
#[derive(Debug, Default, PartialEq, PartialOrd, Clone, Copy)]
pub enum RenderType {
/// Purely software. Typically used with blitting a framebuffer maintained in memory
/// directly to screen using SDL2
#[default]
Software,
/// Software framebuffer blitted to screen using OpenGL (and can use shaders)
SoftOpenGL,
/// OpenGL
OpenGL,
/// Vulkan
Vulkan,
}
impl PixelBuf {
pub fn new(width: u32, height: u32, is_software: bool) -> Self {
pub trait PixelBuffer {
fn width(&self) -> usize;
fn height(&self) -> usize;
fn clear(&mut self);
fn set_pixel(&mut self, x: usize, y: usize, rgba: (u8, u8, u8, u8));
fn read_softbuf_pixel(&self, x: usize, y: usize) -> (u8, u8, u8, u8);
fn read_softbuf_pixels(&mut self) -> &mut [u8];
}
/// A structure holding display data
pub struct SoftFramebuffer {
width: usize,
height: usize,
/// Total length is width * height * CHANNELS, where CHANNELS is RGB bytes
buffer: Vec<u8>,
}
impl SoftFramebuffer {
fn new(width: usize, height: usize) -> Self {
Self {
width,
height,
is_software,
software_buffer: if is_software {
vec![0; (width * height) as usize * CHANNELS]
} else {
Vec::default()
},
buffer: vec![0; (width * height) * CHANNELS],
}
}
}
// #[inline]
// pub fn clear(&mut self) {
// self.data = vec![0; (self.width * self.height) as usize * CHANNELS]
// }
impl PixelBuffer for SoftFramebuffer {
#[inline]
pub const fn width(&self) -> u32 {
fn width(&self) -> usize {
self.width
}
#[inline]
pub const fn height(&self) -> u32 {
fn height(&self) -> usize {
self.height
}
/// Set this pixel at X|Y to RGBA colour
#[inline]
pub fn set_pixel(&mut self, x: usize, y: usize, r: u8, g: u8, b: u8, _a: u8) {
fn clear(&mut self) {
self.buffer.iter_mut().for_each(|n| *n = 0);
}
#[inline]
fn set_pixel(&mut self, x: usize, y: usize, rgba: (u8, u8, u8, u8)) {
// Shitty safeguard. Need to find actual cause of fail
if x >= self.width as usize || y >= self.height as usize {
if x >= self.width || y >= self.height {
return;
}
let pos = y * (self.width as usize * CHANNELS) + x * CHANNELS;
self.software_buffer[pos] = r;
self.software_buffer[pos + 1] = g;
self.software_buffer[pos + 2] = b;
let pos = y * (self.width * CHANNELS) + x * CHANNELS;
self.buffer[pos] = rgba.0;
self.buffer[pos + 1] = rgba.1;
self.buffer[pos + 2] = rgba.2;
self.buffer[pos + 3] = rgba.3;
}
/// Read the colour of a single pixel at X|Y
pub fn read_pixel(&self, x: usize, y: usize) -> (u8, u8, u8, u8) {
let pos = y * (self.width as usize * CHANNELS) + x * CHANNELS;
#[inline]
fn read_softbuf_pixel(&self, x: usize, y: usize) -> (u8, u8, u8, u8) {
let pos = y * (self.width * CHANNELS) + x * CHANNELS;
(
self.software_buffer[pos],
self.software_buffer[pos + 1],
self.software_buffer[pos + 2],
0,
self.buffer[pos],
self.buffer[pos + 1],
self.buffer[pos + 2],
self.buffer[pos + 3],
)
}
pub fn is_software(&self) -> bool {
self.is_software
/// Read the full buffer
#[inline]
fn read_softbuf_pixels(&mut self) -> &mut [u8] {
&mut self.buffer
}
}
/// A structure holding display data
pub struct SoftOpenGL {
width: usize,
height: usize,
frame_buffer: SoftFramebuffer,
gl_texture: Texture,
}
impl SoftOpenGL {
fn new(width: usize, height: usize, ctx: &Context) -> Self {
let mut gl_texture = Texture::new(ctx).unwrap();
gl_texture.set_image(None, width as u32, height as u32, golem::ColorFormat::RGB);
Self {
width,
height,
frame_buffer: SoftFramebuffer::new(width, height),
gl_texture,
}
}
// /// Get the array of pixels. The layout of which is [Row<RGBA>]
pub fn read_pixels(&self) -> &[u8] {
&self.software_buffer
#[inline]
pub fn frame_buffer(&mut self) -> &mut SoftFramebuffer {
&mut self.frame_buffer
}
pub fn read_pixels_mut(&mut self) -> &mut [u8] {
&mut self.software_buffer
#[inline]
pub const fn gl_texture(&self) -> &Texture {
&self.gl_texture
}
pub fn set_gl_filter(&self) -> Result<(), GolemError> {
self.gl_texture.set_minification(TextureFilter::Linear)?;
self.gl_texture.set_magnification(TextureFilter::Linear)
}
pub fn copy_softbuf_to_gl_texture(&mut self) {
self.gl_texture.set_image(
Some(&self.frame_buffer.buffer),
self.width as u32,
self.height as u32,
ColorFormat::RGBA,
);
}
}
impl PixelBuffer for SoftOpenGL {
#[inline]
fn width(&self) -> usize {
self.frame_buffer.width
}
#[inline]
fn height(&self) -> usize {
self.frame_buffer.height
}
#[inline]
fn clear(&mut self) {
self.frame_buffer.clear();
}
#[inline]
fn set_pixel(&mut self, x: usize, y: usize, rgba: (u8, u8, u8, u8)) {
self.frame_buffer.set_pixel(x, y, rgba);
}
/// Read the colour of a single pixel at X|Y
#[inline]
fn read_softbuf_pixel(&self, x: usize, y: usize) -> (u8, u8, u8, u8) {
self.frame_buffer.read_softbuf_pixel(x, y)
}
/// Read the full buffer
#[inline]
fn read_softbuf_pixels(&mut self) -> &mut [u8] {
&mut self.frame_buffer.buffer
}
}
/// A structure holding display data
pub struct RenderTarget {
width: usize,
height: usize,
render_type: RenderType,
/// Total length is width * height * CHANNELS, where CHANNELS is RGB bytes
software: Option<SoftFramebuffer>,
soft_opengl: Option<SoftOpenGL>,
}
impl RenderTarget {
pub fn new(width: usize, height: usize, ctx: &Context, render_type: RenderType) -> Self {
let mut software = None;
let mut soft_opengl = None;
match render_type {
RenderType::Software => software = Some(SoftFramebuffer::new(width, height)),
RenderType::SoftOpenGL => soft_opengl = Some(SoftOpenGL::new(width, height, ctx)),
RenderType::OpenGL => todo!(),
RenderType::Vulkan => todo!(),
}
Self {
width,
height,
render_type,
software,
soft_opengl,
}
}
#[inline]
pub fn width(&self) -> usize {
self.width
}
#[inline]
pub fn height(&self) -> usize {
self.height
}
#[inline]
pub fn render_type(&self) -> RenderType {
self.render_type
}
#[inline]
pub unsafe fn software(&mut self) -> Option<&mut SoftFramebuffer> {
self.software.as_mut()
}
#[inline]
pub unsafe fn software_unchecked(&mut self) -> &mut SoftFramebuffer {
self.software.as_mut().unwrap_unchecked()
}
#[inline]
pub unsafe fn soft_opengl(&mut self) -> Option<&mut SoftOpenGL> {
self.soft_opengl.as_mut()
}
#[inline]
pub unsafe fn soft_opengl_unchecked(&mut self) -> &mut SoftOpenGL {
self.soft_opengl.as_mut().unwrap_unchecked()
}
// // /// Get the array of pixels. The layout of which is [Row<RGBA>]
// pub fn softbuf_pixels(&self) -> &[u8] {
// &self.software
// }
// pub fn softbuf_pixels_mut(&mut self) -> &mut [u8] {
// &mut self.software
// }
}
pub trait PlayRenderer {
/// Drawing the full player view to the `PixelBuf`.
///
/// Doom function name `R_RenderPlayerView`
fn render_player_view(&mut self, player: &Player, level: &Level, buf: &mut PixelBuf);
fn render_player_view(&mut self, player: &Player, level: &Level, buf: &mut RenderTarget);
}
#[cfg(test)]
mod tests {
use crate::PixelBuf;
// TODO: somehow test with gl context
// #[cfg(test)]
// mod tests {
// use crate::PixelBuf;
#[test]
fn write_read_pixel() {
let mut pixels = PixelBuf::new(320, 200, true);
// #[test]
// fn write_read_pixel() {
// let mut pixels = PixelBuf::new(320, 200, true);
pixels.set_pixel(10, 10, 255, 10, 3, 255);
pixels.set_pixel(319, 199, 25, 10, 3, 255);
// pixels.set_pixel(10, 10, 255, 10, 3, 255);
// pixels.set_pixel(319, 199, 25, 10, 3, 255);
let px = pixels.read_pixel(10, 10);
assert_eq!(px, (255, 10, 3, 0));
// let px = pixels.read_pixel(10, 10);
// assert_eq!(px, (255, 10, 3, 0));
let px = pixels.read_pixel(319, 199);
assert_eq!(px, (25, 10, 3, 0));
}
}
// let px = pixels.read_pixel(319, 199);
// assert_eq!(px, (25, 10, 3, 0));
// }
// }

View File

@ -8,10 +8,8 @@ build = "../../build.rs"
[features]
[dependencies]
wad = { path = "../../wad" }
sound-traits = { path = "../traits" }
# versions are set by game-exe crate
log = "*"
glam = "*"
# Required for config options
serde = { version = "*", features = ["serde_derive"] }
wad.workspace = true
sound-traits.workspace = true
log.workspace = true
glam.workspace = true
serde.workspace = true

View File

@ -4,9 +4,9 @@
use faces::DoomguyFace;
use gamestate_traits::{
util::{draw_num, get_num_sprites, get_st_key_sprites},
AmmoType, GameMode, GameTraits, MachinationTrait, PixelBuf, PlayerStatus, Scancode, WeaponType,
WEAPON_INFO,
util::{draw_num_pixels, get_num_sprites, get_st_key_sprites},
AmmoType, GameMode, GameTraits, MachinationTrait, PixelBuffer, PlayerStatus, RenderTarget,
Scancode, WeaponType, WEAPON_INFO,
};
use std::collections::HashMap;
use wad::{
@ -64,8 +64,8 @@ impl Statusbar {
.unwrap_or_else(|| panic!("{name} not in cache"))
}
fn draw_health(&self, big: bool, face: bool, buffer: &mut PixelBuf) {
let f = (buffer.height() / 200) as i32;
fn draw_health_pixels(&self, big: bool, face: bool, pixels: &mut impl PixelBuffer) {
let f = (pixels.height() / 200) as i32;
let nums = if big { &self.big_nums } else { &self.lil_nums };
@ -94,14 +94,14 @@ impl Statusbar {
if h < 10 {
x -= nums[0].width as i32;
}
draw_num(h, x, self.screen_height - 2 - y, 0, nums, self, buffer);
draw_num_pixels(h, x, self.screen_height - 2 - y, 0, nums, self, pixels);
}
fn draw_armour(&self, face: bool, buffer: &mut PixelBuf) {
fn draw_armour_pixels(&self, face: bool, pixels: &mut impl PixelBuffer) {
if self.status.armorpoints <= 0 {
return;
}
let f = (buffer.height() / 200) as i32;
let f = (pixels.height() / 200) as i32;
let nums = &self.lil_nums;
@ -120,10 +120,10 @@ impl Statusbar {
if h < 10 {
x -= nums[0].width as i32;
}
draw_num(h, x, self.screen_height - 2 - y, 0, nums, self, buffer);
draw_num_pixels(h, x, self.screen_height - 2 - y, 0, nums, self, pixels);
}
fn draw_ammo_big(&self, buffer: &mut PixelBuf) {
fn draw_ammo_big_pixels(&self, pixels: &mut impl PixelBuffer) {
if matches!(self.status.readyweapon, WeaponType::NoChange) {
return;
}
@ -137,24 +137,24 @@ impl Statusbar {
if ammo == AmmoType::NoAmmo {
return;
}
let f = (buffer.height() / 200) as i32;
let f = (pixels.height() / 200) as i32;
let height = self.big_nums[0].height as i32 * f;
let start_x = self.big_nums[0].width as i32 * f + self.keys[0].width as i32 * f + 2;
let ammo = self.status.ammo[ammo as usize];
draw_num(
draw_num_pixels(
ammo,
self.screen_width - start_x,
self.screen_height - 2 - height - self.grey_nums[0].height as i32 * f,
0,
&self.big_nums,
self,
buffer,
pixels,
);
}
fn draw_keys(&self, buffer: &mut PixelBuf) {
let f = (buffer.height() / 200) as i32;
fn draw_keys_pixels(&self, pixels: &mut impl PixelBuffer) {
let f = (pixels.height() / 200) as i32;
let height = self.keys[3].height as i32 * f;
let width = self.keys[0].width as i32 * f;
@ -176,17 +176,17 @@ impl Statusbar {
} else {
pad = -3;
}
self.draw_patch(
self.draw_patch_pixels(
patch,
x,
start_y - pad - height * i as i32 - i as i32,
buffer,
pixels,
);
}
}
fn draw_weapons(&self, buffer: &mut PixelBuf) {
let f = (buffer.height() / 200) as i32;
fn draw_weapons_pixels(&self, pixels: &mut impl PixelBuffer) {
let f = (pixels.height() / 200) as i32;
let y = self.grey_nums[0].height as i32 * f;
let x = self.grey_nums[0].width as i32 * f;
let mult = if self.mode == GameMode::Commercial {
@ -209,20 +209,20 @@ impl Statusbar {
} else {
&self.grey_nums
};
draw_num(
draw_num_pixels(
i as u32 + 1,
start_x + x * i as i32 + i as i32,
start_y,
0,
nums,
self,
buffer,
pixels,
);
}
}
fn draw_face(&self, mut big: bool, upper: bool, buffer: &mut PixelBuf) {
let f = (buffer.height() / 200) as i32;
fn draw_face_pixels(&self, mut big: bool, upper: bool, pixels: &mut impl PixelBuffer) {
let f = (pixels.height() / 200) as i32;
if upper {
big = true;
}
@ -237,7 +237,7 @@ impl Statusbar {
self.screen_height - patch.height as i32
};
x = self.screen_width / 2 - patch.width as i32 / 2;
self.draw_patch(patch, x, y, buffer);
self.draw_patch_pixels(patch, x, y, pixels);
};
let patch = self.faces.get_face();
@ -255,7 +255,7 @@ impl Statusbar {
x = offset_x;
y = self.screen_height - offset_y
};
self.draw_patch(patch, x, y, buffer);
self.draw_patch_pixels(patch, x, y, pixels);
}
}
@ -276,18 +276,36 @@ impl MachinationTrait for Statusbar {
&self.palette
}
fn draw(&mut self, buffer: &mut PixelBuf) {
fn draw(&mut self, buffer: &mut RenderTarget) {
self.screen_width = buffer.width() as i32;
self.screen_height = buffer.height() as i32;
let face = true;
if face {
self.draw_face(false, false, buffer);
match buffer.render_type() {
gamestate_traits::RenderType::Software => {
let pixels = unsafe { buffer.software_unchecked() };
if face {
self.draw_face_pixels(false, false, pixels);
}
self.draw_health_pixels(true, face, pixels);
self.draw_armour_pixels(face, pixels);
self.draw_ammo_big_pixels(pixels);
self.draw_weapons_pixels(pixels);
self.draw_keys_pixels(pixels);
}
gamestate_traits::RenderType::SoftOpenGL => {
let pixels = unsafe { buffer.soft_opengl_unchecked() };
if face {
self.draw_face_pixels(false, false, pixels);
}
self.draw_health_pixels(true, face, pixels);
self.draw_armour_pixels(face, pixels);
self.draw_ammo_big_pixels(pixels);
self.draw_weapons_pixels(pixels);
self.draw_keys_pixels(pixels);
}
gamestate_traits::RenderType::OpenGL => todo!(),
gamestate_traits::RenderType::Vulkan => todo!(),
}
self.draw_health(true, face, buffer);
self.draw_armour(face, buffer);
self.draw_ammo_big(buffer);
self.draw_weapons(buffer);
self.draw_keys(buffer);
}
}