#![warn(rust_2018_idioms)]
pub mod camera;
pub mod input;
pub mod math;
pub mod renderer;
pub mod time;
use input::Input;
use math::Vec2;
use renderer::{OpenGLRenderer, RenderCommands};
use time::{Tick, Timer};
pub fn builder() -> NgenBuilder {
NgenBuilder::default()
}
pub trait Simulation {
fn update(&mut self, input: &Input, tick: Tick) -> bool;
fn render(&mut self, render_commands: &mut RenderCommands);
}
#[rustfmt::skip]
fn handle_os_messages(
event_pump: &mut sdl2::EventPump,
renderer: &mut OpenGLRenderer,
input: &mut Input,
) -> Result<bool, String> {
for event in event_pump.poll_iter() {
use sdl2::event::{Event, WindowEvent};
use sdl2::keyboard::Keycode;
use sdl2::mouse::MouseButton;
match event {
Event::Quit { .. } => return Ok(false),
Event::MouseMotion { x, y, .. } => {
input.mouse.screen_pos = Vec2::new(x as _, y as _);
}
Event::MouseButtonDown { mouse_btn, x, y, .. } => {
input.mouse.screen_pos = Vec2::new(x as _, y as _);
match mouse_btn {
MouseButton::Left => input.mouse.left_button.set_is_down(true),
MouseButton::Right => input.mouse.right_button.set_is_down(true),
_ => (),
}
}
Event::MouseButtonUp { mouse_btn, x, y, .. } => {
input.mouse.screen_pos = Vec2::new(x as _, y as _);
match mouse_btn {
MouseButton::Left => input.mouse.left_button.set_is_down(false),
MouseButton::Right => input.mouse.right_button.set_is_down(false),
_ => (),
}
}
Event::KeyDown { keycode: Some(key), .. } => match key {
Keycode::W => input.controller.up.set_is_down(true),
Keycode::A => input.controller.left.set_is_down(true),
Keycode::S => input.controller.down.set_is_down(true),
Keycode::D => input.controller.right.set_is_down(true),
Keycode::Q => input.controller.action_a.set_is_down(true),
Keycode::E => input.controller.action_b.set_is_down(true),
Keycode::Space => input.controller.select.set_is_down(true),
Keycode::Return => {
input.controller.start.set_is_down(true);
input.keyboard.enter.set_is_down(true);
}
Keycode::Escape => input.keyboard.escape.set_is_down(true),
Keycode::F1 => input.keyboard.f[01].set_is_down(true),
Keycode::F2 => input.keyboard.f[02].set_is_down(true),
Keycode::F3 => input.keyboard.f[03].set_is_down(true),
Keycode::F4 => input.keyboard.f[04].set_is_down(true),
Keycode::F5 => input.keyboard.f[05].set_is_down(true),
Keycode::F6 => input.keyboard.f[06].set_is_down(true),
Keycode::F7 => input.keyboard.f[07].set_is_down(true),
Keycode::F8 => input.keyboard.f[08].set_is_down(true),
Keycode::F9 => input.keyboard.f[09].set_is_down(true),
Keycode::F10 => input.keyboard.f[10].set_is_down(true),
Keycode::F11 => input.keyboard.f[11].set_is_down(true),
Keycode::F12 => input.keyboard.f[12].set_is_down(true),
_ => (),
},
Event::KeyUp {
keycode: Some(key), ..
} => match key {
Keycode::W => input.controller.up.set_is_down(false),
Keycode::A => input.controller.left.set_is_down(false),
Keycode::S => input.controller.down.set_is_down(false),
Keycode::D => input.controller.right.set_is_down(false),
Keycode::Q => input.controller.action_a.set_is_down(false),
Keycode::E => input.controller.action_b.set_is_down(false),
Keycode::Space => input.controller.select.set_is_down(false),
Keycode::Return => {
input.controller.start.set_is_down(false);
input.keyboard.enter.set_is_down(false);
}
Keycode::Escape => input.keyboard.escape.set_is_down(false),
Keycode::F1 => input.keyboard.f[01].set_is_down(false),
Keycode::F2 => input.keyboard.f[02].set_is_down(false),
Keycode::F3 => input.keyboard.f[03].set_is_down(false),
Keycode::F4 => input.keyboard.f[04].set_is_down(false),
Keycode::F5 => input.keyboard.f[05].set_is_down(false),
Keycode::F6 => input.keyboard.f[06].set_is_down(false),
Keycode::F7 => input.keyboard.f[07].set_is_down(false),
Keycode::F8 => input.keyboard.f[08].set_is_down(false),
Keycode::F9 => input.keyboard.f[09].set_is_down(false),
Keycode::F10 => input.keyboard.f[10].set_is_down(false),
Keycode::F11 => input.keyboard.f[11].set_is_down(false),
Keycode::F12 => input.keyboard.f[12].set_is_down(false),
_ => (),
},
Event::Window { win_event: WindowEvent::Resized(width, height), .. } => {
renderer.set_window_dim(width, height);
}
_ => (),
}
}
Ok(true)
}
#[derive(Default)]
pub struct NgenBuilder {
window_title: Option<&'static str>,
window_width: u32,
window_height: u32,
max_quads: u32,
}
impl NgenBuilder {
pub fn window(mut self, width: u32, height: u32, title: &'static str) -> Self {
self.window_title = Some(title);
self.window_width = width;
self.window_height = height;
self
}
pub fn max_quads(mut self, max_quads: u32) -> Self {
self.max_quads = max_quads;
self
}
pub fn mount<S: Simulation>(self, mut simulation: S) -> Result<(), String> {
let mut context = sdl2::init()?;
let mut event_pump = context.event_pump()?;
let mut renderer = renderer::OpenGLRenderer::new(
&mut context,
self.window_width,
self.window_height,
self.window_title.unwrap_or("ngen"),
self.max_quads as usize,
)?;
let mut timer = Timer::new(&mut context, 0.01, 0.1)?;
let mut input = Input::default();
'running: loop {
for tick in timer.begin_frame() {
input.frame();
if !handle_os_messages(&mut event_pump, &mut renderer, &mut input)? {
break 'running;
}
if !simulation.update(&input, tick) {
break 'running;
}
}
let render_commands = renderer.begin_frame();
simulation.render(render_commands);
renderer.render();
}
Ok(())
}
}