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
extern crate sdl2;

use std::ops::Deref;
use sdl2::{video, VideoSubsystem};
use crate::math::mat::Mat;
use crate::math::vect::Vect;
use sdl2::video::GLContext;
use crate::render::buffer::Buffer;
use crate::render::batch::Target;
use self::sdl2::{EventPump, Sdl};
use crate::math::rect::Rect;
use crate::render::canvas::Canvas;
use crate::render::texture::TextureSize;
use crate::{Texture};
use crate::render::program::Program;
use crate::math::rgba::{RGBA, BLACK, WHITE};
use std::collections::HashSet;
use self::sdl2::keyboard::Keycode;
use self::sdl2::event::Event;
use self::sdl2::mouse::MouseButton;

/// Window core feature of this library, everything starts here. Only after creating window you can
/// safely use other parts of render module because window also calls gl::load_with() witch makes
/// functions accessible. Little design decision is that (0, 0) is in the middle of screen by default
/// it makes managing gale viewport lot nicer in my opinion. Window also implements Deref to deref
/// inner sdl window.
pub struct Window {
    pub canvas: Canvas,
    pub window: video::Window,
    background_color: RGBA,
}

impl Deref for Window {
    type Target = video::Window;
    fn deref(&self) -> &Self::Target {
        &self.window
    }
}

impl Window {
    /// new creates new window. Cosure is just to deffer window creation for internal reasons.
    /// function also returns event pump from witch you can poll events like key presses and mouse
    /// moves. Third return value is simply here because it cannot be dropped otherwise window
    /// would not render.
    ///
    /// # Example
    /// ```
    /// use rustbatch::Window;
    ///
    /// let (window, event_pump, _gl, _sdl, _video_subsystem) = Window::new(|sys| {
    ///     sys.window("Title", 1000, 600)
    ///     .opengl()
    ///     .build()
    ///     .unwrap()
    /// });
    /// ```

    /// from_buffer creates window with custom buffer. Remember that changing vertex structure
    /// requires also batch with custom program
    pub fn new<F>(gen: F) -> (Window, EventPump, GLContext, Sdl, VideoSubsystem)
        where F: FnOnce(&VideoSubsystem) -> video::Window {
        let sdl = sdl2::init().expect("You probably did not set up your project correctly go to \
        crates documentation, you can find all answers there.");

        let video_subsystem = sdl.video().unwrap();
        let gl_attr = video_subsystem.gl_attr();
        gl_attr.set_context_profile(sdl2::video::GLProfile::Core);
        gl_attr.set_context_version(4, 0);

        let window = gen(&video_subsystem);

        let gl = window.gl_create_context().unwrap();
        let _fl = gl::load_with(|s| video_subsystem.gl_get_proc_address(s) as *const std::os::raw::c_void);

        unsafe {
            gl::Enable(gl::BLEND);
            gl::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA);
        }
        let (w, h) = window.size();

        (Window{canvas: Canvas::new(TextureSize::new(w as i32, h as i32)) ,window, background_color: BLACK}, sdl.event_pump().unwrap(), gl, sdl, video_subsystem)
    }

    /// update updates window, just call it at the end
    pub fn update(&mut self) {
        let size = self.size();
        if size != self.canvas.size() {
            self.canvas.resize(TextureSize::new(size.0 as i32, size.1 as i32))
        }
        self.canvas.draw(&mut self.window, &Mat::IM, &WHITE);
        self.gl_swap_window();
    }

    /// get_viewport_rect returns rectangle that whole screen fits in and that is even if you
    /// rotate camera. Useful when you don't want to draw sprites that are foo screen
    pub fn get_viewport_rect(&self) -> Rect {
        let (w, h) = self.size();
        let w = w as i32 /2;
        let h = h as i32 /2;

        let mut corners: [Vect; 4] = [
            vect!(-w, -h),
            vect!(w, -h),
            vect!(w, h),
            vect!(-w, h),
        ];

        for i in 0..corners.len() {
            corners[i] = self.canvas.camera.unprj(corners[i]);
        }

        Rect::bounds_for(&corners)
    }

    pub fn set_background_color(&mut self, color: &RGBA) {
        self.background_color = color.clone();
    }

    #[inline]
    pub fn clear(&self) {
        super::clear(&self.background_color);
        self.canvas.reset()
    }

    pub fn project_mouse(&self, mouse: Vect) -> Vect {
        let (w, h) = self.window.size();
        let mut pos = mouse - vect!(w, h)/2.0;
        pos.y *= -1.0;
        self.canvas.camera.unprj(pos)
    }
}

impl Target for video::Window {
    fn append(&mut self, data: &[f32], pattern: &[u32], _: u32, program: Option<&Program>, texture: Option<&Texture>, buffer: Option<&Buffer>) {
        let (w, h) = self.size();

        unsafe {
            gl::Viewport(0, 0, w as i32, h as i32);
        }

        match program {
            Some(p) => {
                p.bind();
                p.set_camera(&Mat::IM);
                p.set_view_size(vect!(w, h));
            }
            None => panic!("program mustn't be none when drawing to window, use something that has \
            program in it like canvas of GeomDrawer."),
        }

        if let Some(t) = texture {
            t.bind()
        } else {
            Texture::NONE.bind();
        }

        let buffer = match buffer {
            Some(b) => b,
            None => panic!("buffer mustn't be none when drawing to window, use something that has \
            program in it like canvas or GeomDrawer."),
        };
        buffer.set_vertices_and_indices(data, pattern);
        buffer.bind();
        buffer.draw(pattern.len());
    }
}

pub type KeySet = HashSet<Keycode>;

pub struct KeyState {
    down: KeySet,
    up: KeySet,
    pressed: KeySet,
}

impl KeyState {

    #[inline]
    pub fn zero() -> Self {
        Self {
            down: HashSet::new(),
            up: HashSet::new(),
            pressed: HashSet::new(),
        }
    }

    #[inline]
    pub fn merge(&mut self, other: Self) {
        self.pressed.extend(other.down);
        self.pressed.extend(other.pressed);
    }

    pub fn new(events: &Vec<Event>, previos: Self) -> Self {
        let mut key_state = Self::zero();
        key_state.merge(previos);
        for event in events {
            match event {
                Event::KeyDown { keycode, repeat, .. } => {
                    if let Some(key) = keycode {
                        if !repeat {
                            key_state.down.insert(*key);
                        }
                    }
                }
                Event::KeyUp { keycode, repeat, .. } => {
                    if let Some(key) = keycode {
                        if !repeat {
                            key_state.pressed.remove(key);
                            key_state.up.insert(*key);
                        }
                    }
                }
                _ => ()
            }
        }

        key_state
    }

    pub fn pressed(&self, key: Keycode) -> bool {
        self.pressed.contains(&key)
    }
}

type ButtonSet = HashSet<MouseButton>;

pub struct MouseState {
    down: ButtonSet,
    up: ButtonSet,
    pressed: ButtonSet,
    pub pos: Vect,
    pub prev_pos: Vect,
    pub scroll: Vect,
    pub double_click: bool,
}

impl MouseState {
    pub fn zero() -> Self {
        Self {
            down: HashSet::new(),
            up: HashSet::new(),
            pressed: HashSet::new(),
            pos: Vect::ZERO,
            prev_pos: Vect::ZERO,
            scroll: Vect::ZERO,
            double_click: false,
        }
    }

    pub fn merge(&mut self, other: Self) {
        self.prev_pos = other.pos;
        self.pos = other.pos;
        self.pressed.extend(other.pressed);
        self.pressed.extend(other.down);
    }

    pub fn new(events: &Vec<Event>, previous: Self, win: &Window) -> Self {
        let mut new = Self::zero();
        new.merge(previous);
        for event in events {
           match event {
               Event::MouseWheel { x, y, .. } => {
                   new.scroll = vect!(*x, *y);
               }
               Event::MouseMotion { x, y, ..} => {
                   new.pos = win.project_mouse(vect!(*x, *y));
               }
               Event::MouseButtonDown {mouse_btn, clicks, ..} => {
                   if clicks == &2u8 {
                       new.double_click = true;
                   }
                   new.down.insert(*mouse_btn);
               }
               Event::MouseButtonUp { mouse_btn, clicks, ..} => {
                   if clicks == &2u8 {
                       new.double_click = true;
                   }
                   new.pressed.remove(&mouse_btn);
                   new.up.insert(*mouse_btn);
               }
               _ => ()
           }
        }

        new
    }

    pub fn pressed(&self, button: MouseButton) -> bool {
        self.pressed.contains(&button)
    }
}