瀏覽代碼

Refactored code into modules. Deleted much legacy code. Switched lookup table generation from build.rs to macros

Gary Munnelly 2 年之前
父節點
當前提交
e93b1dab00

+ 3 - 1
Cargo.toml

@@ -1,7 +1,9 @@
 [workspace]
 
 members = [
+	"shared",
+	"macros",
 	"engine",
-	"img2tex",
 	"demo",
+	"tools/img2tex",
 ]

+ 0 - 0
assets/sprites/LAB/readme.txt → demo/assets/sprites/LAB/readme.txt


+ 0 - 0
assets/sprites/LAB/tilesheet-modified.png → demo/assets/sprites/LAB/tilesheet-modified.png


+ 0 - 0
assets/sprites/LAB/tilesheet.png → demo/assets/sprites/LAB/tilesheet.png


+ 6 - 5
demo/src/lib.rs

@@ -1,5 +1,4 @@
-use fourteen_screws::scene::Scene;
-use fourteen_screws::render::Camera;
+use fourteen_screws::{ Camera, Scene, Renderer };
 use serde_json;
 use wasm_bindgen::prelude::*;
 extern crate web_sys;
@@ -8,6 +7,7 @@ extern crate web_sys;
 pub struct FourteenScrewsDemo {
 	scene:  Scene,
 	player: Camera,
+	renderer: Renderer,
 }
 
 #[wasm_bindgen]
@@ -15,10 +15,11 @@ impl FourteenScrewsDemo {
 	pub fn load_level(json_str: &str) -> FourteenScrewsDemo {
 		let json: serde_json::Value = serde_json::from_str(json_str).ok().unwrap();
 		
-		let scene  = Scene::from_json(&json["scene"]).ok().unwrap();
-		let player = Camera::from_json(&json["camera"]).ok().unwrap();
+		let scene    = Scene::from_json(&json["scene"]).ok().unwrap();
+		let player   = Camera::from_json(&json["camera"]).ok().unwrap();
+		let renderer = Renderer::from_json(&json["camera"]).ok().unwrap();
 
-		FourteenScrewsDemo { scene, player }
+		FourteenScrewsDemo { scene, player, renderer }
 	}
 
 	pub fn render(&mut self, buf: &mut[u8]) {

+ 3 - 0
engine/Cargo.toml

@@ -22,3 +22,6 @@ float-cmp = "0.9.0"
 base64 = "0.21.2"
 serde_json = "1.0.99"
 wasm-bindgen = "0.2.86"
+shared = { path = "../shared" }
+macros = { path = "../macros" }
+num-traits = "0.2.15"

+ 0 - 172
engine/build.rs

@@ -1,172 +0,0 @@
-// primarily used for writing the file
-use std::f64::consts::PI;
-use std::{env, fs, path::Path};
-
-const PROJECTION_PLANE_WIDTH: usize  = 320;
-const PROJECTION_PLANE_HEIGHT: usize = 200;
-const DISTANCE_TO_PROJECTION_PLANE: usize = 277;
-
-const TILE_SIZE: f64 = 64.0;
-
-const ANGLE_0:   usize = 0;
-const ANGLE_60:  usize = PROJECTION_PLANE_WIDTH;
-const ANGLE_30:  usize = ANGLE_60 / 2;
-const ANGLE_90:  usize = ANGLE_30 * 3;
-const ANGLE_180: usize = ANGLE_60 * 3;
-const ANGLE_270: usize = ANGLE_90 * 3;
-const ANGLE_360: usize = ANGLE_60 * 6;
-
-const MAX_RAY_LENGTH: usize = 2048;
-const WALL_HEIGHT_SCALE_FACTOR: usize = 18000; 
-const WALL_HEIGHT_MAX: i32            = 640;
-const WALL_HEIGHT_MIN: i32            = 8;
-
-const PLAYER_HEIGHT: usize             = 32;
-const PROJECTION_PLANE_CENTRE_Y: usize = PROJECTION_PLANE_HEIGHT >> 1;
-
-
-fn clamp(x: i32, min: i32, max: i32) -> i32 {
-    if x < min {
-        min
-    } else if x > max {
-        max
-    } else {
-        x
-    }
-}
-
-fn radian(angle: usize) -> f64 {
-    angle as f64 * PI / ANGLE_180 as f64
-}
-
-fn iradian(angle: i32) -> f64 {
-    angle as f64 * PI / ANGLE_180 as f64
-}
-
-fn float_to_fix(x: f64) -> i32 {    
-    (x * 65536.0) as i32
-}
-
-fn stringify(name: &str, arr: &[i32], size: usize) -> String {
-    let mut array_string = String::from("static ");
-    array_string.push_str(name);
-    array_string.push_str(":[i32; ");
-    array_string.push_str(size.to_string().as_str());
-    array_string.push_str("] = [\r\n");
-    for a in arr {
-        // a little bit of formatting is happening as well
-        array_string.push_str("\u{20}\u{20}\u{20}\u{20}");
-        array_string.push_str(a.to_string().as_str());
-        array_string.push_str(",\r\n");
-    }
-    array_string.push_str("];\r\n");
-    array_string
-}
-
-fn main() {
-    const SIZE: usize = ANGLE_360 + 1;
-
-    let mut sin: [i32; SIZE] = [0; SIZE];
-    let mut cos: [i32; SIZE] = [0; SIZE];
-    let mut tan: [i32; SIZE] = [0; SIZE];
-    let mut isin: [i32; SIZE] = [0; SIZE];
-    let mut icos: [i32; SIZE] = [0; SIZE];
-    let mut itan: [i32; SIZE] = [0; SIZE];
-    
-    for i in 0..=1920 {
-        sin[i] = float_to_fix(radian(i).sin());
-        cos[i] = float_to_fix(radian(i).cos());
-        tan[i] = float_to_fix(radian(i).tan());
-        isin[i] = float_to_fix(1.0 / radian(i).sin());
-        icos[i] = float_to_fix(1.0 / radian(i).cos());
-        itan[i] = float_to_fix(1.0 / radian(i).tan());        
-    }
-
-
-
-    let mut output = stringify("SIN", &sin, SIZE);
-    output.push_str(stringify("COS", &cos, SIZE).as_str());
-    output.push_str(stringify("TAN", &tan, SIZE).as_str());
-    output.push_str(stringify("ISIN", &isin, SIZE).as_str());
-    output.push_str(stringify("ICOS", &icos, SIZE).as_str());
-    output.push_str(stringify("ITAN", &itan, SIZE).as_str());
-
-    let mut x_step: [i32; SIZE] = [0; SIZE];
-    let mut y_step: [i32; SIZE] = [0; SIZE];
-
-    for i in 0..=1920 {
-        let mut step: f64;
-
-        if radian(i).tan() == 0.0 {
-            step = f64::MAX
-        } else {
-            step = TILE_SIZE / radian(i).tan();
-
-            if i >= ANGLE_90 && i < ANGLE_270 {
-                if step > 0.0 {
-                  step = -step;
-                }
-            } else {
-                if step < 0.0 {
-                  step = -step;
-                }
-            }
-        }
-
-        x_step[i] = float_to_fix(step);
-    }
-
-    for i in 0..=1920 {
-        let mut step = TILE_SIZE * radian(i).tan();
-
-        if i >= ANGLE_0 && i < ANGLE_180 {
-            if step < 0.0 {
-              step = -step;
-            }
-        } else {
-            if step > 0.0 {
-              step = -step;
-            }
-        }
-
-        y_step[i] = (step * 65536.0) as i32; //float_to_fix(step);
-    }
-
-    output.push_str(stringify("X_STEP", &x_step, SIZE).as_str());
-    output.push_str(stringify("Y_STEP", &y_step, SIZE).as_str());
-
-    let mut fisheye: [i32; PROJECTION_PLANE_WIDTH] = [0; PROJECTION_PLANE_WIDTH];
-
-    for i in 0..PROJECTION_PLANE_WIDTH {
-        fisheye[i] = float_to_fix(1.0 / iradian(i as i32 - ANGLE_30 as i32).cos());    
-    }
-
-    output.push_str(stringify("FISHEYE", &fisheye, PROJECTION_PLANE_WIDTH).as_str());
-    
-    let mut wall_height: [i32; MAX_RAY_LENGTH + 1] = [0; MAX_RAY_LENGTH + 1];
-    for i in 0..=MAX_RAY_LENGTH {
-        wall_height[i] = clamp((WALL_HEIGHT_SCALE_FACTOR / i.max(1)) as i32, WALL_HEIGHT_MIN, WALL_HEIGHT_MAX);
-    }
-
-    output.push_str(stringify("WALL_HEIGHT", &wall_height, MAX_RAY_LENGTH + 1).as_str());
-
-    // let mut FLOOR_TEXTURE_Y_RAYS: [i32; PROJECTION_PLANE_WIDTH * PROJECTION_PLANE_CENTRE_Y] = [0; PROJECTION_PLANE_WIDTH * PROJECTION_PLANE_CENTRE_Y];
-    // let mut FLOOR_TEXTURE_X_RAYS: [i32; PROJECTION_PLANE_WIDTH * PROJECTION_PLANE_CENTRE_Y] = [0; PROJECTION_PLANE_WIDTH * PROJECTION_PLANE_CENTRE_Y];
-
-    // for y in (PROJECTION_PLANE_CENTRE_Y + 1)..PROJECTION_PLANE_HEIGHT {
-    //     let ratio: f64 = PLAYER_HEIGHT as f64 / (y - PROJECTION_PLANE_CENTRE_Y) as f64;
-    //     for sweep in 0..PROJECTION_PLANE_WIDTH {
-    //         let distance = DISTANCE_TO_PROJECTION_PLANE as f64 * ratio * fisheye[sweep];
-    //     }
-    // }
-
-// var diagonalDistance=Math.floor((this.fPlayerDistanceToTheProjectionPlane * ratio) * (this.fFishTable[castColumn]));
-
-// var yEnd = Math.floor(diagonalDistance * this.fSinTable[castArc]);
-// var xEnd = Math.floor(diagonalDistance * this.fCosTable[castArc]);
-
-    // write the string to a file. OUT_DIR environment variable is defined by cargo
-    let out_dir = env::var("OUT_DIR").unwrap();
-    let dest_path = Path::new(&out_dir).join("lookup.rs");
-    fs::write(&dest_path, output).unwrap();
-}

+ 0 - 0
engine/src/collision/mod.rs → engine/src/collision.rs


+ 0 - 20
engine/src/consts.rs

@@ -1,20 +0,0 @@
-pub const PROJECTION_PLANE_HEIGHT: i32  = 200;
-pub const PROJECTION_PLANE_WIDTH: i32   = 320;
-pub const PROJECTION_PLANE_HORIZON: i32 = PROJECTION_PLANE_HEIGHT / 2;
-
-pub const TILE_SIZE: i32    = 64;
-pub const FP_TILE_SIZE: i32 = TILE_SIZE << 16;
-
-pub const TEXTURE_WIDTH: usize = 64;
-pub const TEXTURE_HEIGHT: usize = 64;
-
-pub const WALL_HEIGHT_SCALE_FACTOR: i32 = 18000;
-pub const WALL_HEIGHT_MIN: i32 = 8;
-pub const WALL_HEIGHT_MAX: i32 = 640;
-
-pub const MAX_RAY_LENGTH: i32 = 2048;
-pub const FP_MAX_RAY_LENGTH: i32 = MAX_RAY_LENGTH << 16;
-
-pub const DISTANCE_TO_PROJECTION_PLANE: i32 = 277;
-pub const PLAYER_HEIGHT: i32 = 32;
-pub const WALL_HEIGHT: i32 = 64;

+ 5 - 2
engine/src/lib.rs

@@ -3,11 +3,14 @@
 // use wasm_bindgen::prelude::*;
 extern crate web_sys;
 
-pub mod consts;
-pub mod maths;
+pub mod trig;
 pub mod scene;
 pub mod render;
 
+pub use crate::trig::*;
+pub use crate::scene::*;
+pub use crate::render::*;
+
 // mod consts;
 // mod trig;
 // mod utils;

+ 0 - 113
engine/src/maths/fp.rs

@@ -1,113 +0,0 @@
-const FP_SHIFT: i32 = 16;
-const FP_MULT: f64  = 65536.0;
-const FP_HALF: f64  = 32768.0;
-
-const FP_FLOOR_MASK: i32 = !((1 << FP_SHIFT) - 1);
-
-pub trait ToFixedPoint {
-    fn to_fp(&self) -> i32;
-}
-
-pub trait FromFixedPoint {
-    fn to_f64(&self) -> f64;
-    fn to_i32(&self) -> i32;
-}
-
-impl ToFixedPoint for f64 {
-    fn to_fp(&self) -> i32 {
-        (*self * FP_MULT) as i32
-    }
-}
-
-impl ToFixedPoint for i32 {
-    fn to_fp(&self) -> i32 {
-        *self << FP_SHIFT
-    }
-}
-
-impl FromFixedPoint for i32 {
-	fn to_f64(&self) -> f64 {
-		*self as f64 / FP_MULT
-	}
-
-    fn to_i32(&self) -> i32 {
-    	*self >> FP_SHIFT
-    }
-}
-
-pub const fn add(a: i32, b: i32) -> i32 {
-	a + b
-}
-
-pub const fn sub(a: i32, b: i32) -> i32 {
-	a - b
-}
-
-pub const fn mul(a: i32, b: i32) -> i32 {
-	((a as i64 * b as i64) >> FP_SHIFT) as i32
-}
-
-pub const fn div(a: i32, b: i32) -> i32 {
-	(((a as i64)  << FP_SHIFT) / b as i64) as i32
-}
-
-pub const fn floor(a: i32) -> i32 {
-	a & FP_FLOOR_MASK
-}
-
-#[cfg(test)]
-mod test {
-	use super::*;
-
-	#[test]
-	fn f64_add() {
-		let test_pairs = [
-			(0.5, 0.5),
-			(-0.754, 0.123)
-		];
-
-		for (a, b) in test_pairs {
-			let fp_sum = add(a.to_fp(), b.to_fp());
-			float_cmp::assert_approx_eq!(f64, fp_sum.to_f64(), a + b, epsilon = 0.003, ulps = 2)
-		}
-	}
-
-	#[test]
-	fn f64_sub() {
-		let test_pairs = [
-			(0.5, 0.5),
-			(-0.754, 0.123)
-		];
-
-		for (a, b) in test_pairs {
-			let fp_diff = sub(a.to_fp(), b.to_fp());
-			float_cmp::assert_approx_eq!(f64, fp_diff.to_f64(), a - b, epsilon = 0.003, ulps = 2)
-		}
-	}
-
-	#[test]
-	fn f64_mul() {
-		let test_pairs = [
-			(0.5, 0.5),
-			(-0.754, 0.123)
-		];
-
-		for (a, b) in test_pairs {
-			let fp_prod = mul(a.to_fp(), b.to_fp());
-			float_cmp::assert_approx_eq!(f64, fp_prod.to_f64(), a * b, epsilon = 0.003, ulps = 2)
-		}
-	}
-
-	#[test]
-	fn f64_div() {
-		let test_pairs = [
-			(0.5, 0.5),
-			(-0.754, 0.123)
-		];
-
-		for (a, b) in test_pairs {
-			let fp_quot = div(a.to_fp(), b.to_fp());
-			float_cmp::assert_approx_eq!(f64, fp_quot.to_f64(), a / b, epsilon = 0.003, ulps = 2)
-		}
-	}
-}

+ 0 - 2
engine/src/maths/mod.rs

@@ -1,2 +0,0 @@
-pub mod fp;
-pub mod trig;

+ 0 - 59
engine/src/maths/trig.rs

@@ -1,59 +0,0 @@
-use crate::consts::{ PROJECTION_PLANE_WIDTH, MAX_RAY_LENGTH };
-use core::f64::consts::PI;
-
-include!(concat!(env!("OUT_DIR"), "/lookup.rs"));
-
-pub const ANGLE_60:  i32 = PROJECTION_PLANE_WIDTH;
-
-pub const ANGLE_0:   i32 = 0;
-pub const ANGLE_5:   i32 = ANGLE_60 / 12;
-pub const ANGLE_10:  i32 = ANGLE_60 / 6;
-pub const ANGLE_30:  i32 = ANGLE_60 / 2;
-pub const ANGLE_90:  i32 = ANGLE_30 * 3;
-pub const ANGLE_180: i32 = ANGLE_60 * 3;
-pub const ANGLE_270: i32 = ANGLE_90 * 3;
-pub const ANGLE_360: i32 = ANGLE_60 * 6;
-
-pub fn radian(angle: i32) -> f64 {
-	angle as f64 * PI / ANGLE_180 as f64
-}
-
-pub fn cos(degrees: i32) -> i32 {
-	COS[degrees as usize]
-}
-
-pub fn sin(degrees: i32) -> i32 {
-	SIN[degrees as usize]
-}
-
-pub fn tan(degrees: i32) -> i32 {
-	TAN[degrees as usize]
-}
-
-pub fn icos(degrees: i32) -> i32 {
-	ICOS[degrees as usize]
-}
-
-pub fn isin(degrees: i32) -> i32 {
-	ISIN[degrees as usize]
-}
-
-pub fn itan(degrees: i32) -> i32 {
-	ITAN[degrees as usize]
-}
-
-pub fn xstep(degrees: i32) -> i32 {
-	X_STEP[degrees as usize]
-}
-
-pub fn ystep(degrees: i32) -> i32 {
-	Y_STEP[degrees as usize]
-}
-
-pub fn fisheye_correction(degrees: i32) -> i32 {
-	FISHEYE[degrees as usize]
-}
-
-pub fn wall_height(dist: i32) -> i32 {
-	WALL_HEIGHT[dist.min(MAX_RAY_LENGTH) as usize]
-}

+ 0 - 329
engine/src/raycast.rs

@@ -1,329 +0,0 @@
-use crate::trig;
-use crate::consts;
-use crate::fp;
-use crate::fp::{ ToFixedPoint, FromFixedPoint };
-
-#[derive(Debug, Copy, Clone)]
-pub enum TextureCode {
-	None,
-	Wall(u8, i32, bool),
-	Floor(u8, i32, i32),
-	Ceiling(u8, i32, i32),
-}
-
-#[derive(Debug, Copy, Clone)]
-pub struct Slice {
-	pub texture: TextureCode,
-	pub distance: i32,
-}
-
-impl Slice {
-	fn new(texture: TextureCode, distance: i32) -> Slice {
-		Slice { texture, distance }
-	}
-}
-
-pub struct Player {
-	pub x: i32,
-	pub y: i32,
-	pub rotation: i32,
-	pub move_speed: i32,
-	pub rotate_speed: i32,
-}
-
-impl Player {
-	pub fn new(x: i32, y: i32, rotation: i32, move_speed: i32, rotate_speed: i32) -> Player {
-		Player { x, y, rotation, move_speed, rotate_speed }
-	}
-
-	pub fn pos(&mut self, x: i32, y: i32) {
-		self.x = x;
-		self.y = y;
-	}
-
-	pub fn rotation(&mut self, mut rotation: i32) {
-		// ensure the input rotation is within bounds
-		while rotation >= trig::ANGLE_360 { rotation -= trig::ANGLE_360; }
-		while rotation < trig::ANGLE_0    { rotation += trig::ANGLE_360; }
-		self.rotation = rotation;
-	}
-}
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq)]
-pub enum Tile {
-	Empty,
-	Wall(u8, bool),
-	OutOfBounds,
-}
-
-pub struct World {
-	width: i32,
-	height: i32,
-	y_walls: Vec<Tile>,
-	x_walls: Vec<Tile>,
-	horizon: i32,
-	fp_horizon: i32,
-}
-
-impl World {
-	pub fn new(width: i32, height: i32, map_str: &str) -> Result<World, &str> {
-		if width < 0 || height < 0 {
-			return Err("Width and height must be positive values");
-		}
-
-		if (width * height) as usize != map_str.chars().count() {
-			return Err("Width and height parameters do not match size of serialized map string");
-		}
-
-		let horizon = consts::PROJECTION_PLANE_HORIZON;
-		let fp_horizon = horizon.to_fp();
-
-		let y_walls: Vec<Tile> = map_str.chars()
-			.map(|c| {
-				if c == 'W' || c == 'H' || c == 's' || c == 'c' {
-					Tile::Wall(3, false)
-				} else if c == 'h' { 
-					Tile::Wall(65, false)
-				} else {
-					Tile::Empty
-				}
-			})
-			.collect();
-
-		let x_walls: Vec<Tile> = map_str.chars()
-			.map(|c| {
-				if c == 'W' || c == 'V' {
-					Tile::Wall(3, false)
-				} else if c == 'X' {
-					Tile::Wall(1, true)
-				} else if c == 'v' {
-					Tile::Wall(65, false)
-				} else if c == 's' {
-					Tile::Wall(5, false)
-				} else if c == 'c' {
-					Tile::Wall(4, false)
-				} else {
-					Tile::Empty
-				}
-			})
-			.collect();
-
-		Ok(World { width, height, y_walls, x_walls, horizon, fp_horizon })
-	}
-
-	fn is_within_bounds(&self, x: i32, y: i32) -> bool {
-		x >= 0 && x < self.width && y >= 0 && y < self.height
-	}
-
-	pub fn y_wall(&self, x: i32, y: i32) -> Tile {
-		if !self.is_within_bounds(x, y) { return Tile::OutOfBounds; }
-		self.y_walls[(x + y  * self.width) as usize]
-	}
-
-	pub fn x_wall(&self, x: i32, y: i32) -> Tile {
-		if !self.is_within_bounds(x, y) { return Tile::OutOfBounds; }
-		self.x_walls[(x + y  * self.width) as usize]
-	}
-
-	pub fn horizon(&self) -> &i32 {
-		&self.horizon
-	}
-
-	pub fn move_horizon(&mut self, step: i32) {
-		self.horizon += step;
-		if self.horizon < 20  { self.horizon =  20; }
-		if self.horizon > 180 { self.horizon = 180; }
-		self.fp_horizon = self.horizon.to_fp();
-	}
-
-	fn find_horizontal_intersect(&self, origin_x: i32, origin_y: i32, direction: i32) -> Vec<Slice> {
-		let step_x: i32; // distance to next vertical intersect
-		let step_y: i32; // distance to next horizontal intersect
-		let mut x: i32;  // x coordinate of current ray intersect
-		let mut y: i32;  // y coordinate of current ray intersect
-		let flipped: bool;
-
-		let mut slices = Vec::new();
-
-		// determine if looking up or down and find horizontal intersection
-		if direction > trig::ANGLE_0 && direction < trig::ANGLE_180 { // looking down
-			step_x = trig::xstep(direction);
-			step_y = consts::FP_TILE_SIZE;
-
-			y = ((origin_y.to_i32() / consts::TILE_SIZE) * consts::TILE_SIZE + consts::TILE_SIZE).to_fp();
-			x = fp::add(origin_x, fp::mul(fp::sub(y, origin_y), trig::itan(direction)));
-			flipped = true;
-		} else {                     // looking up
-			step_x = trig::xstep(direction);
-			step_y = -consts::FP_TILE_SIZE;
-
-			y = ((origin_y.to_i32() / consts::TILE_SIZE) * consts::TILE_SIZE).to_fp();
-			x = fp::add(origin_x, fp::mul(fp::sub(y, origin_y), trig::itan(direction)));
-			flipped = false;
-		}
-
-		if direction == trig::ANGLE_0 || direction == trig::ANGLE_180 {
-			return slices;
-			// return Slice::new(TextureCode::None, consts::FP_MAX_RAY_LENGTH);
-		}
-
-		// Cast x axis intersect rays, build up xSlice
-
-		while self.is_within_bounds(fp::div(x, consts::FP_TILE_SIZE).to_i32(), fp::div(y, consts::FP_TILE_SIZE).to_i32()) {
-			if let Tile::Wall(texture, _) = self.y_wall(fp::div(x, consts::FP_TILE_SIZE).to_i32(), fp::div(y, consts::FP_TILE_SIZE).to_i32()) {
-				let slice = Slice::new(
-					TextureCode::Wall(texture, x.to_i32() & (consts::TILE_SIZE - 1), flipped),
-					fp::mul(fp::sub(y, origin_y), trig::isin(direction)).abs(),					
-				);
-				slices.push(slice);
-			}
-
-			x = fp::add(x, step_x);
-			y = fp::add(y, step_y);
-		}
-
-		slices
-	}
-
-	fn find_vertical_intersect(&self, origin_x: i32, origin_y: i32, direction: i32) -> Vec<Slice> {
-		let step_x: i32; // distance to next vertical intersect
-		let step_y: i32; // distance to next horizontal intersect
-		let mut x: i32;  // x coordinate of current ray intersect
-		let mut y: i32;  // y coordinate of current ray intersect
-		let flipped: bool;
-
-		let mut slices = Vec::new();
-
-		// determine if looking left or right and find vertical intersection
-		if direction <= trig::ANGLE_90 || direction > trig::ANGLE_270 { // looking right
-			step_x = consts::FP_TILE_SIZE;
-			step_y = trig::ystep(direction);
-			
-			x = ((origin_x.to_i32() / consts::TILE_SIZE) * consts::TILE_SIZE + consts::TILE_SIZE).to_fp();
-			y = fp::add(origin_y, fp::mul(fp::sub(x, origin_x), trig::tan(direction)));
-			
-			flipped = false;
-		} else {
-			step_x = -consts::FP_TILE_SIZE;
-			step_y = trig::ystep(direction);
-			
-			x = (((origin_x.to_i32() / consts::TILE_SIZE) * consts::TILE_SIZE)).to_fp();
-			y = fp::add(origin_y, fp::mul(fp::sub(x, origin_x), trig::tan(direction)));
-			
-			flipped = true;
-		};
-
-		if direction == trig::ANGLE_90 || direction == trig::ANGLE_270 {
-			return slices;
-		}
-
-		// Cast y axis intersect rays, build up ySlice
-		while self.is_within_bounds(fp::div(x, consts::FP_TILE_SIZE).to_i32(), fp::div(y, consts::FP_TILE_SIZE).to_i32()) {
-			if let Tile::Wall(texture, _) = self.x_wall(fp::div(x, consts::FP_TILE_SIZE).to_i32(), fp::div(y, consts::FP_TILE_SIZE).to_i32()) {
-				let slice = Slice::new(
-					TextureCode::Wall(texture, y.to_i32() & (consts::TILE_SIZE - 1), flipped),
-					fp::mul(fp::sub(x, origin_x), trig::icos(direction)).abs()
-				);
-
-				slices.push(slice);
-			}
-
-			x = fp::add(x, step_x);
-			y = fp::add(y, step_y);
-		}
-
-		slices
-	}
-
-	pub fn find_wall_intersections(&self, origin_x: i32, origin_y: i32, direction: i32) -> Vec<Slice> {
-		let hslices = self.find_horizontal_intersect(origin_x, origin_y, direction);
-		let vslices = self.find_vertical_intersect(origin_x, origin_y, direction);
-		
-		let mut slices = Vec::new();
-		slices.reserve(hslices.len() + vslices.len());
-
-		let mut i = 0;
-		let mut j = 0;
-
-		while i < hslices.len() && j < vslices.len() {
-			if hslices[i].distance < vslices[j].distance {
-				slices.push(hslices[i]);
-				i += 1;
-			} else {
-				slices.push(vslices[j]);
-				j += 1;
-			}
-		}
-
-		while i < hslices.len() {			
-			slices.push(hslices[i]);
-			i += 1;
-		}
-
-		while j < vslices.len() {
-			slices.push(vslices[j]);
-			j += 1;
-		}
-
-		slices
-	}
-
-	pub fn find_floor_intersection(&self, origin_x: i32, origin_y: i32, direction: i32, row: i32, column: i32) -> TextureCode {
-		// convert to fixed point
-		let player_height = consts::PLAYER_HEIGHT.to_fp(); 
-		let pp_distance   = consts::DISTANCE_TO_PROJECTION_PLANE.to_fp();
-
-		// adding 1 to the row exactly on the horizon avoids a division by one error
-		// doubles up the texture at the vanishing point, but probably fine
-		let row = if row == self.horizon { (row + 1).to_fp() } else { row.to_fp() };
-
-		let ratio = fp::div(player_height, fp::sub(row, self.fp_horizon));
-
-		let diagonal_distance = fp::mul(fp::floor(fp::mul(pp_distance, ratio)), trig::fisheye_correction(column));
-
-		let x_end = fp::floor(fp::mul(diagonal_distance, trig::cos(direction)));
-		let y_end = fp::floor(fp::mul(diagonal_distance, trig::sin(direction)));
-
-		let x_end = fp::add(origin_x, x_end);
-		let y_end = fp::add(origin_y, y_end);
-		
-		let x = fp::floor(fp::div(x_end, consts::FP_TILE_SIZE)).to_i32();
-		let y = fp::floor(fp::div(y_end, consts::FP_TILE_SIZE)).to_i32();
-		
-		if !self.is_within_bounds(x, y) {
-			return TextureCode::None;
-		}
-
-		TextureCode::Floor(42, x_end.to_i32() & (consts::TILE_SIZE - 1), y_end.to_i32() & (consts::TILE_SIZE - 1))
-	}
-
-	pub fn find_ceiling_intersection(&self, origin_x: i32, origin_y: i32, direction: i32, row: i32, column: i32) -> TextureCode {
-		// convert to fixed point
-		let player_height = consts::PLAYER_HEIGHT.to_fp(); 
-		let pp_distance   = consts::DISTANCE_TO_PROJECTION_PLANE.to_fp();
-		let wall_height   = consts::WALL_HEIGHT.to_fp();
-
-		// adding 1 to the row exactly on the horizon avoids a division by one error
-		// doubles up the texture at the vanishing point, but probably fine
-		let row = if row == self.horizon { (row + 1).to_fp() } else { row.to_fp() };
-
-		let ratio = fp::div(fp::sub(wall_height, player_height), fp::sub(self.fp_horizon, row));
-
-		let diagonal_distance = fp::mul(fp::floor(fp::mul(pp_distance, ratio)), trig::fisheye_correction(column));
-
-		let x_end = fp::floor(fp::mul(diagonal_distance, trig::cos(direction)));
-		let y_end = fp::floor(fp::mul(diagonal_distance, trig::sin(direction)));
-
-		let x_end = fp::add(origin_x, x_end);
-		let y_end = fp::add(origin_y, y_end);
-		
-		let x = fp::floor(fp::div(x_end, consts::FP_TILE_SIZE)).to_i32();
-		let y = fp::floor(fp::div(y_end, consts::FP_TILE_SIZE)).to_i32();
-		
-		if !self.is_within_bounds(x, y) {
-			return TextureCode::None;
-		}
-
-		TextureCode::Ceiling(23, x_end.to_i32() & (consts::TILE_SIZE - 1), y_end.to_i32() & (consts::TILE_SIZE - 1))
-	}
-}

+ 65 - 4
engine/src/render/mod.rs → engine/src/render.rs

@@ -1,8 +1,9 @@
-use crate::maths::trig;
-// use crate::maths::fp;
-// use crate::maths::fp::{ ToFixedPoint, FromFixedPoint };
-use crate::consts;
+use crate::scene::Scene;
+use crate::trig;
 use serde_json;
+use shared::consts;
+use shared::fp;
+use shared::fp::{ ToFixedPoint, FromFixedPoint };
 
 enum RayCastResult {
 	OutOfBounds,
@@ -72,6 +73,10 @@ impl Camera {
 		self.y = y;
 	}
 
+	pub fn angle(&self) -> i32 {
+		self.angle
+	}
+
 	pub fn from_json(json: &serde_json::Value) -> Result<Camera, &'static str> {
 		let x = json["x"].as_i64().unwrap() as i32;
 		let y = json["y"].as_i64().unwrap() as i32;
@@ -81,6 +86,62 @@ impl Camera {
 	}
 }
 
+pub struct Renderer {
+	
+}
+
+impl Renderer {
+	pub fn render(&self, buf: &mut[u8], scene: &Scene, camera: &Camera) {
+		// angle is the direction camera is facing
+		// need to start out sweep 30 degrees to the left
+		let mut angle = if camera.angle() < trig::ANGLE_30 {
+			camera.angle() - trig::ANGLE_30 + trig::ANGLE_360
+		} else {
+			camera.angle() - trig::ANGLE_30
+		};
+
+		// ray casting uses fixed point notation, so convert camera coordinates to fixed point
+		let origin_x = camera.x().to_fp();
+		let origin_y = camera.y().to_fp();
+
+		// sweep of the rays will be through 60 degrees
+		for sweep in 0..trig::ANGLE_60 {
+			println!("{}", trig::fisheye_correction(sweep));
+		// 	let slices = self.world.find_wall_intersections(origin_x, origin_y, angle);
+		// 	if slices.len() <= 0 { continue; }
+		// 	let mut parameters: Vec<ColumnRenderParameters> = Vec::new();
+		// 	parameters.reserve(slices.len());
+
+		// 	// for each slice, get a reference to its texture and figure out how
+		// 	// it should be drawn
+		// 	for slice in slices {
+		// 		let dist = fp::div(slice.distance, fisheye_correction!(sweep)).to_i32();
+		// 		let wall_height: i32 = trig::wall_height!(dist);
+		// 		let y_min = std::cmp::max(0, self.world.horizon() - wall_height / 2);
+		// 		let y_max = std::cmp::min(consts::PROJECTION_PLANE_HEIGHT - 1, self.world.horizon() + wall_height / 2);
+		// 		let step: f64 = consts::TEXTURE_HEIGHT as f64 / wall_height as f64;
+				
+		// 		if let raycast::TextureCode::Wall(code, texture_column, flipped) = slice.texture {
+		// 			let texture = self.textures.get(code, texture_column, flipped);
+		// 			let tex_pos: f64 = (y_min as f64 - *self.world.horizon() as f64 + wall_height as f64 / 2.0) * step;
+		// 			parameters.push(ColumnRenderParameters::new(texture, step, wall_height, tex_pos, y_min, y_max))	
+		// 		}
+		// 	}
+
+		// 	self.draw_wall_column(buf, origin_x, origin_y, angle, sweep, &mut parameters);
+
+			angle += 1;
+			if angle >= trig::ANGLE_360 {
+				angle -= trig::ANGLE_360;
+			}
+		}
+	}
+
+	pub fn from_json(json: &serde_json::Value) -> Result<Renderer, &'static str> {
+		Ok(Renderer {})
+	}
+}
+
 // struct Colour {
 // 	r: u8,
 // 	g: u8,

+ 0 - 0
engine/src/scene/mod.rs → engine/src/scene.rs


+ 8 - 21
engine/src/trig.rs

@@ -1,22 +1,9 @@
-use crate::consts::{ PROJECTION_PLANE_WIDTH, MAX_RAY_LENGTH };
-use core::f64::consts::PI;
+use macros::insert_lookup_tables;
 
-include!(concat!(env!("OUT_DIR"), "/lookup.rs"));
+pub use shared::consts::trig::*;
+pub use shared::radian;
 
-pub const ANGLE_60:  i32 = PROJECTION_PLANE_WIDTH;
-
-pub const ANGLE_0:   i32 = 0;
-pub const ANGLE_5:   i32 = ANGLE_60 / 12;
-pub const ANGLE_10:  i32 = ANGLE_60 / 6;
-pub const ANGLE_30:  i32 = ANGLE_60 / 2;
-pub const ANGLE_90:  i32 = ANGLE_30 * 3;
-pub const ANGLE_180: i32 = ANGLE_60 * 3;
-pub const ANGLE_270: i32 = ANGLE_90 * 3;
-pub const ANGLE_360: i32 = ANGLE_60 * 6;
-
-pub fn radian(angle: i32) -> f64 {
-	angle as f64 * PI / ANGLE_180 as f64
-}
+insert_lookup_tables!();
 
 pub fn cos(degrees: i32) -> i32 {
 	COS[degrees as usize]
@@ -42,11 +29,11 @@ pub fn itan(degrees: i32) -> i32 {
 	ITAN[degrees as usize]
 }
 
-pub fn xstep(degrees: i32) -> i32 {
+pub fn x_step(degrees: i32) -> i32 {
 	X_STEP[degrees as usize]
 }
 
-pub fn ystep(degrees: i32) -> i32 {
+pub fn y_step(degrees: i32) -> i32 {
 	Y_STEP[degrees as usize]
 }
 
@@ -54,6 +41,6 @@ pub fn fisheye_correction(degrees: i32) -> i32 {
 	FISHEYE[degrees as usize]
 }
 
-pub fn wall_height(dist: i32) -> i32 {
-	WALL_HEIGHT[dist.min(MAX_RAY_LENGTH) as usize]
+pub fn wall_height(distance: i32) -> i32 {
+	WALL_HEIGHT[distance.min(shared::consts::MAX_RAY_LENGTH) as usize]
 }

+ 0 - 55
engine/src/utils.rs

@@ -1,58 +1,3 @@
-use std::iter::Peekable;
-use std::cmp::Ordering;
-
-// ============================================
-// merge code by shepmaster
-// https://stackoverflow.com/a/32020190/4261231
-// ============================================
-
-pub struct MergeAscending<L, R>
-    where L: Iterator<Item = R::Item>,
-          R: Iterator,
-{
-    left: Peekable<L>,
-    right: Peekable<R>,
-}
-
-impl<L, R> MergeAscending<L, R>
-    where L: Iterator<Item = R::Item>,
-          R: Iterator,
-{
-    pub fn new(left: L, right: R) -> Self {
-        MergeAscending {
-            left: left.peekable(),
-            right: right.peekable(),
-        }
-    }
-}
-
-impl<L, R> Iterator for MergeAscending<L, R>
-    where L: Iterator<Item = R::Item>,
-          R: Iterator,
-          L::Item: Ord,
-{
-    type Item = L::Item;
-
-    fn next(&mut self) -> Option<L::Item> {
-        let which = match (self.left.peek(), self.right.peek()) {
-            (Some(l), Some(r)) => Some(l.cmp(r)),
-            (Some(_), None)    => Some(Ordering::Less),
-            (None, Some(_))    => Some(Ordering::Greater),
-            (None, None)       => None,
-        };
-
-        match which {
-            Some(Ordering::Less)    => self.left.next(),
-            Some(Ordering::Equal)   => self.left.next(),
-            Some(Ordering::Greater) => self.right.next(),
-            None                    => None,
-        }
-    }
-}
-// ============================================
-// end shepmaster code
-// ============================================
-
 pub fn set_panic_hook() {
     // When the `console_error_panic_hook` feature is enabled, we can call the
     // `set_panic_hook` function at least once during initialization, and then

+ 12 - 0
macros/Cargo.toml

@@ -0,0 +1,12 @@
+[package]
+name = "macros"
+version = "0.1.0"
+edition = "2021"
+
+[lib]
+proc-macro=true
+
+[dependencies]
+proc-macro2 = "1.0.63"
+quote = "1.0.29"
+shared = { path = "../shared" }

+ 161 - 0
macros/src/lib.rs

@@ -0,0 +1,161 @@
+use shared::consts;
+use shared::fp::{ ToFixedPoint };
+use shared::radian;
+use proc_macro2::TokenStream;
+use quote::quote;
+
+const TILE_SIZE: f64 = consts::TILE_SIZE as f64;
+
+fn clamp(x: i32, min: i32, max: i32) -> i32 {
+    if x < min {
+        min
+    } else if x > max {
+        max
+    } else {
+        x
+    }
+}
+
+fn declare_trig_tables() -> TokenStream {
+    const SIZE: usize    = (consts::ANGLE_360 + 1) as usize;
+
+    let mut sin: [i32; SIZE] = [0; SIZE];
+    let mut cos: [i32; SIZE] = [0; SIZE];
+    let mut tan: [i32; SIZE] = [0; SIZE];
+    let mut isin: [i32; SIZE] = [0; SIZE];
+    let mut icos: [i32; SIZE] = [0; SIZE];
+    let mut itan: [i32; SIZE] = [0; SIZE];
+    
+    for i in 0..SIZE {
+        sin[i] = (radian!(i).sin()).to_fp();
+        cos[i] = (radian!(i).cos()).to_fp();
+        tan[i] = (radian!(i).tan()).to_fp();
+        isin[i] = (1.0 / radian!(i).sin()).to_fp();
+        icos[i] = (1.0 / radian!(i).cos()).to_fp();
+        itan[i] = (1.0 / radian!(i).tan()).to_fp() ;
+    }
+
+    quote! {
+		static SIN: [i32; #SIZE] = [ #(#sin),* ];
+	    static COS: [i32; #SIZE] = [ #(#cos),* ];
+	    static TAN: [i32; #SIZE] = [ #(#tan),* ];
+	    static ISIN: [i32; #SIZE] = [ #(#isin),* ];
+	    static ICOS: [i32; #SIZE] = [ #(#icos),* ];
+	    static ITAN: [i32; #SIZE] = [ #(#itan),* ];
+	}
+}
+
+fn declare_step_tables() -> TokenStream {
+    const SIZE: usize    = (consts::ANGLE_360 + 1) as usize;
+
+	let mut x_step: [i32; SIZE] = [0; SIZE];
+    let mut y_step: [i32; SIZE] = [0; SIZE];
+
+    for i in 0..SIZE {
+        let mut step: f64;
+
+        if radian!(i).tan() == 0.0 {
+            step = f64::MAX
+        } else {
+            step = TILE_SIZE / radian!(i).tan();
+
+            if i >= consts::ANGLE_90.try_into().unwrap() && i < consts::ANGLE_270.try_into().unwrap() {
+                if step > 0.0 {
+                  step = -step;
+                }
+            } else {
+                if step < 0.0 {
+                  step = -step;
+                }
+            }
+        }
+
+        x_step[i] = step.to_fp();
+    }
+
+    for i in 0..SIZE {
+        let mut step = TILE_SIZE * radian!(i).tan();
+
+        if i >= consts::ANGLE_0.try_into().unwrap() && i < consts::ANGLE_180.try_into().unwrap() {
+            if step < 0.0 {
+              step = -step;
+            }
+        } else {
+            if step > 0.0 {
+              step = -step;
+            }
+        }
+
+        y_step[i] = step.to_fp();
+    }
+
+    quote! {
+    	static X_STEP: [i32; #SIZE] = [ #(#x_step),* ];
+    	static Y_STEP: [i32; #SIZE] = [ #(#y_step),* ];
+    }
+}
+
+fn declare_fisheye_table() -> TokenStream {
+    const SIZE: usize = consts::PROJECTION_PLANE_WIDTH as usize;
+
+    let mut fisheye: [i32; SIZE] = [0; SIZE];
+
+    for i in 0..SIZE {
+        fisheye[i] = (1.0 / radian!(i as i32 - consts::ANGLE_30 as i32).cos()).to_fp();
+    }
+
+    quote! {
+        static FISHEYE: [i32; #SIZE] = [ #(#fisheye),* ];
+    }
+}
+
+fn declare_wall_height_table() -> TokenStream {
+    const SIZE: usize = (consts::MAX_RAY_LENGTH + 1) as usize;
+
+    let mut wall_height: [i32; SIZE] = [0; SIZE];
+
+    for i in 0..=consts::MAX_RAY_LENGTH {
+        wall_height[i as usize] = clamp(consts::WALL_HEIGHT_SCALE_FACTOR / i.max(1), consts::WALL_HEIGHT_MIN, consts::WALL_HEIGHT_MAX);
+    }
+
+    quote! {
+        static WALL_HEIGHT: [i32; #SIZE] = [ #(#wall_height),* ];
+    }
+}
+
+fn declare_floor_ceiling_tables() -> TokenStream {
+    // let mut FLOOR_TEXTURE_Y_RAYS: [i32; PROJECTION_PLANE_WIDTH * PROJECTION_PLANE_CENTRE_Y] = [0; PROJECTION_PLANE_WIDTH * PROJECTION_PLANE_CENTRE_Y];
+    // let mut FLOOR_TEXTURE_X_RAYS: [i32; PROJECTION_PLANE_WIDTH * PROJECTION_PLANE_CENTRE_Y] = [0; PROJECTION_PLANE_WIDTH * PROJECTION_PLANE_CENTRE_Y];
+
+    // for y in (PROJECTION_PLANE_CENTRE_Y + 1)..PROJECTION_PLANE_HEIGHT {
+    //     let ratio: f64 = PLAYER_HEIGHT as f64 / (y - PROJECTION_PLANE_CENTRE_Y) as f64;
+    //     for sweep in 0..PROJECTION_PLANE_WIDTH {
+    //         let distance = DISTANCE_TO_PROJECTION_PLANE as f64 * ratio * fisheye[sweep];
+    //     }
+    // }
+
+    // var diagonalDistance=Math.floor((this.fPlayerDistanceToTheProjectionPlane * ratio) * (this.fFishTable[castColumn]));
+
+    // var yEnd = Math.floor(diagonalDistance * this.fSinTable[castArc]);
+    // var xEnd = Math.floor(diagonalDistance * this.fCosTable[castArc]);
+    quote! {
+
+    }
+}
+
+#[proc_macro]
+pub fn insert_lookup_tables(_input: proc_macro::TokenStream) -> proc_macro::TokenStream {
+	let trig_tables          = declare_trig_tables();
+	let step_tables          = declare_step_tables();
+    let fisheye_table        = declare_fisheye_table();
+    let wall_height_table    = declare_wall_height_table();
+    let floor_ceiling_tables = declare_floor_ceiling_tables();
+
+	proc_macro::TokenStream::from(quote! {
+		#trig_tables
+		#step_tables
+        #fisheye_table
+        #wall_height_table
+        #floor_ceiling_tables
+	})
+}

+ 9 - 0
shared/Cargo.toml

@@ -0,0 +1,9 @@
+[package]
+name = "shared"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dev-dependencies]
+float-cmp = "0.9.0"

+ 7 - 0
shared/src/consts.rs

@@ -0,0 +1,7 @@
+pub mod render;
+pub mod display;
+pub mod trig;
+
+pub use crate::consts::render::*;
+pub use crate::consts::display::*;
+pub use crate::consts::trig::*;

+ 7 - 0
shared/src/consts/display.rs

@@ -0,0 +1,7 @@
+pub const PROJECTION_PLANE_HEIGHT: i32      = 200;
+pub const PROJECTION_PLANE_WIDTH: i32       = 320;
+pub const PROJECTION_PLANE_HORIZON: i32     = PROJECTION_PLANE_HEIGHT / 2;
+pub const DISTANCE_TO_PROJECTION_PLANE: i32 = 277;
+
+pub const TILE_SIZE: i32    = 64;
+pub const FP_TILE_SIZE: i32 = TILE_SIZE << 16;

+ 12 - 0
shared/src/consts/render.rs

@@ -0,0 +1,12 @@
+pub const TEXTURE_WIDTH: usize  = 64;
+pub const TEXTURE_HEIGHT: usize = 64;
+
+pub const WALL_HEIGHT_SCALE_FACTOR: i32 = 18000;
+pub const WALL_HEIGHT_MIN: i32          = 8;
+pub const WALL_HEIGHT_MAX: i32          = 640;
+
+pub const MAX_RAY_LENGTH: i32    = 2048;
+pub const FP_MAX_RAY_LENGTH: i32 = MAX_RAY_LENGTH << 16;
+
+pub const WALL_HEIGHT: i32   = 64;
+pub const PLAYER_HEIGHT: i32 = WALL_HEIGHT >> 1;

+ 12 - 0
shared/src/consts/trig.rs

@@ -0,0 +1,12 @@
+use crate::consts::display::PROJECTION_PLANE_WIDTH;
+
+pub const ANGLE_60:  i32 = PROJECTION_PLANE_WIDTH;
+
+pub const ANGLE_0:   i32 = 0;
+pub const ANGLE_5:   i32 = ANGLE_60 / 12;
+pub const ANGLE_10:  i32 = ANGLE_60 / 6;
+pub const ANGLE_30:  i32 = ANGLE_60 / 2;
+pub const ANGLE_90:  i32 = ANGLE_30 * 3;
+pub const ANGLE_180: i32 = ANGLE_60 * 3;
+pub const ANGLE_270: i32 = ANGLE_90 * 3;
+pub const ANGLE_360: i32 = ANGLE_60 * 6;

+ 0 - 1
engine/src/fp.rs → shared/src/fp.rs

@@ -1,6 +1,5 @@
 const FP_SHIFT: i32 = 16;
 const FP_MULT: f64  = 65536.0;
-const FP_HALF: f64  = 32768.0;
 
 const FP_FLOOR_MASK: i32 = !((1 << FP_SHIFT) - 1);
 

+ 9 - 0
shared/src/lib.rs

@@ -0,0 +1,9 @@
+pub mod consts;
+pub mod fp;
+
+#[macro_export]
+macro_rules! radian {
+    ($angle:expr) => {
+        $angle as f64 * std::f64::consts::PI / shared::consts::ANGLE_180 as f64
+    }
+}

+ 0 - 0
img2tex/Cargo.toml → tools/img2tex/Cargo.toml


+ 0 - 0
img2tex/README.md → tools/img2tex/README.md


+ 0 - 0
img2tex/src/lib.rs → tools/img2tex/src/lib.rs


+ 0 - 0
img2tex/src/main.rs → tools/img2tex/src/main.rs