tge 0.0.4

A lightweight cross-platform 2D game framework written in pure Rust and based on OpenGL 3.3+.
Documentation
use super::{vertex, Vertex};
use super::opengl::{VertexArray, BufferUsage, Buffer, VertexBuffer, ElementBuffer, PrimitiveType};
use crate::error::{GameError, GameResult};
use glow::Context;
use std::rc::Rc;

pub struct Renderer {
    vertex_array: VertexArray,
    vertex_buffer: VertexBuffer,
    vertex_size: usize,
    element_buffer: ElementBuffer,
    element_size: usize,
}

impl Renderer {
    pub fn init_vertex_size(&mut self, usage: BufferUsage, size: usize) {
        self.vertex_buffer.bind();
        self.vertex_buffer.init_size(usage, vertex::ATTRIBUTE_STRIDE * size);
        init_vertex_attribute_pointer(&self.vertex_buffer);
        self.vertex_buffer.unbind();
        self.vertex_size = size;
    }

    pub fn init_with_vertices(&mut self, usage: BufferUsage, vertices: &[Vertex]) {
        self.vertex_buffer.bind();
        self.vertex_buffer.init_with_data(usage, &convert_vertices_to_data(vertices));
        init_vertex_attribute_pointer(&self.vertex_buffer);
        self.vertex_buffer.unbind();
        self.vertex_size = vertices.len();
    }

    pub fn update_vertices(&self, offset: usize, vertices: &[Vertex]) {
        self.vertex_buffer.bind();
        self.vertex_buffer.sub_data(offset, &convert_vertices_to_data(vertices));
        self.vertex_buffer.unbind();
    }

    pub fn vertex_size(&self) -> usize {
        self.vertex_size
    }

    pub fn init_element_size(&mut self, usage: BufferUsage, size: usize) {
        self.element_buffer.bind();
        self.element_buffer.init_size(usage, size);
        self.element_buffer.unbind();
        self.element_size = size;
    }

    pub fn init_with_elements(&mut self, usage: BufferUsage, elements: &[u16]) {
        self.element_buffer.bind();
        self.element_buffer.init_with_data(usage, elements);
        self.element_buffer.unbind();
        self.element_size = elements.len();
    }

    pub fn update_elements(&self, offset: usize, elements: &[u16]) {
        self.element_buffer.bind();
        self.element_buffer.sub_data(offset, elements);
        self.element_buffer.unbind();
    }

    pub fn element_size(&self) -> usize {
        self.element_size
    }

    pub fn draw_elements(&self, primitive: PrimitiveType, count: usize, offset: usize) {
        self.vertex_array.bind();
        self.vertex_array.draw_elements(primitive, count, offset);
        self.vertex_array.unbind();
    }
}

pub struct RendererBuilder {
    gl: Rc<Context>,
    vertex_array: VertexArray,
    vertex_buffer: Option<VertexBuffer>,
    vertex_size: Option<usize>,
    element_buffer: Option<ElementBuffer>,
    element_size: Option<usize>,
}

impl RendererBuilder {
    pub fn new(gl: Rc<Context>) -> GameResult<Self> {
        let vertex_array = VertexArray::new(gl.clone())
            .map_err(|error| GameError::InitError(error.into()))?;
        vertex_array.bind();
        Ok(Self {
            gl,
            vertex_array,
            vertex_buffer: None,
            vertex_size: None,
            element_buffer: None,
            element_size: None,
        })
    }

    fn assert_vertex_buffer_not_init(&self) {
        assert!(self.vertex_buffer.is_none(), "vertex buffer has been setup");
    }

    pub fn init_vertex_size(mut self, usage: BufferUsage, size: usize) -> Self {
        self.assert_vertex_buffer_not_init();
        let vertex_buffer = Buffer::new_vertex(self.gl.clone()).unwrap();
        vertex_buffer.bind();
        vertex_buffer.init_size(usage, vertex::ATTRIBUTE_STRIDE * size);
        init_vertex_attribute_pointer(&vertex_buffer);
        self.vertex_buffer = Some(vertex_buffer);
        self.vertex_size = Some(size);
        self
    }

    pub fn init_with_vertices(mut self, usage: BufferUsage, vertices: &[Vertex]) -> Self {
        self.assert_vertex_buffer_not_init();
        let vertex_buffer = Buffer::new_vertex(self.gl.clone()).unwrap();
        vertex_buffer.bind();
        vertex_buffer.init_with_data(usage, &convert_vertices_to_data(vertices));
        init_vertex_attribute_pointer(&vertex_buffer);
        self.vertex_buffer = Some(vertex_buffer);
        self.vertex_size = Some(vertices.len());
        self
    }

    fn assert_element_buffer_not_init(&self) {
        assert!(self.element_buffer.is_none(), "element buffer has been setup");
    }

    pub fn init_element_size(mut self, usage: BufferUsage, size: usize) -> Self {
        self.assert_element_buffer_not_init();
        let element_buffer = Buffer::new_element(self.gl.clone()).unwrap();
        element_buffer.bind();
        element_buffer.init_size(usage, size);
        self.element_buffer = Some(element_buffer);
        self.element_size = Some(size);
        self
    }

    pub fn init_with_elements(mut self, usage: BufferUsage, elements: &[u16]) -> Self {
        self.assert_element_buffer_not_init();
        let element_buffer = Buffer::new_element(self.gl.clone()).unwrap();
        element_buffer.bind();
        element_buffer.init_with_data(usage, elements);
        self.element_buffer = Some(element_buffer);
        self.element_size = Some(elements.len());
        self
    }

    pub fn build(self) -> GameResult<Renderer> {
        let vertex_array = self.vertex_array;
        let vertex_buffer = self.vertex_buffer
            .ok_or_else(|| GameError::InitError("must setup vertex buffer".into()))?;
        let vertex_size = self.vertex_size
            .ok_or_else(|| GameError::InitError("must setup vertex buffer".into()))?;
        let element_buffer = self.element_buffer
            .ok_or_else(|| GameError::InitError("must setup element buffer".into()))?;
        let element_size = self.element_size
            .ok_or_else(|| GameError::InitError("must setup element buffer".into()))?;
        vertex_array.unbind();
        vertex_buffer.unbind();
        element_buffer.unbind();
        Ok(Renderer {
            vertex_array,
            vertex_buffer,
            vertex_size,
            element_buffer,
            element_size,
        })
    }
}

fn init_vertex_attribute_pointer(vertex_buffer: &VertexBuffer) {
    vertex_buffer.set_attrib_pointer_f32(0, vertex::ATTRIBUTE_POSITION_SIZE, vertex::ATTRIBUTE_STRIDE, vertex::ATTRIBUTE_OFFSET_0);
    vertex_buffer.set_attrib_pointer_f32(1, vertex::ATTRIBUTE_UV_SIZE, vertex::ATTRIBUTE_STRIDE, vertex::ATTRIBUTE_OFFSET_1);
    vertex_buffer.set_attrib_pointer_f32(2, vertex::ATTRIBUTE_COLOR_SIZE, vertex::ATTRIBUTE_STRIDE, vertex::ATTRIBUTE_OFFSET_2);
}

fn convert_vertices_to_data(vertices: &[Vertex]) -> Vec<f32> {
    let mut data = Vec::with_capacity(vertex::ATTRIBUTE_STRIDE * vertices.len());
    for vertex in vertices {
        data.push(vertex.position.x);
        data.push(vertex.position.y);
        data.push(vertex.uv.x);
        data.push(vertex.uv.y);
        data.push(vertex.color.red);
        data.push(vertex.color.green);
        data.push(vertex.color.blue);
        data.push(vertex.color.alpha);
    }
    data
}