Compare commits
3 Commits
feature/im
...
feature/im
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
313fb3c577 | ||
|
|
dad59e261d | ||
|
|
93a3c8548e |
@@ -1,7 +1,12 @@
|
|||||||
# RustyDoom
|
# RustyDoom
|
||||||
|
|
||||||
A Rust version of the original DOOM from the offically released source code found at [https://github.com/id-software/doom](https://github.com/id-software/doom)
|
A Rust version of the original DOOM base on the offically released source code found at [https://github.com/id-software/doom](https://github.com/id-software/doom)
|
||||||
|
|
||||||
This is more of a learning exercise than something good!
|
This is more of a learning exercise than something good! For example, the original `z_zone.c` and `z_zone.h` will be more "Rust-like" compared to the original version.
|
||||||
|
|
||||||
This repository will only contain the SHAREWARE verison of the original DOOM1, but will be able to run both IWAD and PWADS.
|
This repository will only contain the SHAREWARE verison of the original DOOM1, but will be able to run both IWAD and PWADS.
|
||||||
|
|
||||||
|
This repository will be done in two parts before a "release"
|
||||||
|
|
||||||
|
1. An initial port keeping as close to the original C code as possible, which some minor modifications where it is clear on how to convert the code to Rust
|
||||||
|
2. Rust-ify any (hopefully all) parts of the code, that is removing `unsafe` and other such things. This will also include breaking down some of the `uber` sized funtions of the original source into more managable bits
|
||||||
|
|||||||
@@ -8,10 +8,13 @@ use crate::m_argv;
|
|||||||
use crate::m_argv::M_CheckParm;
|
use crate::m_argv::M_CheckParm;
|
||||||
use crate::m_argv::M_GetOptionalArgumentValueByArgument;
|
use crate::m_argv::M_GetOptionalArgumentValueByArgument;
|
||||||
use crate::m_argv::PARM_NOT_FOUND;
|
use crate::m_argv::PARM_NOT_FOUND;
|
||||||
|
use crate::m_menu::M_Init;
|
||||||
use crate::m_misc::M_LoadDefaults;
|
use crate::m_misc::M_LoadDefaults;
|
||||||
|
|
||||||
|
use crate::renderer::R_Init;
|
||||||
use crate::v_video::V_Init;
|
use crate::v_video::V_Init;
|
||||||
use crate::w_wad::{W_CheckNumForName, W_InitMultipleFiles};
|
use crate::w_wad::{W_CheckNumForName, W_InitMultipleFiles};
|
||||||
|
use crate::z_zone::Z_Init;
|
||||||
|
|
||||||
use crate::doomdef::{
|
use crate::doomdef::{
|
||||||
VERSION,
|
VERSION,
|
||||||
@@ -408,11 +411,9 @@ pub fn D_DoomMain() {
|
|||||||
println!("M_LoadDefaults: Load system defaults.");
|
println!("M_LoadDefaults: Load system defaults.");
|
||||||
M_LoadDefaults();
|
M_LoadDefaults();
|
||||||
|
|
||||||
/* TODO: Implement subsytem inits
|
|
||||||
|
|
||||||
println!("Z_init: Init zone memory allocation daemon.");
|
println!("Z_init: Init zone memory allocation daemon.");
|
||||||
Z_Init();
|
Z_Init();
|
||||||
*/
|
|
||||||
println!("W_Init: Init WADfiles");
|
println!("W_Init: Init WADfiles");
|
||||||
W_InitMultipleFiles(DOOMGLOBALS::with_ref(|g| g.wadfiles.clone())); // we are loading so how cares about a borrow
|
W_InitMultipleFiles(DOOMGLOBALS::with_ref(|g| g.wadfiles.clone())); // we are loading so how cares about a borrow
|
||||||
|
|
||||||
@@ -467,14 +468,15 @@ pub fn D_DoomMain() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Implement other inits
|
|
||||||
/*
|
|
||||||
println!("M_Init: Init miscellaneous info.");
|
println!("M_Init: Init miscellaneous info.");
|
||||||
M_Init();
|
M_Init();
|
||||||
|
|
||||||
println!("R_Init: Init DOOM refresh daemon - ")
|
println!("R_Init: Init DOOM refresh daemon - ");
|
||||||
R_Init();
|
R_Init();
|
||||||
|
|
||||||
|
// TODO: Implement other inits
|
||||||
|
/*
|
||||||
|
|
||||||
println!("P_Init: Init Playloop state.");
|
println!("P_Init: Init Playloop state.");
|
||||||
P_Init();
|
P_Init();
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
const MAXCHAR: i8 = i8::MAX;
|
pub const MAXCHAR: i8 = i8::MAX;
|
||||||
const MAXSHORT: i16 = i16::MAX;
|
pub const MAXSHORT: i16 = i16::MAX;
|
||||||
const MAXINT: i32 = i32::MAX;
|
pub const MAXINT: i32 = i32::MAX;
|
||||||
const MAXLONG: i32 = i32::MAX;
|
pub const MAXLONG: i32 = i32::MAX;
|
||||||
|
|
||||||
// Minimum values
|
// Minimum values
|
||||||
const MINCHAR: i8 = i8::MIN;
|
pub const MINCHAR: i8 = i8::MIN;
|
||||||
const MINSHORT: i16 = i16::MIN;
|
pub const MINSHORT: i16 = i16::MIN;
|
||||||
const MININT: i32 = i32::MIN;
|
pub const MININT: i32 = i32::MIN;
|
||||||
const MINLONG: i32 = i32::MIN;
|
pub const MINLONG: i32 = i32::MIN;
|
||||||
323
src/m_menu/mod.rs
Normal file
323
src/m_menu/mod.rs
Normal file
@@ -0,0 +1,323 @@
|
|||||||
|
// Menu Widget stuff i.e episode selection etc.
|
||||||
|
|
||||||
|
// TODO: Implement d_event.handle
|
||||||
|
|
||||||
|
use std::ptr::addr_of_mut;
|
||||||
|
|
||||||
|
use crate::doomdef::{DOOMGLOBALS, GameMode};
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
struct menuitem_t {
|
||||||
|
status: i32,
|
||||||
|
name: [u8; 10],
|
||||||
|
routine: Option<fn(choice: i32)>,
|
||||||
|
|
||||||
|
// hotkey in menu
|
||||||
|
alpha_key: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
struct menu_t {
|
||||||
|
|
||||||
|
/// Number of items in the menu
|
||||||
|
numitems: i32,
|
||||||
|
|
||||||
|
/// Pointer to the previous menu
|
||||||
|
prevMenu: *mut menu_t,
|
||||||
|
|
||||||
|
/// The menu items
|
||||||
|
menuItems: *mut menuitem_t,
|
||||||
|
|
||||||
|
/// draw routine
|
||||||
|
routine: Option<fn()>,
|
||||||
|
|
||||||
|
/// X of the menu
|
||||||
|
x: i32,
|
||||||
|
|
||||||
|
/// y of the menu
|
||||||
|
y: i32,
|
||||||
|
|
||||||
|
/// The last item the user was on in the menu
|
||||||
|
lastOn: i32
|
||||||
|
}
|
||||||
|
|
||||||
|
/// TEMP function
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn VoidRoutine() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn VoidRoutineWithChoice(choice: i32) {
|
||||||
|
let _ = choice;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Main Menu
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
mod MainMenu {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// Enumeration of the main menu options
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
pub(super) enum main_e {
|
||||||
|
newgame = 0,
|
||||||
|
options,
|
||||||
|
loadgame,
|
||||||
|
savegame,
|
||||||
|
readthis,
|
||||||
|
quitdoom,
|
||||||
|
main_end
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Main Menu items
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
pub(super) static mut MainMenu: [menuitem_t; 6] = [
|
||||||
|
menuitem_t {
|
||||||
|
status: 1,
|
||||||
|
name: *b"M_NGAME\0\0\0",
|
||||||
|
routine: Some(VoidRoutineWithChoice),
|
||||||
|
alpha_key: b'n'
|
||||||
|
},
|
||||||
|
menuitem_t {
|
||||||
|
status: 1,
|
||||||
|
name: *b"M_OPTION\0\0",
|
||||||
|
routine: Some(VoidRoutineWithChoice),
|
||||||
|
alpha_key: b'o'
|
||||||
|
},
|
||||||
|
menuitem_t {
|
||||||
|
status: 1,
|
||||||
|
name: *b"M_LOADG\0\0\0",
|
||||||
|
routine: Some(VoidRoutineWithChoice),
|
||||||
|
alpha_key: b'l'
|
||||||
|
},
|
||||||
|
menuitem_t {
|
||||||
|
status: 1,
|
||||||
|
name: *b"M_SAVEG\0\0\0",
|
||||||
|
routine: Some(VoidRoutineWithChoice),
|
||||||
|
alpha_key: b's'
|
||||||
|
},
|
||||||
|
menuitem_t {
|
||||||
|
status: 1,
|
||||||
|
name: *b"M_RDTHIS\0\0",
|
||||||
|
routine: Some(VoidRoutineWithChoice),
|
||||||
|
alpha_key: b'r'
|
||||||
|
},
|
||||||
|
menuitem_t {
|
||||||
|
status: 1,
|
||||||
|
name: *b"M_QUITG\0\0\0",
|
||||||
|
routine: Some(VoidRoutineWithChoice),
|
||||||
|
alpha_key: b'q'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
/// Default MainMenu
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
pub(super) static mut MainDef: menu_t = menu_t {
|
||||||
|
numitems: main_e::main_end as i32,
|
||||||
|
prevMenu: std::ptr::null_mut(),
|
||||||
|
menuItems: &raw mut MainMenu as *mut menuitem_t, // Use &raw mut instead of .as_mut_ptr() to avoid illegal static mut references (Feels dirty)
|
||||||
|
routine: Some(VoidRoutine),
|
||||||
|
x: 97,
|
||||||
|
y: 64,
|
||||||
|
lastOn: 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// New Game menu
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
mod NewGameMenu {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// Enumeration of the New Game options
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
pub(super) enum newgame_e {
|
||||||
|
killthings,
|
||||||
|
toorough,
|
||||||
|
hurtme,
|
||||||
|
violence,
|
||||||
|
nightmare,
|
||||||
|
newg_end
|
||||||
|
}
|
||||||
|
|
||||||
|
/// New Game Menu items
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
pub(super) static mut NewGameMenu: [menuitem_t; 5] = [
|
||||||
|
menuitem_t {
|
||||||
|
status: 1,
|
||||||
|
name: *b"M_JKILL\0\0\0",
|
||||||
|
routine: Some(VoidRoutineWithChoice),
|
||||||
|
alpha_key: b'i'
|
||||||
|
},
|
||||||
|
menuitem_t {
|
||||||
|
status: 1,
|
||||||
|
name: *b"M_ROUGH\0\0\0",
|
||||||
|
routine: Some(VoidRoutineWithChoice),
|
||||||
|
alpha_key: b'h'
|
||||||
|
},
|
||||||
|
menuitem_t {
|
||||||
|
status: 1,
|
||||||
|
name: *b"M_HURT\0\0\0\0",
|
||||||
|
routine: Some(VoidRoutineWithChoice),
|
||||||
|
alpha_key: b'h'
|
||||||
|
},
|
||||||
|
menuitem_t {
|
||||||
|
status: 1,
|
||||||
|
name: *b"M_ULTRA\0\0\0",
|
||||||
|
routine: Some(VoidRoutineWithChoice),
|
||||||
|
alpha_key: b'u'
|
||||||
|
},
|
||||||
|
menuitem_t {
|
||||||
|
status: 1,
|
||||||
|
name: *b"M_NMARE\0\0\0",
|
||||||
|
routine: Some(VoidRoutineWithChoice),
|
||||||
|
alpha_key: b'n'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
pub(super) static mut NewDef: menu_t = menu_t {
|
||||||
|
numitems: newgame_e::newg_end as i32,
|
||||||
|
prevMenu: std::ptr::null_mut(), // TODO: Add EpiDef
|
||||||
|
menuItems: &raw mut NewGameMenu as *mut menuitem_t, // Use &raw mut instead of .as_mut_ptr() to avoid illegal static mut references (Feels dirty)
|
||||||
|
routine: Some(VoidRoutine),
|
||||||
|
x: 48,
|
||||||
|
y: 63,
|
||||||
|
lastOn: newgame_e::hurtme as i32,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: add OptionsMenu
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
mod ReadThis1 {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// Enumeration of the main menu options
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
pub(super) enum read_e {
|
||||||
|
rdthisempty1 = 0,
|
||||||
|
read1_end,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read1 Menu items
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
pub(super) static mut ReadMenu1: [menuitem_t; 1] = [
|
||||||
|
menuitem_t {
|
||||||
|
status: 1,
|
||||||
|
name: *b"\0\0\0\0\0\0\0\0\0\0",
|
||||||
|
routine: Some(VoidRoutineWithChoice),
|
||||||
|
alpha_key: b'0'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
/// Default MainMenu
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
pub(super) static mut Read1Def: menu_t = menu_t {
|
||||||
|
numitems: read_e::rdthisempty1 as i32,
|
||||||
|
prevMenu: addr_of_mut!(MainMenu::MainDef),
|
||||||
|
menuItems: &raw mut ReadMenu1 as *mut menuitem_t, // Use &raw mut instead of .as_mut_ptr() to avoid illegal static mut references (Feels dirty)
|
||||||
|
routine: Some(VoidRoutine),
|
||||||
|
x: 280,
|
||||||
|
y: 185,
|
||||||
|
lastOn: 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: The rest of this hell!
|
||||||
|
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
static mut currentMenu: *mut menu_t = std::ptr::null_mut();
|
||||||
|
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
static mut menuactive: bool = false;
|
||||||
|
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
static mut itemOn: i32 = 0;
|
||||||
|
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
static mut whichSkull: i32 = 0;
|
||||||
|
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
static mut skullAnimCounter: i32 = 0;
|
||||||
|
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
pub(crate) static mut detailLevel: i32 = 0;
|
||||||
|
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
pub static mut screenSize: i32 = 0;
|
||||||
|
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
pub(crate) static mut screenBlocks: i32 = 0;
|
||||||
|
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
static mut messageToPrint: i32 = 0;
|
||||||
|
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
static mut messageString: *mut u8 = std::ptr::null_mut();
|
||||||
|
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
static mut messageLastMenuActive: i32 = 0;
|
||||||
|
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
static mut quickSaveSlot: i32 = -1;
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub fn M_Ticket() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub fn M_Drawer() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub fn M_Init() {
|
||||||
|
unsafe {
|
||||||
|
currentMenu = addr_of_mut!(MainMenu::MainDef);
|
||||||
|
menuactive = false;
|
||||||
|
itemOn = (*currentMenu).lastOn;
|
||||||
|
whichSkull = 0;
|
||||||
|
skullAnimCounter = 10;
|
||||||
|
screenSize = screenBlocks - 3;
|
||||||
|
messageToPrint = 0;
|
||||||
|
messageString = std::ptr::null_mut();
|
||||||
|
messageLastMenuActive = menuactive as i32;
|
||||||
|
quickSaveSlot = -1;
|
||||||
|
|
||||||
|
|
||||||
|
match DOOMGLOBALS::with_ref(|g| g.gamemode) {
|
||||||
|
GameMode::Commercial => {
|
||||||
|
/*
|
||||||
|
Original source code comment:
|
||||||
|
// This is used because DOOM 2 had only one HELP
|
||||||
|
// page. I use CREDIT as second page now, but
|
||||||
|
// kept this hack for educational purposes.
|
||||||
|
*/
|
||||||
|
MainMenu::MainMenu[MainMenu::main_e::readthis as usize] = MainMenu::MainMenu[MainMenu::main_e::quitdoom as usize];
|
||||||
|
MainMenu::MainDef.numitems -=1;
|
||||||
|
MainMenu::MainDef.y += 8;
|
||||||
|
NewGameMenu::NewDef.prevMenu = addr_of_mut!(MainMenu::MainDef);
|
||||||
|
ReadThis1::Read1Def.routine = Some(VoidRoutine);
|
||||||
|
ReadThis1::Read1Def.x = 330;
|
||||||
|
ReadThis1::Read1Def.y = 165;
|
||||||
|
ReadThis1::ReadMenu1[ReadThis1::read_e::rdthisempty1 as usize].routine = Some(VoidRoutineWithChoice);
|
||||||
|
},
|
||||||
|
GameMode::Shareware | GameMode::Registered => {
|
||||||
|
// TODO EpiDef.numitems--
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// do nothing :)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub fn M_StartControlPanel() {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -4,9 +4,12 @@ mod doomdef;
|
|||||||
mod doomtype;
|
mod doomtype;
|
||||||
mod hu_stuff;
|
mod hu_stuff;
|
||||||
mod m_argv;
|
mod m_argv;
|
||||||
|
mod m_menu;
|
||||||
mod m_misc;
|
mod m_misc;
|
||||||
|
mod renderer;
|
||||||
mod v_video;
|
mod v_video;
|
||||||
mod w_wad;
|
mod w_wad;
|
||||||
|
mod z_zone;
|
||||||
mod math;
|
mod math;
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,46 +2,63 @@
|
|||||||
|
|
||||||
This provides fixed-point arithmetic as 32-bit 16.16
|
This provides fixed-point arithmetic as 32-bit 16.16
|
||||||
*/
|
*/
|
||||||
|
use std::ops::ShrAssign;
|
||||||
|
|
||||||
|
use crate::doomtype::{
|
||||||
|
MININT,
|
||||||
|
MAXINT
|
||||||
|
};
|
||||||
|
|
||||||
pub const FRACBITS: i32 = 16;
|
pub const FRACBITS: i32 = 16;
|
||||||
pub const FRACUNIT: i32 = 1 << FRACBITS;
|
pub const FRACUNIT: i32 = 1 << FRACBITS;
|
||||||
|
|
||||||
pub const MININT: i32 = 0x80000000;
|
|
||||||
pub const MAXINT: i32 = 0x7fffffff;
|
|
||||||
/// Struct representing a fixed point 32-bit value
|
/// Struct representing a fixed point 32-bit value
|
||||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||||
pub struct FixedPoint {
|
#[allow(non_camel_case_types)]
|
||||||
|
pub struct fixed_t {
|
||||||
pub value: i32,
|
pub value: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FixedPoint {
|
impl fixed_t {
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
pub const fn new(val: i32) -> Self {
|
||||||
pub fn FixedMul(a: FixedPoint, b: FixedPoint) -> FixedPoint {
|
fixed_t { value: val }
|
||||||
let value = ((a.value as u64) * (b.value as u64) >> FRACBITS) as i32;
|
|
||||||
FixedPoint{value}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
pub fn FixedDiv(a: FixedPoint, b: FixedPoint) -> FixedPoint {
|
pub fn FixedMul(a: fixed_t, b: fixed_t) -> fixed_t {
|
||||||
|
let value = ((a.value as u64) * (b.value as u64) >> FRACBITS) as i32;
|
||||||
|
fixed_t{value}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub fn FixedDiv(a: fixed_t, b: fixed_t) -> fixed_t {
|
||||||
if (i32::abs(a.value) >> 14) >= i32::abs(b.value) {
|
if (i32::abs(a.value) >> 14) >= i32::abs(b.value) {
|
||||||
if a.value^b.value == 0 {
|
if a.value^b.value == 0 {
|
||||||
FixedPoint{value: MININT};
|
fixed_t{value: MININT};
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
FixedPoint{value: MAXINT};
|
fixed_t{value: MAXINT};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
FixedPoint::FixedDiv2(a,b)
|
fixed_t::FixedDiv2(a,b)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
pub fn FixedDiv2(a: FixedPoint, b: FixedPoint) -> FixedPoint {
|
pub fn FixedDiv2(a: fixed_t, b: fixed_t) -> fixed_t {
|
||||||
let c: f64 = ((a.value as f64) / (b.value as f64)) * FRACUNIT as f64;
|
let c: f64 = ((a.value as f64) / (b.value as f64)) * FRACUNIT as f64;
|
||||||
if c >= 2147483648.0 || c < -2147483648.0 {
|
if c >= 2147483648.0 || c < -2147483648.0 {
|
||||||
panic!("FixedDiv: divide by zero");
|
panic!("FixedDiv: divide by zero");
|
||||||
}
|
}
|
||||||
FixedPoint { value: c as i32 }
|
fixed_t { value: c as i32 }
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ShrAssign<i32> for fixed_t {
|
||||||
|
fn shr_assign(&mut self, rhs: i32) {
|
||||||
|
// Shift the underlying wrapped i32 value directly
|
||||||
|
self.value >>= rhs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
// pub mod m_fixed;
|
mod m_fixed;
|
||||||
|
|
||||||
// pub use m_fixed::FixedPoint;
|
pub use m_fixed::fixed_t;
|
||||||
|
pub use m_fixed::FRACUNIT;
|
||||||
|
pub use m_fixed::FRACBITS;
|
||||||
130
src/renderer/mod.rs
Normal file
130
src/renderer/mod.rs
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
// Main entry to Render stuff
|
||||||
|
// i.e r_main c/h
|
||||||
|
mod r_data;
|
||||||
|
mod r_defs;
|
||||||
|
mod r_plane;
|
||||||
|
|
||||||
|
|
||||||
|
use crate::doomdef::SCREENWIDTH;
|
||||||
|
use crate::math::{fixed_t, FRACUNIT};
|
||||||
|
use crate::m_menu::{detailLevel, screenBlocks};
|
||||||
|
use r_data::R_InitData;
|
||||||
|
use r_defs::lighttable_t;
|
||||||
|
use r_plane::R_InitPlanes;
|
||||||
|
|
||||||
|
// Light constants
|
||||||
|
// Orignal source comment: Now why not 32 levels here?
|
||||||
|
pub const LIGHTLEVELS: i32 = 16;
|
||||||
|
pub const MAXLIGHTSCALE: i32 = 48;
|
||||||
|
pub const LIGHTSCALESHIFT: i32 = 12;
|
||||||
|
pub const MAXLIGHTZ: i32 = 128;
|
||||||
|
pub const LIGHTZSHIFT: i32 = 20;
|
||||||
|
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
pub static mut scalelight: [[*mut lighttable_t; MAXLIGHTSCALE as usize]; LIGHTLEVELS as usize] =
|
||||||
|
[[std::ptr::null_mut(); MAXLIGHTSCALE as usize]; LIGHTLEVELS as usize];
|
||||||
|
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
pub static mut scalelightfixed: [*mut lighttable_t; MAXLIGHTSCALE as usize] =
|
||||||
|
[std::ptr::null_mut(); MAXLIGHTSCALE as usize];
|
||||||
|
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
pub static mut zlight: [[*mut lighttable_t; MAXLIGHTZ as usize]; LIGHTLEVELS as usize] =
|
||||||
|
[[std::ptr::null_mut(); MAXLIGHTZ as usize]; LIGHTLEVELS as usize];
|
||||||
|
|
||||||
|
/// R_InitLightTables
|
||||||
|
/// Only inits the zlight table,
|
||||||
|
/// because the scalelight table changes with view size.
|
||||||
|
pub const DISTMAP:i32 = 2;
|
||||||
|
|
||||||
|
|
||||||
|
/// Number of diminishing brightness levels.
|
||||||
|
/// There a 0-31, i.e. 32 LUT in the COLORMAP lump.
|
||||||
|
pub const NUMCOLORMAPS: i32 = 32;
|
||||||
|
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
static mut viewcos: fixed_t = fixed_t{value: 0};
|
||||||
|
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
static mut viewsin: fixed_t = fixed_t{value: 0};
|
||||||
|
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
static mut setsizeneeded: bool = false;
|
||||||
|
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
static mut setblocks: i32 = 0;
|
||||||
|
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
static mut setdetail: i32 = 0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn R_InitPointToAngle() {
|
||||||
|
// this is unused in the original source
|
||||||
|
// we get this data from tables
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn R_InitTables() {
|
||||||
|
// this is unused in the original source
|
||||||
|
// we get this data from tables
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn R_InitLightTables() {
|
||||||
|
let mut startmap: i32;
|
||||||
|
let mut scale: fixed_t;
|
||||||
|
let mut level: i32;
|
||||||
|
for i in 0..(LIGHTLEVELS as usize) {
|
||||||
|
startmap = ((LIGHTLEVELS - 1 - (i as i32)) * 2) * NUMCOLORMAPS/LIGHTLEVELS;
|
||||||
|
for j in 0..(MAXLIGHTZ as usize) {
|
||||||
|
scale = fixed_t::FixedDiv(fixed_t{value: (SCREENWIDTH/2*FRACUNIT)}, fixed_t{value: ((j as i32) + 1) << LIGHTZSHIFT});
|
||||||
|
scale >>= LIGHTSCALESHIFT;
|
||||||
|
|
||||||
|
level = startmap - scale.value/DISTMAP;
|
||||||
|
level = level.clamp(0, NUMCOLORMAPS - 1);
|
||||||
|
|
||||||
|
// zlight[i][j] = colormaps + level * 256; //TODO: Implement zlight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* // TODO: Implement player_t
|
||||||
|
pub fn R_RenderPlayerView(player: *const player_t) {
|
||||||
|
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub fn R_Init() {
|
||||||
|
R_InitData();
|
||||||
|
println!("R_InitData");
|
||||||
|
R_InitPointToAngle();
|
||||||
|
println!("R_InitPointToAngle");
|
||||||
|
R_InitTables();
|
||||||
|
// viewwidth / viewheight / detailLevel are set by the defaults
|
||||||
|
println!("R_InitTables");
|
||||||
|
unsafe {R_SetViewSize(screenBlocks, detailLevel);}
|
||||||
|
R_InitPlanes();
|
||||||
|
println!("R_InitPlanes");
|
||||||
|
R_InitLightTables();
|
||||||
|
println!("R_InitLightTables");
|
||||||
|
//R_InitSkyMap();
|
||||||
|
println!("R_InitSkyMap");
|
||||||
|
//R_InitTranslationTables();
|
||||||
|
println!("R_InitTranslationTables")
|
||||||
|
|
||||||
|
// framecount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub fn R_SetViewSize(blocks: i32, detail: i32) {
|
||||||
|
unsafe {
|
||||||
|
setsizeneeded = true;
|
||||||
|
setblocks = blocks;
|
||||||
|
setdetail = detail;
|
||||||
|
}
|
||||||
|
}
|
||||||
60
src/renderer/r_data.rs
Normal file
60
src/renderer/r_data.rs
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn R_InitTextures() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn R_InitFlats() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn R_InitSpriteLumps() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn R_InitColormaps() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub fn R_GetColumn (tex: i32, col: i32) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// R_InitData
|
||||||
|
/// Locates all the lumps that will be used by all views
|
||||||
|
/// This must be called after `W_Init`
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub fn R_InitData() {
|
||||||
|
R_InitTextures();
|
||||||
|
println!("InitTextures");
|
||||||
|
R_InitFlats();
|
||||||
|
println!("InitFlats");
|
||||||
|
R_InitSpriteLumps();
|
||||||
|
println!("InitSprites");
|
||||||
|
R_InitColormaps();
|
||||||
|
println!("InitColormaps");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub fn R_PrecacheLevel() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub fn R_FlatNumForName(name: &str) -> i32 {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub fn R_TextureNumForName(name: &str) -> i32 {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub fn R_CheckTextureNumForName(name: &str) -> i32 {
|
||||||
|
1
|
||||||
|
}
|
||||||
441
src/renderer/r_defs.rs
Normal file
441
src/renderer/r_defs.rs
Normal file
@@ -0,0 +1,441 @@
|
|||||||
|
use crate::doomdef::SCREENWIDTH;
|
||||||
|
use crate::m_fixed::{fixed_t};
|
||||||
|
use crate::tables::angle_t;
|
||||||
|
|
||||||
|
// TODO: Implement d_think.h and p_mobj.h
|
||||||
|
|
||||||
|
/// Original Source comment:
|
||||||
|
/// Silhouette, needed for clipping Segs (mainly)
|
||||||
|
/// and sprites representing things.
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
|
#[repr(i32)]
|
||||||
|
pub enum Silhouette {
|
||||||
|
None = 0,
|
||||||
|
Bottom = 1,
|
||||||
|
Top = 2,
|
||||||
|
Both = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const MAXDRAWSEGS: i32 = 256;
|
||||||
|
|
||||||
|
/// Original source comment
|
||||||
|
/// This could be wider for >8 bit display.
|
||||||
|
/// Indeed, true color support is posibble
|
||||||
|
/// precalculating 24bpp lightmap/colormap LUT.
|
||||||
|
/// from darkening PLAYPAL to all black.
|
||||||
|
/// Could even us emore than 32 levels.
|
||||||
|
type lighttable_t = u8;
|
||||||
|
|
||||||
|
/// Original Source comment:
|
||||||
|
/// Your plain vanilla vertex.
|
||||||
|
/// Note: transformed values not buffered locally,
|
||||||
|
/// like some DOOM-alikes ("wt", "WebView") did.
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct vertex_t {
|
||||||
|
x: fixed_t,
|
||||||
|
y: fixed_t
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Original Source comment:
|
||||||
|
/// Each sector has a degenmobj_t in its center
|
||||||
|
/// for sound origin purposes.
|
||||||
|
/// I suppose this does not handle sound from
|
||||||
|
/// moving objects (doppler), because
|
||||||
|
/// position is prolly just buffered, not
|
||||||
|
/// updated.
|
||||||
|
pub struct degenmobj_t {
|
||||||
|
//TODO: implement d_think.h
|
||||||
|
// thinker: thinker_t,
|
||||||
|
x: fixed_t,
|
||||||
|
y: fixed_t,
|
||||||
|
z: fixed_t
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sectors represent a distinct, closed polygon area on the map with uniform
|
||||||
|
/// floor heights, ceiling heights, lighting levels, and floor/ceiling textures.
|
||||||
|
/// They also serve as containers for gameplay entities (`mobj_t`).
|
||||||
|
/// Original Source comment:
|
||||||
|
/// The SECTORS record, at runtime.
|
||||||
|
/// Stores things/mobjs.
|
||||||
|
pub struct sector_t {
|
||||||
|
/// Height of the floor
|
||||||
|
pub(crate) floorheight: fixed_t,
|
||||||
|
|
||||||
|
/// Height of the ceiling
|
||||||
|
pub(crate) ceilingheight: fixed_t,
|
||||||
|
|
||||||
|
/// Texture index applied to the floor surface
|
||||||
|
pub(crate) floorpic: i16,
|
||||||
|
|
||||||
|
/// Texture index applied to the ceiling surface
|
||||||
|
pub(crate) ceilingpic: i16,
|
||||||
|
|
||||||
|
/// The current brightness level for the sector
|
||||||
|
pub(crate) lightlevel: i16,
|
||||||
|
|
||||||
|
/// Sector type/behaviour flags (e.g sewage / lava damaging floors)
|
||||||
|
pub(crate) special: i16,
|
||||||
|
|
||||||
|
/// Unique ID tag used by linedef triggers to identify and manipluate this sector
|
||||||
|
pub(crate) tag: i16,
|
||||||
|
|
||||||
|
/// Sound propagation state variable used during sound traversal calculations.
|
||||||
|
/// Values: 0 = untraversed, 1 or 2 = sound lines - 1.
|
||||||
|
pub(crate) soundtraversed: i32,
|
||||||
|
|
||||||
|
//TODO: mobj_t implementation (p_mobj.h)
|
||||||
|
/// Pointer to the map object (`mobj_t`) that generated a sound in this sector, or null.
|
||||||
|
pub(crate) soundtarget: *mut mobj_t,
|
||||||
|
|
||||||
|
/// A bounding box `[ymin, ymax, xmin, xmax]` mapping blocks for height changes and collisions.
|
||||||
|
pub(crate) blockbox: [i32; 4],
|
||||||
|
|
||||||
|
/// simplified, stationary map object representing the central origin point for sound playback.
|
||||||
|
pub(crate) soundorg: degenmobj_t,
|
||||||
|
|
||||||
|
/// A frame check counter variable used to avoid re-processing this sector multiple times per tick.
|
||||||
|
pub(crate) validcount: i32,
|
||||||
|
|
||||||
|
/// A dynamic collection of all map objects currently inside this sector.
|
||||||
|
/// TODO: Change back to *mut mobj_t linked list later
|
||||||
|
pub(crate) thinglist: Vec<mobj_t>,
|
||||||
|
|
||||||
|
/// A generic raw pointer to an active thinker struct (e.g., `plat_t`, `ceiling_t`) for handling moving parts
|
||||||
|
pub(crate) specialdata: *mut (),
|
||||||
|
|
||||||
|
/// The total number of linedefs that form the boundaries of this sector.
|
||||||
|
pub(crate) linecount: i32,
|
||||||
|
|
||||||
|
/// A raw pointer to an array of pointers pointing to the `line_s` (linedef) structs that border this sector.
|
||||||
|
pub(crate) lines: *mut *mut line_s,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Linedefs are made up of either one or two sidedefs (front and back).
|
||||||
|
/// A sidedef contains texture alignment offsets, texture indices, and
|
||||||
|
/// points directly to the sector it faces.
|
||||||
|
pub struct sidedef_t {
|
||||||
|
|
||||||
|
/// Horizontal alignment offset added to the calculated texture column.
|
||||||
|
pub(crate) textureoffset: fixed_t,
|
||||||
|
|
||||||
|
/// Vertical alignment offset added to the calculated texture top.
|
||||||
|
pub(crate) rowoffset: fixed_t,
|
||||||
|
|
||||||
|
// texture indices
|
||||||
|
|
||||||
|
/// The index of the upper wall texture (above the ceiling gap).
|
||||||
|
pub(crate) toptexture: i16,
|
||||||
|
|
||||||
|
/// The index of the lower wall texture (below the floor gap).
|
||||||
|
pub(crate) bottomtexture: i16,
|
||||||
|
|
||||||
|
/// The index of the middle wall texture (used on single-sided walls or windows).
|
||||||
|
pub(crate) midtexture: i16,
|
||||||
|
|
||||||
|
/// A raw pointer to the sector this specific side faces.
|
||||||
|
pub(crate) sector: *mut sector_t,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The line slope classification type.
|
||||||
|
///
|
||||||
|
/// Used by the collision and rendering engines to optimize intersection
|
||||||
|
/// checks and line-of-sight tracking based on the orientation of a wall line.
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
enum slopetype_t {
|
||||||
|
|
||||||
|
/// A perfectly horizontal line (dy == 0).
|
||||||
|
ST_HORIZONTAL,
|
||||||
|
|
||||||
|
/// A perfectly vertical line (dx == 0).
|
||||||
|
ST_VERTICAL,
|
||||||
|
|
||||||
|
/// A line with a positive slope rising from bottom-left to top-right (dx and dy have the same sign).
|
||||||
|
ST_POSITIVE,
|
||||||
|
|
||||||
|
/// A line with a negative slope falling from top-left to bottom-right (dx and dy have opposite signs).
|
||||||
|
ST_NEGATIVE
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Linedefs form the walls and structural geometry of a map. They contain
|
||||||
|
/// pointers to their start/end vertices, front/back siding data, physical
|
||||||
|
/// triggers (`special`), and precalculated bounding extents to assist the physics engine.
|
||||||
|
pub struct linedef_t {
|
||||||
|
/// Raw pointer to the starting vertex (v1) of the wall.
|
||||||
|
pub(crate) v1: *mut vertex_t,
|
||||||
|
|
||||||
|
/// Raw pointer to the ending vertex (v2) of the wall.
|
||||||
|
pub(crate) v2: *mut vertex_t,
|
||||||
|
|
||||||
|
/// Precalculated horizontal delta value (v2.x - v1.x) for quick side-of-line checking.
|
||||||
|
pub(crate) dx: fixed_t,
|
||||||
|
|
||||||
|
/// Precalculated vertical delta value (v2.y - v1.y) for quick side-of-line checking.
|
||||||
|
pub(crate) dy: fixed_t,
|
||||||
|
|
||||||
|
/// Configuration flags handling behavior (e.g., solid wall, blocks monsters, secret map wall).
|
||||||
|
pub(crate) flags: i16,
|
||||||
|
|
||||||
|
/// The linedef activation type/trigger function index (e.g., doors, teleporters, lifts).
|
||||||
|
pub(crate) special: i16,
|
||||||
|
|
||||||
|
/// A unique ID tag matching sector targets to process execution triggers.
|
||||||
|
pub(crate) tag: i16,
|
||||||
|
|
||||||
|
/// Sidedef lookups. `sidenum[0]` is the front side; `sidenum[1]` is the back side (or -1 if single-sided).
|
||||||
|
pub(crate) sidenum: [i16; 2],
|
||||||
|
|
||||||
|
/// Bounding box layout `[ymin, ymax, xmin, xmax]` representing the full grid extent of the line.
|
||||||
|
pub(crate) bbox: [fixed_t; 4],
|
||||||
|
|
||||||
|
/// Slope classification used to optimize physics intersection and visibility clipping loops.
|
||||||
|
pub(crate) slopetype: slopetype_t,
|
||||||
|
|
||||||
|
/// Raw pointer to the sector directly in front of this linedef's first side.
|
||||||
|
pub(crate) frontsector: *mut sector_t,
|
||||||
|
|
||||||
|
/// Raw pointer to the sector behind this linedef's second side, or null if single-sided.
|
||||||
|
pub(crate) backsector: *mut sector_t,
|
||||||
|
|
||||||
|
/// A frame counter lookup variable used to avoid parsing or processing this line multiple times per tick.
|
||||||
|
pub(crate) validcount: i32,
|
||||||
|
|
||||||
|
/// A generic raw pointer to a running thinker action layout (e.g., standard scrolling wall textures).
|
||||||
|
pub(crate) specialdata: *mut (),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Subsectors are the structural leaves at the bottom of the BSP tree.
|
||||||
|
/// They represent smaller, guaranteed-convex sub-polygons carved out of a
|
||||||
|
/// larger sector, defined by a sequential slice of rendering lines (`seg_t`).
|
||||||
|
pub struct subsectordef_t {
|
||||||
|
/// A raw pointer to the parent sector that this subsector belongs to.
|
||||||
|
pub(crate) sector: *mut sector_t,
|
||||||
|
|
||||||
|
/// The total number of consecutive line segments (`seg_t`) that form this subsector.
|
||||||
|
pub(crate) numlines: i16,
|
||||||
|
|
||||||
|
/// The starting array index inside the global `segs` array for this subsector's lines.
|
||||||
|
pub(crate) firstline: i16,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Segs are clipped slices of linedefs generated by the node builder.
|
||||||
|
/// The rendering engine processes them in strict front-to-back order
|
||||||
|
/// using the BSP tree to draw wall surfaces without sorting artifacts.
|
||||||
|
pub struct segdef_t {
|
||||||
|
/// Raw pointer to the starting vertex (v1) of this specific segment.
|
||||||
|
pub(crate) v1: *mut vertex_t,
|
||||||
|
|
||||||
|
/// Raw pointer to the ending vertex (v2) of this specific segment.
|
||||||
|
pub(crate) v2: *mut vertex_t,
|
||||||
|
|
||||||
|
/// The distance offset along the original linedef where this segment begins.
|
||||||
|
pub(crate) offset: fixed_t,
|
||||||
|
|
||||||
|
/// The absolute binary angle of the segment (represented as a 32-bit unsigned integer).
|
||||||
|
pub(crate) angle: angle_t,
|
||||||
|
|
||||||
|
/// Raw pointer to the sidedef structure containing the texture data for this segment.
|
||||||
|
pub(crate) sidedef: *mut sidedef_t,
|
||||||
|
|
||||||
|
/// Raw pointer to the parent linedef structure this segment was split out from.
|
||||||
|
pub(crate) linedef: *mut linedef_t,
|
||||||
|
|
||||||
|
/// Raw pointer to the sector directly in front of this segment.
|
||||||
|
pub(crate) frontsector: *mut sector_t,
|
||||||
|
|
||||||
|
/// Raw pointer to the sector behind this segment, or null if it belongs to a solid one-sided wall.
|
||||||
|
pub(crate) backsector: *mut sector_t,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Nodes form the internal branching skeleton of the map's 3D visibility tree.
|
||||||
|
/// Each node acts as a cutting plane line segment that splits a sub-region into
|
||||||
|
pub struct node_t {
|
||||||
|
/// Starting X coordinate of the division splitter line.
|
||||||
|
pub(crate) x: fixed_t,
|
||||||
|
|
||||||
|
/// Starting Y coordinate of the division splitter line.
|
||||||
|
pub(crate) y: fixed_t,
|
||||||
|
|
||||||
|
/// Horizontal delta length of the division splitter vector.
|
||||||
|
pub(crate) dx: fixed_t,
|
||||||
|
|
||||||
|
/// Vertical delta length of the division splitter vector.
|
||||||
|
pub(crate) dy: fixed_t,
|
||||||
|
|
||||||
|
/// Precalculated bounding boxes [ymin, ymax, xmin, xmax] for the right [0] and left [1] child spaces.
|
||||||
|
pub(crate) bbox: [[FixedT; 4]; 2],
|
||||||
|
|
||||||
|
/// References to left [0] and right [1] children. If highest bit (0x8000) is set, it targets a subsector_t index.
|
||||||
|
pub(crate) children: [u16; 2],
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A runtime record of a drawn wall segment.
|
||||||
|
/// Stores clipping and scaling metadata to correctly occlude and scale sprites behind it.
|
||||||
|
pub struct drawseg_t {
|
||||||
|
/// Pointer to the line segment being rendered (usually seg_t).
|
||||||
|
pub(crate) curline: *mut sector_t,
|
||||||
|
|
||||||
|
/// Starting horizontal screen column (left boundary).
|
||||||
|
pub(crate) x1: i32,
|
||||||
|
|
||||||
|
/// Ending horizontal screen column (right boundary).
|
||||||
|
pub(crate) x2: i32,
|
||||||
|
|
||||||
|
/// Initial texture scale at the starting column (x1).
|
||||||
|
pub(crate) scale1: fixed_t,
|
||||||
|
|
||||||
|
/// Final texture scale at the ending column (x2).
|
||||||
|
pub(crate) scale2: fixed_t,
|
||||||
|
|
||||||
|
/// Amount to change the texture scale per horizontal pixel step.
|
||||||
|
pub(crate) scalestep: fixed_t,
|
||||||
|
|
||||||
|
/// Silhouette type flags handling sprite occlusion (e.g., SIL_BOTTOM, SIL_TOP).
|
||||||
|
pub(crate) silhouette: i32,
|
||||||
|
|
||||||
|
/// Maximum floor height boundary for bottom silhouette clipping.
|
||||||
|
pub(crate) bsilheight: fixed_t,
|
||||||
|
|
||||||
|
/// Minimum ceiling height boundary for top silhouette clipping.
|
||||||
|
pub(crate) tsilheight: fixed_t,
|
||||||
|
|
||||||
|
/// Pointer to the screen column array managing top clipping bounds for sprites.
|
||||||
|
pub(crate) sprtopclip: *mut u16,
|
||||||
|
|
||||||
|
/// Pointer to the screen column array managing bottom clipping bounds for sprites.
|
||||||
|
pub(crate) sprbottomclip: *mut u16,
|
||||||
|
|
||||||
|
/// Pointer to the column offset array used for rendering masked textures.
|
||||||
|
pub(crate) maskedtexturecol: *mut u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A hardware-agnostic 2D picture or graphic asset.
|
||||||
|
/// Uses a column-oriented format to optimize rapid vertical stretching and scaling.
|
||||||
|
pub struct patch_t {
|
||||||
|
/// Total width of the graphic patch in pixels.
|
||||||
|
pub(crate) width: u16,
|
||||||
|
|
||||||
|
/// Total height of the graphic patch in pixels.
|
||||||
|
pub(crate) height: u16,
|
||||||
|
|
||||||
|
/// Horizontal offset relative to the origin (used to center weapon and sprite frames).
|
||||||
|
pub(crate) leftoffset: u16,
|
||||||
|
|
||||||
|
/// Vertical offset relative to the origin (used to center weapon and sprite frames).
|
||||||
|
pub(crate) topoffset: u16,
|
||||||
|
|
||||||
|
/// Array of byte offsets from the start of the patch to the start of each vertical pixel column.
|
||||||
|
/// only [width] used
|
||||||
|
// the [0] is &columnofs[width]
|
||||||
|
pub(crate) columnofs: [i32; 8],
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A visible sprite entry prepared for rendering.
|
||||||
|
/// Tracks screen bounds, scaling, and clipping parameters for a partly visible map object.
|
||||||
|
pub struct vissprite_t {
|
||||||
|
/// Pointer to the previous sprite in the doubly linked list.
|
||||||
|
pub(crate) prev: *mut vissprite_t,
|
||||||
|
|
||||||
|
/// Pointer to the next sprite in the doubly linked list.
|
||||||
|
pub(crate) next: *mut vissprite_t,
|
||||||
|
|
||||||
|
/// Starting horizontal screen column (left boundary).
|
||||||
|
pub(crate) x1: i32,
|
||||||
|
|
||||||
|
/// Ending horizontal screen column (right boundary).
|
||||||
|
pub(crate) x2: i32,
|
||||||
|
|
||||||
|
/// Global X coordinate used for line-side visibility checks.
|
||||||
|
pub(crate) gx: fixed_t,
|
||||||
|
|
||||||
|
/// Global Y coordinate used for line-side visibility checks.
|
||||||
|
pub(crate) gy: fixed_t,
|
||||||
|
|
||||||
|
/// Global bottom Z coordinate used for silhouette clipping calculations.
|
||||||
|
pub(crate) gz: fixed_t,
|
||||||
|
|
||||||
|
/// Global top Z coordinate used for silhouette clipping calculations.
|
||||||
|
pub(crate) gzt: fixed_t,
|
||||||
|
|
||||||
|
/// Horizontal texture offset fraction at screen column x1.
|
||||||
|
pub(crate) startfrac: fixed_t,
|
||||||
|
|
||||||
|
/// Scale factor of the sprite relative to its distance from the camera.
|
||||||
|
pub(crate) scale: fixed_t,
|
||||||
|
|
||||||
|
/// Horizontal step scale factor (inverted and negative if the sprite is flipped).
|
||||||
|
pub(crate) xiscale: fixed_t,
|
||||||
|
|
||||||
|
/// Vertical texture alignment midpoint coordinate.
|
||||||
|
pub(crate) texturemid: fixed_t,
|
||||||
|
|
||||||
|
/// The index of the graphic patch asset being rendered.
|
||||||
|
pub(crate) patch: i32,
|
||||||
|
|
||||||
|
/// Pointer to the color lookup array handling lighting, shadows, and full-brightness frames.
|
||||||
|
pub(crate) colormap: *mut lighttable_t,
|
||||||
|
|
||||||
|
/// Cached copy of the parent map object's physics and state flags.
|
||||||
|
pub(crate) mobjflags: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Animation frame data for a sprite object.
|
||||||
|
/// Tracks raw graphic lump IDs and horizontal flip bits for all 8 view rotations.
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct spriteframe_t {
|
||||||
|
/// True if the sprite has directional rotation variants; false if it looks identical from all sides.
|
||||||
|
pub(crate) rotate: bool,
|
||||||
|
|
||||||
|
/// WAD graphic lump indices representing the image assets for view angles 0 to 7.
|
||||||
|
pub(crate) lump: [i16; 8],
|
||||||
|
|
||||||
|
/// Bitmask flags (1 = mirror image horizontally) applied to view angles 0 to 7 to save space.
|
||||||
|
pub(crate) flip: [u8; 8],
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A top-level sprite asset container managing a series of sequential animation frames.
|
||||||
|
pub struct spritedef_t {
|
||||||
|
/// Total number of animation frames allocated for this sprite sequence.
|
||||||
|
pub(crate) numframes: i32,
|
||||||
|
|
||||||
|
/// Raw pointer to the array of frame records tracking rotation and asset data.
|
||||||
|
pub(crate) spriteframes: *mut spriteframe_t,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A floor or ceiling polygon segment prepared for flat horizontal texture mapping.
|
||||||
|
/// Merges drawing spans to prevent visible gaps or layout cracking during screen refresh.
|
||||||
|
pub struct visplane_t {
|
||||||
|
/// Absolute height level of the floor or ceiling plane in fixed-point space.
|
||||||
|
pub(crate) height: fixed_t,
|
||||||
|
|
||||||
|
/// WAD graphic texture flat index applied to the surface.
|
||||||
|
pub(crate) picnum: i32,
|
||||||
|
|
||||||
|
/// Calculated light/brightness visibility level of the flat polygon plane.
|
||||||
|
pub(crate) lightlevel: i32,
|
||||||
|
|
||||||
|
/// The leftmost horizontal screen column index bound for drawing.
|
||||||
|
pub(crate) minx: i32,
|
||||||
|
|
||||||
|
/// The rightmost horizontal screen column index bound for drawing.
|
||||||
|
pub(crate) maxx: i32,
|
||||||
|
|
||||||
|
/// Memory padding byte mimicking historical x-1 lookups.
|
||||||
|
pub(crate) pad1: u8,
|
||||||
|
|
||||||
|
/// Dynamic horizontal boundary array tracing top rendering margins across columns.
|
||||||
|
pub(crate) top: [u8; SCREENWIDTH],
|
||||||
|
|
||||||
|
/// Memory padding byte mimicking historical array bounds checks.
|
||||||
|
pub(crate) pad2: u8,
|
||||||
|
|
||||||
|
/// Memory padding byte mimicking historical array bounds checks.
|
||||||
|
pub(crate) pad3: u8,
|
||||||
|
|
||||||
|
/// Dynamic horizontal boundary array tracing bottom rendering margins across columns.
|
||||||
|
pub(crate) bottom: [u8; SCREENWIDTH],
|
||||||
|
|
||||||
|
/// Memory padding byte mimicking historical x+1 lookups.
|
||||||
|
pub(crate) pad4: u8,
|
||||||
|
}
|
||||||
39
src/renderer/r_plane.rs
Normal file
39
src/renderer/r_plane.rs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub fn R_InitPlanes() {
|
||||||
|
// Original source comment
|
||||||
|
// doh!
|
||||||
|
// wtf?
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub fn R_ClearPlanes() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub fn R_MapPlane (y: i32, x1: i32, x2: i32) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub fn R_MakeSpans(x: i32, t1: i32, b1: i32, t2: i32, b2: i32) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub fn R_DrawPlanes() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: Implement visplane_t
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub fn R_FindPlane(height: fixed_t, picnum: i32, lightlevel: i32) -> *const visplane_t {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub fn R_CheckPlane(pl: *const visplane_t, start: i32, stop: i32) -> *const visplane_t {
|
||||||
|
|
||||||
|
}
|
||||||
|
*/
|
||||||
1843
src/tables/mod.rs
Normal file
1843
src/tables/mod.rs
Normal file
File diff suppressed because it is too large
Load Diff
165
src/z_zone/mod.rs
Normal file
165
src/z_zone/mod.rs
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
/// Memory allocator stuff
|
||||||
|
/// Compared to the original source, this will be some what "lean n' mean"
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
|
||||||
|
/// The PurgeTags (PUs)
|
||||||
|
/// Tags < 100 are not overwritten untill freed
|
||||||
|
pub enum PurgeTag {
|
||||||
|
/// Static the entire execution time
|
||||||
|
Static = 1,
|
||||||
|
/// Static while playing
|
||||||
|
Sound = 2,
|
||||||
|
/// static while playing
|
||||||
|
Music = 3,
|
||||||
|
/// Original source comment: anything else dave wants static
|
||||||
|
Dave = 4,
|
||||||
|
/// static until level exit
|
||||||
|
Level = 50,
|
||||||
|
/// static for special thinkers in a level
|
||||||
|
LevSpec = 51,
|
||||||
|
// Tags >= 100 are "purgable" whenever needed
|
||||||
|
PurgeLevel = 100,
|
||||||
|
Cache = 101,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Provides metadata on
|
||||||
|
struct AllocMeta {
|
||||||
|
/// The size of the allocation
|
||||||
|
size: usize,
|
||||||
|
/// The PU tag associated with the allocation
|
||||||
|
tag: i32,
|
||||||
|
/// A pointer to the caller's tracking pointer, cleared automatically upon freeing
|
||||||
|
user: *mut *mut u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
thread_local! {
|
||||||
|
// We promise not to be naughty and access this from another thread :)
|
||||||
|
/// Mapping from pointers (as usize) to the correspond AllocationMeta
|
||||||
|
static REGISTRY: RefCell<HashMap<usize, AllocMeta>> = RefCell::new(HashMap::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper method that we can use to access the registry
|
||||||
|
fn with_registry<F, R>(f: F) -> R
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut HashMap<usize, AllocMeta>) -> R
|
||||||
|
{
|
||||||
|
REGISTRY.with(|registry| f(&mut *registry.borrow_mut()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
/// Initalized the Zone memory allocator sub-system
|
||||||
|
pub fn Z_Init() {
|
||||||
|
// do nothing, we get lazy init of the registry.
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
/// Allocate a contiguous chunk of zeroed memory from the zone allocator
|
||||||
|
/// # Safety
|
||||||
|
/// This function returns a raw pointer that bypasses Rust's automatic memory
|
||||||
|
/// tracking. The returned memory must be manually reclaimed using `Z_Free`.
|
||||||
|
pub unsafe fn Z_Malloc(size: usize, tag: i32, user: *mut *mut u8) -> *mut u8 {
|
||||||
|
let mut buffer = vec![0u8; size];
|
||||||
|
let ptr = buffer.as_mut_ptr();
|
||||||
|
|
||||||
|
// ensures Rust does not drop the buffer at the end of this block
|
||||||
|
std::mem::forget(buffer);
|
||||||
|
|
||||||
|
// Update the engine's tracking pointer if one was provided
|
||||||
|
if !user.is_null() {
|
||||||
|
unsafe {
|
||||||
|
*user = ptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save allocation details using the pointer address as the key
|
||||||
|
with_registry(|map| {
|
||||||
|
map.insert(ptr as usize, AllocMeta { size, tag, user });
|
||||||
|
});
|
||||||
|
|
||||||
|
ptr
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
/// Reclaims and frees a allocation from Z_Malloc
|
||||||
|
/// # Safety
|
||||||
|
/// The provided pointer must point to a valid allocation tracked by the registry.
|
||||||
|
/// Passing an invalid or already freed pointer can result in undefined behavior
|
||||||
|
pub unsafe fn Z_Free(ptr: *mut u8) {
|
||||||
|
if ptr.is_null() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let removed = with_registry(|map| map.remove(&(ptr as usize)));
|
||||||
|
|
||||||
|
if let Some(meta) = removed {
|
||||||
|
// Clear the caller's tracking pointer to prevent use-after-free
|
||||||
|
if !meta.user.is_null() {
|
||||||
|
unsafe {
|
||||||
|
*meta.user = std::ptr::null_mut();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// re-build the vector so Rust can drop it
|
||||||
|
unsafe {
|
||||||
|
let _dropped_vec = Vec::from_raw_parts(ptr, meta.size, meta.size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
/// Frees all memory allocations between the specific lifetime PU tags.
|
||||||
|
/// # Safety
|
||||||
|
/// All pointers tracked within this tag boundary are invalidated and deallocate
|
||||||
|
pub unsafe fn Z_FreeTags(low_tag: i32, high_tag: i32) {
|
||||||
|
|
||||||
|
// get all of the allocations to free
|
||||||
|
let tags_to_free: Vec<usize> = with_registry(|map| {
|
||||||
|
map.iter()
|
||||||
|
.filter(|(_, meta)| meta.tag >= low_tag && meta.tag <= high_tag)
|
||||||
|
.map(|(ptr_addr,_)| *ptr_addr)
|
||||||
|
.collect()
|
||||||
|
});
|
||||||
|
|
||||||
|
// remove each pointer returned from the map and drop the memory
|
||||||
|
|
||||||
|
for ptr_addr in tags_to_free {
|
||||||
|
let removed_ptr = with_registry(|map| map.remove(&ptr_addr));
|
||||||
|
|
||||||
|
if let Some(meta) = removed_ptr {
|
||||||
|
let p = ptr_addr as *mut u8;
|
||||||
|
unsafe {
|
||||||
|
// clear caller tracking pointer to preven use after free
|
||||||
|
if !meta.user.is_null() {
|
||||||
|
*meta.user = std::ptr::null_mut();
|
||||||
|
}
|
||||||
|
|
||||||
|
let _dropped_vec = Vec::from_raw_parts(p, meta.size, meta.size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal ChangeTag function
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn Z_ChangeTagInternal(ptr: *mut u8, tag: i32) {
|
||||||
|
if ptr.is_null() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
with_registry(|map| {
|
||||||
|
if let Some(meta) = map.get_mut(&(ptr as usize)) {
|
||||||
|
meta.tag = tag;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/// Modifies the lifetime tag of an active allocation.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// The provided pointer must point to a valid allocation tracked by the registry
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub unsafe fn Z_ChangeTag(ptr: *mut u8, tag: i32) {
|
||||||
|
// removes the original helper macro from the original C, we don't need withcraft here
|
||||||
|
Z_ChangeTagInternal(ptr, tag);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user