diff --git a/libs/gfx/include/psemek/gfx/texture.hpp b/libs/gfx/include/psemek/gfx/texture.hpp index 80b9ad7e..29f5452e 100644 --- a/libs/gfx/include/psemek/gfx/texture.hpp +++ b/libs/gfx/include/psemek/gfx/texture.hpp @@ -310,4 +310,82 @@ namespace psemek::gfx texture_3d(GLuint id); }; + struct texture_2d_array + { + texture_2d_array(); + texture_2d_array(texture_2d_array const &) = delete; + texture_2d_array(texture_2d_array &&); + + texture_2d_array & operator = (texture_2d_array const &) = delete; + texture_2d_array & operator = (texture_2d_array &&); + + ~texture_2d_array(); + + static texture_2d_array null(); + + GLuint id() const { return id_; } + + void bind() const; + + explicit operator bool () const { return id_ != 0; } + + int width() const { return width_; } + int height() const { return height_; } + int depth() const { return depth_; } + + geom::vector size() const { return {width_, height_, depth_}; } + + void load(GLint internal_format, std::size_t width, std::size_t height, std::size_t depth, GLenum format, GLenum type, const void * data); + + template + void load(std::size_t width, std::size_t height, std::size_t depth, Pixel const * data = nullptr) + { + using traits = pixel_traits; + load(traits::internal_format, width, height, depth, traits::format, traits::type, data); + } + + template + void load(util::array const & p) + { + load(p.width(), p.height(), p.depth(), p.data()); + } + + void pixels(GLenum format, GLenum type, void * data) const; + + template + Pixmap pixels() const + { + using traits = pixel_traits; + Pixmap p({width_, height_, depth_}); + pixels(traits::format, traits::type, p.data()); + return p; + } + + static texture_2d_array from_data(GLint internal_format, std::size_t width, std::size_t height, std::size_t depth, GLenum format, GLenum type, const void * data); + + template + static texture_2d_array from_pixmap(Pixmap const & p) + { + texture_2d_array t; + t.load(p); + return t; + } + + void generate_mipmap(); + void nearest_filter(); + void linear_filter(); + void anisotropy(); + + void repeat(); + void clamp(); + + private: + GLuint id_; + int width_ = 0; + int height_ = 0; + int depth_ = 0; + + texture_2d_array(GLuint id); + }; + } diff --git a/libs/gfx/source/texture.cpp b/libs/gfx/source/texture.cpp index 4fed6ba9..3ea803e0 100644 --- a/libs/gfx/source/texture.cpp +++ b/libs/gfx/source/texture.cpp @@ -357,4 +357,128 @@ namespace psemek::gfx gl::TexParameteri(gl::TEXTURE_3D, gl::TEXTURE_WRAP_R, gl::CLAMP_TO_EDGE); } + texture_2d_array::texture_2d_array() + { + gl::GenTextures(1, &id_); + } + + texture_2d_array::texture_2d_array(GLuint id) + : id_(id) + {} + + texture_2d_array texture_2d_array::null() + { + return texture_2d_array(0); + } + + void texture_2d_array::bind() const + { + gl::BindTexture(gl::TEXTURE_2D_ARRAY, id_); + } + + texture_2d_array::texture_2d_array(texture_2d_array && other) + : id_(other.id_) + , width_(other.width_) + , height_(other.height_) + , depth_(other.depth_) + { + other.id_ = 0; + other.width_ = 0; + other.height_ = 0; + other.depth_ = 0; + } + + texture_2d_array & texture_2d_array::operator = (texture_2d_array && other) + { + if (this == &other) return *this; + + gl::DeleteTextures(1, &id_); + id_ = other.id_; + width_ = other.width_; + height_ = other.height_; + depth_ = other.depth_; + other.id_ = 0; + other.width_ = 0; + other.height_ = 0; + other.depth_ = 0; + + return *this; + } + + texture_2d_array::~texture_2d_array() + { + gl::DeleteTextures(1, &id_); + } + + void texture_2d_array::load(GLint internal_format, std::size_t width, std::size_t height, std::size_t depth, GLenum format, GLenum type, const void * data) + { + bind(); + gl::TexImage3D(gl::TEXTURE_2D_ARRAY, 0, internal_format, width, height, depth, 0, format, type, data); + + width_ = width; + height_ = height; + depth_ = depth; + } + + void texture_2d_array::pixels(GLenum format, GLenum type, void * data) const + { + bind(); + gl::GetTexImage(gl::TEXTURE_2D_ARRAY, 0, format, type, data); + } + + texture_2d_array texture_2d_array::from_data(GLint internal_format, std::size_t width, std::size_t height, std::size_t depth, GLenum format, GLenum type, const void * data) + { + texture_2d_array tex; + tex.load(internal_format, width, height, depth, format, type, data); + return tex; + } + + void texture_2d_array::generate_mipmap() + { + bind(); + gl::GenerateMipmap(gl::TEXTURE_2D_ARRAY); + } + + void texture_2d_array::nearest_filter() + { + bind(); + gl::TexParameteri(gl::TEXTURE_2D_ARRAY, gl::TEXTURE_MAG_FILTER, gl::NEAREST); + gl::TexParameteri(gl::TEXTURE_2D_ARRAY, gl::TEXTURE_MIN_FILTER, gl::LINEAR_MIPMAP_LINEAR); + } + + void texture_2d_array::linear_filter() + { + bind(); + gl::TexParameteri(gl::TEXTURE_2D_ARRAY, gl::TEXTURE_MAG_FILTER, gl::LINEAR); + gl::TexParameteri(gl::TEXTURE_2D_ARRAY, gl::TEXTURE_MIN_FILTER, gl::LINEAR_MIPMAP_LINEAR); + } + + void texture_2d_array::anisotropy() + { + if (!gl::exts::var_EXT_texture_filter_anisotropic) return; + + if (!max_anisotropy_level) + { + max_anisotropy_level = 0; + gl::GetFloatv(gl::MAX_TEXTURE_MAX_ANISOTROPY_EXT, &(*max_anisotropy_level)); + } + gl::TexParameterf(gl::TEXTURE_2D_ARRAY, gl::TEXTURE_MAX_ANISOTROPY_EXT, *max_anisotropy_level); + } + + void texture_2d_array::repeat() + { + bind(); + gl::TexParameteri(gl::TEXTURE_2D_ARRAY, gl::TEXTURE_WRAP_S, gl::REPEAT); + gl::TexParameteri(gl::TEXTURE_2D_ARRAY, gl::TEXTURE_WRAP_T, gl::REPEAT); + gl::TexParameteri(gl::TEXTURE_2D_ARRAY, gl::TEXTURE_WRAP_R, gl::REPEAT); + } + + void texture_2d_array::clamp() + { + bind(); + gl::TexParameteri(gl::TEXTURE_2D_ARRAY, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE); + gl::TexParameteri(gl::TEXTURE_2D_ARRAY, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE); + gl::TexParameteri(gl::TEXTURE_2D_ARRAY, gl::TEXTURE_WRAP_R, gl::CLAMP_TO_EDGE); + } + }