diff --git a/engine/NBGraphics/Buffers.hpp b/engine/NBGraphics/Buffers.hpp index 8990d8f..31c5494 100644 --- a/engine/NBGraphics/Buffers.hpp +++ b/engine/NBGraphics/Buffers.hpp @@ -4,7 +4,6 @@ #include "GLLoad.hpp" -#include #include #include @@ -17,145 +16,113 @@ namespace nb { extern const std::unordered_map BufferTypes; +template +struct GLSLEnum; +template +struct GLSLType; + +#define GLSLTypeTraits(type_, size_, value_) \ +template<>\ +struct GLSLEnum {\ + using type = type_;\ + static constexpr size_t size = size_;\ + static constexpr GLenum value=value_;\ +};\ +template<>\ +struct GLSLType {\ + using type = type_;\ + static constexpr size_t size = size_;\ + static constexpr GLenum value=value_;\ +}; + +#define GLSLTypeEquivalence(original, newtype) \ +template<>\ +struct GLSLType : public GLSLType {}; + +GLSLTypeTraits(uint8_t, 1, GL_UNSIGNED_BYTE); +GLSLTypeTraits(uint16_t, 2, GL_UNSIGNED_SHORT); +GLSLTypeTraits(uint32_t, 4, GL_UNSIGNED_INT); +GLSLTypeTraits(int8_t, 1, GL_BYTE); +GLSLTypeTraits(int16_t, 2, GL_SHORT); +GLSLTypeTraits(int32_t, 4, GL_INT); +GLSLTypeTraits(float, 4, GL_FLOAT); +GLSLTypeTraits(double, 8, GL_DOUBLE); + class BufferError : public Error { protected: using Base = Error; - using Base::Base; - + public: + using Base::Base; enum Codes : unsigned int { UNDEFINED, DATA_OVERFLOW, - INVALID_BUFFER, - HANDLE_OVERWRITE, + DATA_ERROR }; static const std::string type; static const ErrorCodeMap ErrorMessages; }; -template class Buffer : public OpenGLObject { - using Codes = BufferError::Codes; - - public: - using OpenGLObject::OpenGLObject; - Buffer() = default; - Buffer(Buffer&& other) { - *this = std::move(other); - } - Buffer& operator=(Buffer&& rhs) { - auto targ_name = BufferTypes.at(Target); - if (Target != rhs.Target) { - THROW(BufferError( - Codes::INVALID_BUFFER, - "w/ ("+targ_name+") := ("+BufferTypes.at(rhs.Target)+")" - )); - } - //nb::logger.log(std::to_string(_id) + ":=" + std::to_string(rhs._id)); - if (_id) { - THROW(BufferError( - Codes::HANDLE_OVERWRITE, - "w/ BufferType = " + targ_name - )); - } - _id = rhs._id; - _usage = rhs._usage; - _size = rhs._size; - - rhs._id = 0; - rhs._usage = GL_STATIC_DRAW; - rhs._size = 0; - return *this; - } - - virtual void bind() const override { - if (_id) { - glBindBuffer(Target, _id); - } else { - WARN(BufferError( - Codes::INVALID_BUFFER, - "w/ BufferType " + BufferTypes.at(Target) - ), 0xFE); - } - } - virtual void unbind() const override { glBindBuffer(Target, 0); } - virtual GLenum usage() const { return _usage; } - virtual GLenum usage(GLenum usage_) { - _usage = usage_; - data(data(), _usage); - return _usage; - } - virtual size_t size() const { return _size; } - virtual ByteVector data() const { - bind(); - ByteVector ret(_size); - glGetBufferSubData(Target, 0, _size, ret.data()); - return ret; - } - virtual void data( - const void* data_, - GLsizei size_, - GLenum usage_=GL_STATIC_DRAW - ) { - declare(); - bind(); - glBufferData(Target, size_, data_, usage_); - _size = size_; - _usage = usage_; - } - virtual void data(const ByteVector& data_, GLenum usage_=GL_STATIC_DRAW) { - data(data_.data(), data_.size(), usage_); - - } - virtual void subdata(void* data_, GLsizeiptr size_, GLintptr offset_=0) { - bind(); - if (offset_+size_ <= _size) { - THROW(BufferError(BufferError::Codes::DATA_OVERFLOW)); - } - glBufferSubData(Target, offset_, size_, data_); - } - virtual void subdata(ByteVector& data_, GLintptr offset_=0) { - bind(); - size_t size_ = data_.size(); - if (offset_+size_ <= _size) { - THROW(BufferError(BufferError::Codes::DATA_OVERFLOW)); - } - glBufferSubData(Target, offset_, data_.size(), data_.data()); - } - // virtual void clear() const { glClearBufferData(Target, ) } - static const GLenum Target; - protected: + using Codes = BufferError::Codes; using OpenGLObject::_id; GLenum _usage = GL_STATIC_DRAW; size_t _size; + Buffer(GLenum target) : Target(target) {} virtual GLuint declare() override { if (!_id) { glGenBuffers(1, &_id); } + bind(); return _id; } virtual void remove() override { if (_id) { + unbind(); glDeleteBuffers(1, &_id); _id = 0; } } + public: + const GLenum Target; + using OpenGLObject::OpenGLObject; + Buffer(Buffer&& other); + Buffer& operator=(Buffer&& rhs); + + virtual void bind() const override { + if (_id) { + glBindBuffer(Target, _id); + } else { + THROW(OGLError( + OGLError::Codes::INVALID_OBJECT, + "w/ BufferType " + BufferTypes.at(Target) + )); + } + } + virtual void unbind() const override { glBindBuffer(Target, 0); } + GLenum usage() const; + size_t size() const; + ByteVector data() const; + + void data(const ByteVector& data, GLenum usage=GL_STATIC_DRAW); + void data(const void* data, size_t size, GLenum usage=GL_STATIC_DRAW); + void subdata(const void* data, size_t size, GLintptr offset=0); + void subdata(const ByteVector& data_, GLintptr offset=0); + // virtual void clear() const { glClearBufferData(Target, ) } + }; -template -const GLenum Buffer::Target = BufferType::Target; - -template -class ImmutableBuffer : public virtual Buffer { +/* template +class ImmutableBuffer : public virtual Buffer { public: - using Base = Buffer; - using Base::Target; - using Base::bind; - ImmutableBuffer() = default; - ImmutableBuffer(size_t size_, GLenum usage_ = GL_STATIC_DRAW, GLbitfield flags_=0x0) : size(size_), Base::usage(usage_) { + ImmutableBuffer( + size_t size_, + GLenum usage_ = GL_STATIC_DRAW, + GLbitfield flags_=0x0 + ) : size(size_), Base::usage(usage_) { glBufferStorage(Target, size, nullptr, flags_); } @@ -177,63 +144,72 @@ class ImmutableBuffer : public virtual Buffer { const size_t size; protected: - using Base::_id; std::shared_ptr _map = nullptr; GLbitfield _mapAccess = 0; +}; */ + +class ArrayBuffer : public Buffer { + public: + template + ArrayBuffer(const std::vector& data, GLenum usage=GL_STATIC_DRAW) + : Buffer(GL_ARRAY_BUFFER) { + Buffer::data(vectorToBytes(data), usage); + } }; -template -class ArrayBuffer : public Buffer> { - using Base = Buffer>; - using BufferType = Base; - - public: - using Base::Base; - using Base::data; - ArrayBuffer(const ByteVector&, GLenum usage_=GL_STATIC_DRAW); - - static const GLenum Target = GL_ARRAY_BUFFER; - +class ElementBuffer : protected Buffer { protected: - using Base::_usage; -}; - -template <> -class ArrayBuffer -: public virtual ArrayBuffer, public virtual ImmutableBuffer> { - using Base = ArrayBuffer; - using BufferType = ImmutableBuffer>; - using BufferType::BufferType; - + GLenum _glslType; + size_t _typeSize; public: - using Base::Target; - -}; - -template -class ElementBuffer : public Buffer> { - using Base = Buffer>; - using BufferType = Base; + using Buffer::Target; + using Buffer::bind; + using Buffer::usage; + using Buffer::unbind; + ElementBuffer(); + template + ElementBuffer(const T* const data_ptr, size_t size, GLenum usage=GL_STATIC_DRAW) + : Buffer(GL_ELEMENT_ARRAY_BUFFER) { + data(data_ptr, size, usage); + } + template + ElementBuffer(const std::vector& data_, GLenum usage_=GL_STATIC_DRAW) + : ElementBuffer(data_.data(), data_.size(), usage_) {} - public: - using Base::Base; - static const GLenum Target = GL_ELEMENT_ARRAY_BUFFER; + size_t size() const; + size_t typeSize() const; + ByteVector data() const; + GLenum glslType() const; + template + void data(const T* data_, size_t size_, GLenum usage_=GL_STATIC_DRAW) { + declare(); + using TypeInfo = GLSLType; + _typeSize = TypeInfo::size; + Buffer::data(data_, size_*_typeSize, usage_); + _glslType = TypeInfo::value; + } + template + void data(const std::vector& data_, GLenum usage_=GL_STATIC_DRAW) { + data(data_.data(), data_.size(), usage_); + } + template + void subdata(const T* data, size_t size, size_t offset) { + using TypeInfo = GLSLType; + if ( _glslType != TypeInfo::value ) { + THROW( BufferError( + Codes::DATA_ERROR, + "Check element buffer data types" + ) ); + } + Buffer::subdata(data, size*_typeSize, offset); + } + template + void subdata(const std::vector& data, size_t offset=0) { + subdata(data.data(), data.size(), offset); + } - protected: - -}; - -template <> -class ElementBuffer -: public virtual ElementBuffer, public virtual ImmutableBuffer> { - using Base = ElementBuffer; - using BufferType = ImmutableBuffer>; - - public: - using BufferType::BufferType; - using Base::Target; }; diff --git a/engine/NBGraphics/CMakeLists.txt b/engine/NBGraphics/CMakeLists.txt index 5a94b3f..31090ba 100644 --- a/engine/NBGraphics/CMakeLists.txt +++ b/engine/NBGraphics/CMakeLists.txt @@ -1,7 +1,11 @@ find_package(OpenGL) add_subdirectory(${GLFW_PATH} ${GLFW_PATH}/build) -include_directories(${GLFW_PATH}/include ${GLAD_PATH}/include) +include_directories( + ${GLFW_PATH}/include + ${GLAD_PATH}/include + ${STBIMAGE_PATH} +) set(GLFW_BUILD_DOCS OFF CACHE BOOL "" FORCE) set(GLFW_BUILD_TESTS OFF CACHE BOOL "" FORCE) @@ -9,6 +13,8 @@ set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) toAbsolutePath(NB_GRAPHICS_SOURCE ./src/Buffers.cpp + ./src/FrameBuffers.cpp + ./src/Image.cpp ./src/OGLObjects.cpp ./src/ProgramPipeline.cpp ./src/Textures.cpp @@ -20,7 +26,9 @@ toAbsolutePath(NB_GRAPHICS_INCLUDE ./Buffers.hpp ./Camera.hpp ./Draw.hpp + ./FrameBuffers.hpp ./GLLoad.hpp + ./Image.hpp ./OGLObjects.hpp ./ProgramPipeline.hpp ./Textures.hpp diff --git a/engine/NBGraphics/FrameBuffers.hpp b/engine/NBGraphics/FrameBuffers.hpp new file mode 100644 index 0000000..c7efa25 --- /dev/null +++ b/engine/NBGraphics/FrameBuffers.hpp @@ -0,0 +1,236 @@ +#pragma once +#ifndef _NB_FRAMEBUFFER +#define _NB_FRAMEBUFFER + +#include "GLLoad.hpp" + +#include + +#include "Image.hpp" +#include "OGLObjects.hpp" +#include "Textures.hpp" + +namespace nb { + +class FrameBufferError : public Error { + protected: + using Base = Error; + + public: + using Base::Base; + enum Codes : unsigned int { + UNDEFINED, INVALID_VALUE + }; + static const std::string type; + static const ErrorCodeMap ErrorMessages; +}; + +class RenderBuffer : public RenderTarget { + protected: + GLuint declare() override { + if (!_id) { + glRenderbufferStorage( + GL_RENDERBUFFER, + format, + width, + height + ); + } + return _id; + } + void remove() override { + if (_id) { + unbind(); + glDeleteRenderbuffers(1, &_id); + _id = 0; + } + } + + public: + const unsigned int width; + const unsigned int height; + const GLenum format; + template + RenderBuffer(unsigned int width_, unsigned int height_) : + width(width_), + height(height_), + format(OGLPixelFormat::glFormat), + RenderTarget(GL_RENDERBUFFER) + { declare(); } + RenderBuffer(RenderBuffer&& rhs) : + width(rhs.width), + height(rhs.height), + format(rhs.format), + RenderTarget(std::move(rhs)) + {} + RenderBuffer& operator=(RenderBuffer&&) = delete; + void bind() const override { + if (_id) { + glBindRenderbuffer(GL_RENDERBUFFER, _id); + } else { + THROW(OGLError( + OGLError::Codes::INVALID_OBJECT, + "w/ RenderBuffer" + )); + } + } + void unbind() const override { + glBindRenderbuffer(GL_RENDERBUFFER, 0); + } + +}; + +class FrameBufferBase : public OpenGLObject { + private: + static bool texTypeIs2D(GLenum texType) { + switch(texType) { + case GL_TEXTURE_2D: + default: + return true; + break; + } + } + + protected: + using AttachmentMap = std::unordered_map>; + AttachmentMap _attachments; + FrameBufferBase(GLenum target); + GLuint declare() override; + void remove() override; + std::shared_ptr detach(GLenum); + void attach( + GLenum attachment, + std::shared_ptr texture, + unsigned int level=0 + ); + void attach( + GLenum attachment, + std::shared_ptr texture, + unsigned int level, + unsigned int layer + ); + void attach( + GLenum attachment, + std::shared_ptr renderBuffer + ) { + declare(); + if (attachment == GL_NONE) { + THROW(FrameBufferError(FrameBufferError::INVALID_VALUE, + "Cannot attach RenderBuffer to `GL_NONE`" + )); + } + glFramebufferRenderbuffer( + Target, + attachment, + GL_RENDERBUFFER, + renderBuffer->id() + ); + _attachments[attachment] = renderBuffer; + } + + public: + const GLenum Target; + void bind() const override; + void unbind() const override; + GLenum status() const; + AttachmentMap getAllBuffers() const; + std::shared_ptr getBuffer(GLenum) const; +}; + +class ReadFrameBuffer; +class WriteFrameBuffer; + +class ReadFrameBuffer : public virtual FrameBufferBase { + friend WriteFrameBuffer; + protected: + using FrameBufferBase::_attachments; + GLenum _location; + + public: + using FrameBufferBase::attach; + ReadFrameBuffer(); + ReadFrameBuffer& operator=(WriteFrameBuffer&&); + ReadFrameBuffer& operator=(ReadFrameBuffer&&); + std::shared_ptr getReadBuffer() const; + GLenum getReadLocation() const; + std::shared_ptr setReadBuffer(GLenum attachment); + template + std::shared_ptr setReadBuffer( + GLenum attachment, + std::shared_ptr target, + Args... args + ) { + declare(); + FrameBufferBase::attach(attachment, target, args...); + setReadBuffer(attachment); + return getReadBuffer(); + } +}; + +class WriteFrameBuffer : public virtual FrameBufferBase { + friend ReadFrameBuffer; + protected: + using DrawTexVec = std::vector>; + using FrameBufferBase::_attachments; + std::vector _locations; + + public: + WriteFrameBuffer(); + WriteFrameBuffer& operator=(ReadFrameBuffer&&); + WriteFrameBuffer& operator=(WriteFrameBuffer&&); + template + void attach( + GLenum attachment, + std::shared_ptr texture, + T... args + ) { FrameBufferBase::attach(attachment, texture, args...); } + DrawTexVec getWriteBuffers() const; + std::vector getWriteLocations() const; + DrawTexVec setWriteBuffers(const std::vector& attachments); + std::shared_ptr setWriteBuffer(GLenum attachment); + template + std::shared_ptr setWriteBuffer( + GLenum attachment, + std::shared_ptr texture, + T... args + ) { + attach(attachment, texture, args...); + setWriteBuffer(attachment); + return std::static_pointer_cast(_attachments.at(attachment)); + } + + RGBA clearColorValue() const; + RGBA clearColorValue(const RGBA&); + RGBA clearColorValue(float r, float g, float b, float a); + double clearDepthValue() const; + double clearDepthValue(double); + GLint clearStencilValue() const; + GLint clearStencilValue(GLint); + void clear(GLbitfield mask=( + GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT + )) const; + void clearColor() const; + void clearDepth() const; + void clearDepth(float) const; + void clearStencil() const; + void clearStencil(GLint) const; + void clearDepthStencil() const; + void clearDepthStencil(float, GLint) const; +}; + +class FrameBuffer : public ReadFrameBuffer, public WriteFrameBuffer { + protected: + using FrameBufferBase::_attachments; + using ReadFrameBuffer::_location; + using WriteFrameBuffer::_locations; + + public: + FrameBuffer(); + FrameBuffer& operator=(FrameBuffer&&); + FrameBuffer& operator=(ReadFrameBuffer&&); + FrameBuffer& operator=(WriteFrameBuffer&&); +}; + +} // namespace nb + +#endif // _NB_FRAMEBUFFER \ No newline at end of file diff --git a/engine/NBGraphics/Image.hpp b/engine/NBGraphics/Image.hpp new file mode 100644 index 0000000..4301193 --- /dev/null +++ b/engine/NBGraphics/Image.hpp @@ -0,0 +1,216 @@ +#pragma once +#ifndef _NB_IMAGE +#define _NB_IMAGE + +#include + +#include + +namespace nb { + +template +struct Color {}; + +struct Red : Color {}; +struct Green : Color {}; +struct Blue : Color {}; +struct Alpha : Color {}; + +struct Depth; +struct Stencil; + +template +struct PixelChannel; + +template +struct PixelChannel { + using type=T; + T value; + T& r=value; +}; +template +struct PixelChannel { + using type=T; + T value; + T& g=value; +}; +template +struct PixelChannel { + using type=T; + T value; + T& b=value; +}; +template +struct PixelChannel { + using type=T; + T value; + T& a=value; +}; + +template +struct PixelChannel { + using type=T; + T value; + T& z=value; +}; + +template<> +struct PixelChannel { + using type=uint8_t; + uint8_t value; + uint8_t& u=value; +}; + +template +struct Pixel : public PixelChannel... { + Pixel() = default; + Pixel& operator=(const Pixel& rhs) { + (( + [&](const typename PixelChannel::type& val){ + this->PixelChannel::value = val; + }(rhs.PixelChannel::value) + ), ...); + return *this; + } + + + Pixel(const Pixel& cpy) + : Pixel(cpy.PixelChannel::value...) {} + + Pixel(typename PixelChannel::type... vals) + : PixelChannel{vals}... {} +}; + +template +struct PixelReference; + +template +struct PixelReference> +: public Pixel { + using PixelType = Pixel; + using Base = Pixel; + using Base::Base; + + PixelReference(PixelType& pixel) + : Base(pixel.PixelChannel::value...) {} + + operator PixelType() { + return PixelType(this->PixelChannel::value...); + } + + PixelReference& operator=(const PixelType& pixel) { + (( + [&](const typename PixelChannel::type& val){ + this->PixelChannel::value = val; + }(pixel.PixelChannel::value) + ), ...); + return *this; + } + +}; + +template +using RGBA = Pixel; + +template +using RGB = Pixel; + +class ImageError : public Error { + protected: + using Base = Error; + using Base::Base; + + public: + enum Codes : unsigned int { + UNDEFINED, OUT_OF_BOUNDS + }; + static const std::string type; + static const ErrorCodeMap ErrorMessages; + +}; + +template +class Image; + +template +class Image> { + protected: + using Codes = ImageError::Codes; + T* _data; + Image(unsigned int x, unsigned int y) + : width(x), height(y), _data(nullptr) {} + + public: + using PixelType = Pixel; + static constexpr size_t NumberChannels = sizeof...(Channels); + const unsigned int width; + const unsigned int height; + Image(unsigned int x, unsigned int y, const T* data) + : Image(x, y) { + size_t data_size = width*height*NumberChannels; + _data = new T[data_size]; + std::memcpy(_data, data, data_size*sizeof(T)); + } + Image(const Image&) = delete; + Image& operator=(const Image&) = delete; + Image& operator=(Image&&) = delete; + Image(Image&& mv) + : width(mv.width), height(mv.height), _data(mv._data) { + mv._data = nullptr; + } + + ~Image() { + if (_data) { + delete[] _data; + } + } + + const T* data() const { return _data; } + Image&& copy() const { + return Image(width, height, _data); + } + PixelReference at(unsigned int x, unsigned int y) { + if (!(x= ("; + note += std::to_string(width)+", "+std::to_string(height); + note += ")"; + THROW(ImageError( + Codes::OUT_OF_BOUNDS, + note + )); + } + auto coord = y*height+x; + return PixelReference( + _data[coord*NumberChannels+Find::value-1]... + ); + } + +}; + +template +class ImageReference; + +template +class ImageReference> : public Image> { + protected: + using Base = Image>; + using Base::_data; + + public: + using Base::width; + using Base::height; + using Base::copy; + ImageReference(unsigned int x, unsigned int y, T* data) + : Base(x, y) { + _data = data; + } + ~ImageReference() noexcept { + Base::_data = nullptr; + } + +}; + +} // namespace nb + +#endif // _NB_IMAGE \ No newline at end of file diff --git a/engine/NBGraphics/OGLObjects.hpp b/engine/NBGraphics/OGLObjects.hpp index bb0dc8a..1df966a 100644 --- a/engine/NBGraphics/OGLObjects.hpp +++ b/engine/NBGraphics/OGLObjects.hpp @@ -15,7 +15,7 @@ class OGLError : public Error { using Base::Base; enum Codes : unsigned int { - UNDEFINED, HANGING_OBJECT + UNDEFINED, HANGING_OBJECT, INVALID_OBJECT }; static const std::string type; @@ -26,6 +26,8 @@ class OpenGLObject { public: OpenGLObject(const OpenGLObject&) = delete; OpenGLObject& operator=(const OpenGLObject&) = delete; + OpenGLObject(OpenGLObject&&); + OpenGLObject& operator=(OpenGLObject&&); virtual ~OpenGLObject() {}; virtual void bind() const = 0; @@ -33,6 +35,7 @@ class OpenGLObject { GLuint id() const { return _id; } protected: + using Codes = OGLError::Codes; OpenGLObject() = default; virtual GLuint declare() = 0; diff --git a/engine/NBGraphics/ProgramPipeline.hpp b/engine/NBGraphics/ProgramPipeline.hpp index fce51a7..260a771 100644 --- a/engine/NBGraphics/ProgramPipeline.hpp +++ b/engine/NBGraphics/ProgramPipeline.hpp @@ -54,12 +54,14 @@ class Shader : public OpenGLObject { using Base::Base; using Base::id; Shader(GLenum, const std::string&); + Shader(GLenum, const std::vector&); Shader(Shader&&); Shader& operator=(Shader&&) = delete; ~Shader() { remove(); } operator bool(); virtual void bind() const override { /* TODO: Some warning of some kind perhaps*/ } virtual void unbind() const override { /* TODO: Some warning of some kind perhaps*/ } + bool success() const; GLint status(GLenum) const; std::string log() const; const GLenum target; @@ -68,6 +70,22 @@ class Shader : public OpenGLObject { protected: using Base::_id; GLint _success; + std::vector _sources; + + virtual void compile() { + int num_srcs = _sources.size(); + std::vector src_ptrs(num_srcs); + for (int i=0; i < num_srcs; ++i) { + src_ptrs[i] = _sources[i].data(); + } + glShaderSource(_id, num_srcs, src_ptrs.data(), NULL); + glCompileShader(_id); + _success = status(GL_COMPILE_STATUS); + if (!_success) { + WARN(log(), 0x0FE); + } + } + virtual GLuint declare() override { if (!_id) { _id = _id = glCreateShader(target); diff --git a/engine/NBGraphics/Textures.hpp b/engine/NBGraphics/Textures.hpp index 8984e72..433abc7 100644 --- a/engine/NBGraphics/Textures.hpp +++ b/engine/NBGraphics/Textures.hpp @@ -3,58 +3,150 @@ #define _NB_TEXTURES #include "Buffers.hpp" +#include "Image.hpp" #include "OGLObjects.hpp" namespace nb { -template -class TextureBuffer : public virtual Buffer> { - using Base = Buffer>; - using BufferType = Base; - +class TextureBuffer : public virtual Buffer { public: - using Base::Base; - static const GLenum Target = GL_TEXTURE_BUFFER; + TextureBuffer() : Buffer(GL_TEXTURE_BUFFER) {} +}; + +class RenderTarget : public OpenGLObject { protected: - -}; - -template <> -class TextureBuffer -: public virtual TextureBuffer, public virtual ImmutableBuffer> { - using Base = TextureBuffer; - using BufferType = ImmutableBuffer>; - - public: - using BufferType::BufferType; - using Base::Target; -}; - -class Texture : public OpenGLObject { using Base = OpenGLObject; -public: + using Base::_id; + RenderTarget(GLenum target) : Target(target) {} + RenderTarget(RenderTarget&& rhs) + : Target(rhs.Target), Base(std::move(rhs)) {} + + public: using Base::Base; using Base::id; + const GLenum Target; +}; - Texture(GLenum); - - virtual void bind() const override { glBindTexture(target, _id); } - virtual void unbind() const override { glBindTexture(target, 0); } - - const GLenum target; - - friend Base; - +class Texture : public RenderTarget { protected: + using Base = RenderTarget; using Base::_id; - + Texture(GLenum); + virtual GLuint declare() override { + if (!_id) { + glGenTextures(1, &_id); + } + bind(); + return _id; + } virtual void remove() override { if (_id) { + bind(); glDeleteTextures(0, &_id); } } + public: + using Base::Base; + using Base::id; + using Base::Target; + + virtual void bind() const override { + if (_id) { + glBindTexture(Target, _id); + } else { + THROW(OGLError( + OGLError::Codes::INVALID_OBJECT, + "w/ Texture" + )); + } + } + virtual void unbind() const override { + glBindTexture(Target, 0); + } + + template + void parameter(GLenum param_, const T& val_); + void parameter(GLenum param_, const int& val_) { + declare(); + glTexParameteri(_id, param_, val_); + } + void parameter(GLenum param_, const std::vector& val_) { + declare(); + glTexParameteriv(_id, param_, val_.data()); + } + void parameter(GLenum param_, const std::vector& val_) { + declare(); + glTexParameterfv(_id, param_, val_.data()); + } + void parameter(GLenum param_, const std::vector& val_) { + declare(); + glTexParameterIuiv(_id, param_, val_.data()); + } + void generateMipmaps() const { + bind(); + glGenerateMipmap(Target); + } + +}; + +template +class ImageTexture; + +template +struct OGLPixelFormat; + +template +struct OGLPixelFormat>; + +template<> +struct OGLPixelFormat> { + static constexpr GLenum glFormat = GL_RGB; + static constexpr GLenum glData = GL_UNSIGNED_BYTE; +}; + +template<> +struct OGLPixelFormat> { + static constexpr GLenum glFormat = GL_RGBA; + static constexpr GLenum glData = GL_UNSIGNED_BYTE; +}; + +template +class ImageTexture> : public Texture { + protected: + using Texture::Texture; + using PixelType = Pixel; + + public: + using Texture::generateMipmaps; + ImageTexture() : Texture(GL_TEXTURE_2D) {} + ImageTexture(ImageTexture&& cpy) { + *this = std::move(cpy); + } + ImageTexture& operator=(ImageTexture&& rhs) { + return Texture::operator=(std::move(rhs)); + } + void setImage(const Image& img, unsigned int layer) { + using Format = OGLPixelFormat; + declare(); + glTexImage2D( + Target, + layer, + Format::glFormat, + img.width, + img.height, + 0, + Format::glFormat, + Format::glData, + img.data() + ); + } + void setImage(const Image& img, bool generateMipmap=true) { + setImage(img, (unsigned int)0); + if (generateMipmap) { generateMipmaps(); } + } + }; } // namespace nb diff --git a/engine/NBGraphics/VertexArray.hpp b/engine/NBGraphics/VertexArray.hpp index 2f97c28..a2e494d 100644 --- a/engine/NBGraphics/VertexArray.hpp +++ b/engine/NBGraphics/VertexArray.hpp @@ -67,7 +67,7 @@ class VAOError : public Error { using Base::Base; enum Codes : unsigned int { - UNDEFINED, INVALID_ATTRIBUTE, INVALID_VAO + UNDEFINED, INVALID_ATTRIBUTE }; static const std::string type; @@ -90,7 +90,10 @@ class VAO : public OpenGLObject { if(_id) { glBindVertexArray(_id); } else { - THROW(VAOError(Codes::INVALID_VAO)); + THROW(OGLError( + OGLError::Codes::INVALID_OBJECT, + "w/ VertexArrayObject" + )); } } virtual void unbind() const override { glBindVertexArray(0); } @@ -119,14 +122,29 @@ class VAO : public OpenGLObject { if (!_id) { glGenVertexArrays(1, &_id); } + bind(); return _id; } }; -using VBO = ArrayBuffer; +class VertexError : public Error { + protected: + using Base = Error; + + public: + using Base::Base; + enum Codes : unsigned int { + UNDEFINED, MISALIGNED_DATA + }; + static const std::string type; + static const ErrorCodeMap ErrorMessages; + +}; + +using VBO = ArrayBuffer; using VertBufVec = SharedVector; -using EBO = ElementBuffer; +using EBO = ElementBuffer; struct VertexData { std::shared_ptr vbo; @@ -136,27 +154,70 @@ struct VertexData { using VertexDataVec = std::vector; class VertexGroup { + protected: + using Codes = VertexError::Codes; + VertexDataVec _vertex_data; + std::shared_ptr _ebo; + std::shared_ptr _vao; + GLenum _primitive; + public: + GLenum primitive=GL_TRIANGLES; VertexGroup(VertexGroup&&); VertexGroup& operator=(VertexGroup&&); - VertexGroup(const VertexData&); - VertexGroup(const VertexDataVec&); - VertexGroup(const ByteVector&, const VertexAttributeList&); + VertexGroup( + const VertexDataVec& vertex_data, + const std::shared_ptr& ebo + ) + : _vao(std::make_shared()), _ebo(ebo) { + setBuffers(vertex_data); + } + + template + VertexGroup( + const VertexDataVec& vertex_data, + T... args + ) + : VertexGroup(vertex_data, std::make_shared(args...)) { + setBuffers(vertex_data); + } + + template + VertexGroup( + const VertexData& vertex_data, + T... args + ) + : VertexGroup(VertexDataVec{vertex_data}, args...) {} + + template + VertexGroup( + const ByteVector& vertex_data, + const VertexAttributeList& vertex_specification, + T... args + ) : VertexGroup({ + std::make_shared(vertex_data), + vertex_specification}, + args... + ) {} void bind() const { - _ebo->bind(); _vao->bind(); + _ebo->bind(); } void unbind() const { _vao->unbind(); _ebo->unbind(); } + void draw() const; + VertexDataVec getBuffers(); VertexData getBuffers(size_t); size_t setBuffers(const VertexDataVec&); size_t addBuffer(const VertexData&); + size_t addBuffer(const ByteVector& data, const VertexAttributeList& attrs); + VertexDataVec dropBuffers(); VertexData dropBuffer(size_t); VertexAttributePointer attribute(size_t) const; @@ -167,10 +228,10 @@ class VertexGroup { void enable(); void disable(); - protected: - VertexDataVec _vertex_data; - std::shared_ptr _ebo; - std::shared_ptr _vao; + std::shared_ptr indices() const; + std::shared_ptr indices(const std::vector& data); + std::shared_ptr indices(std::shared_ptr buffer); + }; } // namespace nb diff --git a/engine/NBGraphics/src/Buffers.cpp b/engine/NBGraphics/src/Buffers.cpp index 99379da..e6fdbda 100644 --- a/engine/NBGraphics/src/Buffers.cpp +++ b/engine/NBGraphics/src/Buffers.cpp @@ -12,15 +12,92 @@ const std::string BufferError::type = "nb::BufferError"; const ErrorCodeMap BufferError::ErrorMessages = { { BufferErrorCodes::UNDEFINED, "Error" }, { BufferErrorCodes::DATA_OVERFLOW, "Attempting buffer overflow" }, - { BufferErrorCodes::INVALID_BUFFER, "Attempting operation on invalid buffer" }, - { BufferErrorCodes::HANDLE_OVERWRITE, "Attempting to overwrite active buffer"} + {BufferErrorCodes::DATA_ERROR, "Invalid memory operation"} }; -template<> -ArrayBuffer::ArrayBuffer(const ByteVector& data_, GLenum usage_) -: BufferType() { - data(data_, usage_); +Buffer::Buffer(Buffer&& rval) : Target(rval.Target) { + *this = std::move(rval); } +Buffer& Buffer::operator=(Buffer&& rhs) { + if (Target != rhs.Target) { + auto targ_name = BufferTypes.at(Target); + THROW(OGLError( + OGLError::Codes::INVALID_OBJECT, + "w/ BufferType "+targ_name+" != BufferType "+BufferTypes.at(rhs.Target) + )); + } + OpenGLObject::operator=(std::move(rhs)); + _usage = rhs._usage; + _size = rhs._size; + + rhs._usage = GL_STATIC_DRAW; + rhs._size = 0; + return *this; +} + +GLenum Buffer::usage() const { return _usage; } + +size_t Buffer::size() const { return _size; } + +ByteVector Buffer::data() const { + bind(); + ByteVector ret(_size); + glGetBufferSubData(Target, 0, _size, ret.data()); + return ret; +} + +void Buffer::data(const void* data_, size_t size_, GLenum usage_) { + declare(); + glBufferData(Target, size_, data_, usage_); + _size = size_; + _usage = usage_; +} + +void Buffer::data(const ByteVector& data_, GLenum usage_) { + declare(); + data(data_.data(), data_.size(), usage_); +} + +void Buffer::subdata(const void* data_, size_t size_, GLintptr offset_) { + bind(); + if (offset_+size_ <= _size) { + THROW(BufferError(BufferError::Codes::DATA_OVERFLOW)); + } + glBufferSubData(Target, offset_, size_, data_); +} + +void Buffer::subdata(const ByteVector& data_, GLintptr offset_) { + bind(); + size_t size_ = data_.size(); + subdata(data_.data(), size_); +} + +ElementBuffer::ElementBuffer() : Buffer(GL_ELEMENT_ARRAY_BUFFER) {} + +ByteVector ElementBuffer::data() const { + return data(); +} + +size_t ElementBuffer::size() const { return _size / _typeSize; } + +size_t ElementBuffer::typeSize() const { return _typeSize; } + +GLenum ElementBuffer::glslType() const { return _glslType; } + +template void ElementBuffer::data(const std::vector& data, GLenum usage); +template void ElementBuffer::data(const std::vector& data, GLenum usage); +template void ElementBuffer::data(const std::vector& data, GLenum usage); +template void ElementBuffer::data(const uint8_t* const data, size_t size, GLenum usage); +template void ElementBuffer::data(const uint16_t* const data, size_t size, GLenum usage); +template void ElementBuffer::data(const uint32_t* const data, size_t size, GLenum usage); + +template void ElementBuffer::subdata(const uint8_t* const data, size_t size, size_t offset=0); +template void ElementBuffer::subdata(const uint16_t* const data, size_t size, size_t offset=0); +template void ElementBuffer::subdata(const uint32_t* const data, size_t size, size_t offset=0); +template void ElementBuffer::subdata(const std::vector& data, size_t offset=0); +template void ElementBuffer::subdata(const std::vector& data, size_t offset=0); +template void ElementBuffer::subdata(const std::vector& data, size_t offset=0); + } \ No newline at end of file diff --git a/engine/NBGraphics/src/FrameBuffers.cpp b/engine/NBGraphics/src/FrameBuffers.cpp new file mode 100644 index 0000000..4dcd893 --- /dev/null +++ b/engine/NBGraphics/src/FrameBuffers.cpp @@ -0,0 +1,342 @@ +#include "FrameBuffers.hpp" + +namespace nb { + +using FBOErrorCodes = FrameBufferError::Codes; +const std::string FrameBufferError::type = "nb::FrameBufferError"; +const ErrorCodeMap FrameBufferError::ErrorMessages = { + {FBOErrorCodes::UNDEFINED, "Error"}, + {FBOErrorCodes::INVALID_VALUE, "Invalid value"} +}; + +GLuint FrameBufferBase::declare() { + if (!_id) { + glGenFramebuffers(1, &_id); + } + bind(); + return _id; +} + +void FrameBufferBase::remove() { + if (_id) { + unbind(); + glDeleteFramebuffers(1, &_id); + _id=0; + } +} + +FrameBufferBase::FrameBufferBase(GLenum target) : Target(target) {} + +std::shared_ptr FrameBufferBase::detach(GLenum attch_) { + bind(); + auto ret = _attachments.at(attch_); + _attachments.erase(attch_); + glFramebufferTexture(Target, attch_, 0, 0); + return ret; +} + +void FrameBufferBase::attach( + GLenum attachment_, + std::shared_ptr texture_, + unsigned int level_, + unsigned int layer_ +) { + declare(); + if (attachment_ == GL_NONE) { + THROW(FrameBufferError(FrameBufferError::INVALID_VALUE, + "Cannot attach object to `GL_NONE`" + )); + } + glFramebufferTextureLayer( + Target, + attachment_, + texture_->id(), + level_, + layer_ + ); + _attachments[attachment_] = texture_; +} + +void FrameBufferBase::attach( + GLenum attachment_, + std::shared_ptr texture_, + unsigned int level_ +) { + declare(); + if (attachment_ == GL_NONE) { + THROW(FrameBufferError(FrameBufferError::INVALID_VALUE, + "Cannot attach object to `GL_NONE`" + )); + } + if (texTypeIs2D(texture_->Target)) { + glFramebufferTexture2D( + Target, + attachment_, + texture_->Target, + texture_->id(), + level_ + ); + } else { + glFramebufferTexture1D( + Target, + attachment_, + texture_->Target, + texture_->id(), + level_ + ); + } + _attachments[attachment_] = texture_; +} + +void FrameBufferBase::bind() const { + if (_id) { + glBindFramebuffer(Target, _id); + } else { + THROW(OGLError( + OGLError::Codes::INVALID_OBJECT, + "w/ FrameBuffer" + )); + } +} + +void FrameBufferBase::unbind() const { + glBindFramebuffer(Target, 0); +} + +GLenum FrameBufferBase::status() const { + bind(); + return glCheckFramebufferStatus(Target); +} + +FrameBufferBase::AttachmentMap FrameBufferBase::getAllBuffers() const { + return _attachments; +} + +std::shared_ptr FrameBufferBase::getBuffer(GLenum attch_) const { + try { + return _attachments.at(attch_); + } catch (const std::out_of_range& e) { + THROW(e); + } +} + +// class ReadFrameBuffer + +ReadFrameBuffer::ReadFrameBuffer() +: _location(GL_NONE), FrameBufferBase(GL_READ_FRAMEBUFFER) {} + +ReadFrameBuffer& ReadFrameBuffer::operator=(WriteFrameBuffer&& mv) { + if (mv.id()) { + _attachments = mv.getAllBuffers(); + mv.setWriteBuffer(GL_NONE); + mv._attachments = {}; + mv._locations = {}; + FrameBufferBase::OpenGLObject::operator=(std::move(mv)); + } + return *this; +} + +ReadFrameBuffer& ReadFrameBuffer::operator=(ReadFrameBuffer&& mv) { + if (mv.id()) { + _attachments = mv._attachments; + _location = mv._location; + mv._attachments = {}; + mv._location = GL_NONE; + FrameBufferBase::OpenGLObject::operator=(std::move(mv)); + } + return *this; +} + +std::shared_ptr ReadFrameBuffer::getReadBuffer() const { + if (_location == GL_NONE) { return nullptr; } + return getBuffer(_location); +} + +GLenum ReadFrameBuffer::getReadLocation() const { return _location; } + +std::shared_ptr ReadFrameBuffer::setReadBuffer(GLenum attch_) { + bind(); + if (attch_!=GL_NONE) { + try { + const auto& x = _attachments.at(attch_); + } catch (const std::out_of_range& e) { + THROW(FrameBufferError(FrameBufferError::INVALID_VALUE, + "No buffer set at attachment" + )); + } + } + _location = attch_; + glReadBuffer(_location); + return getReadBuffer(); +} + +// class WriteFrameBuffer + +WriteFrameBuffer::WriteFrameBuffer() +: FrameBufferBase(GL_DRAW_FRAMEBUFFER) {} + +WriteFrameBuffer& WriteFrameBuffer::operator=(ReadFrameBuffer&& rhs) { + if (rhs.id()) { + _attachments = rhs._attachments; + rhs.setReadBuffer(GL_NONE); + rhs._attachments = {}; + rhs._location = GL_NONE; + FrameBufferBase::OpenGLObject::operator=(std::move(rhs)); + } + return *this; +} + +WriteFrameBuffer& WriteFrameBuffer::operator=(WriteFrameBuffer&& rhs) { + if (rhs.id()) { + _attachments = rhs._attachments; + _locations = rhs._locations; + rhs._attachments = {}; + rhs._locations = {}; + FrameBufferBase::OpenGLObject::operator=(std::move(rhs)); + } + return *this; +} + +using fRGBA = RGBA; + +std::vector> WriteFrameBuffer::getWriteBuffers() const { + const size_t count = _locations.size(); + std::vector> ret(count); + for (int i = 0; i < count; ++i) { + ret[i] = std::static_pointer_cast(getBuffer(_locations[i])); + } + return ret; +} + +std::vector WriteFrameBuffer::getWriteLocations() const { + return _locations; +} + +WriteFrameBuffer::DrawTexVec WriteFrameBuffer::setWriteBuffers(const std::vector& attchs_) { + bind(); + for (GLenum loc : attchs_) { + if (getBuffer(loc)->Target == GL_RENDERBUFFER) { + THROW(FrameBufferError(FrameBufferError::INVALID_VALUE, + "Cannot write to renderbuffer [ @ Attachment = "+std::to_string(loc)+" ]" + )); + } + } + _locations = attchs_; + glDrawBuffers(_locations.size(), _locations.data()); + return getWriteBuffers(); +} + +std::shared_ptr WriteFrameBuffer::setWriteBuffer(GLenum attch_) { + bind(); + if (attch_ == GL_NONE) { + std::vector tmp(_locations.size(), GL_NONE); + glDrawBuffers(tmp.size(), tmp.data()); + _locations = {}; + return nullptr; + } + setWriteBuffers({attch_}); + return std::static_pointer_cast(getBuffer(attch_)); +} + +fRGBA WriteFrameBuffer::clearColorValue() const { + bind(); + float rgba[4]; + glGetFloatv(GL_COLOR_CLEAR_VALUE, rgba); + return fRGBA{rgba[0], rgba[1], rgba[2], rgba[3]}; +} + +fRGBA WriteFrameBuffer::clearColorValue(const fRGBA& color) { + declare(); + glClearColor(color.r, color.g, color.b, color.a); + return color; +} + +fRGBA WriteFrameBuffer::clearColorValue(float r, float g, float b, float a) { + return clearColorValue({r, g,b , a}); +} + +double WriteFrameBuffer::clearDepthValue() const { + bind(); + double ret; + glGetDoublev(GL_DEPTH_CLEAR_VALUE, &ret); + return ret; +} + +double WriteFrameBuffer::clearDepthValue(double d_) { + declare(); + glClearDepth(d_); + return d_; +} + +GLint WriteFrameBuffer::clearStencilValue() const { + bind(); + GLint ret; + glGetIntegerv(GL_STENCIL_CLEAR_VALUE, &ret); + return ret; +} + +GLint WriteFrameBuffer::clearStencilValue(GLint s_) { + declare(); + glClearStencil(s_); + return s_; +} + +void WriteFrameBuffer::clear(GLbitfield mask_) const { + bind(); + glClear(mask_); +} + +void WriteFrameBuffer::clearDepth() const { + clear(GL_DEPTH_BUFFER_BIT); +} + +void WriteFrameBuffer::clearDepth(float val_) const { + bind(); + glClearBufferfv(GL_DEPTH, 0, &val_); +} + +void WriteFrameBuffer::clearStencil() const { + clear(GL_STENCIL_BUFFER_BIT); +} + +void WriteFrameBuffer::clearStencil(GLint val_) const { + bind(); + glClearBufferiv(GL_STENCIL, 0, &val_); +} + +void WriteFrameBuffer::clearDepthStencil() const { + clear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); +} + +void WriteFrameBuffer::clearDepthStencil(float d_, GLint s_) const { + bind(); + glClearBufferfi(GL_DEPTH_STENCIL, 0, d_, s_); +} + +FrameBuffer::FrameBuffer() +: FrameBufferBase(GL_FRAMEBUFFER) {} + +FrameBuffer& FrameBuffer::operator=(FrameBuffer&& rhs) { + if (rhs.id()) { + _attachments = rhs._attachments; + _location = rhs._location; + _locations = rhs._locations; + rhs._attachments = {}; + rhs._locations = {}; + rhs._location = GL_NONE; + OpenGLObject::operator=(std::move(rhs)); + } + return *this; +} + +FrameBuffer& FrameBuffer::operator=(ReadFrameBuffer&& rhs) { + ReadFrameBuffer::operator=(std::move(rhs)); + return *this; +} + +FrameBuffer& FrameBuffer::operator=(WriteFrameBuffer&& rhs) { + WriteFrameBuffer::operator=(std::move(rhs)); + return *this; +} + +} // namespace nb \ No newline at end of file diff --git a/engine/NBGraphics/src/Image.cpp b/engine/NBGraphics/src/Image.cpp new file mode 100644 index 0000000..ea44ede --- /dev/null +++ b/engine/NBGraphics/src/Image.cpp @@ -0,0 +1,8 @@ +#include "Image.hpp" + +using ImageErrorCodes = nb::ImageError::Codes; +const std::string nb::ImageError::type = "nb::ImageError"; +const nb::ErrorCodeMap nb::ImageError::ErrorMessages = { + { ImageErrorCodes::UNDEFINED, "Error" }, + {ImageErrorCodes::OUT_OF_BOUNDS, "Out of bounds"} +}; \ No newline at end of file diff --git a/engine/NBGraphics/src/OGLObjects.cpp b/engine/NBGraphics/src/OGLObjects.cpp index d66a2dc..9ba59e6 100644 --- a/engine/NBGraphics/src/OGLObjects.cpp +++ b/engine/NBGraphics/src/OGLObjects.cpp @@ -5,7 +5,19 @@ namespace nb { const std::string OGLError::type = "nb::OGLError"; const ErrorCodeMap OGLError::ErrorMessages = { { OGLError::Codes::UNDEFINED, "Error" }, - {OGLError::Codes::HANGING_OBJECT, "Attempting to leave a hanging OpenGL object"} + {OGLError::Codes::HANGING_OBJECT, "Attempting to leave a hanging OpenGL object"}, + {OGLError::Codes::INVALID_OBJECT, "Attempting operation with invalid object"} }; +OpenGLObject::OpenGLObject(OpenGLObject&& rval) { + *this = std::move(rval); +} + +OpenGLObject& OpenGLObject::operator=(OpenGLObject&& rhs) { + if (_id) { THROW(OGLError(Codes::HANGING_OBJECT)); } + _id = rhs._id; + rhs._id = 0; + return *this; +} + } // namespace nb \ No newline at end of file diff --git a/engine/NBGraphics/src/ProgramPipeline.cpp b/engine/NBGraphics/src/ProgramPipeline.cpp index e45b65a..783c49a 100644 --- a/engine/NBGraphics/src/ProgramPipeline.cpp +++ b/engine/NBGraphics/src/ProgramPipeline.cpp @@ -15,22 +15,29 @@ const ErrorCodeMap ProgramError::ErrorMessages = { {ProgramErrCodes::LINKING_ERROR, "Linker error"} }; -Shader::Shader(GLenum target_, const std::string& source_) : target(target_) { +Shader::Shader(GLenum target_, const std::string& source_) +: Shader(target_, std::vector({source_})) {} + +Shader::Shader(GLenum target_, const std::vector& strings_) +: target(target_), _sources(strings_) { declare(); - const char* src_ptr= source_.data(); - glShaderSource(_id, 1, &src_ptr, NULL); - glCompileShader(_id); - _success = status(GL_COMPILE_STATUS); - if (!_success) { - WARN(log(), 0x0FE); - } + compile(); } -Shader::Shader(Shader&& cpy) : target(cpy.target) { +Shader::Shader(Shader&& cpy) +: target(cpy.target), _success(cpy._success), _sources(cpy._sources) { _id = cpy._id; + + cpy._success = false; + cpy._sources = {}; + cpy._id = 0; } -Shader::operator bool() { return _success; } +Shader::operator bool() { return success(); } + +bool Shader::success() const { + return _success; +} GLint Shader::status(GLenum parameter) const { GLint param = 0; diff --git a/engine/NBGraphics/src/Textures.cpp b/engine/NBGraphics/src/Textures.cpp index 9b448c7..dc77258 100644 --- a/engine/NBGraphics/src/Textures.cpp +++ b/engine/NBGraphics/src/Textures.cpp @@ -1,10 +1,7 @@ #include "Textures.hpp" -namespace nb{ - -Texture::Texture(GLenum target_) : target(target_) { - glGenTextures(1, &_id); -} +namespace nb { +Texture::Texture(GLenum target_) : Base(target_) {} } // namespace nb \ No newline at end of file diff --git a/engine/NBGraphics/src/VertexArray.cpp b/engine/NBGraphics/src/VertexArray.cpp index f7ae2d3..f080e38 100644 --- a/engine/NBGraphics/src/VertexArray.cpp +++ b/engine/NBGraphics/src/VertexArray.cpp @@ -6,8 +6,14 @@ using VAOErrorCodes = VAOError::Codes; const std::string VAOError::type = "nb::VAOError"; const ErrorCodeMap VAOError::ErrorMessages = { { VAOErrorCodes::UNDEFINED, "Error" }, - { VAOErrorCodes::INVALID_ATTRIBUTE, "Targeting invalid attribute"}, - { VAOErrorCodes::INVALID_VAO, "Targeting invalid VAO" } + { VAOErrorCodes::INVALID_ATTRIBUTE, "Targeting invalid attribute"} +}; + +using VertexCodes = VertexError::Codes; +const std::string VertexError::type = "nb::VertexError"; +const ErrorCodeMap VertexError::ErrorMessages = { + { VertexError::UNDEFINED, "Error" }, + { VertexError::MISALIGNED_DATA, "Data does not match vertex layout"} }; VAO::VAO(const VertexAttributePointerList& attr_ptrs) { @@ -19,12 +25,9 @@ VAO::VAO(VAO&& other) { } VAO& VAO::operator=(VAO&& rhs) { - _id = rhs._id; + OpenGLObject::operator=(std::move(rhs)); _attrs = rhs._attrs; - - rhs._id = 0; rhs._attrs = {}; - return *this; } @@ -115,25 +118,17 @@ VertexGroup& VertexGroup::operator=(VertexGroup&& rhs) { return *this; } -VertexGroup::VertexGroup(const VertexDataVec& vertex_data_) -: _vao(std::make_shared()), _ebo(std::make_shared()) { - setBuffers(vertex_data_); +void VertexGroup::draw() const { + bind(); + glDrawElements(primitive, _ebo->size(), _ebo->glslType(), 0); } -VertexGroup::VertexGroup(const VertexData& vertex_data_) -: VertexGroup(VertexDataVec({vertex_data_})) {} - -VertexGroup::VertexGroup( - const ByteVector& data_, - const VertexAttributeList& attrs_ -) : VertexGroup({std::make_shared(data_), attrs_}) {} - size_t VertexGroup::addBuffer(const VertexData& vertex_data_) { VertexData data = vertex_data_; _vertex_data.emplace_back(data); VertexAttributePointerList attrs = _vao->attributes(); GLuint idx = attrs.size(); - for (auto attr : data.attrs) { + for (const auto& attr : data.attrs) { attrs.emplace_back( VertexAttributePointer{attr, data.vbo->id(),idx} ); @@ -143,6 +138,13 @@ size_t VertexGroup::addBuffer(const VertexData& vertex_data_) { return _vertex_data.size(); } +size_t VertexGroup::addBuffer(const ByteVector& data_, const VertexAttributeList& attrs_) { + return addBuffer({ + std::make_shared(data_), + attrs_ + }); +} + VertexData VertexGroup::getBuffers(size_t idx) { return _vertex_data[idx]; } @@ -155,8 +157,8 @@ size_t VertexGroup::setBuffers(const VertexDataVec& vertex_data_) { _vertex_data = vertex_data_; VertexAttributePointerList attr_ptrs; GLuint idx = 0; - for (auto data : _vertex_data) { - for (auto attr : data.attrs) { + for (const auto& data : _vertex_data) { + for (const auto& attr : data.attrs) { attr_ptrs.emplace_back( VertexAttributePointer{attr, data.vbo->id(), idx} ); @@ -167,9 +169,15 @@ size_t VertexGroup::setBuffers(const VertexDataVec& vertex_data_) { return _vertex_data.size(); } +VertexDataVec VertexGroup::dropBuffers() { + auto ret = _vertex_data; + setBuffers({}); + return ret; +} + VertexData VertexGroup::dropBuffer(size_t idx) { if (idx >= _vertex_data.size()) { - THROW(BufferError(BufferError::Codes::INVALID_BUFFER)); + THROW(NBError(NBError::INDEX_ERROR)); } VertexDataVec tmp; VertexData ret; @@ -228,4 +236,16 @@ void VertexGroup::enable() { _vao->enable(); } void VertexGroup::disable() { _vao->disable(); } +std::shared_ptr VertexGroup::indices() const { return _ebo; } + +std::shared_ptr VertexGroup::indices(const std::vector& data) { + _ebo->data(data); + return _ebo; +} + +std::shared_ptr VertexGroup::indices(std::shared_ptr buffer) { + _ebo = buffer; + return _ebo; +} + } // namespace nb diff --git a/engine/NBGraphics/tests/CMakeLists.txt b/engine/NBGraphics/tests/CMakeLists.txt index 8e6d9d6..bc5f12f 100644 --- a/engine/NBGraphics/tests/CMakeLists.txt +++ b/engine/NBGraphics/tests/CMakeLists.txt @@ -3,6 +3,7 @@ cmake_minimum_required(VERSION 3.26.0) if (NB_BUILD_TESTS) enable_testing() include(GoogleTest) + add_executable(TestWindow ./TestWindow.cpp ) @@ -11,4 +12,15 @@ if (NB_BUILD_TESTS) NBGraphics ) + add_executable(TestImages + ./testImages.cpp + ) + target_link_libraries(TestImages + NBCore + NBGraphics + GTest::gtest_main + ) + + gtest_discover_tests(TestImages) + endif() \ No newline at end of file diff --git a/engine/NBGraphics/tests/TestWindow.cpp b/engine/NBGraphics/tests/TestWindow.cpp index daacb70..42dff78 100644 --- a/engine/NBGraphics/tests/TestWindow.cpp +++ b/engine/NBGraphics/tests/TestWindow.cpp @@ -1,7 +1,12 @@ #include "GLLoad.hpp" +#define STB_IMAGE_IMPLEMENTATION +#include "stb_image.h" + +#include "Image.hpp" #include "ProgramPipeline.hpp" #include "VertexArray.hpp" #include "Window.hpp" +#include "Textures.hpp" int main() { nb::logger.log("Howdy!"); @@ -10,54 +15,90 @@ int main() { window.setWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); window.init(); - nb::logger.log("Goob"); - auto vert = std::make_shared( GL_VERTEX_SHADER, "#version 330 core\n" "layout (location = 0) in vec2 aPos;\n" + "layout (location = 1) in vec2 tPos;\n" + "out vec2 vPos;\n" "void main()\n" "{\n" " gl_Position = vec4(aPos.x, aPos.y, 0.0, 1.0);\n" + " vPos = tPos;\n" "}\0" ); - LOG("Vertex Shader: " + vert->log()); + LOG(vert->log()); auto frag = std::make_shared( GL_FRAGMENT_SHADER, "#version 330 core\n" + "in vec2 vPos;\n" "out vec4 FragColor;\n" + "uniform sampler2D tex;\n" "void main()\n" "{\n" - " FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n" + " FragColor = texture(tex, vPos);\n" "}\n\0" ); - LOG("Fragment shader: " + frag->log()); + LOG(frag->log()); nb::ByteVector data = nb::vectorToBytes({ - -0.5, -0.5, 0.5, -0.5, 0.0, 0.5 + -0.5, -0.5, 0,0, + -0.5, 0.5, 0,1, + 0.5, 0.5, 1,1, + 0.5, -0.5, 1,0 }); nb::Program prog({vert, frag}); prog.bind(); + std::vector indxs = {0, 1, 2, 0, 2, 3}; + nb::VertexGroup tri(data, { nb::VertexAttribute{ 2, GL_FLOAT, false, - {0, 8} + {0, 16} + }, + nb::VertexAttribute{ + 2, + GL_FLOAT, + false, + {8, 16} } - }); + }, indxs); tri.bind(); + int width, height, numChannels; + auto raw_img_data = stbi_load( + "./awesomeface.png", + &width, + &height, + &numChannels, + 0 + ); + LOG(numChannels); + using RGBA = nb::Pixel; + nb::ImageReference img(width, height, raw_img_data); + auto tex = nb::ImageTexture(); + tex.parameter(GL_TEXTURE_MAG_FILTER, GL_LINEAR); + tex.parameter(GL_TEXTURE_MIN_FILTER, GL_LINEAR); + tex.setImage(img); + tex.bind(); + tri.bind(); + LOG(img.at(256, 256).r); + stbi_image_free(raw_img_data); + + LOG(prog.log()); + GLFWwindow* window_ptr = window.getWindow(); while(!glfwWindowShouldClose(window_ptr)) { - glDrawArrays(GL_TRIANGLES, 0, 3); glClearColor(0.2f, 0.3f, 0.3f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); + tri.draw(); glfwPollEvents(); glfwSwapBuffers(window_ptr); } diff --git a/engine/NBGraphics/tests/awesomeface.png b/engine/NBGraphics/tests/awesomeface.png new file mode 100644 index 0000000..9840caf Binary files /dev/null and b/engine/NBGraphics/tests/awesomeface.png differ diff --git a/engine/NBGraphics/tests/testImages.cpp b/engine/NBGraphics/tests/testImages.cpp new file mode 100644 index 0000000..c7f5627 --- /dev/null +++ b/engine/NBGraphics/tests/testImages.cpp @@ -0,0 +1,156 @@ +#include + +#include + +#define STB_IMAGE_IMPLEMENTATION +#include "stb_image.h" + +#include "Image.hpp" + +TEST(TestImage, SinglePixel) { + using RGB = nb::Pixel; + RGB p1; + p1.r = 0x00; + p1.g = 0x0F; + p1.b = 0xFF; + ASSERT_EQ(p1.r, 0x00); + ASSERT_EQ(p1.g, 0x0F); + ASSERT_EQ(p1.b, 0xFF); + RGB p2(p1); + ASSERT_EQ(p2.r, 0x00); + ASSERT_EQ(p2.g, 0x0F); + ASSERT_EQ(p2.b, 0xFF); + RGB p3(0xA, 0xB, 0xC); + ASSERT_EQ(p3.r, 0xA); + ASSERT_EQ(p3.g, 0xB); + ASSERT_EQ(p3.b, 0xC); + RGB p4 = p3; + ASSERT_EQ(p4.r, 0xA); + ASSERT_EQ(p4.g, 0xB); + ASSERT_EQ(p4.b, 0xC); + RGB p5(0xA0, 0xB0, 0xC0); + ASSERT_EQ(p5.r, 0xA0); + ASSERT_EQ(p5.g, 0xB0); + ASSERT_EQ(p5.b, 0xC0); + + using RBAG = nb::Pixel; + float a=0.5, b=0.4, c=0.0, d=0.3; + RBAG p6 = {a, b, c, d}; + ASSERT_EQ(p6.r, a); + ASSERT_EQ(p6.b, b); + ASSERT_EQ(p6.a, c); + ASSERT_EQ(p6.g, d); + RBAG p7 = p6; + ASSERT_EQ(p7.r, a); + ASSERT_EQ(p7.b, b); + ASSERT_EQ(p7.a, c); + ASSERT_EQ(p7.g, d); +} + +TEST(TestImage, PixelReference) { + using RGB = nb::Pixel; + using RGBRef = nb::PixelReference; + + using RBAG = nb::Pixel; + using RBAGRef = nb::PixelReference; + + RGB p1{255, 254, 253}; + RGBRef rp1(p1); + ASSERT_EQ(rp1.r, p1.r); + ASSERT_EQ(rp1.g, p1.g); + ASSERT_EQ(rp1.b, p1.b); + + RGB p2{0xa, 0xb, 0xc}; + rp1 = p2; + ASSERT_EQ(p1.r, p2.r); + ASSERT_EQ(p1.g, p2.g); + ASSERT_EQ(p1.b, p2.b); + + RGB p3 = rp1; + ASSERT_EQ(p3.r, p1.r); + ASSERT_EQ(p3.g, p1.g); + ASSERT_EQ(p3.b, p1.b); + + RGBRef rp2 = rp1; + rp2 = RGB(2, 4, 8); + ASSERT_EQ(p1.r, 2); + ASSERT_EQ(p1.g, 4); + ASSERT_EQ(p1.b, 8); + + int val = 27; + rp1.b = val; + ASSERT_EQ(rp1.r, p1.r); + ASSERT_EQ(rp1.g, p1.g); + ASSERT_EQ(rp1.b, p1.b); + ASSERT_EQ(p1.b, val); +} + +TEST(TestImage, SimpleImage) { + unsigned char data[] = { + 0x00, 0x00, + 0x00, 0xFF, + 0xFF, 0x00, + 0xFF, 0xFF + }; + using RG = nb::Pixel; + nb::Image img( + 2, 2, data + ); + for (int i = 0; i < sizeof(data); ++i) { + ASSERT_EQ(img.data()[i], data[i]); + } + auto pixel = img.at(0, 1); + ASSERT_EQ(pixel.r, data[4]); + ASSERT_EQ(pixel.b, data[5]); + +} + +TEST(TestImage, LoadImage) { + int width, height, numChannels; + unsigned char* data = stbi_load( + "./awesomeface.png", + &width, + &height, + &numChannels, + 0 + ); + using RGBA = nb::Pixel; + nb::Image img( + width, height, data + ); + for(int i = 0; i < height; ++i) { + for (int j = 0; j < width; ++j) { + auto pixel = img.at(j, i); + ASSERT_EQ(pixel.r, data[4*(i*width+j)]); + ASSERT_EQ(pixel.g, data[4*(i*width+j) + 1]); + ASSERT_EQ(pixel.b, data[4*(i*width+j) + 2]); + ASSERT_EQ(pixel.a, data[4*(i*width+j) + 3]); + } + } + stbi_image_free(data); +} + +TEST(TestImage, ImageBounds) { + size_t len = 10; + uint8_t val = 0xAA; + std::vector vec(2*len*len, val); + using RB = nb::Pixel; + nb::Image img( + len, len, vec.data() + ); + for (int i=0; i < 15; ++i) { + if (i