rustbatch 0.4.0

purely game dewelopment crate that offers simple but powerfull 2D rendering and some fast solutions for game world bottle necks
Documentation
use crate::{Texture, Sprite, Mat, Vect};
use crate::render::program::Program;
use crate::render::buffer::Buffer;
use crate::render::batch::{Target, VertexData};
use crate::render::texture::{TextureSize, TextureConfig};
use crate::math::rgba::{RGBA, TRANSPARENT};

/// Canvas is for offscreen drawing. Widow uses canvas and not direct draw calls from batches.
/// the main advantage is that you can apply shader to canvas as a whole so you can make some post
/// processing effects with fragment screen shader. Canvas can draw to another canvas or window.
///
/// # example
///
/// ```
/// use rustbatch::{Window, Batch, Vect, FPS};
/// use rustbatch::render::particle::system::{ParticleSystem, Initial, RandomizedProperty, Dynamic, Property};
/// use rustbatch::render::particle::system;
/// use rustbatch::math::rgba::{Graph, GraphPoint};
/// use std::f32::consts::PI;
/// use rustbatch::math::curve::Curve;
/// use rustbatch::render::particle::shapes::{SymmetricShape, Triangle};
/// use rustbatch::vect;
/// use rustbatch::render::program::Program;
/// use rustbatch::render::shader::Shader;
/// use std::ffi::CString;
///
///let (mut window, mut pump, _f, _g, _r) = Window::new(|sys| sys.window("segmentation", 400, 400).opengl().resizable().build().unwrap());
/// window.canvas.set_program(Program::from_shaders(&[Shader::default_vertex(), Shader::from_source(
/// &CString::new("
///             #version 330 core\n
///
///            in vec4 color;\n
///            in vec2 region;\n\n
///            in vec2 texture_size_f;
///
///            out vec4 result_color;\n\n
///
///            uniform sampler2D sp;\n\n
///            vec4 ncolor;
///            void main(){\n\
///                ncolor = texture(sp, region) * color;
///                if (ncolor.a == 0.0) {
///                    if (texture(sp, region + vec2(4.0, 0)/texture_size_f).a != 0.0) {
///                        ncolor = vec4(1.0, 1.0, 1.0, 1.0);
///                    }
///                }
///                result_color = ncolor;\n\
///            }").unwrap()
/// , gl::FRAGMENT_SHADER).expect("compilation failed")]).unwrap());
///
///let mut sys = system::no_texture(system::ParticleConfig {
///    rotation_relative_to_spawn_direction: true,
///    inverted: false,
///
///    gravity: Vect::DOWN * 2.0,
///    color: Graph::new(vec![GraphPoint::new(0.0, [0.0, 0.0, 1.0, 1.0]), GraphPoint::new(0.5, [1.0, 1.0, 0.0, 1.0]), GraphPoint::new(1.0, [0.0, 1.0, 0.0, 1.0])]),
///    spread: PI,
///    friction: 2.0,
///
///    origin_attraction: 10.0,
///    initial: Initial {
///        velocity: RandomizedProperty::new(300.0, 150.0),
///        rotation: RandomizedProperty::no_random(0.0),
///        twerk: RandomizedProperty::no_random(10.0),
///        live_time: RandomizedProperty::new(1.0, 0.5),
///    },
///    dynamic: Dynamic {
///        acceleration: Property { curve: Curve::NONE, value: 0.0 },
///        twerk_acceleration: Property { curve: Curve::NONE, value: 0.0 },
///        scale: Property { curve: Curve::LINEAR_DECREASING, value: 20.0 }
///    }
///}, SymmetricShape::new(10, 0.0));
///
///let mut fps = FPS::new(1f32);
///'main: loop {
///    for event in pump.poll_iter() {
///        match event {
///            sdl2::event::Event::Quit { .. } => break 'main,
///            _ => {}
///        }
///    }
///
///    let delta = fps.increase(0f32);
///
///
///    window.clear();
///
///    sys.update(delta);
///
///    sys.spawn(1, vect!(0, 0), vect!(1, 0));
///
///    sys.draw(&mut window.canvas);
///
///    window.update();
///
///}
/// ```
pub struct Canvas {
    framebuffer: gl::types::GLuint,
    data: VertexData,
    texture: Texture,
    program: Program,
    buffer: Buffer,
    drawer: Sprite,
    pub(crate) camera: Mat,
}

impl Target for Canvas {

    fn append(&mut self, data: &[f32], pattern: &[u32], vertex_size: u32, program: Option<&Program>, texture: Option<&Texture>, buffer: Option<&Buffer>) {
        self.bind();

        unsafe {
            gl::Viewport(0, 0, self.texture.w, self.texture.h);
        }

        match program {
            Some(p) => {
                p.bind();
                p.set_camera(&self.camera.transform_from_window_space((self.texture.w, self.texture.h)));
                p.set_view_size(self.texture.size());
            }
            None => panic!("Program mustn't be None. If you are using sprite to draw directly to \
            canvas use batch instead. Using sprites to draw is fundamentally ineffective so i decided \
            to not support it at all."),
        }

        if let Some(t) = texture {
            t.bind()
        }

        let buffer = match buffer {
            Some(b) => b,
            None => {
                if self.buffer.data_size as u32 != vertex_size {
                        panic!("incorrect vertex size, this canvas accepts only vertex size {}, but \
              you inputted vertex data with vertex size {}", self.buffer.data_size, vertex_size);
                    }
                &self.buffer
            },
        };

        buffer.set_vertices_and_indices(data, pattern);
        buffer.bind();
        buffer.draw(pattern.len());

        Self::unbind()
    }


}

impl Canvas {
    #[inline]
    pub fn new(size: TextureSize) -> Self {
        Self::customized(size, Buffer::default(), Program::default(), TextureConfig::DEFAULT)
    }

    /// customizes allows greater customization of canvas
    pub fn customized(size: TextureSize, buffer: Buffer, program: Program, config: TextureConfig) -> Self {
        let mut framebuffer: gl::types::GLuint = 0;

        unsafe {
            gl::GenFramebuffers(1, &mut framebuffer);
            gl::BindFramebuffer(gl::FRAMEBUFFER, framebuffer);
        }

        let texture = Texture::empty_texture(size, config);
        program.set_texture_size(texture.size());

        unsafe {
            gl::FramebufferTexture2D(gl::FRAMEBUFFER, gl::COLOR_ATTACHMENT0, gl::TEXTURE_2D, texture.id(), 0);
            assert!(gl::CheckFramebufferStatus(gl::FRAMEBUFFER) == gl::FRAMEBUFFER_COMPLETE);
        }

        Self::unbind();

        Self {

            data: VertexData::custom(buffer.data_size as u32),
            drawer: Sprite::new(texture.frame()),
            camera: Mat::IM,
            framebuffer,
            texture,
            program,
            buffer,
        }
    }

    #[inline]
    pub fn set_program(&mut self, program: Program) {
        program.set_texture_size(self.texture.size());
        self.program = program;
    }

    #[inline]
    pub fn set_buffer(&mut self, buffer: Buffer) {
        self.buffer = buffer;
    }

    /// resize resizes canvas and erases its content
    #[inline]
    pub fn resize(&mut self, size: TextureSize) {
        self.texture.resize_and_clear(size);
        self.program.set_texture_size(self.texture.size());
        self.drawer = Sprite::new(self.texture.frame());
    }

    /// bind binds the canvas so you can draw on it as you would on window
    #[inline]
    pub fn bind(&self) {
        unsafe {
            gl::BindFramebuffer(gl::FRAMEBUFFER, self.framebuffer);
        }
    }

    /// unbind unbinds the current canvas. If you don't call this your draw calls will still affect
    /// this canvas and not the widow.
    #[inline]
    pub fn unbind() {
        unsafe {
            gl::BindFramebuffer(gl::FRAMEBUFFER, 0);
        }
    }

    /// set camera sets the matrix by witch are all shapes transformed so you can squeeze thinks
    /// however you like
    #[inline]
    pub fn set_camera_matrix(&mut self, mat: Mat) {
        self.camera = mat;
    }

    /// set_camera is more human readable way of setting viewport
    #[inline]
    pub fn set_camera(&mut self, position: Vect, zoom: f32) {
        self.camera = Mat::IM.moved(position.inverted()).scaled(Vect::ZERO, zoom);
    }

    ///size returns current size of canvas
    #[inline]
    pub fn size(&self) -> (u32, u32){
        (self.texture.w as u32, self.texture.h as u32)
    }

    /// clear clears the window
    #[inline]
    pub fn clear(&self, color: &RGBA) {
        self.bind();
        super::clear(color);
        Self::unbind();
    }

    /// reset clears canvas with transparent color
    #[inline]
    pub fn reset(&self) {
        self.bind();
        super::clear(&TRANSPARENT);
        Self::unbind();
    }

    #[inline]
    pub fn draw<T: Target>(&mut self, other: &mut T, mat: &Mat, color: &RGBA) {
        self.data.clear();
        self.drawer.draw_with_matrix(&mut self.data, mat, color);
        other.append(&self.data.vertices, &self.data.indices, self.data.vertex_size, Some(&self.program), Some(&self.texture), Some(&self.buffer));
    }
}