Created
December 22, 2020 18:03
-
-
Save matthewd673/9081337a7ff1101c721c7ccdfbb85ffb to your computer and use it in GitHub Desktop.
ray_engine
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//ray_engine rs | |
//requires sdl2 crate | |
extern crate sdl2; | |
use sdl2::pixels::Color; | |
use sdl2::event::Event; | |
use sdl2::keyboard::Keycode; | |
use sdl2::rect::Point; | |
use sdl2::rect::Rect; | |
use std::fs; | |
use std::f32::consts; | |
use std::time::Duration; | |
struct Camera { | |
x: f32, | |
y: f32, | |
a: f32, | |
speed: f32, | |
r_speed: f32, | |
view_dist: f32, | |
} | |
struct Wall { | |
a: Point, | |
b: Point, | |
color: Color, | |
} | |
struct WorldData { | |
walls: Vec<Wall>, | |
} | |
struct FPoint { | |
x: f32, | |
y: f32, | |
} | |
struct Intersection { | |
pos: FPoint, | |
order: i32, | |
} | |
struct Column { | |
dist: f32, | |
color: Color, | |
} | |
const TWOPI: f32 = consts::PI * 2.0; | |
const WIDTH: i32 = 320; | |
const HEIGHT: i32 = 200; | |
pub fn main() { | |
//setup | |
let mut cam: Camera = Camera { | |
x: 0.0, | |
y: 0.0, | |
a: 0.0, | |
speed: 1.0, | |
r_speed: 0.1, | |
view_dist: 200.0 | |
}; | |
let world_data: WorldData = load_world_data("world.txt"); | |
//SDL2 | |
let sdl_context = sdl2::init().unwrap(); | |
let video_subsystem = sdl_context.video().unwrap(); | |
let window = video_subsystem.window("ray_engine", 320, 480) | |
.position_centered() | |
.build() | |
.unwrap(); | |
let mut canvas = window.into_canvas().build().unwrap(); | |
//canvas.set_draw_color(Color::RGB(0, 255, 255)); | |
//canvas.clear(); | |
//canvas.present(); | |
let mut event_pump = sdl_context.event_pump().unwrap(); | |
'running: loop { | |
//clear canvas | |
canvas.set_draw_color(Color::RGB(0, 0, 0)); | |
canvas.clear(); | |
//render loop | |
//draw world | |
canvas.set_draw_color(Color::RGB(255, 0, 0)); | |
for w in &world_data.walls { | |
canvas.draw_line(w.a, w.b); | |
} | |
//draw camera pos | |
canvas.set_draw_color(Color::RGB(255, 255, 255)); | |
canvas.fill_rect(Rect::new(cam.x as i32 - 1, cam.y as i32 - 1, 3, 3)); | |
//draw camera ray | |
//clean ray | |
while cam.a > TWOPI { | |
cam.a -= TWOPI; | |
} | |
while cam.a < -TWOPI { | |
cam.a += TWOPI; | |
} | |
canvas.set_draw_color(Color::RGB(255, 255, 0)); | |
canvas.draw_line( | |
Point::new(cam.x as i32, cam.y as i32), | |
Point::new( | |
(cam.x + cam.a.cos() * cam.view_dist) as i32, | |
(cam.y + cam.a.sin() * cam.view_dist) as i32, | |
) | |
); | |
//key events | |
for event in event_pump.poll_iter() { | |
match event { | |
Event::KeyDown { keycode: Some(Keycode::W), .. } => { | |
cam.x += cam.a.cos() * cam.speed; | |
cam.y += cam.a.sin() * cam.speed; | |
}, | |
Event::KeyDown { keycode: Some(Keycode::S), .. } => { | |
cam.x -= cam.a.cos() * cam.speed; | |
cam.y -= cam.a.sin() * cam.speed; | |
}, | |
Event::KeyDown { keycode: Some(Keycode::Left), .. } => { | |
cam.a += cam.r_speed; | |
} | |
Event::KeyDown { keycode: Some(Keycode::Right), .. } => { | |
cam.a -= cam.r_speed; | |
} | |
//quit | |
Event::Quit {..} | | |
Event::KeyDown { keycode: Some(Keycode::Escape), .. } => { | |
break 'running | |
}, | |
_ => {} | |
} | |
} | |
//render line collisions | |
let scan_result = scan_rays(cam.a, &world_data, &FPoint { x: cam.x, y: cam.y }, WIDTH, cam.view_dist, 90); | |
let r_points = scan_result.0; | |
let cols = scan_result.1; | |
canvas.set_draw_color(Color::RGB(0, 255, 0)); | |
for p in r_points { | |
canvas.draw_rect(Rect::new(p.pos.x as i32 - 1, p.pos.y as i32 - 1, 3, 3)); | |
} | |
let mut x = 0; | |
for c in cols { | |
canvas.set_draw_color(c.color); | |
let top_offset = 200; | |
let max_height = 200; | |
let min_height = 1; | |
let mut height = 0; | |
if c.dist < cam.view_dist { | |
// 1.0 - (c.dist / cam.view_dist) <-- percentage of closeness | |
// top_offset + (max_height / 2) <-- vertical center [temp: 300] | |
// | |
height = ((1.0 - (c.dist / cam.view_dist)) * (max_height - min_height) as f32) as i32; | |
} | |
let half_height = height / 2; | |
canvas.draw_rect(Rect::new(x, 300 - half_height, 1, height as u32)); | |
x += 1; | |
} | |
// The rest of the game loop goes here... | |
canvas.present(); | |
::std::thread::sleep(Duration::new(0, 1_000_000_000u32 / 60)); | |
} | |
} | |
fn load_world_data(filename: &str) -> WorldData { | |
let mut wall_vec: Vec<Wall> = vec![]; | |
let data = fs::read_to_string(filename) | |
.expect("Could not read file"); | |
let lines: Vec<&str> = data.split("\r\n").collect(); | |
let mut wall_col = Color::RGB(255, 255, 255); | |
for l in lines { | |
let p_split: Vec<&str> = l.split(' ').collect(); | |
if p_split[0] == ";" { | |
continue; | |
} | |
if p_split[0] == "e" { | |
let p_a_split: Vec<&str> = p_split[1].split(',').collect(); | |
let p_b_split: Vec<&str> = p_split[2].split(',').collect(); | |
let p_a = Point::new( | |
p_a_split[0].parse::<i32>().unwrap(), | |
p_a_split[1].parse::<i32>().unwrap() | |
); | |
let p_b = Point::new( | |
p_b_split[0].parse::<i32>().unwrap(), | |
p_b_split[1].parse::<i32>().unwrap(), | |
); | |
wall_vec.push(Wall { a: p_a, b: p_b, color: wall_col }); | |
} | |
if p_split[0] == "c" { | |
let p_c_split: Vec<&str> = p_split[1].split(',').collect(); | |
let r = p_c_split[0].parse::<u8>().unwrap(); | |
let g = p_c_split[1].parse::<u8>().unwrap(); | |
let b = p_c_split[2].parse::<u8>().unwrap(); | |
wall_col = Color::RGB(r, g, b); | |
} | |
} | |
//return | |
WorldData { walls: wall_vec } | |
} | |
fn scan_rays(a: f32, world: &WorldData, pos: &FPoint, ray_ct: i32, view_dist: f32, fov: i32) -> (Vec<Intersection>, Vec<Column>) { | |
let mut intersections: Vec<Intersection> = vec![]; | |
let mut columns: Vec<Column> = vec![]; | |
let a_inc: f32 = 0.005; | |
let mut r_a: f32 = a - (a_inc * (ray_ct / 2) as f32); | |
let mut i: i32 = 0; | |
loop { | |
if i > ray_ct { | |
break; | |
} | |
let ray_end = FPoint { x: pos.x + r_a.cos() * view_dist, y: pos.y + r_a.sin() * view_dist }; | |
let mut closest_dist: f32 = 99999.0; | |
let mut closest_color = Color::RGB(255, 255, 255); | |
for w in &world.walls { | |
let ll_result = line_line_collision(pos, &ray_end, &FPoint { x: w.a.x as f32, y: w.a.y as f32 }, &FPoint { x: w.b.x as f32, y: w.b.y as f32 }); | |
let i_point = FPoint { x: ll_result.1, y: ll_result.2 }; | |
if ll_result.0 { | |
//update closest point if appropriate | |
let i_dist = dist_between_points(&i_point, pos); | |
if i_dist < closest_dist { | |
closest_dist = i_dist; | |
closest_color = w.color; | |
} | |
//push intersection regardless | |
intersections.push( Intersection { pos: i_point, order: i } ); | |
} | |
} | |
columns.push(Column { dist: closest_dist, color: closest_color } ); | |
r_a += a_inc; | |
i += 1; | |
} | |
(intersections, columns) | |
} | |
fn line_line_collision(a1: &FPoint, a2: &FPoint, b1: &FPoint, b2: &FPoint) -> (bool, f32, f32) { | |
//gross looking setup | |
let x1 = a1.x; | |
let x2 = a2.x; | |
let x3 = b1.x; | |
let x4 = b2.x; | |
let y1 = a1.y; | |
let y2 = a2.y; | |
let y3 = b1.y; | |
let y4 = b2.y; | |
//http://www.jeffreythompson.org/collision-detection/line-line.php | |
let u_a: f32 = ((x4-x3)*(y1-y3) - (y4-y3)*(x1-x3)) / ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1)); | |
let u_b: f32 = ((x2-x1)*(y1-y3) - (y2-y1)*(x1-x3)) / ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1)); | |
if u_a >= 0.0 && u_a <= 1.0 && u_b >= 0.0 && u_b <= 1.0 { | |
return (true, x1 + (u_a * (x2 - x1)), y1 + (u_a * (y2 - y1))); | |
} | |
(false, 0.0, 0.0) | |
} | |
fn dist_between_points(a: &FPoint, b: &FPoint) -> f32 { | |
((a.x - b.x).powi(2) + (a.y - b.y).powi(2)).sqrt() | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
; rect 1 | |
c 255,0,0 | |
e 35,11 53,11 | |
e 35,11 35,30 | |
e 35,30 53,30 | |
e 53,11 53,30 | |
; rect 2 | |
c 255,255,0 | |
e 42,30 42,51 | |
e 52,51 54,51 | |
e 54,51 53,30 | |
e 42,51 54,51 | |
; house | |
c 0,0,255 | |
e 8,78 23,64 | |
e 23,64 38,78 | |
e 8,78 8,94 | |
e 8,94 39,94 | |
e 39,94 38,78 | |
; triangle | |
c 255,0,255 | |
e 66,74 80,52 | |
e 80,52 92,74 | |
e 92,74 66,74 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment