mod app_clock;
mod core_audio;
mod event_handler;
pub use self::core_audio::CoreAudio;
use std::ffi::CStr;
use std::fs::File;
use std::io::BufReader;
use std::time::Instant;
use glutin::dpi::LogicalSize;
use glutin::EventsLoop;
use glutin::WindowBuilder;
use glutin::{ContextBuilder, ContextTrait, WindowedContext};
use gl::types::*;
use self::app_clock::AppClock;
use self::event_handler::EventHandler;
use super::mark_app_created_flag;
use crate::app_info::AppInfo;
use crate::asset_id::{AppAssetId, IdU16};
use crate::renderer::atlas::Atlas;
use crate::renderer::core_renderer::CoreRenderer;
use crate::renderer::core_renderer::Texture;
use crate::renderer::render_buffer::RenderBuffer;
use crate::renderer::Renderer;
use crate::{timer, App, AppContext};
#[macro_export]
macro_rules! nuuro_header {
() => {};
}
pub fn run<AS: 'static + AppAssetId, AP: 'static + App<AS>>(info: AppInfo, mut app: AP) {
mark_app_created_flag();
let core_audio = CoreAudio::new(AS::Sound::count(), AS::Music::count());
let mut events_loop = EventsLoop::new();
let mut event_handler = EventHandler::new();
let window = WindowBuilder::new()
.with_title(info.title)
.with_dimensions(LogicalSize::new(
info.window_pixels.0 as f64,
info.window_pixels.1 as f64,
))
.with_resizable(info.resizable);
let gl_context = ContextBuilder::new()
.with_gl_debug_flag(true)
.with_gl(glutin::GlRequest::Specific(glutin::Api::OpenGl, (3, 0)))
.build_windowed(window, &events_loop)
.unwrap();
unsafe { gl_context.make_current().unwrap() };
let timer = Instant::now();
init_gl(&gl_context);
let mut renderer = build_renderer(&info);
gl_error_check();
let mut ctx = AppContext::new(core_audio, renderer.app_dims(), renderer.native_px());
if info.print_gl_info {
print_gl_info();
}
app.start(&mut ctx);
let mut clock = AppClock::new(timer, &info);
loop {
events_loop.poll_events(|event| {
if !event_handler.process_events(event, &mut app, &mut ctx, &renderer) {
ctx.close();
}
});
unsafe {
gl::ClearColor(0., 0., 0., 1.0);
gl::Clear(gl::COLOR_BUFFER_BIT);
}
let screen_dims = (
gl_context.window().get_inner_size().unwrap().width as u32,
gl_context.window().get_inner_size().unwrap().height as u32,
);
if screen_dims.0 > 0 && screen_dims.1 > 0 {
renderer.set_screen_dims(screen_dims);
ctx.set_dims(renderer.app_dims(), renderer.native_px());
app.render(&mut renderer, &ctx);
renderer.flush();
}
gl_error_check();
gl_context.swap_buffers().unwrap();
let elapsed = clock.step();
match (ctx.is_fullscreen(), ctx.desires_fullscreen()) {
(false, true) => {
gl_context
.window()
.set_fullscreen(Some(events_loop.get_primary_monitor()));
ctx.set_is_fullscreen(true);
}
(true, false) => {
gl_context.window().set_fullscreen(None);
ctx.set_is_fullscreen(false);
}
(false, false) | (true, true) => {}
}
let normalized_elapsed = elapsed.min(crate::MAX_TIMESTEP);
timer::update_all(normalized_elapsed);
app.advance(normalized_elapsed, &mut ctx);
if ctx.take_close_request() {
break;
}
}
}
#[allow(dead_code)]
pub fn println(string: String) {
println!("{}", string);
}
fn build_renderer<AS: AppAssetId>(info: &AppInfo) -> Renderer<AS> {
let sprites_atlas =
Atlas::new(BufReader::new(File::open("assets/sprites.atlas").unwrap())).unwrap();
let render_buffer = RenderBuffer::new(&info, info.window_pixels, sprites_atlas);
let sprites_tex = Texture::new("assets/sprites.png");
let core_renderer = CoreRenderer::new(sprites_tex);
Renderer::<AS>::new(render_buffer, core_renderer)
}
fn init_gl(gl_context: &WindowedContext) {
gl::load_with(|name| gl_context.get_proc_address(name) as *const _);
unsafe {
gl::Enable(gl::BLEND);
gl::BlendFunc(gl::ONE, gl::ONE_MINUS_SRC_ALPHA);
}
}
fn print_gl_info() {
println!("OpenGL version: {:?}", gl_get_string(gl::VERSION));
println!(
"GLSL version: {:?}",
gl_get_string(gl::SHADING_LANGUAGE_VERSION)
);
println!("Vendor: {:?}", gl_get_string(gl::VENDOR));
println!("Renderer: {:?}", gl_get_string(gl::RENDERER));
}
fn gl_get_string<'a>(name: GLenum) -> &'a CStr {
unsafe { CStr::from_ptr(gl::GetString(name) as *const i8) }
}
fn gl_error_check() {
let error = unsafe { gl::GetError() };
assert!(
error == gl::NO_ERROR,
"unexpected OpenGL error, code {}",
error
);
}