Skip to content

Creating custom drawable objects

Ethan Almloff edited this page Jun 1, 2024 · 3 revisions

This is not necessary to do, the default drawables that come with the library should be enough for most. However, if you have a solid understanding of OpenGL here is how you make a custom drawable.

use glium::uniform;
use glium::Surface;
use lumenpyx::drawable_object::Drawable;
use lumenpyx::LumenpyxProgram;
use lumenpyx::Transform;
use lumenpyx::Vertex;

// put your glsl shader in a file and reference it here
// if you don't do this the shader won't be including in the binary and it will panic
const GENERATE_CIRCLE_VERTEX_SHADER_SRC: &str =
    include_str!(r"..\shaders\primitives\circle_ahr_shader.vert");
const GENERATE_CIRCLE_FRAGMENT_SHADER_SRC: &str =
    include_str!(r"..\shaders\primitives\circle_ahr_shader.frag");

pub struct Circle {
    color: [f32; 4],
    radius: f32,
    transform: Transform,
}

impl Drawable for Circle {
    fn draw_albedo(
        &self,
        program: &LumenpyxProgram,
        transform: &Transform,
        albedo_framebuffer: &mut glium::framebuffer::SimpleFrameBuffer,
    ) {
        let color = self.color;
        let radius = self.radius;
        let transform = self.transform;

        let display = &program.display;
        let indices = &program.indices;

        // attempt to load the shader
        // as long as the load_shaders function was setup correctly, this shouldn't panic
        let shader = program.get_shader("circle_ahr_shader").unwrap();

        // this is a whole screen shape
        let shape = lumenpyx::shaders::FULL_SCREEN_QUAD;

        let vertex_buffer = glium::VertexBuffer::new(display, &shape).unwrap();

        // these are setup by name in the glsl shader file at the top of the file
        // ex.
        // ```glsl
        // uniform vec4 circle color
        // ```
        let uniforms = &uniform! {
            circle_color: color,
            radius_squared: radius.powi(2),
            matrix: transform.get_matrix(), // notice we use the transform passed in, not the one in the struct (the one passed in is correctly scaled and positioned based on the camera)
        };

        albedo_framebuffer
            .draw(
                &vertex_buffer,
                indices,
                &shader,
                uniforms,
                &Default::default(),
            )
            .unwrap();
    }

    // there are virtually identical functions for the other framebuffers (height, normal, and roughness)

    // this is called every frame, so make sure to check if the shader is already loaded
    fn try_load_shaders(&self, program: &mut LumenpyxProgram) {
        // check if the shader is loaded
        if program.get_shader("circle_ahr_shader").is_none() {
            // if not we create the shader
            let shader = glium::Program::from_source(
                &program.display,
                GENERATE_CIRCLE_VERTEX_SHADER_SRC,
                GENERATE_CIRCLE_FRAGMENT_SHADER_SRC,
                None,
            )
            .unwrap();

            // then we add the shader to the program to be accessed later
            program.add_shader(shader, "circle_ahr_shader");
        }
    }

    // this is so that objects scale properly with camera movement and dimensions
    fn get_transform(&self) -> Transform {
        self.transform
    }

    fn set_transform(&mut self, transform: Transform) {
        self.transform = transform;
    }
}

Clone this wiki locally