use std::path::Path;
use std::fs::File;
use std::io::Read;
use std::cell::Cell;
use std::time::Duration;
use glium::{self, DisplayBuild};
use rusttype;
use text;
use mesh;
use math::Vec2;
use color::Color;
static VERTEX_SHADER: &'static str = r"
#version 150
in vec3 position;
in vec2 uv;
out vec3 v_position;
out vec2 v_uv;
uniform mat4 u_model;
uniform mat4 u_projection;
void main() {
gl_Position = u_projection * u_model * vec4(position, 1);
v_position = position;
v_uv = uv;
}
";
static FRAGMENT_SHADER: &'static str = r"
#version 140
in vec3 v_position;
in vec2 v_uv;
out vec4 color;
uniform vec4 u_color;
uniform sampler2D u_texture;
void main() {
color = texture(u_texture, v_uv) * u_color;
}
";
fn load_bytes<P: AsRef<Path>>(path: P) -> Vec<u8> {
let mut bytes = Vec::new();
File::open(path).and_then(|mut file| file.read_to_end(&mut bytes)).unwrap();
bytes
}
pub struct Context {
pub display: glium::Display,
pub color: Cell<Color>,
pub background_color: Cell<Color>,
pub size: Cell<Vec2>,
pub mouse: Cell<Vec2>,
pub dt: Cell<Duration>,
pub quad: mesh::Mesh,
pub white: Texture,
pub default_shader: Shader,
}
impl Context {
pub fn new(title: &str, size: (u32, u32)) -> Self {
let display = glium::glutin::WindowBuilder::new()
.with_title(title)
.with_dimensions(size.0, size.1)
.with_vsync()
.build_glium()
.unwrap();
let quad = new_mesh(&display, &[
mesh::Vertex::new(Vec2::new(0.0, 0.0), Vec2::new(0.0, 0.0)),
mesh::Vertex::new(Vec2::new(1.0, 0.0), Vec2::new(1.0, 0.0)),
mesh::Vertex::new(Vec2::new(0.0, 1.0), Vec2::new(0.0, 1.0)),
mesh::Vertex::new(Vec2::new(1.0, 1.0), Vec2::new(1.0, 1.0)),
], &[0, 1, 2, 3]);
let white = new_texture(&display, vec![255, 255, 255, 255], (1, 1));
let default_shader = new_shader(&display, VERTEX_SHADER, FRAGMENT_SHADER);
Context {
display: display,
color: Cell::new((1.0, 1.0, 1.0, 1.0)),
background_color: Cell::new((0.0, 0.0, 0.0, 1.0)),
size: Cell::new(Vec2::new(size.0 as f32, size.1 as f32)),
mouse: Cell::new(Vec2::zero()),
dt: Cell::new(Default::default()),
quad: quad,
white: white,
default_shader: default_shader,
}
}
pub fn new_shader(&self, vertex_src: &str, fragment_src: &str) -> Shader {
new_shader(&self.display, vertex_src, fragment_src)
}
pub fn new_mesh(&self, vertices: &[mesh::Vertex], indices: &[mesh::Index]) -> mesh::Mesh {
new_mesh(&self.display, vertices, indices)
}
pub fn new_texture(&self, bytes: Vec<u8>, size: (u32, u32)) -> Texture {
new_texture(&self.display, bytes, size)
}
pub fn new_text(&self, text: &str, font: &text::Font, height: f32) -> Texture {
let text::Text { bytes, size } = text::Text::new(text, font, height);
self.new_texture(bytes, size)
}
pub fn new_font<P: AsRef<Path>>(&self, path: P) -> text::Font {
let bytes = load_bytes(path);
rusttype::FontCollection::from_bytes(bytes).into_font().unwrap()
}
pub fn dt(&self) -> f32 {
let dt = self.dt.get();
dt.subsec_nanos() as f32 / 1_000_000_000.0 + dt.as_secs() as f32
}
pub fn size(&self) -> Vec2 {
self.size.get()
}
}
fn new_shader(display: &glium::Display, vertex_src: &str, fragment_src: &str) -> Shader {
Shader::from_source(display, vertex_src, fragment_src, None).unwrap()
}
fn new_mesh(display: &glium::Display, vertices: &[mesh::Vertex], indices: &[mesh::Index]) -> mesh::Mesh {
mesh::Mesh {
vertices: glium::VertexBuffer::new(display, vertices).unwrap(),
indices: glium::IndexBuffer::new(display, glium::index::PrimitiveType::TriangleStrip, indices).unwrap(),
}
}
fn new_texture(display: &glium::Display, bytes: Vec<u8>, size: (u32, u32)) -> Texture {
Texture::new(display,
glium::texture::RawImage2d::from_raw_rgba(bytes, size)).unwrap()
}
pub type Texture = glium::texture::Texture2d;
pub type Shader = glium::Program;