#opengl #framework #graphics

glume

A simple Windowing and OpenGL context creation framework

10 releases (6 breaking)

0.7.2 May 21, 2025
0.7.1 May 5, 2025
0.6.0 May 3, 2025
0.5.1 Jul 11, 2024
0.1.0 Jul 1, 2024

#535 in Graphics APIs


Used in glenda

MIT/Apache

19KB
304 lines

A simple Windowing and OpenGL context creation 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 {
        gl::Enable(gl::DEBUG_OUTPUT);
    }

    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 {
        gl::Enable(gl::DEBUG_OUTPUT);
    }

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

    println!("Press space to change the state, or escape to close the window.");

    // 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() -> Self {
        let program = create_example_program();
        let vao = create_example_vertex_array();

        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() -> u32 {
    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 = compile_shader(vcode, gl::VERTEX_SHADER);
    let fshader = compile_shader(fcode, gl::FRAGMENT_SHADER);

    unsafe {
        let program = gl::CreateProgram();
        gl::AttachShader(program, vshader);
        gl::AttachShader(program, fshader);
        gl::LinkProgram(program);
        gl::DetachShader(program, vshader);
        gl::DetachShader(program, fshader);
        gl::DeleteShader(vshader);
        gl::DeleteShader(fshader);

        program
    }
}

fn compile_shader(source: &str, shader_type: u32) -> u32 {
    let shader = unsafe { gl::CreateShader(shader_type) };
    let c_str = std::ffi::CString::new(source).unwrap();
    unsafe {
        gl::ShaderSource(shader, 1, &c_str.as_ptr(), std::ptr::null());
        gl::CompileShader(shader);
    }

    shader
}

fn create_example_vertex_array() -> u32 {
    #[rustfmt::skip]
    let vertices: &[f32] = &[
        // positions    colors
        -0.5,  0.0,     1.0, 1.0, 0.0,
         0.0,  0.5,     0.0, 1.0, 1.0,
         0.5,  0.0,     1.0, 0.0, 1.0,
         0.0, -0.5,     1.0, 1.0, 1.0,
        -0.5,  0.0,     1.0, 1.0, 0.0,
         0.0,  0.5,     0.0, 1.0, 1.0,
    ];

    let mut vbo = 0;
    unsafe {
        gl::CreateBuffers(1, &mut vbo);
        let size = (vertices.len() * std::mem::size_of::<f32>()) as isize;
        let ptr = vertices.as_ptr() as *const _;
        gl::NamedBufferData(
            vbo,
            size,
            ptr,
            gl::STATIC_DRAW,
        );
    }

    let mut vao = 0;
    unsafe {
        gl::CreateVertexArrays(1, &mut vao);
        let stride = 5 * std::mem::size_of::<f32>() as i32;

        let mut offset = 0;
        gl::VertexArrayVertexBuffer(vao, 0, vbo, offset, stride);
        gl::EnableVertexArrayAttrib(vao, 0);

        offset += 2 * std::mem::size_of::<f32>() as isize;
        gl::VertexArrayVertexBuffer(vao, 1, vbo, offset, stride);
        gl::EnableVertexArrayAttrib(vao, 1);
    }

    vao
}

Dependencies

~3–16MB
~162K SLoC