diff --git a/libs/gfx/include/psemek/gfx/texture.hpp b/libs/gfx/include/psemek/gfx/texture.hpp index f94b461f..411bf6a3 100644 --- a/libs/gfx/include/psemek/gfx/texture.hpp +++ b/libs/gfx/include/psemek/gfx/texture.hpp @@ -42,6 +42,80 @@ namespace psemek::gfx static constexpr GLenum type = gl::FLOAT; }; + struct texture_1d + { + texture_1d(); + texture_1d(texture_1d const &) = delete; + texture_1d(texture_1d &&); + + texture_1d & operator = (texture_1d const &) = delete; + texture_1d & operator = (texture_1d &&); + + ~texture_1d(); + + static texture_1d null(); + + GLuint id() const { return id_; } + + void bind() const; + + explicit operator bool () const { return id_ != 0; } + + int width() const { return width_; } + + geom::vector size() const { return {width_}; } + + void load(GLint internal_format, std::size_t width, GLenum format, GLenum type, const void * data); + + template + void load(std::size_t width, Pixel const * data = nullptr) + { + using traits = pixel_traits; + load(traits::internal_format, width, traits::format, traits::type, data); + } + + template + void load(util::array const & p) + { + load(p.width(), p.data()); + } + + void pixels(GLenum format, GLenum type, void * data) const; + + template + Pixmap pixels() const + { + using traits = pixel_traits; + Pixmap p({width_}); + pixels(traits::format, traits::type, p.data()); + return p; + } + + static texture_1d from_data(GLint internal_format, std::size_t width, GLenum format, GLenum type, const void * data); + + template + static texture_1d from_pixmap(Pixmap const & p) + { + texture_1d 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; + + texture_1d(GLuint id); + }; + struct texture_2d { texture_2d(); @@ -87,7 +161,7 @@ namespace psemek::gfx Pixmap pixels() const { using traits = pixel_traits; - Pixmap p(width_, height_); + Pixmap p({width_, height_}); pixels(traits::format, traits::type, p.data()); return p; } @@ -118,4 +192,82 @@ namespace psemek::gfx texture_2d(GLuint id); }; + struct texture_3d + { + texture_3d(); + texture_3d(texture_3d const &) = delete; + texture_3d(texture_3d &&); + + texture_3d & operator = (texture_3d const &) = delete; + texture_3d & operator = (texture_3d &&); + + ~texture_3d(); + + static texture_3d 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_3d 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_3d from_pixmap(Pixmap const & p) + { + texture_3d 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_3d(GLuint id); + }; + } diff --git a/libs/gfx/source/texture.cpp b/libs/gfx/source/texture.cpp index e674a7c3..4fed6ba9 100644 --- a/libs/gfx/source/texture.cpp +++ b/libs/gfx/source/texture.cpp @@ -4,6 +4,118 @@ namespace psemek::gfx { + static std::optional max_anisotropy_level; + + texture_1d::texture_1d() + { + gl::GenTextures(1, &id_); + } + + texture_1d::texture_1d(GLuint id) + : id_(id) + {} + + texture_1d texture_1d::null() + { + return texture_1d(0); + } + + void texture_1d::bind() const + { + gl::BindTexture(gl::TEXTURE_1D, id_); + } + + texture_1d::texture_1d(texture_1d && other) + : id_(other.id_) + , width_(other.width_) + { + other.id_ = 0; + other.width_ = 0; + } + + texture_1d & texture_1d::operator = (texture_1d && other) + { + if (this == &other) return *this; + + gl::DeleteTextures(1, &id_); + id_ = other.id_; + width_ = other.width_; + other.id_ = 0; + other.width_ = 0; + + return *this; + } + + texture_1d::~texture_1d() + { + gl::DeleteTextures(1, &id_); + } + + void texture_1d::load(GLint internal_format, std::size_t width, GLenum format, GLenum type, const void * data) + { + bind(); + gl::TexImage1D(gl::TEXTURE_1D, 0, internal_format, width, 0, format, type, data); + + width_ = width; + } + + void texture_1d::pixels(GLenum format, GLenum type, void * data) const + { + bind(); + gl::GetTexImage(gl::TEXTURE_1D, 0, format, type, data); + } + + texture_1d texture_1d::from_data(GLint internal_format, std::size_t width, GLenum format, GLenum type, const void * data) + { + texture_1d tex; + tex.load(internal_format, width, format, type, data); + return tex; + } + + void texture_1d::generate_mipmap() + { + bind(); + gl::GenerateMipmap(gl::TEXTURE_1D); + } + + void texture_1d::nearest_filter() + { + bind(); + gl::TexParameteri(gl::TEXTURE_1D, gl::TEXTURE_MAG_FILTER, gl::NEAREST); + gl::TexParameteri(gl::TEXTURE_1D, gl::TEXTURE_MIN_FILTER, gl::LINEAR_MIPMAP_LINEAR); + } + + void texture_1d::linear_filter() + { + bind(); + gl::TexParameteri(gl::TEXTURE_1D, gl::TEXTURE_MAG_FILTER, gl::LINEAR); + gl::TexParameteri(gl::TEXTURE_1D, gl::TEXTURE_MIN_FILTER, gl::LINEAR_MIPMAP_LINEAR); + } + + void texture_1d::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_1D, gl::TEXTURE_MAX_ANISOTROPY_EXT, *max_anisotropy_level); + } + + void texture_1d::repeat() + { + bind(); + gl::TexParameteri(gl::TEXTURE_1D, gl::TEXTURE_WRAP_S, gl::REPEAT); + } + + void texture_1d::clamp() + { + bind(); + gl::TexParameteri(gl::TEXTURE_1D, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE); + } + texture_2d::texture_2d() { gl::GenTextures(1, &id_); @@ -99,13 +211,12 @@ namespace psemek::gfx { if (!gl::exts::var_EXT_texture_filter_anisotropic) return; - static std::optional level; - if (!level) + if (!max_anisotropy_level) { - level = 0; - gl::GetFloatv(gl::MAX_TEXTURE_MAX_ANISOTROPY_EXT, &(*level)); + max_anisotropy_level = 0; + gl::GetFloatv(gl::MAX_TEXTURE_MAX_ANISOTROPY_EXT, &(*max_anisotropy_level)); } - gl::TexParameterf(gl::TEXTURE_2D, gl::TEXTURE_MAX_ANISOTROPY_EXT, *level); + gl::TexParameterf(gl::TEXTURE_2D, gl::TEXTURE_MAX_ANISOTROPY_EXT, *max_anisotropy_level); } void texture_2d::repeat() @@ -122,4 +233,128 @@ namespace psemek::gfx gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE); } + texture_3d::texture_3d() + { + gl::GenTextures(1, &id_); + } + + texture_3d::texture_3d(GLuint id) + : id_(id) + {} + + texture_3d texture_3d::null() + { + return texture_3d(0); + } + + void texture_3d::bind() const + { + gl::BindTexture(gl::TEXTURE_3D, id_); + } + + texture_3d::texture_3d(texture_3d && 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_3d & texture_3d::operator = (texture_3d && 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_3d::~texture_3d() + { + gl::DeleteTextures(1, &id_); + } + + void texture_3d::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_3D, 0, internal_format, width, height, depth, 0, format, type, data); + + width_ = width; + height_ = height; + depth_ = depth; + } + + void texture_3d::pixels(GLenum format, GLenum type, void * data) const + { + bind(); + gl::GetTexImage(gl::TEXTURE_3D, 0, format, type, data); + } + + texture_3d texture_3d::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_3d tex; + tex.load(internal_format, width, height, depth, format, type, data); + return tex; + } + + void texture_3d::generate_mipmap() + { + bind(); + gl::GenerateMipmap(gl::TEXTURE_3D); + } + + void texture_3d::nearest_filter() + { + bind(); + gl::TexParameteri(gl::TEXTURE_3D, gl::TEXTURE_MAG_FILTER, gl::NEAREST); + gl::TexParameteri(gl::TEXTURE_3D, gl::TEXTURE_MIN_FILTER, gl::LINEAR_MIPMAP_LINEAR); + } + + void texture_3d::linear_filter() + { + bind(); + gl::TexParameteri(gl::TEXTURE_3D, gl::TEXTURE_MAG_FILTER, gl::LINEAR); + gl::TexParameteri(gl::TEXTURE_3D, gl::TEXTURE_MIN_FILTER, gl::LINEAR_MIPMAP_LINEAR); + } + + void texture_3d::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_3D, gl::TEXTURE_MAX_ANISOTROPY_EXT, *max_anisotropy_level); + } + + void texture_3d::repeat() + { + bind(); + gl::TexParameteri(gl::TEXTURE_3D, gl::TEXTURE_WRAP_S, gl::REPEAT); + gl::TexParameteri(gl::TEXTURE_3D, gl::TEXTURE_WRAP_T, gl::REPEAT); + gl::TexParameteri(gl::TEXTURE_3D, gl::TEXTURE_WRAP_R, gl::REPEAT); + } + + void texture_3d::clamp() + { + bind(); + gl::TexParameteri(gl::TEXTURE_3D, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE); + gl::TexParameteri(gl::TEXTURE_3D, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE); + gl::TexParameteri(gl::TEXTURE_3D, gl::TEXTURE_WRAP_R, gl::CLAMP_TO_EDGE); + } + }