Skip to content

Instantly share code, notes, and snippets.

@matthewd673
Created December 22, 2020 18:03
Show Gist options
  • Save matthewd673/9081337a7ff1101c721c7ccdfbb85ffb to your computer and use it in GitHub Desktop.
Save matthewd673/9081337a7ff1101c721c7ccdfbb85ffb to your computer and use it in GitHub Desktop.
ray_engine
//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()
}
; 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