macropix 0.1.0

Pixel-perfect rendering for macroquad
Documentation
use macroquad::prelude::*;

pub struct Macropix {
    pub camera_position: Vec2,

    game_size: Vec2,
    screen_size: Vec2,
    render_size: Vec2,
    screen_gap: Vec2,
    render_scale: f32,

    render_targ: RenderTarget,
    render_camera: Camera2D,
    render_params: DrawTextureParams,
}

impl Macropix {
    pub fn new(width: u32, height: u32) -> Self {
        let render_targ = render_target(width, height);
        render_targ.texture.set_filter(FilterMode::Nearest);

        let render_camera = Camera2D {
            zoom: vec2(1.0 / width as f32 * 2.0, -1.0 / height as f32 * 2.0),
            render_target: Some(render_targ),
            ..Default::default()
        };

        let render_params = DrawTextureParams {
            flip_y: true,
            ..Default::default()
        };

        Macropix {
            camera_position: vec2(0.0, 0.0),

            game_size: vec2(width as f32, height as f32),
            screen_size: Vec2::ZERO,
            render_size: Vec2::ZERO,
            screen_gap: Vec2::ZERO,
            render_scale: 0.0,

            render_targ,
            render_camera,
            render_params,
        }
    }

    pub fn mouse_position(&self) -> Vec2 {
        let pos_tuple = mouse_position();

        let global_position = vec2(pos_tuple.0, pos_tuple.1);
        let relative_position = (global_position - self.screen_gap / 2.0) / self.render_scale;

        vec2(relative_position.x.round(), relative_position.y.round())
            .clamp(Vec2::ZERO, self.game_size)
    }

    pub fn begin_draw(&mut self) {
        self.screen_size = vec2(screen_width(), screen_height());

        let hor_scale = (self.screen_size.x / self.game_size.x).floor().max(1.0);
        let ver_scale = (self.screen_size.y / self.game_size.y).floor().max(1.0);

        self.render_scale = hor_scale.min(ver_scale);
        self.render_size = self.game_size * self.render_scale;
        self.render_params.dest_size = Some(self.render_size);
        self.render_camera.target = self.camera_position + self.game_size / 2.0;

        self.screen_gap = self.screen_size - self.render_size;

        set_camera(&self.render_camera);
    }

    pub fn end_draw(&mut self) {
        set_default_camera();

        draw_texture_ex(
            self.render_targ.texture,
            (self.screen_size.x / 2.0 - self.render_size.x / 2.0).round(),
            (self.screen_size.y / 2.0 - self.render_size.y / 2.0).round(),
            WHITE,
            self.render_params.clone(),
        );
    }
}