1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
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;