Forráskód Böngészése

Floor texturing implemented

Gary Munnelly 2 éve
szülő
commit
76967e700e
5 módosított fájl, 118 hozzáadás és 74 törlés
  1. 23 1
      engine/build.rs
  2. 6 1
      engine/src/consts.rs
  3. 6 0
      engine/src/fp.rs
  4. 50 16
      engine/src/lib.rs
  5. 33 56
      engine/src/raycast.rs

+ 23 - 1
engine/build.rs

@@ -2,7 +2,10 @@
 use std::f64::consts::PI;
 use std::{env, fs, path::Path};
 
-const PROJECTION_PLANE_WIDTH: usize = 320;
+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;
@@ -18,6 +21,10 @@ 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
@@ -143,6 +150,21 @@ fn main() {
 
     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");

+ 6 - 1
engine/src/consts.rs

@@ -2,6 +2,8 @@ use crate::fp::ToFixedPoint;
 
 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;
 
@@ -13,4 +15,7 @@ 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 FP_MAX_RAY_LENGTH: i32 = MAX_RAY_LENGTH << 16;
+
+pub const DISTANCE_TO_PROJECTION_PLANE: i32 = 277;
+pub const PLAYER_HEIGHT: i32 = 32;

+ 6 - 0
engine/src/fp.rs

@@ -2,6 +2,8 @@ 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;
 }
@@ -49,6 +51,10 @@ 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::*;

+ 50 - 16
engine/src/lib.rs

@@ -352,7 +352,7 @@ impl Cluiche {
 		self.player.rotation(self.player.rotation + self.player.rotate_speed);
 	}
 
-	fn draw_wall_column(&self, buf: &mut[u8], column: i32, parameters: &mut Vec<ColumnRenderParameters>) {
+	fn draw_wall_column(&self, buf: &mut[u8], origin_x: i32, origin_y: i32, direction: i32, column: i32, parameters: &mut Vec<ColumnRenderParameters>) {
 		let y_min = parameters[0].y_min;
 		let y_max = parameters[0].y_max;
 
@@ -372,8 +372,40 @@ impl Cluiche {
 				(r, g, b, a) = blend_colours(r, g, b, a, slice.texture[tex_y + 0], slice.texture[tex_y + 1], slice.texture[tex_y + 2], slice.texture[tex_y + 3]);
 			}
 
+			if a < 255 && y >= consts::PROJECTION_PLANE_HORIZON {
+				let floor = self.world.find_floor_intersection(origin_x, origin_y, direction, y, column);
+
+				if let raycast::TextureCode::Floor(code, x, y) = floor {
+					let texture = self.textures.get(code, x, false);
+					let tex_y = (y * 4) as usize;
+					(r, g, b, a) = blend_colours(r, g, b, a, texture[tex_y + 0], texture[tex_y + 1], texture[tex_y + 2], texture[tex_y + 3]);
+				} else {
+					(r, g, b, a) = blend_colours(r, g, b, a, 0x70, 0x70, 0x70, 0xFF);
+				}
+			}
+
 			(buf[idx + 0], buf[idx + 1], buf[idx + 2], buf[idx + 3]) = blend_colours(r, g, b, a, buf[idx + 0], buf[idx + 1], buf[idx + 2], buf[idx + 3]);
 		}
+
+		for y in (y_max + 1)..consts::PROJECTION_PLANE_HEIGHT {
+			let floor = self.world.find_floor_intersection(origin_x, origin_y, direction, y, column);
+			let idx: usize = 4 * (column + y * consts::PROJECTION_PLANE_WIDTH) as usize;
+
+			if let raycast::TextureCode::Floor(code, x, y) = floor {
+				let texture = self.textures.get(code, x, false);
+				let tex_y = (y * 4) as usize;
+
+				buf[idx + 0] = texture[tex_y + 0];
+				buf[idx + 1] = texture[tex_y + 1];
+				buf[idx + 2] = texture[tex_y + 2];
+				buf[idx + 3] = texture[tex_y + 3];
+			} else {
+				buf[idx + 0] = 0x70;
+				buf[idx + 1] = 0x70;
+				buf[idx + 2] = 0x70;
+				buf[idx + 3] = 0xFF;
+			}
+		}
 	}
 
 	fn draw_background(&self, buf: &mut[u8]) {
@@ -388,15 +420,15 @@ impl Cluiche {
 			}
 		}
 
-		for y in consts::PROJECTION_PLANE_HEIGHT / 2..consts::PROJECTION_PLANE_HEIGHT {
-			for x in 0..consts::PROJECTION_PLANE_WIDTH {
-				let idx: usize = 4 * (x + y * consts::PROJECTION_PLANE_WIDTH) as usize;
-				buf[idx + 0] = 0x70;
-				buf[idx + 1] = 0x70;
-				buf[idx + 2] = 0x70;
-				buf[idx + 3] = 0xFF; // alpha channel
-			}
-		}
+		// for y in consts::PROJECTION_PLANE_HEIGHT / 2..consts::PROJECTION_PLANE_HEIGHT {
+		// 	for x in 0..consts::PROJECTION_PLANE_WIDTH {
+		// 		let idx: usize = 4 * (x + y * consts::PROJECTION_PLANE_WIDTH) as usize;
+		// 		buf[idx + 0] = 0x70;
+		// 		buf[idx + 1] = 0x70;
+		// 		buf[idx + 2] = 0x70;
+		// 		buf[idx + 3] = 0xFF; // alpha channel
+		// 	}
+		// }
 	}
 
 	pub fn render(&mut self, buf: &mut[u8]) {
@@ -416,7 +448,7 @@ impl Cluiche {
 
 		// sweep of the rays will be through 60 degrees
 		for sweep in 0..trig::ANGLE_60 {
-			let slices = self.world.find_closest_intersect(origin_x, origin_y, angle);
+			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());
@@ -429,13 +461,15 @@ impl Cluiche {
 				let y_min = std::cmp::max(0, (200 - wall_height) / 2);
 				let y_max = std::cmp::min(200 - 1, y_min + wall_height);
 				let step: f64 = consts::TEXTURE_HEIGHT as f64 / wall_height as f64;
-				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 - consts::PROJECTION_PLANE_HEIGHT as f64 / 2.0 + wall_height as f64 / 2.0) * step;
-				parameters.push(ColumnRenderParameters::new(texture, step, wall_height, tex_pos, y_min, y_max))
+				
+				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 - consts::PROJECTION_PLANE_HEIGHT as f64 / 2.0 + 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, sweep, &mut parameters);
+			self.draw_wall_column(buf, origin_x, origin_y, angle, sweep, &mut parameters);
 
 			angle += 1;
 			if angle >= trig::ANGLE_360 {

+ 33 - 56
engine/src/raycast.rs

@@ -5,7 +5,9 @@ use crate::fp::{ ToFixedPoint, FromFixedPoint };
 
 #[derive(Debug, Copy, Clone)]
 pub enum TextureCode {
-	Wall(u8, i32, bool)
+	None,
+	Wall(u8, i32, bool),
+	Floor(u8, i32, i32),
 }
 
 #[derive(Debug, Copy, Clone)]
@@ -216,7 +218,7 @@ impl World {
 		slices
 	}
 
-	pub fn find_closest_intersect(&self, origin_x: i32, origin_y: i32, direction: i32) -> Vec<Slice> {
+	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);
 		
@@ -248,59 +250,34 @@ impl World {
 
 		slices
 	}
-}
 
-// #[cfg(test)]
-// mod test {
-// 	use super::*;
-// 	use float_cmp;
-
-// 	#[test]
-// 	fn create_new_world() {
-// 		let width: i32 = 3;
-// 		let height: i32 = 3;
-// 		let world_str = "WHWVOVWHW";
-// 		let world = World::new(width, height, world_str).unwrap();
-
-// 		assert_eq!(world.width, width);
-// 		assert_eq!(world.height, height);
-// 		assert_eq!(world.y_walls, vec!(
-// 			Tile::Wall,  Tile::Wall,  Tile::Wall,
-// 			Tile::Empty, Tile::Empty, Tile::Empty,
-// 			Tile::Wall,  Tile::Wall,  Tile::Wall
-// 		));
-// 		assert_eq!(world.x_walls, vec!(
-// 			Tile::Wall, Tile::Empty, Tile::Wall,
-// 			Tile::Wall, Tile::Empty, Tile::Wall,
-// 			Tile::Wall, Tile::Empty, Tile::Wall
-// 		));
-// 	}
-
-// 	#[test]
-// 	fn cast_ray() {
-// 		let width: i32 = 3;
-// 		let height: i32 = 3;
-// 		let world_str = "WHWVOVWHW";
-// 		let world = World::new(width, height, world_str).unwrap();
-
-// 		assert_eq!(world.find_horizontal_intersect(64.to_fp(), 64.to_fp(), trig::ANGLE_0).to_i32(),   consts::MAX_RAY_LENGTH);
-// 		assert_eq!(world.find_horizontal_intersect(64.to_fp(), 64.to_fp(), trig::ANGLE_90).to_i32(),  64);
-// 		assert_eq!(world.find_horizontal_intersect(64.to_fp(), 64.to_fp(), trig::ANGLE_180).to_i32(), consts::MAX_RAY_LENGTH);
-// 		assert_eq!(world.find_horizontal_intersect(64.to_fp(), 64.to_fp(), trig::ANGLE_270).to_i32(), 64);
+	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 horizon       = consts::PROJECTION_PLANE_HORIZON.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 == consts::PROJECTION_PLANE_HORIZON { (row + 1).to_fp() } else { row.to_fp() };
+
+		let ratio = fp::div(player_height, fp::sub(row, 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);
 		
-// 		assert_eq!(world.find_vertical_intersect(64.to_fp(), 64.to_fp(), trig::ANGLE_0).to_i32(),   64);
-// 		assert_eq!(world.find_vertical_intersect(64.to_fp(), 64.to_fp(), trig::ANGLE_90).to_i32(),  consts::MAX_RAY_LENGTH);
-// 		assert_eq!(world.find_vertical_intersect(64.to_fp(), 64.to_fp(), trig::ANGLE_180).to_i32(), 64);
-// 		assert_eq!(world.find_vertical_intersect(64.to_fp(), 64.to_fp(), trig::ANGLE_270).to_i32(), consts::MAX_RAY_LENGTH);
-
-// 		let world = World::new(7, 7, "WHHHHHWVOOOOOVVOOOOOVVOOOOOVVOOOOOVVOOOOOVWHHHHHW").unwrap();
-// 		assert_eq!(world.find_horizontal_intersect(76.to_fp(), 76.to_fp(), 295).to_i32(), 374);
-// 		assert_eq!(world.find_vertical_intersect(76.to_fp(), 76.to_fp(), 295).to_i32(), consts::MAX_RAY_LENGTH);
-
-// 		assert_eq!(world.find_horizontal_intersect(160.to_fp(), 160.to_fp(), 1730).to_i32(), 274);
-// 		assert_eq!(world.find_vertical_intersect(160.to_fp(), 160.to_fp(), 1730).to_i32(), consts::MAX_RAY_LENGTH);
-
-// 		let world = World::new(13, 6, "WHHHHWHWHHHHWVOOOOVOVOOOOVVOOOOVOVOOOOVVOOOOVOOOOOOVVOOOOOOVOOOOVWHHHHWHWHHHWW").unwrap();
-// 		assert_eq!(world.find_vertical_intersect(698.to_fp(), 145.to_fp(), 820).to_i32(), 278);
-// 	}
-// }
+		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))
+	}
+}