use egaku2d_core::axgeom;
pub use glutin;
use glutin::PossiblyCurrent;
use egaku2d_core;
use egaku2d_core::gl;
pub use egaku2d_core::batch;
pub use egaku2d_core::shapes;
pub use egaku2d_core::sprite;
pub use egaku2d_core::uniforms;
pub use egaku2d_core::SimpleCanvas;
use egaku2d_core::FixedAspectVec2;
use egaku2d_core::AspectRatio;
mod onein {
use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
static INSTANCES: AtomicUsize = AtomicUsize::new(0);
pub fn assert_only_one_instance() {
assert_eq!(
INSTANCES.fetch_add(1, SeqCst),
0,
"Cannot have multiple instances of the egaku2d system at the same time!"
);
}
pub fn decrement_one_instance() {
assert_eq!(
INSTANCES.fetch_sub(1, SeqCst),
1,
"The last egaku2d system object was not properly destroyed"
);
}
}
pub struct RefreshTimer {
interval: usize,
last_time: std::time::Instant,
}
impl RefreshTimer {
pub fn new(interval: usize) -> RefreshTimer {
RefreshTimer {
interval,
last_time: std::time::Instant::now(),
}
}
pub fn is_ready(&mut self) -> bool {
if self.last_time.elapsed().as_millis() >= self.interval as u128 {
self.last_time = std::time::Instant::now();
true
} else {
false
}
}
}
#[cfg(feature = "fullscreen")]
pub use self::fullscreen::FullScreenSystem;
#[cfg(feature = "fullscreen")]
pub mod fullscreen {
use super::*;
impl Drop for FullScreenSystem {
fn drop(&mut self) {
onein::decrement_one_instance();
}
}
pub struct FullScreenSystem {
inner: SimpleCanvas,
window_dim: FixedAspectVec2,
windowed_context: Option<glutin::WindowedContext<PossiblyCurrent>>,
}
impl FullScreenSystem {
pub fn new(events_loop: &glutin::event_loop::EventLoop<()>) -> Self {
onein::assert_only_one_instance();
use glutin::window::Fullscreen;
let fullscreen = Fullscreen::Borderless(prompt_for_monitor(events_loop));
let gl_window = glutin::window::WindowBuilder::new().with_fullscreen(Some(fullscreen));
let windowed_context = glutin::ContextBuilder::new()
.with_gl(glutin::GlRequest::Specific(glutin::Api::OpenGlEs, (3, 0)))
.with_vsync(true)
.build_windowed(gl_window, &events_loop)
.unwrap();
let windowed_context = unsafe { windowed_context.make_current().unwrap() };
let glutin::dpi::PhysicalSize { width, height } =
windowed_context.window().inner_size();
gl::load_with(|symbol| windowed_context.get_proc_address(symbol) as *const _);
assert_eq!(unsafe { gl::GetError() }, gl::NO_ERROR);
let window_dim = axgeom::FixedAspectVec2 {
ratio: AspectRatio(vec2(width as f64, height as f64)),
width: width as f64,
};
let windowed_context = Some(windowed_context);
let mut f = FullScreenSystem {
windowed_context,
window_dim,
inner: unsafe { SimpleCanvas::new(window_dim) },
};
f.set_viewport_from_width(width as f32);
f
}
pub fn update_window_dim(&mut self) {
let dpi = self
.windowed_context
.as_ref()
.unwrap()
.window()
.scale_factor();
let size = self
.windowed_context
.as_ref()
.unwrap()
.window()
.inner_size();
println!("resizing context!!! {:?}", (dpi, size));
self.windowed_context.as_mut().unwrap().resize(size);
self.window_dim = axgeom::FixedAspectVec2 {
ratio: AspectRatio(vec2(size.width as f64, size.height as f64)),
width: size.width as f64,
};
let ctx = unsafe {
self.windowed_context
.take()
.unwrap()
.make_not_current()
.unwrap()
};
self.windowed_context = Some(unsafe { ctx.make_current().unwrap() });
}
pub fn set_viewport_from_width(&mut self, width: f32) {
self.inner.set_viewport(self.window_dim, width);
}
pub fn set_viewport_min(&mut self, d: f32) {
if self.get_dim().x < self.get_dim().y {
self.set_viewport_from_width(d);
} else {
self.set_viewport_from_height(d);
}
}
pub fn set_viewport_from_height(&mut self, height: f32) {
let width = self.window_dim.ratio.width_over_height() as f32 * height;
self.inner.set_viewport(self.window_dim, width);
}
pub fn texture(
&mut self,
file: &str,
grid_dim: [u8; 2],
) -> image::ImageResult<sprite::Texture> {
crate::texture(file, grid_dim)
}
pub fn canvas(&self) -> &SimpleCanvas {
&self.inner
}
pub fn canvas_mut(&mut self) -> &mut SimpleCanvas {
&mut self.inner
}
pub fn get_dim(&self) -> Vec2<usize> {
self.window_dim.as_vec().inner_as()
}
pub fn swap_buffers(&mut self) {
self.windowed_context
.as_mut()
.unwrap()
.swap_buffers()
.unwrap();
assert_eq!(unsafe { gl::GetError() }, gl::NO_ERROR);
}
}
}
pub struct WindowedSystem {
inner: SimpleCanvas,
window_dim: FixedAspectVec2,
windowed_context: glutin::WindowedContext<PossiblyCurrent>,
}
impl Drop for WindowedSystem {
fn drop(&mut self) {
onein::decrement_one_instance();
}
}
impl WindowedSystem {
pub fn new(
dim: [usize; 2],
events_loop: &glutin::event_loop::EventLoop<()>,
title: &str,
) -> WindowedSystem {
onein::assert_only_one_instance();
let dim = axgeom::vec2(dim[0], dim[1]);
let dim = dim.inner_as::<f32>();
let game_world = axgeom::Rect::new(0.0, dim.x, 0.0, dim.y);
let width = game_world.x.distance() as f64;
let height = game_world.y.distance() as f64;
let monitor = prompt_for_monitor(events_loop);
let dpi = monitor.scale_factor();
let p: glutin::dpi::LogicalSize<f64> =
glutin::dpi::PhysicalSize { width, height }.to_logical(dpi);
let gl_window = glutin::window::WindowBuilder::new()
.with_inner_size(p)
.with_resizable(false)
.with_title(title);
let windowed_context = glutin::ContextBuilder::new()
.with_gl(glutin::GlRequest::Specific(glutin::Api::OpenGlEs, (3, 0)))
.with_vsync(true)
.build_windowed(gl_window, &events_loop)
.unwrap();
let windowed_context = unsafe { windowed_context.make_current().unwrap() };
gl::load_with(|symbol| windowed_context.get_proc_address(symbol) as *const _);
assert_eq!(unsafe { gl::GetError() }, gl::NO_ERROR);
let glutin::dpi::PhysicalSize { width, height } = windowed_context.window().inner_size();
assert_eq!(width as usize, dim.x as usize);
assert_eq!(height as usize, dim.y as usize);
let window_dim = FixedAspectVec2 {
ratio: AspectRatio(axgeom::vec2(width as f64, height as f64)),
width: width as f64,
};
WindowedSystem {
windowed_context,
window_dim,
inner: unsafe { SimpleCanvas::new(window_dim) },
}
}
pub fn set_viewport_from_width(&mut self, width: f32) {
self.inner.set_viewport(self.window_dim, width);
}
pub fn set_viewport_min(&mut self, d: f32) {
if self.get_dim()[0] < self.get_dim()[1] {
self.set_viewport_from_width(d);
} else {
self.set_viewport_from_height(d);
}
}
pub fn set_viewport_from_height(&mut self, height: f32) {
let width = self.window_dim.ratio.width_over_height() as f32 * height;
self.inner.set_viewport(self.window_dim, width);
}
pub fn get_dim(&self) -> [usize; 2] {
let v = self.window_dim.as_vec().inner_as();
[v.x, v.y]
}
pub fn texture(
&mut self,
file: &str,
grid_dim: [u8; 2],
) -> image::ImageResult<sprite::Texture> {
crate::texture(file, grid_dim)
}
pub fn canvas(&self) -> &SimpleCanvas {
&self.inner
}
pub fn canvas_mut(&mut self) -> &mut SimpleCanvas {
&mut self.inner
}
pub fn swap_buffers(&mut self) {
self.windowed_context.swap_buffers().unwrap();
assert_eq!(unsafe { gl::GetError() }, gl::NO_ERROR);
}
}
use glutin::event_loop::EventLoop;
use glutin::monitor::MonitorHandle;
fn prompt_for_monitor(el: &EventLoop<()>) -> MonitorHandle {
let num = 0;
let monitor = el
.available_monitors()
.nth(num)
.expect("Please enter a valid ID");
monitor
}
use egaku2d_core::gl::types::GLuint;
use egaku2d_core::gl_ok;
use egaku2d_core::sprite::*;
fn texture(file: &str, grid_dim: [u8; 2]) -> image::ImageResult<sprite::Texture> {
match image::open(&file.to_string()) {
Err(err) => Err(err),
Ok(img) => {
use image::GenericImageView;
let (width, height) = img.dimensions();
let img = match img {
image::DynamicImage::ImageRgba8(img) => img,
img => img.to_rgba(),
};
let id = build_opengl_mipmapped_texture(width, height, img);
Ok(unsafe { Texture::new(id, grid_dim, [width as f32, height as f32]) })
}
}
}
fn build_opengl_mipmapped_texture(width: u32, height: u32, image: image::RgbaImage) -> GLuint {
unsafe {
let mut texture_id: GLuint = 0;
gl::GenTextures(1, &mut texture_id);
gl_ok!();
gl::BindTexture(gl::TEXTURE_2D, texture_id);
gl_ok!();
let raw = image.into_raw();
gl::TexImage2D(
gl::TEXTURE_2D,
0,
gl::RGBA as i32,
width as i32,
height as i32,
0,
gl::RGBA,
gl::UNSIGNED_BYTE,
raw.as_ptr() as *const _,
);
gl_ok!();
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::NEAREST as i32);
gl_ok!();
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::NEAREST as i32);
gl_ok!();
gl::BindTexture(gl::TEXTURE_2D, 0);
gl_ok!();
texture_id
}
}