// Copyright (C) Oscar Shrimpton 2019
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option)
// any later version.
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
// You should have received a copy of the GNU General Public License along
// with this program. If not, see .
//! Renders ./example.bsp
extern crate stockton_types;
extern crate stockton_levels;
extern crate stockton_render;
extern crate winit;
extern crate log;
extern crate simple_logger;
use std::time::SystemTime;
use std::f32::consts::PI;
use winit::{
event::{Event, WindowEvent, ElementState},
event_loop::{ControlFlow, EventLoop},
window::WindowBuilder
};
use stockton_levels::{
prelude::*,
q3::Q3BSPFile
};
use stockton_types::{World, Vector3, Vector2};
use stockton_render::Renderer;
/// Movement speed, world units per second
const SPEED: f32 = 100.0;
/// Pixels required to rotate 90 degrees
const PIXELS_PER_90D: f32 = 100.0;
/// Sensitivity, derived from above
const SENSITIVITY: f32 = PI / (2.0 * PIXELS_PER_90D);
#[derive(Debug)]
struct KeyState {
pub up: bool,
pub left: bool,
pub right: bool,
pub down: bool,
pub inwards: bool,
pub out: bool
}
impl KeyState {
pub fn new() -> KeyState {
KeyState {
up: false,
left: false,
right: false,
down: false,
inwards: false,
out: false,
}
}
/// Helper function to get our movement request as a normalized vector
pub fn as_vector(&self) -> Vector3 {
let mut vec = Vector3::new(0.0, 0.0, 0.0);
if self.up {
vec.y = 1.0;
} else if self.down {
vec.y = -1.0;
}
if self.right {
vec.x = 1.0;
} else if self.left {
vec.x = -1.0;
}
if self.inwards {
vec.z = 1.0;
} else if self.out {
vec.z = -1.0;
}
vec
}
}
fn main() {
// Initialise logger
simple_logger::init_with_level(log::Level::Debug).unwrap();
// Make a window
let event_loop = EventLoop::new();
let window = WindowBuilder::new().build(&event_loop).unwrap();
if let Err(_) = window.set_cursor_grab(true) {
println!("warning: cursor not grabbed");
}
window.set_cursor_visible(false);
// Parse the map file and swizzle the co-ords
let data = include_bytes!("../data/newtest.bsp").to_vec().into_boxed_slice();
let bsp: Result, stockton_levels::types::ParseError> = Q3BSPFile::parse_file(&data);
let bsp: Q3BSPFile = bsp.unwrap();
let bsp: Q3BSPFile = bsp.swizzle_to();
// Load into a world and create the new renderer
let world = World::new(bsp);
let mut renderer = Renderer::new(world, &window).unwrap();
// Done loading - This is our main loop.
let mut last_update = SystemTime::now();
let mut key_state = KeyState::new();
let mut last_cursor_pos = Vector2::new(0.0, 0.0);
// Keep rendering the world
event_loop.run(move |event, _, flow| {
*flow = ControlFlow::Poll;
match event {
Event::WindowEvent {
event,
..
} => match event {
// Close when requested
WindowEvent::CloseRequested => {
*flow = ControlFlow::Exit
},
WindowEvent::Resized(_) => {
unsafe { renderer.context.handle_surface_change().unwrap() };
}
// Update our keystates
WindowEvent::KeyboardInput {input, ..} => match input.scancode {
// A
30 => key_state.left = input.state == ElementState::Pressed,
// D
32 => key_state.right = input.state == ElementState::Pressed,
// W (in)
17 => key_state.inwards = input.state == ElementState::Pressed,
// S (out)
31 => key_state.out = input.state == ElementState::Pressed,
// Space (up)
57 => key_state.up = input.state == ElementState::Pressed,
// Ctrl (down)
42 => key_state.down = input.state == ElementState::Pressed,
_ => ()
},
// Look around with mouse
WindowEvent::CursorMoved {
position,
..
} => {
// Don't do anything on the first frame
if last_cursor_pos.x != 0.0 || last_cursor_pos.y == 0.0 {
// Figure out how much to rotate by
let x_offset = (position.x as f32 - last_cursor_pos.x) * SENSITIVITY;
let y_offset = (position.y as f32 - last_cursor_pos.y) * SENSITIVITY;
// Rotate
renderer.context.rotate(Vector3::new(
-y_offset,
x_offset,
0.0
));
}
// For tracking how much the mouse has moved
last_cursor_pos.x = position.x as f32;
last_cursor_pos.y = position.y as f32;
}
_ => ()
},
// Nothing new happened
Event::MainEventsCleared => {
// Draw as many frames as we can
// This isn't ideal, but it'll do for now.
window.request_redraw()
},
// Redraw - either from above or resizing, etc
Event::RedrawRequested(_) => {
// Time since last frame drawn. Again, not ideal.
let delta = last_update.elapsed().unwrap().as_secs_f32();
last_update = SystemTime::now();
// Move our camera if needed
let delta_pos = key_state.as_vector() * delta * SPEED;
if delta_pos.x != 0.0 || delta_pos.y != 0.0 || delta_pos.z != 0.0 {
renderer.context.move_camera_relative(delta_pos);
}
// Render the frame
renderer.render_frame().unwrap()
}
_ => ()
}
});
}