#opengl #graphics #gamedev #events #window-event

glume

A simple to use all-in-one OpenGL application framework

6 releases (breaking)

0.5.1 Jul 11, 2024
0.5.0 Jul 11, 2024
0.4.0 Jul 5, 2024
0.3.0 Jul 1, 2024
0.1.0 Jul 1, 2024

#531 in Graphics APIs

MIT/Apache

41KB
958 lines

A simple to use all-in-one OpenGL application framework.

Examples

Barebones Example

// the gl crate is exported publicly
use glume::gl;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // initial configuration for the window
    let window_config = glume::window::WindowConfiguration {
        title: "Hello, world!".to_string(),
        size: (800, 600),
        gl_version: (4, 5),
    };

    let window = window_config.build_window();

    // after the window is created, we can call OpenGL functions, not before
    unsafe {
        use glume::gl_utils::standard_debug_callback;
        gl::Enable(gl::DEBUG_OUTPUT);
        gl::DebugMessageCallback(Some(standard_debug_callback), std::ptr::null());
    }

    window.run(|wc, event| {
        use glume::window::Event;
        match event {
            Event::Resized(width, height) => {
                unsafe {
                    gl::Viewport(0, 0, width as i32, height as i32);
                }
            }

            Event::RedrawRequested => {
                unsafe {
                    gl::ClearColor(0.2, 0.2, 0.2, 1.0);
                    gl::Clear(gl::COLOR_BUFFER_BIT);
                }
            }

            Event::KeyPressed(key) => {
                use glume::window::VirtualKeyCode as Vk;
                match key {
                    Vk::Escape => wc.close(),
                    _ => (),
                }
            }

            _ => (),
        }

        Ok(())
    });
}

Example with persistent state

use glume::gl;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // initial configuration for the window
    let window_config = glume::window::WindowConfiguration {
        title: "Hello, world!".to_string(),
        size: (800, 600),
        gl_version: (4, 5),
    };

    let window = window_config.build_window();

    // after the window is created, we can call OpenGL functions, not before
    unsafe {
        use glume::gl_utils::standard_debug_callback;
        gl::Enable(gl::DEBUG_OUTPUT);
        gl::DebugMessageCallback(Some(standard_debug_callback), std::ptr::null());
    }

    // if you have persistent state, initialize it here, including OpenGL resources
    let mut app = ExampleApp::new()?;

    // don't forget to move the app into the closure
    window.run(move |wc, event| {
        use glume::window::Event;
        match event {
            Event::Resized(width, height) => {
                unsafe {
                    gl::Viewport(0, 0, width as i32, height as i32);
                }
            }

            Event::RedrawRequested => {
                unsafe {
                    gl::ClearColor(0.2, 0.2, 0.2, 1.0);
                    gl::Clear(gl::COLOR_BUFFER_BIT);
                }

                app.render();
            }

            Event::KeyPressed(key) => {
                use glume::window::VirtualKeyCode as Vk;
                match key {
                    Vk::Escape => wc.close(),
                    Vk::Space => {
                        app.state_counter = (app.state_counter + 1) % 4;
                        wc.request_redraw();
                    }
                    _ => (),
                }
            }

            _ => (),
        }

        Ok(())
    });
}

struct ExampleApp {
    program: u32,
    vao: u32,
    state_counter: i32,
}

impl ExampleApp {
    fn new() -> Result<Self, Box<dyn std::error::Error>> {
        let program = create_example_program()?;
        let vao = create_example_vertex_array()?;

        Ok(Self {
            program,
            vao,
            state_counter: 0,
        })
    }

    fn render(&self) {
        unsafe {
            gl::UseProgram(self.program);
            gl::BindVertexArray(self.vao);
            gl::DrawArrays(gl::TRIANGLES, self.state_counter, 3);
        }
    }
}

fn create_example_program() -> Result<u32, Box<dyn std::error::Error>> {
    let vcode = r#"
        #version 450 core
        layout (location=0) in vec2 position;
        layout (location=1) in vec3 color;
        out vec3 v_color;
        void main() {
            gl_Position = vec4(position, 0.0, 1.0);
            v_color = color;
        }
    "#;

    let fcode = r#"
        #version 450 core
        in vec3 v_color;
        out vec4 f_color;
        void main() {
            f_color = vec4(v_color, 1.0);
        }
    "#;

    let vshader = glume::gl_utils::compile_shader(vcode, gl::VERTEX_SHADER)?;
    let fshader = glume::gl_utils::compile_shader(fcode, gl::FRAGMENT_SHADER)?;
    let shaders = &[vshader, fshader];

    let program = glume::gl_utils::link_shader_program(shaders)?;

    unsafe {
        gl::DeleteShader(vshader);
        gl::DeleteShader(fshader);
    }

    Ok(program)
}

fn create_example_vertex_array() -> Result<u32, Box<dyn std::error::Error>> {
    let vertices: &[f32] = &[
        // positions
        -0.5, 0.0,
        0.0, 0.5,
        0.5, 0.0,
        0.0, -0.5,
        -0.5, 0.0,
        0.0, 0.5,

        // colors
        1.0, 1.0, 0.0,
        0.0, 1.0, 1.0,
        1.0, 0.0, 1.0,
        1.0, 1.0, 1.0,
        1.0, 1.0, 0.0,
        0.0, 1.0, 1.0,
    ];

    let vbo = glume::gl_utils::create_buffer_f32(vertices, gl::STATIC_DRAW)?;

    let mut vao = 0;
    unsafe {
        gl::GenVertexArrays(1, &mut vao);
        gl::BindVertexArray(vao);

        gl::BindBuffer(gl::ARRAY_BUFFER, vbo);

        let stride = 0;

        let offset = 0 as *const _;
        gl::VertexAttribPointer(0, 2, gl::FLOAT, gl::FALSE, stride, offset);
        gl::EnableVertexAttribArray(0);

        let offset = (12 * std::mem::size_of::<f32>()) as *const _;
        gl::VertexAttribPointer(1, 3, gl::FLOAT, gl::FALSE, stride, offset);
        gl::EnableVertexAttribArray(1);
    }

    Ok(vao)
}

Dependencies

~3–14MB
~180K SLoC