diff --git a/.clangd b/.clangd new file mode 100644 index 0000000..df95768 --- /dev/null +++ b/.clangd @@ -0,0 +1,2 @@ +CompileFlags: + Add: [-std=c++20] diff --git a/meson.build b/meson.build index 6dddccf..df15915 100644 --- a/meson.build +++ b/meson.build @@ -1,4 +1,4 @@ -project('opengl-learning', 'cpp', 'c') +project('opengl-learning', 'cpp', 'c', default_options: ['cpp_std=c++20']) glfw = dependency('glfw3') libepoxy = dependency('epoxy') diff --git a/src/fragment.glsl b/src/fragment.glsl new file mode 100644 index 0000000..31e169f --- /dev/null +++ b/src/fragment.glsl @@ -0,0 +1,8 @@ +// Set the GLSL version to 3.3 and use the OpenGL core profile +#version 330 core +out vec4 FragColor; +in vec3 ourColor; + +void main() { + FragColor = vec4(ourColor, 1.0); +} diff --git a/src/main.cc b/src/main.cc index 5ede541..1af3d70 100644 --- a/src/main.cc +++ b/src/main.cc @@ -1,35 +1,17 @@ -#include #include #include -#include + +#include +#include + +#include "shader.hh" +#include "utilities.hh" void framebuffer_size_callback(GLFWwindow* window, int width, int height); void process_input(GLFWwindow* window); void key_pressed(GLFWwindow *window, int key, int scancode, int action, int mods); int check_shader_compilation_status(unsigned int shaderId); -const char* vertexShaderCode = R"( -// Set the GLSL version to 3.3 and use the OpenGL core profile -#version 330 core -layout (location = 0) in vec3 aPos; - -void main() { - gl_Position = vec4(aPos, 1.0); -} -)"; - -const char* fragmentShaderCode = R"( -// Set the GLSL version to 3.3 and use the OpenGL core profile -#version 330 core -out vec4 FragColor; - -uniform vec4 ourColor; - -void main() { - FragColor = ourColor; -} -)"; - bool wireframe; bool rectangleToggled; @@ -52,9 +34,10 @@ struct GlRenderObjects { GlRenderObjects* triangle() { // An array of verticies containing data for a triangle. float vertices[] = { - -0.5f, -0.5f, 0.0f, - 0.5f, -0.5f, 0.0f, - 0.0f, 0.5f, 0.0f + // positions // colors + -0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, + 0.5f, -0.5f, 0.0f, 0.0f,1.0f, 0.0f, + 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f }; unsigned int vertexBufferObject, vertexArrayObject; @@ -71,12 +54,18 @@ GlRenderObjects* triangle() { glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); // Tell OpenGL how to process the Vertex attributes so it can pass it to the Vertex shader. - // This will pass the vertex to the location "0". We tell OpenGL the size of each vertex is 3 floats. + + // This will pass the position attribute to the location "0". We tell OpenGL the size of each position attribute is 3 floats. // We tell OpenGL that we're using floating point types. // We tell OpenGL we don't want our data to be normalized as its already normalized. - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0); glEnableVertexAttribArray(0); + // Pass the color attribute data to the location 1. + // We tell it to offset the data per "vertex" by 3 floats to get the color data instead of the position data. + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3*sizeof(float))); + glEnableVertexAttribArray(1); + glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); @@ -85,10 +74,11 @@ GlRenderObjects* triangle() { GlRenderObjects* rectangle() { float vertices[] = { - 0.5f, 0.5f, 0.0f, - 0.5f, -0.5f, 0.0f, - -0.5f, -0.5f, 0.0f, - -0.5f, 0.5f, 0.0f + // positions // colors + 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, + 0.5f, -0.5f, 0.0f, 0.0f,1.0f, 0.0f, + -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, + -0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f }; unsigned int indices[] = { @@ -115,10 +105,15 @@ GlRenderObjects* rectangle() { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementBufferObject); // Copy indicies into the GPU's memory. glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); + // Tell OpenGL on how to process the verticies. - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void *)0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void *)0); glEnableVertexAttribArray(0); + // Process the color data. + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void *)(3*sizeof(float))); + glEnableVertexAttribArray(1); + // Unbind the current buffers and arrays from the state. glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); @@ -155,54 +150,10 @@ int main() { // Register a callback for window size changes. glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); - unsigned int vertexShader; - // Create a OpenGL shader object. - vertexShader = glCreateShader(GL_VERTEX_SHADER); - // Pass the source code to the state. - glShaderSource(vertexShader, 1, &vertexShaderCode, nullptr); - // Compile the shader - glCompileShader(vertexShader); + std::string currentPath = utilities::getCurrentPath(); - int success; - char infoLog[512]; - // Check if the shader successfully compiled. - glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success); - if (!check_shader_compilation_status(vertexShader)) { - glGetShaderInfoLog(vertexShader, 512, nullptr, infoLog); - std::cout << "ERROR::SHADER::VERTEX::COMPILIATION_FAILED\n" << infoLog << std::endl; - } - - unsigned int fragmentShader; - // Create a OpenGL Shader object. - fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); - // Give the object the source code of the shader, sent with a single string. - glShaderSource(fragmentShader, 1, &fragmentShaderCode, NULL); - // Compile the shader. - glCompileShader(fragmentShader); - - if (!check_shader_compilation_status(fragmentShader)) { - glGetShaderInfoLog(fragmentShader, 512, nullptr, infoLog); - std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl; - } - - unsigned int shaderProgram; - // Create a OpenGL Shader Program object. - shaderProgram = glCreateProgram(); - // Attach the Vertex Shader to the Shader Program. - glAttachShader(shaderProgram, vertexShader); - // Attach the Fragment Shader to the Shader Program. - glAttachShader(shaderProgram, fragmentShader); - // Link the Shader Program. - glLinkProgram(shaderProgram); - - glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success); - if (!success) { - glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog); - std::cout << "ERROR::SHADER::LINKING::LINKING_FAILED\n" << infoLog << std::endl; - } - - glDeleteShader(vertexShader); - glDeleteShader(fragmentShader); + // Create a shader class. + Shader* shader = new Shader(std::format("{}/vertex.glsl", currentPath).c_str(), std::format("{}/fragment.glsl", currentPath).c_str()); GlRenderObjects* triangleRenderObjs = triangle(); GlRenderObjects* rectangleRenderObjs = rectangle(); @@ -219,14 +170,8 @@ int main() { // Clear the screen and use the color from the state. glClear(GL_COLOR_BUFFER_BIT); - // Use the shaderProgram shader program for drawing verticies. - glUseProgram(shaderProgram); - - // Change the uniform color. - float timeValue = glfwGetTime(); - float greenValue = (std::sin(timeValue) / 2.0f) + 0.5f; - int vertexColorLocation = glGetUniformLocation(shaderProgram, "ourColor"); - glUniform4f(vertexColorLocation, 0.0f, greenValue, 0.0f, 1.0f); + // activate the shader + shader->activate(); if (!rectangleToggled) { // Bind the state to use the vertexArrayObject object so OpenGL knows what to do with the verticies. @@ -250,7 +195,7 @@ int main() { // Clean up objects from memory. delete triangleRenderObjs; delete rectangleRenderObjs; - glDeleteProgram(shaderProgram); + delete shader; glfwTerminate(); return 0; diff --git a/src/meson.build b/src/meson.build index 0058d3a..69cf8c1 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,6 +1,14 @@ sources = [ - 'main.cc' + 'main.cc', + 'shader.cc', + 'utilities.cc' ] -executable('main', sources, dependencies: [glfw, libepoxy]) +copy = find_program('cp') + +fragment = custom_target('fragment', output: 'fragment.glsl', input: 'fragment.glsl', command: [copy, '@INPUT@', '@OUTPUT@']) +vertex = custom_target('vertex', output: 'vertex.glsl', input: 'vertex.glsl', command: [copy, '@INPUT@', '@OUTPUT@']) + + +executable('main', [sources] + [fragment, vertex], dependencies: [glfw, libepoxy]) executable('fixedfunction', ['fixedfunction.cc'], dependencies: [glfw, libepoxy]) diff --git a/src/shader.cc b/src/shader.cc new file mode 100644 index 0000000..49db14f --- /dev/null +++ b/src/shader.cc @@ -0,0 +1,105 @@ +#include "shader.hh" +#include +#include +#include +#include +#include + +Shader::Shader(const char* vertexPath, const char* fragmentPath) { + std::string vertexCode, fragmentCode; + std::ifstream vShaderFile, fShaderFile; + + vShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit); + fShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit); + + try { + // open shader files. + vShaderFile.open(vertexPath); + fShaderFile.open(fragmentPath); + + std::stringstream vShaderStream, fShaderStream; + + // read files into stringstreams. + vShaderStream << vShaderFile.rdbuf(); + fShaderStream << fShaderFile.rdbuf(); + + // close the file handles. + vShaderFile.close(); + fShaderFile.close(); + + // convert the streams into c++ strings. + vertexCode = vShaderStream.str(); + fragmentCode = fShaderStream.str(); + } catch (std::ifstream::failure e) { + std::cout << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ" << std::endl; + } + + // compile vertex and fragment shaders. + uint vertex, fragment; + vertex = compileVertexShader(vertexCode); + fragment = compileFragmentShader(fragmentCode); + + // link the shader to a shader program. + id = glCreateProgram(); + glAttachShader(id, vertex); + glAttachShader(id, fragment); + glLinkProgram(id); + + // check if the shader program successfully linked. + int success; + char infoLog[512]; + glGetProgramiv(id, GL_LINK_STATUS, &success); + if (!success) { + glGetProgramInfoLog(id, 512, nullptr, infoLog); + std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED" << std::endl << infoLog << std::endl; + } + + // clean up un-needed shader objects. + glDeleteShader(vertex); + glDeleteShader(fragment); +} + +Shader::~Shader() { + glDeleteProgram(id); +} + +uint Shader::compileShader(std::string code, GLenum type, std::string shaderTypeName) { + int success; + uint shader; + char infoLog[512]; + const char* codeCString = code.c_str(); + + shader = glCreateShader(type); + glShaderSource(shader, 1, &codeCString, nullptr); + glCompileShader(shader); + glGetShaderiv(shader, GL_COMPILE_STATUS, &success); + + if (!success) { + glGetShaderInfoLog(shader, 512, NULL, infoLog); + std::cout << std::format("ERROR::SHADER::{}::COMPILATION_FAILED", shaderTypeName) << std::endl << infoLog << std::endl; + } + + return shader; +} + +uint Shader::compileVertexShader(std::string code) { + return compileShader(code, GL_VERTEX_SHADER, "VERTEX"); +} + +uint Shader::compileFragmentShader(std::string code) { + return compileShader(code, GL_FRAGMENT_SHADER, "FRAGMENT"); +} + +void Shader::activate() { + glUseProgram(id); +} + +void Shader::setBool(const std::string &name, bool value) const { + glUniform1f(glGetUniformLocation(id, name.c_str()), (int)value); +} +void Shader::setInt(const std::string &name, int value) const { + glUniform1f(glGetUniformLocation(id, name.c_str()), value); +} +void Shader::setFloat(const std::string &name, float value) const { + glUniform1f(glGetUniformLocation(id, name.c_str()), value); +} diff --git a/src/shader.hh b/src/shader.hh new file mode 100644 index 0000000..6b747b1 --- /dev/null +++ b/src/shader.hh @@ -0,0 +1,25 @@ +#ifndef SHADER_HH +#define SHADER_HH +#include +#include +class Shader { + public: + // The Shader ID. + unsigned int id; + + // Constructor that builds the shader from a path of vertex and fragment shaders. + Shader(const char* vertexPath, const char* fragmentPath); + // Destructor that cleans up memory. + ~Shader(); + // activate the shader. + void activate(); + // utility uniform functions + void setBool(const std::string &name, bool value) const; + void setInt(const std::string &name, int value) const; + void setFloat(const std::string &name, float value) const; + private: + uint compileShader(std::string code, GLenum type, std::string shaderTypeName); + uint compileVertexShader(std::string code); + uint compileFragmentShader(std::string code); +}; +#endif diff --git a/src/utilities.cc b/src/utilities.cc new file mode 100644 index 0000000..cda6226 --- /dev/null +++ b/src/utilities.cc @@ -0,0 +1,38 @@ +#include "utilities.hh" +#include +#include +#include +#include +#include + +std::string utilities::getCurrentPath() { + char result[PATH_MAX]; + ssize_t count = readlink("/proc/self/exe", result, PATH_MAX); + std::string executablePath = std::string(result, (count > 0) ? count : 0); + std::vector stringVector = utilities::splitString(executablePath, '/'); + stringVector.pop_back(); + return utilities::combineString(stringVector, '/'); +} + +std::vector utilities::splitString(std::string string, char delimiter) { + size_t last = 0; + size_t next = 0; + std::vector vector; + while ((next = string.find(delimiter, last)) != std::string::npos) { + vector.push_back(string.substr(last, next-last)); + last = next + 1; + } + vector.push_back(string.substr(last)); + return vector; +} + +std::string utilities::combineString(std::vector stringVector, char delimiter) { + std::stringstream stringStream; + for (auto it = stringVector.begin() ; it != stringVector.end(); it++) { + if (it != stringVector.begin()) { + stringStream << delimiter; + } + stringStream << *it; + } + return stringStream.str(); +} diff --git a/src/utilities.hh b/src/utilities.hh new file mode 100644 index 0000000..fb32785 --- /dev/null +++ b/src/utilities.hh @@ -0,0 +1,11 @@ +#ifndef UTILITIES_HH +#define UTILITIES_HH +#include +#include + +namespace utilities { + std::string getCurrentPath(); + std::vector splitString(std::string string, char delimiter); + std::string combineString(std::vector stringVector, char delimiter); +} +#endif diff --git a/src/vertex.glsl b/src/vertex.glsl new file mode 100644 index 0000000..b67dfce --- /dev/null +++ b/src/vertex.glsl @@ -0,0 +1,10 @@ +// Set the GLSL version to 3.3 and use the OpenGL core profile +#version 330 core +layout (location = 0) in vec3 aPos; +layout (location = 1) in vec3 aColor; +out vec3 ourColor; + +void main() { + gl_Position = vec4(aPos, 1.0); + ourColor = aColor; +}