// // Created by brett on 6/11/23. // #ifndef PARKSNREC_OPENGL_H #define PARKSNREC_OPENGL_H #include "glad/gl.h" #include #include "blt/math/math.h" #include "blt/std/string.h" #include #include #include "stb/stb_image.h" namespace blt::graphics { class GLTexture2D { private: GLuint textureID = 0; struct { int width = 0, height = 0, channels = 4; } textureInfo; public: GLTexture2D() { glGenTextures(1, &textureID); } void bind() const { glBindTexture(GL_TEXTURE_2D, textureID); } void allocate(GLenum type, int width, int height, int channels = 4) { auto storage_type = channels == 4 ? GL_RGBA : GL_RGB; bind(); glTexImage2D(GL_TEXTURE_2D, 0, storage_type, width, height, 0, storage_type, type, nullptr); textureInfo.width = width; textureInfo.height = height; textureInfo.channels = channels; } void upload(void* data, GLenum type, int width, int height, int channels = 4) { auto storage_type = channels == 4 ? GL_RGBA : GL_RGB; bind(); if (textureInfo.width != width || textureInfo.height != height || textureInfo.channels != channels) allocate(type, width, height, channels); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, storage_type, type, data); textureInfo.width = width; textureInfo.height = height; textureInfo.channels = channels; } ~GLTexture2D() { glDeleteTextures(1, &textureID); } }; struct VBOData { GLenum target; void* data; GLuint size; GLenum usage = GL_STATIC_DRAW; VBOData(GLenum target, void* data, GLuint size); VBOData(GLenum target, void* data, GLuint size, GLenum usage); }; struct VBOAttributes { GLuint loc; GLint size; GLenum type; GLsizei stride; GLint offset = 0; VBOAttributes(GLuint loc, GLint size, GLenum type, GLsizei stride, GLint offset); VBOAttributes(GLuint loc, GLint size, GLenum type, GLsizei stride); VBOAttributes(GLuint loc, GLint size, GLenum type); }; class VAOStorageObject { private: struct VAO { GLuint vaoID = 0; }; struct VBO { GLuint vboID; GLuint bindType; }; VAO vao; std::vector associatedVBOs; VBO ebo; public: VAOStorageObject() { glGenVertexArrays(1, &vao.vaoID); } /** * Binds only the VAO and none of the VBOs */ inline void bind() const { glBindVertexArray(vao.vaoID); } inline void bindEBO() const { glBindBuffer(ebo.bindType, ebo.vboID); } /** * Binds both the VAO and all the associated VBOs */ inline void bindAll() const { bind(); for (const VBO& v : associatedVBOs) glBindBuffer(v.bindType, v.vboID); } void createVBO(VBOData data) { VBO vbo{}; vbo.bindType = data.target; glGenBuffers(1, &vbo.vboID); glBindBuffer(data.target, vbo.vboID); glBufferData(data.target, data.size, data.data, data.usage); associatedVBOs.push_back(vbo); if (vbo.bindType == GL_ELEMENT_ARRAY_BUFFER) ebo = vbo; } void createVBO(VBOData data, VBOAttributes attribs) { createVBO(data); glVertexAttribPointer( attribs.loc, attribs.size, attribs.type, GL_FALSE, attribs.stride, (GLvoid*) (size_t) attribs.offset ); glEnableVertexAttribArray(attribs.loc); } void createVBO(VBOData data, const std::vector& attribs){ createVBO(data); for (auto attrib : attribs) { glVertexAttribPointer( attrib.loc, attrib.size, attrib.type, GL_FALSE, attrib.stride, (GLvoid*) (size_t) attrib.offset ); glEnableVertexAttribArray(attrib.loc); } } ~VAOStorageObject() { glDeleteVertexArrays(1, &vao.vaoID); for (const auto& v : associatedVBOs) glDeleteBuffers(1, &v.vboID); } }; class ShaderBase { protected: struct IntDefaultedToMinusOne { GLint i = -1; }; std::unordered_map uniformVars; GLuint programID = 0; inline GLint getUniformLocation(const std::string& name) { if (uniformVars[name].i != -1) return uniformVars[name].i; int loc = glGetUniformLocation(programID, name.c_str()); uniformVars[name].i = loc; return loc; } public: inline void bind() const { glUseProgram(programID); } inline void setBool(const std::string& name, bool value) { glUniform1i(getUniformLocation(name), (int) value); } inline void setInt(const std::string& name, int value) { glUniform1i(getUniformLocation(name), value); } inline void setFloat(const std::string& name, float value) { glUniform1f(getUniformLocation(name), value); } inline void setMatrix(const std::string& name, blt::mat4x4& matrix) { glUniformMatrix4fv(getUniformLocation(name), 1, GL_FALSE, matrix.ptr()); } inline void setVec3(const std::string& name, const blt::vec3& vec) { glUniform3f(getUniformLocation(name), vec.x(), vec.y(), vec.z()); } inline void setVec4(const std::string& name, const blt::vec4& vec) { glUniform4f(getUniformLocation(name), vec.x(), vec.y(), vec.z(), vec.w()); } inline void setVec2(const std::string& name, float x, float y) { glUniform2f(getUniformLocation(name), x, y); } inline void setVec3(const std::string& name, float x, float y, float z) { glUniform3f(getUniformLocation(name), x, y, z); } inline void setVec4(const std::string& name, float x, float y, float z, float w) { glUniform4f(getUniformLocation(name), x, y, z, w); } }; /** * This part was made for this assignment and will likely be used in future projects */ class ComputeShader : public ShaderBase { private: GLuint shaderID = 0; public: explicit ComputeShader(const std::string& shader_source, bool loadAsString = true); inline void execute(int x, int y, int z) const { bind(); glDispatchCompute(x, y, z); } ~ComputeShader(); }; /** * Note: This is taken from my final project, * https://github.com/Tri11Paragon/COSC-3P98-Final-Project/blob/main/include/render/gl.h */ class Shader : public ShaderBase { private: GLuint vertexShaderID = 0; GLuint fragmentShaderID = 0; // while these will remain unused. (Webgl2 apparently doesn't support them despite being based on GL4.3? that's a TODO!) GLuint geometryShaderID = 0; // this would be very useful however it is highly unlikely webgl will support it // im leaving some of this stuff in here because I might expand the native application to use some of it. // im trying to keep the web and native versions the same though GLuint tessellationShaderID = 0; static unsigned int createShader(const std::string& source, int type); public: /** * Creates a shader * @param vertex vertex shader source or file * @param fragment fragment shader source or file * @param geometry geometry shader source or file (optional) * @param load_as_string load the shader as a string (true) or use the string to load the shader as a file (false) */ Shader( const std::string& vertex, const std::string& fragment, const std::string& geometry = "", bool load_as_string = true ); Shader(Shader&& move) noexcept; // used to set the location of VAOs to the in variables in opengl shaders. void bindAttribute(int attribute, const std::string& name) const; // used to set location of shared UBOs like the perspective and view matrix void setUniformBlockLocation(const std::string& name, int location) const; ~Shader(); }; } #endif //PARKSNREC_OPENGL_H