#pragma once #include #include #include #include #include #include #include namespace hex::gl { namespace { template GLuint getType() { if constexpr (std::is_same_v) return GL_FLOAT; else if constexpr (std::is_same_v) return GL_UNSIGNED_INT; else static_assert(hex::always_false::value, "Unsupported type"); } } template class Vector { public: Vector() = default; Vector(std::array data) : m_data(data) { } T &operator[](size_t index) { return this->m_data[index]; } const T &operator[](size_t index) const { return this->m_data[index]; } T *data() { return this->m_data.data(); } const T *data() const { return this->m_data.data(); } [[nodiscard]] size_t size() const { return this->m_data.size(); } auto operator+(const Vector& other) { auto copy = *this; for (size_t i = 0; i < Size; i++) copy[i] += other[i]; return copy; } auto operator-(const Vector& other) { auto copy = *this; for (size_t i = 0; i < Size; i++) copy[i] -= other[i]; return copy; } auto dot(const Vector& other) { T result = 0; for (size_t i = 0; i < Size; i++) result += this->m_data[i] * other[i]; return result; } auto cross(const Vector& other) { static_assert(Size == 3, "Cross product is only defined for 3D vectors"); return Vector({ this->m_data[1] * other[2] - this->m_data[2] * other[1], this->m_data[2] * other[0] - this->m_data[0] * other[2], this->m_data[0] * other[1] - this->m_data[1] * other[0] }); } auto normalize() { auto copy = *this; auto length = std::sqrt(copy.dot(copy)); for (size_t i = 0; i < Size; i++) copy[i] /= length; return copy; } private: std::array m_data; }; class Shader { public: Shader() = default; Shader(std::string_view vertexSource, std::string_view fragmentSource); ~Shader(); Shader(const Shader&) = delete; Shader(Shader&& other) noexcept; Shader& operator=(const Shader&) = delete; Shader& operator=(Shader&& other) noexcept; void bind() const; void unbind() const; void setUniform(std::string_view name, const float &value); void setUniform(std::string_view name, const Vector &value); private: void compile(GLuint shader, std::string_view source); GLint getUniformLocation(std::string_view name); private: GLuint m_program = 0; std::map m_uniforms; }; enum class BufferType { Vertex = GL_ARRAY_BUFFER, Index = GL_ELEMENT_ARRAY_BUFFER }; template class Buffer { public: Buffer() = default; Buffer(BufferType type, std::span data); ~Buffer(); Buffer(const Buffer&) = delete; Buffer(Buffer&& other) noexcept; Buffer& operator=(const Buffer&) = delete; Buffer& operator=(Buffer&& other) noexcept; void bind() const; void unbind() const; void draw() const; size_t getSize() const; private: GLuint m_buffer = 0; size_t m_size = 0; GLuint m_type = 0; }; extern template class Buffer; extern template class Buffer; class VertexArray { public: VertexArray(); ~VertexArray(); VertexArray(const VertexArray&) = delete; VertexArray(VertexArray&& other) noexcept; VertexArray& operator=(const VertexArray&) = delete; VertexArray& operator=(VertexArray&& other) noexcept; template void addBuffer(u32 index, const Buffer &buffer) const { glEnableVertexAttribArray(index); buffer.bind(); glVertexAttribPointer(index, 3, getType(), GL_FALSE, 3 * sizeof(T), nullptr); buffer.unbind(); } void bind() const; void unbind() const; private: GLuint m_array; }; class Texture { public: Texture(u32 width, u32 height); ~Texture(); Texture(const Texture&) = delete; Texture(Texture&& other) noexcept; Texture& operator=(const Texture&) = delete; Texture& operator=(Texture&& other) noexcept; void bind() const; void unbind() const; GLuint getTexture() const; u32 getWidth() const; u32 getHeight() const; GLuint release(); private: GLuint m_texture; u32 m_width, m_height; }; class FrameBuffer { public: FrameBuffer(); ~FrameBuffer(); FrameBuffer(const FrameBuffer&) = delete; FrameBuffer(FrameBuffer&& other) noexcept; FrameBuffer& operator=(const FrameBuffer&) = delete; FrameBuffer& operator=(FrameBuffer&& other) noexcept; void bind() const; void unbind() const; void attachTexture(const Texture &texture) const; private: GLuint m_frameBuffer, m_renderBuffer; }; }