#![warn(rust_2018_idioms)]
pub mod arena;
pub mod assets;
pub mod camera;
pub mod input;
pub mod math;
pub mod platform;
pub mod renderer;
pub mod time;
use math::Vec2;
use renderer::OpenGLRenderer;
use time::Timer;
pub use arena::TempArena;
pub use camera::Camera;
pub use input::Input;
pub use platform::Platform;
pub use renderer::RenderCommands;
pub use time::Tick;
pub fn builder() -> NgenBuilder {
NgenBuilder {
window_title: None,
window_width: 1920,
window_height: 1080,
max_quads: 25_000,
dt: 0.01,
max_frame: 0.1,
temp_memory: 0,
}
}
pub trait Simulation {
fn update(
&mut self,
platform: &mut Platform<'_>,
input: &Input,
tick: Tick,
) -> Result<bool, String>;
fn render(&mut self, render_commands: &mut RenderCommands) -> Result<(), String>;
}
#[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.bumper_left.set_is_down(true),
Keycode::E => input.controller.bumper_right.set_is_down(true),
Keycode::Left => input.controller.action_a.set_is_down(true),
Keycode::Up => input.controller.action_b.set_is_down(true),
Keycode::Right => input.controller.action_c.set_is_down(true),
Keycode::Down => input.controller.action_d.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.bumper_left.set_is_down(false),
Keycode::E => input.controller.bumper_right.set_is_down(false),
Keycode::Left => input.controller.action_a.set_is_down(false),
Keycode::Up => input.controller.action_b.set_is_down(false),
Keycode::Right => input.controller.action_c.set_is_down(false),
Keycode::Down => input.controller.action_d.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)
}
pub struct NgenBuilder {
window_title: Option<&'static str>,
window_width: u32,
window_height: u32,
max_quads: u32,
dt: f32,
max_frame: f32,
temp_memory: usize,
}
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 dt(mut self, dt: f32) -> Self {
self.dt = dt;
self
}
pub fn max_quads(mut self, max_quads: u32) -> Self {
self.max_quads = max_quads;
self
}
pub fn max_frame(mut self, max_frame: f32) -> Self {
self.max_frame = max_frame;
self
}
pub fn temp_memory(mut self, memory: usize) -> Self {
self.temp_memory = memory;
self
}
pub fn mount_default<S: Simulation + Default>(self) -> Result<(), String> {
self.mount(S::default())
}
pub fn mount<S: Simulation>(self, simulation: S) -> Result<(), String> {
self.mount_init(move |_| simulation)
}
pub fn mount_init<S: Simulation, I: FnOnce(&mut Platform<'_>) -> S>(self, init: I) -> 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();
let mut temp_arena = TempArena::with_capacity(self.temp_memory)?;
let mut simulation = {
let mut platform = Platform::new(&mut renderer, &mut temp_arena);
init(&mut platform)
};
'running: loop {
for tick in timer.begin_frame() {
input.frame();
temp_arena.reset();
if !handle_os_messages(&mut event_pump, &mut renderer, &mut input)? {
break 'running;
}
let mut platform = Platform::new(&mut renderer, &mut temp_arena);
if !simulation.update(&mut platform, &input, tick)? {
break 'running;
}
}
let render_commands = renderer.begin_frame();
simulation.render(render_commands)?;
renderer.render();
}
Ok(())
}
}