Search code examples
openglrustglfw

GLFW-RS only shows black square instead of rotating cubes


I am trying to reformat C++ code I created from learnopengl.com with Rust bindings and it isn't quite working as expected. I am only getting a black square when I should be getting 10 rotating textured cubes.

Here's an image of my output window whenever I run the application.

Could somebody help me figure out what's going wrong? I can't understand it and it's driving me up the walls.

The code is quite lengthy so here is link to the repository if that makes it easier to read. Github GLFW Repo

main.rs

extern crate gl;
extern crate glfw;
extern crate glm;
extern crate image;

mod shader;

// use glfw::ffi;
use gl::types::*;
use glfw::{Action, Context, Key};
use glm::*;

use std::ffi::c_void;
use std::mem::size_of;

use shader::shader::Shader;

fn main() {
    let mut glfw = glfw::init(glfw::FAIL_ON_ERRORS).unwrap();

    glfw.window_hint(glfw::WindowHint::ContextVersion(4, 3));
    glfw.window_hint(glfw::WindowHint::OpenGlForwardCompat(true));
    glfw.window_hint(glfw::WindowHint::OpenGlProfile(
        glfw::OpenGlProfileHint::Core,
    ));

    let (mut window, events) = glfw
        .create_window(800, 600, "Hello this is window", glfw::WindowMode::Windowed)
        .expect("Failed to create GLFW window.");

    window.set_title("OpenGL Custom App");

    window.set_key_polling(true);
    window.make_current();
    window.set_framebuffer_size_polling(true);

    load_gl_functions(&mut window);

    // ffi::glfwSetWindowSizeCallback(*window, cbfun: Option<GLFWwindowsizefun>);

    // First three verticies, last two texture
    let _vertices: [f32; 180] = [
        -0.5, -0.5, -0.5, 0.0, 0.0, 0.5, -0.5, -0.5, 1.0, 0.0, 0.5, 0.5, -0.5, 1.0, 1.0, 0.5, 0.5,
        -0.5, 1.0, 1.0, -0.5, 0.5, -0.5, 0.0, 1.0, -0.5, -0.5, -0.5, 0.0, 0.0, -0.5, -0.5, 0.5,
        0.0, 0.0, 0.5, -0.5, 0.5, 1.0, 0.0, 0.5, 0.5, 0.5, 1.0, 1.0, 0.5, 0.5, 0.5, 1.0, 1.0, -0.5,
        0.5, 0.5, 0.0, 1.0, -0.5, -0.5, 0.5, 0.0, 0.0, -0.5, 0.5, 0.5, 1.0, 0.0, -0.5, 0.5, -0.5,
        1.0, 1.0, -0.5, -0.5, -0.5, 0.0, 1.0, -0.5, -0.5, -0.5, 0.0, 1.0, -0.5, -0.5, 0.5, 0.0,
        0.0, -0.5, 0.5, 0.5, 1.0, 0.0, 0.5, 0.5, 0.5, 1.0, 0.0, 0.5, 0.5, -0.5, 1.0, 1.0, 0.5,
        -0.5, -0.5, 0.0, 1.0, 0.5, -0.5, -0.5, 0.0, 1.0, 0.5, -0.5, 0.5, 0.0, 0.0, 0.5, 0.5, 0.5,
        1.0, 0.0, -0.5, -0.5, -0.5, 0.0, 1.0, 0.5, -0.5, -0.5, 1.0, 1.0, 0.5, -0.5, 0.5, 1.0, 0.0,
        0.5, -0.5, 0.5, 1.0, 0.0, -0.5, -0.5, 0.5, 0.0, 0.0, -0.5, -0.5, -0.5, 0.0, 1.0, -0.5, 0.5,
        -0.5, 0.0, 1.0, 0.5, 0.5, -0.5, 1.0, 1.0, 0.5, 0.5, 0.5, 1.0, 0.0, 0.5, 0.5, 0.5, 1.0, 0.0,
        -0.5, 0.5, 0.5, 0.0, 0.0, -0.5, 0.5, -0.5, 0.0, 1.0,
    ];

    let cube_positions: [Vec3; 10] = [
        glm::vec3(0.0, 0.0, 0.0),
        glm::vec3(2.0, 5.0, -15.0),
        glm::vec3(-1.5, -2.2, -2.5),
        glm::vec3(-3.8, -2.0, -12.3),
        glm::vec3(2.4, -0.4, -3.5),
        glm::vec3(-1.7, 3.0, -7.5),
        glm::vec3(1.3, -2.0, -2.5),
        glm::vec3(1.5, 2.0, -2.5),
        glm::vec3(1.5, 0.2, -1.5),
        glm::vec3(-1.3, 1.0, -1.5),
    ];

    let mut vbo: GLuint = 0;
    let mut vao: GLuint = 0;

    let mut texture1: u32 = 0;
    let mut texture2: u32 = 0;

    let shader = Shader::create_shader(
        "src/shaders/vertShader.vert",
        "src/shaders/fragShader.frag",
        &mut window,
    )
    .expect("There was an error creating the program.");

    unsafe {
        gl::Enable(gl::DEPTH_TEST);

        // Vertices pointer

        gl::GenBuffers(1, &mut vbo);
        gl::GenVertexArrays(1, &mut vao);

        gl::BindVertexArray(vao);

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

        gl::BufferData(
            gl::ARRAY_BUFFER,
            size_of::<[f32; 180]>() as isize,
            _vertices.as_ptr() as *const c_void,
            gl::STATIC_DRAW,
        );

        // Vertext pointer
        gl::VertexAttribPointer(
            0,
            3,
            gl::FLOAT,
            gl::FALSE,
            5 * size_of::<f32>() as i32,
            0 as *const c_void,
        );
        gl::EnableVertexAttribArray(0);

        // Texture pointer
        gl::VertexAttribPointer(
            1,
            2,
            gl::FLOAT,
            gl::FALSE,
            5 * size_of::<f32>() as i32,
            (3 * size_of::<f32>()) as *const c_void,
        );
        gl::EnableVertexAttribArray(1);

        // Bind textures

        gl::GenTextures(1, &mut texture1 as *mut u32);

        gl::ActiveTexture(gl::TEXTURE0);
        gl::BindTexture(gl::TEXTURE_2D, texture1);

        gl::TexParameteri(
            gl::TEXTURE_2D,
            gl::TEXTURE_WRAP_S,
            gl::MIRRORED_REPEAT as i32,
        );
        gl::TexParameteri(
            gl::TEXTURE_2D,
            gl::TEXTURE_WRAP_R,
            gl::MIRRORED_REPEAT as i32,
        );
        gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as i32);
        gl::TexParameteri(
            gl::TEXTURE_2D,
            gl::TEXTURE_MIN_FILTER,
            gl::LINEAR_MIPMAP_LINEAR as i32,
        );
        let tex1 = image::open("E:\\Repos\\Rust\\RSOpenGL\\src\\textures\\wall.jpg")
            .expect("Failed to load image.")
            .into_rgb();

        gl::TexImage2D(
            gl::TEXTURE_2D,
            0,
            gl::RGB as i32,
            tex1.width() as i32,
            tex1.height() as i32,
            0,
            gl::RGB,
            gl::UNSIGNED_BYTE,
            tex1.as_ptr() as *const c_void,
        );

        gl::GenerateMipmap(gl::TEXTURE_2D);

        // Texture 2
        gl::GenTextures(1, &mut texture2 as *mut u32);

        gl::ActiveTexture(gl::TEXTURE1);
        gl::BindTexture(gl::TEXTURE_2D, texture2);

        gl::TexParameteri(
            gl::TEXTURE_2D,
            gl::TEXTURE_WRAP_S,
            gl::MIRRORED_REPEAT as i32,
        );
        gl::TexParameteri(
            gl::TEXTURE_2D,
            gl::TEXTURE_WRAP_R,
            gl::MIRRORED_REPEAT as i32,
        );
        gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as i32);
        gl::TexParameteri(
            gl::TEXTURE_2D,
            gl::TEXTURE_MIN_FILTER,
            gl::LINEAR_MIPMAP_LINEAR as i32,
        );
        let tex2 = image::open("E:\\Repos\\Rust\\RSOpenGL\\src\\textures\\awesomeface.png")
            .expect("Failed to load image.")
            .into_rgb();

        gl::TexImage2D(
            gl::TEXTURE_2D,
            0,
            gl::RGB as i32,
            tex2.width() as i32,
            tex2.height() as i32,
            0,
            gl::RGB,
            gl::UNSIGNED_BYTE,
            tex2.as_ptr() as *const c_void,
        );

        gl::GenerateMipmap(gl::TEXTURE_2D);

        gl::ClearColor(0.2, 0.3, 0.3, 1.0);

        shader.use_shader();
        shader.set_int32("texture1", 0);
        shader.set_int32("texture2", 1);
    }

    let mut last_frame = glfw.get_time();
    while window.should_close() == false {
        let curr_frame: f64 = glfw.get_time();
        let delta_time: f64 = curr_frame - last_frame;
        println!("{}", delta_time);
        last_frame = curr_frame;

        // process input

        unsafe {
            gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT);

            gl::ActiveTexture(gl::TEXTURE0);
            gl::BindTexture(gl::TEXTURE_2D, texture1);
            gl::ActiveTexture(gl::TEXTURE1);
            gl::BindTexture(gl::TEXTURE_2D, texture2);

            let projection: glm::Mat4 = glm::ext::perspective::<f32>(
                glm::radians(90.0),
                800 as f32 / 600 as f32,
                0.1,
                100.0,
            );
            shader.set_mat4("projection", projection);

            let view: glm::Mat4 = glm::ext::look_at::<f32>(
                glm::vec3(0.0, 0.0, 3.0),
                glm::vec3(0.0, 0.0, 3.0) + glm::vec3(0.0, 0.0, -1.0),
                glm::vec3(0.0, 1.0, 0.0),
            );
            shader.set_mat4("view", view);
            // println!("{:?}\n{:?}", view, projection);

            shader.use_shader();

            gl::BindVertexArray(vao);
            let mut i: f32 = 0.0;
            for cube in cube_positions.iter() {
                let mut model: glm::Mat4 = glm::mat4(
                    1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0,
                );
                model = glm::ext::translate(&model, *cube);
                let angle: f32 = 20.0 + i;
                i += 1.0;
                model = glm::ext::rotate(
                    &model,
                    glm::radians(angle) * glfw.get_time() as f32,
                    glm::vec3(1.0, 0.3, 0.5),
                );
                shader.set_mat4("model", model);
                // println!("{:?}", model);

                gl::DrawArrays(gl::TRIANGLES, 0, 36);
            }
        }

        glfw.poll_events();
        for (_, event) in glfw::flush_messages(&events) {
            handle_window_event(&mut window, event);
        }
        window.swap_buffers();
    }

    unsafe {
        gl::DeleteVertexArrays(1, &vao);
        gl::DeleteBuffers(1, &vbo);

        // glfw::ffi::glfwTerminate(); apparently not necessary
    }
}

fn handle_window_event(window: &mut glfw::Window, event: glfw::WindowEvent) {
    match event {
        glfw::WindowEvent::Key(Key::Escape, _, Action::Press, _) => window.set_should_close(true),
        _ => {}
    }
}

fn load_gl_functions(window: &mut glfw::Window) {
    gl::ClearColor::load_with(|_s| window.get_proc_address("glClearColor"));
    gl::Clear::load_with(|_s| window.get_proc_address("glClear"));
    gl::GenBuffers::load_with(|_s| window.get_proc_address("glGenBuffers"));
    gl::GenVertexArrays::load_with(|_s| window.get_proc_address("glGenVertexArrays"));
    gl::BindBuffer::load_with(|_s| window.get_proc_address("glBindBuffer"));
    gl::BufferData::load_with(|_s| window.get_proc_address("glBufferData"));
    gl::VertexAttribPointer::load_with(|_s| window.get_proc_address("glVertexAttribPointer"));
    gl::EnableVertexAttribArray::load_with(|_s| {
        window.get_proc_address("glEnableVertexAttribArray")
    });
    gl::BindVertexArray::load_with(|_s| window.get_proc_address("glBindVertexArray"));

    gl::DeleteVertexArrays::load_with(|_s| window.get_proc_address("glDeleteVertexArrays"));
    gl::DeleteBuffers::load_with(|_s| window.get_proc_address("glDeleteBuffers"));

    gl::GenTextures::load_with(|_s| window.get_proc_address("glGenTextures"));
    gl::TexParameteri::load_with(|_s| window.get_proc_address("glTexParameteri"));
    gl::TexImage2D::load_with(|_s| window.get_proc_address("glTexImage2D"));
    gl::BindTexture::load_with(|_s| window.get_proc_address("glBindTexture"));
    gl::ActiveTexture::load_with(|_s| window.get_proc_address("glActiveTexture"));
    gl::GenerateMipmap::load_with(|_s| window.get_proc_address("glGenerateMipmap"));

    gl::Enable::load_with(|_s| window.get_proc_address("glEnable"));
    gl::DrawArrays::load_with(|_s| window.get_proc_address("glDrawArrays"));
}

shader.rs

extern crate gl;
extern crate glfw;
extern crate glm;

pub mod shader {
    pub struct Shader {
        pub id: u32,
    }

    impl Shader {
        pub fn create_shader(vert_file_path: &str, frag_file_path: &str, window: &mut glfw::Window) -> std::result::Result<Shader, std::io::Error> {
            if gl::CreateProgram::is_loaded() == false {
                let result = Shader::load_gl_procs(window);
                if result == false {
                    return Err(std::io::Error::new(
                        std::io::ErrorKind::Other,
                        "ERROR::SHADER::CANNOT_LOAD_PROGRAMS",
                    ));
                }
            }

            let vert_text = std::fs::read_to_string(vert_file_path).expect("Unable to read vert file");
            let vert_text_ptr = vert_text.as_ptr() as *const *const gl::types::GLchar;

            let frag_text = std::fs::read_to_string(frag_file_path).expect("Unable to read frag file");
            let frag_text_ptr = frag_text.as_ptr() as *const *const gl::types::GLchar;
            //println!("{:?} \n{:?}", vert_text_ptr, frag_text_ptr);

            let id: gl::types::GLuint;

            unsafe {
                let vert_shader = gl::CreateShader(gl::VERTEX_SHADER);
                gl::ShaderSource(vert_shader, 1, vert_text_ptr, &0);
                gl::CompileShader(vert_shader);

                /*  STATUS_ACCESS_VIOLATION error
                let mut success: i32;
                let mut info_log: [gl::types::GLchar; 512] = [0; 512];

                gl::GetShaderiv(vert_shader, gl::COMPILE_STATUS, success as *mut i32);
                println!("{:?}", success);

                if success == 0 {
                    gl::GetShaderInfoLog(vert_shader, 512, 0 as *mut gl::types::GLsizei, &mut info_log[0]);

                    for i in info_log.iter() {
                        print!("{}", i);
                    }
                } */

                let frag_shader = gl::CreateShader(gl::FRAGMENT_SHADER);
                gl::ShaderSource(frag_shader, 1, frag_text_ptr, &0);
                gl::CompileShader(frag_shader);

                id = gl::CreateProgram();

                gl::AttachShader(id, vert_shader);
                gl::AttachShader(id, frag_shader);
                gl::LinkProgram(id);

                gl::DeleteShader(vert_shader);
                gl::DeleteShader(frag_shader);
            }

            let created_shader: Shader = Shader { id: id };

            return Ok(created_shader);
        }

        pub fn load_gl_procs(window: &mut glfw::Window) -> bool {
            gl::CreateShader::load_with(|_s| window.get_proc_address("glCreateShader"));
            gl::ShaderSource::load_with(|_s| window.get_proc_address("glShaderSource"));
            gl::CompileShader::load_with(|_s| window.get_proc_address("glCompileShader"));
            gl::GetShaderiv::load_with(|_s| window.get_proc_address("glGetShaderiv"));
            gl::CreateProgram::load_with(|_s| window.get_proc_address("glCreateProgram"));
            gl::AttachShader::load_with(|_s| window.get_proc_address("glAttachShader"));
            gl::LinkProgram::load_with(|_s| window.get_proc_address("glLinkProgram"));
            gl::GetProgramiv::load_with(|_s| window.get_proc_address("glGetProgramiv"));
            gl::GetShaderInfoLog::load_with(|_s| window.get_proc_address("glGetShaderInfoLog"));
            gl::GetProgramInfoLog::load_with(|_s| window.get_proc_address("glGetProgramInfoLog"));
            gl::DeleteShader::load_with(|_s| window.get_proc_address("glDeleteShader"));
            gl::UseProgram::load_with(|_s| window.get_proc_address("glUseProgram"));

            gl::Uniform1i::load_with(|_s| window.get_proc_address("glUniform1i"));
            gl::Uniform1f::load_with(|_s| window.get_proc_address("glUniform1f"));
            gl::UniformMatrix4fv::load_with(|_s| window.get_proc_address("glUniformMatrix4fv"));
            gl::GetUniformLocation::load_with(|_s| window.get_proc_address("glGetUniformLocation"));


            // TODO Check to see if all of these are loaded

            return true;
        }

        pub unsafe fn use_shader(&self) {
            gl::UseProgram(self.id as gl::types::GLuint);
        }

        pub unsafe fn set_bool(&self, name: &str, value: bool) {
            gl::Uniform1i(gl::GetUniformLocation(self.id, name.as_ptr() as *const gl::types::GLchar), value as gl::types::GLint);
        }

        pub unsafe fn set_int32(&self, name: &str, value: i32) {
            gl::Uniform1i(gl::GetUniformLocation(self.id, name.as_ptr() as *const gl::types::GLchar), value as gl::types::GLint);
        }

        pub unsafe fn set_float32(&self, name: &str, value: f32) {
            gl::Uniform1f(gl::GetUniformLocation(self.id, name.as_ptr() as *const gl::types::GLchar), value as gl::types::GLfloat);
        }

        pub unsafe fn set_mat4(&self, name: &str, value: glm::Mat4) {
            gl::UniformMatrix4fv(gl::GetUniformLocation(self.id, name.as_ptr() as *const gl::types::GLchar), 1, gl::FALSE, &value[0][0]);
        }
    }
}

vertShader.vert

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoord;

out vec2 TexCoord;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main()
{
    gl_Position = projection * view * model * vec4(aPos, 1.0);
    TexCoord = aTexCoord;
}

fragShader.frag

#version 330 core

out vec4 FragColor;

in vec2 TexCoord;

uniform sampler2D texture1;
uniform sampler2D texture2;

void main()
{
//   FragColor = vec4(ourColor, 1.0f);
    FragColor = mix(texture(texture1, TexCoord), texture(texture2, TexCoord), 0.5);
}

Solution

  • There were actually two main problems with my program, both regarding CStrings.

    Problem 1: OpenGL couldn't create the program because the text I sent it did not have a null termination character, so it had to be recasted as a CString with one.

    let v_shader_code = CString::new(vert_text.as_bytes()).unwrap();
    let f_shader_code = CString::new(frag_text.as_bytes()).unwrap();
    

    Problem 2: The "set_*()" functions wouldn't work because the name was being passed as an &str without a null termination character. This also has to be cast into a c string.

    pub unsafe fn set_bool(&self, name: &str, value: bool) {
        gl::Uniform1i(gl::GetUniformLocation(self.id, CString::new(name).expect("Error converting &str to CString").as_c_str().as_ptr()), value as i32);
    }