diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 05c37b8..0fbae7b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,6 +1,12 @@ add_executable(Exponent main.cc + GLProgramLoader.cc ) target_link_libraries(Exponent PUBLIC glfw glm::glm) -target_include_directories(Exponent PUBLIC ${CMAKE_SOURCE_DIR}/include) \ No newline at end of file +target_include_directories(Exponent PUBLIC ${CMAKE_SOURCE_DIR}/include) + +target_compile_definitions(Exponent PRIVATE GLFW_INCLUDE_NONE) + +file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/shaders/shader.frag" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/shaders/") +file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/shaders/shader.vert" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/shaders/") \ No newline at end of file diff --git a/src/GLProgramLoader.cc b/src/GLProgramLoader.cc new file mode 100644 index 0000000..48da08f --- /dev/null +++ b/src/GLProgramLoader.cc @@ -0,0 +1,81 @@ +#include "GLProgramLoader.hh" + +#include +#include +#include +#include + +static GLuint compile_shader(GLenum shader_type, const std::string &path) { + if (shader_type != GL_VERTEX_SHADER && shader_type != GL_FRAGMENT_SHADER) { + throw GLProgramException(std::format("Unsupported shader type {} provided.", shader_type)); + } + + // Check that the path actually exists + std::ifstream shader_file_handle; + shader_file_handle.open(path); + + if (!shader_file_handle.is_open()) { + throw GLProgramException(std::format("Failed to open path {}.", path)); + } + + // Read the data in. + std::stringstream shader_source_stream; + shader_source_stream << shader_file_handle.rdbuf(); + std::string shader_source_str = shader_source_stream.str(); + const GLchar *shader_source = shader_source_str.c_str(); + + GLuint shader_id = glCreateShader(shader_type); + + if (shader_id == 0) { + throw GLProgramException(std::format("Error when creating shader type {}.", shader_type)); + } + + glShaderSource(shader_id, 1, &shader_source, NULL); + glCompileShader(shader_id); + GLint did_compile; + glGetShaderiv(shader_id, GL_COMPILE_STATUS, &did_compile); + if(!did_compile) { + GLint len = 0; + + glGetShaderiv(shader_id, GL_INFO_LOG_LENGTH, &len); + std::string error_log(len, ' '); + + glGetShaderInfoLog(shader_id, len, &len, error_log.data()); + + glDeleteShader(shader_id); + throw GLProgramException(std::format("Shader {} compilation failed:\n{}", path, error_log)); + } + + return shader_id; +} + +GLuint compile_and_link_program(const std::vector> &shaders) { + // Firstly, compile all the shaders and get all of their ids. + // I would love to use a functional map here, but C++ doesn't really do that. + std::vector shader_ids = {}; + for(auto &s:shaders) { + shader_ids.push_back(compile_shader(s.first, s.second)); + } + + GLuint program_id = glCreateProgram(); + for (auto &s:shader_ids) { + glAttachShader(program_id, s); + } + glLinkProgram(program_id); + + GLint link_worked; + glGetProgramiv(program_id, GL_LINK_STATUS, &link_worked); + if (!link_worked) { + GLint len = 0; + glGetProgramiv(program_id, GL_INFO_LOG_LENGTH, &len); + + std::string error_log(len, ' '); + + glGetProgramInfoLog(program_id, len, &len, error_log.data()); + + glDeleteProgram(program_id); + throw GLProgramException(std::format("Program {} link failed:\n{}", program_id, error_log)); + } + + return program_id; +} \ No newline at end of file diff --git a/src/GLProgramLoader.hh b/src/GLProgramLoader.hh new file mode 100644 index 0000000..73701a1 --- /dev/null +++ b/src/GLProgramLoader.hh @@ -0,0 +1,24 @@ +#pragma once + +#include "glad/gl.h" + +#include +#include +#include + +class GLProgramException : public std::exception { + std::string m_what; + public: + GLProgramException(const char *str):m_what(str) { + } + + GLProgramException(const std::string &str):m_what(str) { + } + + virtual const char* what() const throw() { + return m_what.c_str(); + } +}; + + +GLuint compile_and_link_program(const std::vector> &shaders); \ No newline at end of file diff --git a/src/main.cc b/src/main.cc index effde50..6c2c6af 100644 --- a/src/main.cc +++ b/src/main.cc @@ -1,9 +1,20 @@ -#define GLAD_GL_IMPLEMENTATION -#include "glad/gl.h" -#define GLFW_INCLUDE_NONE #include "GLFW/glfw3.h" -//http://seshbot.com/blog/2015/05/05/an-introduction-to-opengl-getting-started/ +#include "GLProgramLoader.hh" + +#include +#include +#include +#include +#include + +// This has to come last. +#define GLAD_GL_IMPLEMENTATION +#include + +static void dbg_log(const char* str) { + std::cout << str << std::endl; +} static void key_callback(GLFWwindow *w, int key, int scancode, int action, int mods) { if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) { @@ -11,14 +22,74 @@ static void key_callback(GLFWwindow *w, int key, int scancode, int action, int m } } +#define VERTEX_SHADER_LOCATION "shaders/shader.vert" +#define FRAGMENT_SHADER_LOCATION "shaders/shader.frag" + +GLuint g_program_id = 0; + +static GLuint time_unif; + +static void init_shaders() { + dbg_log("Initialising shaders"); + + const std::vector> shaders = { + {GL_VERTEX_SHADER, VERTEX_SHADER_LOCATION}, + {GL_FRAGMENT_SHADER, FRAGMENT_SHADER_LOCATION}, + }; + + try { + g_program_id = compile_and_link_program(shaders); + } catch (GLProgramException e) { + std::cerr << e.what() << std::endl; + } catch (...) { + std::cerr << "Unexpected exception while compiling shaders." << std::endl; + } + + glUseProgram(g_program_id); + + glGetUniformLocation(g_program_id, "time"); +} + +static const GLfloat verts[6] = { + 0.0f, 0.0f, 0.0f, 0.5f, 0.5f, 0.0f +}; + +static GLuint vertex_buf; +static GLuint array_buf; +static const GLuint vertex_data_position = 0; + +// Do the work of initialising opengl so we can draw stuff +static void init_opengl() { + dbg_log("Initialising opengl"); + // Configure the context's capabilities + glClearColor(0.0, 0.0, 0.0, 1.0); + + glGenVertexArrays(1, &array_buf); + glBindVertexArray(array_buf); + + glGenBuffers(1, &vertex_buf); + glBindBuffer(GL_ARRAY_BUFFER, vertex_buf); + glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW); + + + init_shaders(); + + glVertexAttribPointer(vertex_data_position, 2, GL_FLOAT, GL_FALSE, 0, NULL); + glEnableVertexAttribArray(vertex_data_position); + +} + int main() { + dbg_log("Initialising glfw"); // Initialise GLFW if (!glfwInit()) { exit(EXIT_FAILURE); } glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + // Create our context GLFWwindow *w = glfwCreateWindow(640, 480, "Test", NULL, NULL); if (!w) { @@ -32,13 +103,7 @@ int main() { // Set up the key handler glfwSetKeyCallback(w, key_callback); - // Configure the context's capabilities - glClearColor(1.0, 0.0, 0.0, 1.0); - glEnable(GL_DEPTH_TEST); - glEnable(GL_BLEND); // Maybe this allows transparency? - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - // TODO: Actually make some opengl shit happen + init_opengl(); // Main Loop while(!glfwWindowShouldClose(w)) { @@ -50,6 +115,14 @@ int main() { glClear(GL_COLOR_BUFFER_BIT); */ + float time = glfwGetTime(); + + glClear(GL_COLOR_BUFFER_BIT); + + glUniform1f(time_unif, time); + glBindVertexArray(array_buf); + glDrawArrays(GL_TRIANGLES, 0, 3); + // TODO Draw primitives glfwSwapBuffers(w); diff --git a/src/shaders/shader.frag b/src/shaders/shader.frag new file mode 100644 index 0000000..53a13b6 --- /dev/null +++ b/src/shaders/shader.frag @@ -0,0 +1,8 @@ +#version 430 core + +out vec4 f_color; + +// All fragments are the same colour for now. +void main() { + f_color = vec4(1.0, 1.0, 1.0, 1.0); +} \ No newline at end of file diff --git a/src/shaders/shader.vert b/src/shaders/shader.vert new file mode 100644 index 0000000..6958689 --- /dev/null +++ b/src/shaders/shader.vert @@ -0,0 +1,17 @@ +#version 430 core + +layout (location = 0) in vec4 v_position; + +uniform float time; + +// Simple passthrough shader for now. +void main() { + vec2 new_xy = v_position.xy * mat2x2( + cos(time), -sin(time), + sin(time), cos(time) + ); + + vec4 new_position = vec4(new_xy.x, new_xy.y, 0.0, 1.0); + + gl_Position = new_position; +} \ No newline at end of file