heavyli_engine 0.0.7

A game engine based on 'OpenGL'.
Documentation
use crate::ecs::registry::Registry;
use crate::render::{
    camera::Camera,
    utils::{init_buffers, init_texture},
};
use crate::rlua::{UserData, UserDataMethods};
use crate::{
    math::{
        projection_type::ProjectionType,
        transform::{self, Transform},
    },
    render::{
        shader::{self, ShaderValue},
        shape::{
            buffers::{self, Buffers},
            shape_vertices::ShapeVertices,
            vertices::{DrawType, Vertex},
        },
        texture_2d::{self, TextureFilter},
        utils,
    },
};
use std::sync::{Arc, Mutex};

#[derive(Clone)]
pub struct Renderer2D {
    registry: Arc<Mutex<Registry>>,
    vertices: Vec<Vertex>,
    buffers: Buffers,
    shader_id: u32,
}

#[derive(Clone, Copy, Debug)]
pub struct TextureID(u32);

const Z_NEAR: f32 = 0.1;
const Z_FAR: f32 = 100.0;
const FOV: f32 = 45.0;
const ORTHO_DISTANCE: f32 = 1.75;

pub fn generate_sprite_2d_buffers(vertices: &mut Vec<Vertex>) -> Buffers {
    let buffers = unsafe { buffers::generate_buffers() };

    unsafe {
        init_buffers(&buffers, vertices);
    }

    return buffers;
}

pub fn generate_sprite_2d_vertices() -> Vec<Vertex> {
    return ShapeVertices::Rectangle(glm::vec2(0.5, 0.5), glm::vec2(-0.5, -0.5)).value();
}

impl UserData for Renderer2D {
    fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
        methods.add_method_mut(
            "add_sprite",
            |_,
             renderer,
             (entity_id, pos_x, pos_y, scale_x, scale_y, texture_id): (
                usize,
                f32,
                f32,
                f32,
                f32,
                u32,
            )| {
                renderer.add_sprite(
                    entity_id,
                    glm::vec3(pos_x, pos_y, 0.0),
                    glm::vec3(scale_x, scale_y, 0.0),
                    texture_id,
                );

                Ok(())
            },
        );

        methods.add_method_mut(
            "set_sprite_position",
            |_, renderer, (entity_id, pos_x, pos_y): (usize, f32, f32)| {
                renderer.set_sprite_position(entity_id, glm::vec3(pos_x, pos_y, 0.0));

                Ok(())
            },
        );

        methods.add_method_mut(
            "set_camera_position",
            |_, renderer, (entity_id, pos_x, pos_y): (usize, f32, f32)| {
                renderer.set_camera_position(entity_id, glm::vec3(pos_x, pos_y, 0.0));

                Ok(())
            },
        );
    }
}

impl Renderer2D {
    pub fn new(
        registry: Arc<Mutex<Registry>>,
        buffers: Buffers,
        vertices: Vec<Vertex>,
        shader_id: u32,
    ) -> Self {
        Self {
            registry: registry,
            buffers: buffers,
            vertices: vertices,
            shader_id: shader_id,
        }
    }

    pub fn add_texture(image: &str) -> u32 {
        let texture_id = unsafe { texture_2d::generate_texture_id(TextureFilter::Pixelated) };

        unsafe {
            init_texture(texture_id, image, false);
        }

        return texture_id;
    }

    pub fn add_sprite(
        &mut self,
        entity_id: usize,
        position: glm::Vec3,
        scale: glm::Vec3,
        texture_id: u32,
    ) {
        // Set Transform Position and Scale:
        let mut transform = transform::create_transform_by_position(position);
        transform.scale = scale;

        let mut registry = self.registry.lock().unwrap();

        registry.add_component(entity_id, TextureID { 0: texture_id });

        registry.add_component(entity_id, transform);
    }

    pub fn set_sprite_position(&mut self, entity_id: usize, position: glm::Vec3) {
        self.registry
            .lock()
            .unwrap()
            .get_component::<Transform>(entity_id)
            .unwrap()
            .borrow_mut()
            .lock()
            .unwrap()
            .position = position;
    }

    pub fn set_camera_position(&mut self, entity_id: usize, position: glm::Vec3) {
        let cam_pos_z = self
            .registry
            .lock()
            .unwrap()
            .get_component::<Camera>(entity_id)
            .unwrap()
            .borrow_mut()
            .lock()
            .unwrap()
            .get_position_z();
        self.registry
            .lock()
            .unwrap()
            .get_component::<Camera>(entity_id)
            .unwrap()
            .borrow_mut()
            .lock()
            .unwrap()
            .set_position(glm::vec3(position.x, position.y, cam_pos_z));
    }

    pub fn render(&self, screen_size: glm::Vec2, view: &glm::Mat4) {
        let mut registry = self.registry.lock().unwrap();
        let shape_entities;

        // For now, check all entities with Buffers component
        // untill there will be a better version of Registry::get_view:
        match registry.get_view::<TextureID>() {
            Some(entities) => shape_entities = entities,
            None => return,
        }

        // Set shader program to run:
        unsafe {
            shader::use_program(self.shader_id);
        }

        for entity_id in shape_entities {
            unsafe {
                texture_2d::bind(
                    registry
                        .get_component::<TextureID>(entity_id)
                        .unwrap()
                        .borrow_mut()
                        .lock()
                        .unwrap()
                        .0,
                );

                // Set Transformation in Shader:
                shader::set_value(
                    self.shader_id,
                    "translation",
                    ShaderValue::Mat4(transform::translate(
                        &registry
                            .get_component::<Transform>(entity_id)
                            .unwrap()
                            .borrow_mut()
                            .lock()
                            .unwrap(),
                        Z_NEAR,
                        Z_FAR,
                        FOV,
                        *view,
                        glm::vec2(screen_size.x, screen_size.y),
                        ORTHO_DISTANCE,
                        ProjectionType::Orthographic,
                    )),
                );

                // Render:
                buffers::bind_vao(self.buffers.vao);

                utils::draw_arrays(DrawType::Triangles, self.vertices.len() as i32);
            }
        }
    }

    pub fn delete_all_sprites(&mut self) {
        // For now, check all entities with Buffers component
        // untill there will be a better version of Registry::get_view:
        let shape_entities = self
            .registry
            .lock()
            .unwrap()
            .get_view::<TextureID>()
            .unwrap();

        // Free VBO, VAO:
        unsafe {
            buffers::delete_buffers(&mut self.buffers);
        }

        for entity_id in shape_entities {
            unsafe {
                // Free Texture ID:
                texture_2d::delete_texture(
                    &mut self
                        .registry
                        .lock()
                        .unwrap()
                        .get_component::<TextureID>(entity_id)
                        .unwrap()
                        .borrow_mut()
                        .lock()
                        .unwrap()
                        .0,
                );
            }
        }
    }
}