// ===================================================================================================================== // glw, an open-source library that wraps OpenGL structures into classes. // Copyright (C) 2024 Medusa Slockbower // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . // ===================================================================================================================== #ifndef GLW_TEXTURE_H #define GLW_TEXTURE_H #include #include #include "common.h" #include #include #include #include namespace ocu = open_cpp_utils; namespace glw { // ===================================================================================================================== // Definitions // ===================================================================================================================== using swizzle_t = glm::vec<4, enum_t>; using wrapping_t = glm::vec<3, enum_t>; // Enums --------------------------------------------------------------------------------------------------------------- enum texture_type : enum_t { texture1D = GL_TEXTURE_1D , texture1DArray = GL_TEXTURE_1D_ARRAY , texture2D = GL_TEXTURE_2D , textureRect = GL_TEXTURE_RECTANGLE , texture2DArray = GL_TEXTURE_2D_ARRAY , texture2DMS = GL_TEXTURE_2D_MULTISAMPLE , texture2DMSArray = GL_TEXTURE_2D_MULTISAMPLE_ARRAY , textureCubemap = GL_TEXTURE_CUBE_MAP , textureCubemapArray = GL_TEXTURE_CUBE_MAP_ARRAY , texture3D = GL_TEXTURE_3D }; enum texture_param : enum_t { depth_stencil_mode = GL_DEPTH_STENCIL_TEXTURE_MODE , base_mip_level = GL_TEXTURE_BASE_LEVEL , max_mip_level = GL_TEXTURE_MAX_LEVEL , compare_mode = GL_TEXTURE_COMPARE_MODE , compare_func = GL_TEXTURE_COMPARE_FUNC , lod_min = GL_TEXTURE_MIN_LOD , lod_max = GL_TEXTURE_MAX_LOD , lod_bias = GL_TEXTURE_LOD_BIAS , min_filter = GL_TEXTURE_MIN_FILTER , mag_filter = GL_TEXTURE_MAG_FILTER , swizzle = GL_TEXTURE_SWIZZLE_RGBA , swizzle_r = GL_TEXTURE_SWIZZLE_R , swizzle_g = GL_TEXTURE_SWIZZLE_G , swizzle_b = GL_TEXTURE_SWIZZLE_B , swizzle_a = GL_TEXTURE_SWIZZLE_A , wrap_s = GL_TEXTURE_WRAP_S , wrap_t = GL_TEXTURE_WRAP_T , wrap_r = GL_TEXTURE_WRAP_R , border = GL_TEXTURE_BORDER_COLOR }; enum filtering : enum_t { nearest = GL_NEAREST , linear = GL_LINEAR , point = GL_NEAREST_MIPMAP_NEAREST , bilinear = GL_LINEAR_MIPMAP_NEAREST , trilinear = GL_LINEAR_MIPMAP_LINEAR }; enum wrapping : enum_t { clamp = GL_CLAMP_TO_EDGE , clamp_to_border = GL_CLAMP_TO_BORDER , mirrored = GL_MIRRORED_REPEAT , repeate = GL_REPEAT , mirrored_clamp = GL_MIRROR_CLAMP_TO_EDGE }; enum swizzle : enum_t { red = GL_TEXTURE_SWIZZLE_R , green = GL_TEXTURE_SWIZZLE_G , blue = GL_TEXTURE_SWIZZLE_B , alpha = GL_TEXTURE_SWIZZLE_A }; template class texture { // Constants =========================================================================================================== public: inline static constexpr enum_t type = T_; inline static constexpr enum_t format = PF_; inline static constexpr struct type_traits { static constexpr enum_t type = T_; static constexpr enum_t format = PF_; static constexpr bool layered = contains_enum{}; } traits {}; // Functions =========================================================================================================== private: void get_current_params_(); // Constructors -------------------------------------------------------------------------------------------------------- public: texture(size_t size); texture(glm::ivec2 size); texture(glm::ivec2 size, size_t samples); texture(glm::ivec3 size); texture(glm::ivec3 size, size_t samples); texture(texture&& move); ~texture(); inline constexpr enum_t get_type() const { return type; } inline constexpr enum_t get_format() const { return format; } // Handle -------------------------------------------------------------------------------------------------------------- /** * \brief ONLY USE THIS IF YOU KNOW WHAT YOU ARE DOING * \return The OpenGL name for the texture */ handle_t handle() { return handle_; } // Capacity ------------------------------------------------------------------------------------------------------------ size_t size() const requires(contains_enum::value) { return size_.x; } glm::ivec2 size() const requires(contains_enum::value) { return size_; } glm::ivec3 size() const requires(contains_enum::value) { return size_; } void resize(size_t size); void resize(glm::ivec2 size); void resize(glm::ivec2 size, size_t samples); void resize(glm::ivec3 size); void resize(glm::ivec3 size, size_t samples); size_t num_samples() const { return samples_; } // Modifiers ----------------------------------------------------------------------------------------------------------- void upload(const void* pixels, size_t size, index_t offset = 0, index_t level = 0, enum_t layout = r_i, enum_t type = uint8) requires(contains_enum::value) { glTextureSubImage1D(handle_, level, offset, size, layout, type, pixels); glGenerateTextureMipmap(handle_); } void upload(const void* pixels, glm::ivec2 size, glm::ivec2 offset = glm::ivec2(0), index_t level = 0, enum_t layout = r_i, enum_t type = uint8) requires(contains_enum::value) { glTextureSubImage2D(handle_, level, offset.x, offset.y, size.x, size.y, layout, type, pixels); } void upload(const void* pixels, glm::ivec3 size, glm::ivec3 offset = glm::ivec3(0), index_t level = 0, enum_t layout = r_i, enum_t type = uint8) requires(contains_enum::value) { glTextureSubImage3D(handle_, level, offset.x, offset.y, offset.x, size.x, size.y, size.z, layout, type, pixels); } void clear(size_t size = -1, index_t offset = 0, index_t level = 0, const void* data = nullptr, enum_t layout = r_i, enum_t type = uint8) { static_assert(contains_enum::value); size = size < 0 ? size_.x - offset : size; glClearTexSubImage(handle_, level, offset, 0, 0, size, 1, 1, layout, type, data); } void clear(glm::ivec2 size = glm::ivec2(-1), glm::ivec2 offset = glm::ivec2(0), index_t level = 0, const void* data = nullptr, enum_t layout = r_i, enum_t type = uint8) { static_assert(contains_enum::value); size.x = size.x < 0 ? size_.x - offset.x : size.x; size.y = size.y < 0 ? size_.y - offset.y : size.y; glClearTexSubImage(handle_, level, offset.x, offset.y, 0, size.x, size.y, 1, layout, type, data); } void clear(glm::ivec3 size = glm::ivec3(-1), glm::ivec3 offset = glm::ivec3(0), index_t level = 0, const void* data = nullptr, enum_t layout = r_i, enum_t type = uint8) { static_assert(contains_enum::value); size.x = size.x < 0 ? size_.x - offset.x : size.x; size.y = size.y < 0 ? size_.y - offset.y : size.y; size.z = size.z < 0 ? size_.z - offset.z : size.z; glClearTexSubImage(handle_, level, offset.x, offset.y, offset.z, size.x, size.y, size.z, layout, type, data); } template void copy(const texture& src, size_t size = -1, offset_t src_offset = 0, index_t src_level = 0, offset_t dst_offset = 0, index_t dst_level = 0) { using otexture = texture; static_assert(contains_enum::value && type == otexture::type); size = size < 0 ? src.size_.x - src_offset : size; glCopyImageSubData( src.handle_, otexture::type, src_level, src_offset, 0, 0, handle_, type, dst_level, dst_offset, 0, 0, size, 0, 0 ); } template void copy(const texture& src, glm::ivec2 size = glm::ivec2(-1), glm::ivec2 src_offset = glm::ivec2(0), index_t src_level = 0, glm::ivec2 dst_offset = glm::ivec2(0), index_t dst_level = 0) { using otexture = texture; static_assert(contains_enum::value && type == otexture::type); size.x = size.x < 0 ? src.size_.x - src_offset.x : size.x; size.y = size.y < 0 ? src.size_.y - src_offset.y : size.y; glCopyImageSubData( src.handle_, otexture::type, src_level, src_offset.x, src_offset.y, 0, handle_, type, dst_level, dst_offset.x, dst_offset.y, 0, size.x, size.y, 0 ); } template void copy(const texture& src, glm::ivec3 size = glm::ivec3(-1), glm::ivec3 src_offset = glm::ivec3(0), index_t src_level = 0, glm::ivec3 dst_offset = glm::ivec3(0), index_t dst_level = 0) { using otexture = texture; static_assert(contains_enum::value && type == otexture::type); size.x = size.x < 0 ? src.size_.x - src_offset.x : size.x; size.y = size.y < 0 ? src.size_.y - src_offset.y : size.y; size.z = size.z < 0 ? src.size_.z - src_offset.z : size.z; glCopyImageSubData( src.handle_, otexture::type, src_level, src_offset.x, src_offset.y, src_offset.z, handle_, type, dst_level, dst_offset.x, dst_offset.y, dst_offset.z, size.x, size.y, size.z ); } // Binding ------------------------------------------------------------------------------------------------------------- void bind(location_t loc) { glActiveTexture(GL_TEXTURE0 + loc); glBindTexture(type, handle_); } void bind_image(location_t loc, enum_t access) { glBindImageTexture(loc, handle_, 0, traits.layered, 0, access, format); } void bind_image(location_t loc, index_t level, bool layered, index_t layer, enum_t access, enum_t format) { glBindImageTexture(loc, handle_, level, layered, layer, access, format); } // Depth / Stencil ----------------------------------------------------------------------------------------------------- void set_ds_mode(enum_t mode) { glTextureParameteri(handle_, GL_DEPTH_STENCIL_TEXTURE_MODE, depth_stencil_mode_ = mode); } enum_t get_ds_mode() const { return depth_stencil_mode_; } void set_compare_mode(enum_t mode) { glTextureParameteri(handle_, GL_TEXTURE_COMPARE_MODE, compare_mode_ = mode); } enum_t get_compare_mode() const { return compare_mode_; } void set_compare_func(enum_t func) { glTextureParameteri(handle_, GL_TEXTURE_COMPARE_FUNC, compare_func_ = func); } enum_t get_compare_func() const { return compare_func_; } void set_comparison(enum_t mode, enum_t func) { set_compare_mode(mode); set_compare_func(func); } // Mip Mapping --------------------------------------------------------------------------------------------------------- // TODO: Fix bug from optimization in resize, might be better to make a "rebuild" function void set_num_mip_levels(size_t value) requires(contains_enum::value) { mip_levels_ = value; resize(size_.x); } void set_num_mip_levels(size_t value) requires(contains_enum::value) { mip_levels_ = value; resize(size_.x, size_.y); } void set_num_mip_levels(size_t value) requires(contains_enum::value) { mip_levels_ = value; resize(size_.x, size_.y, size_.z); } size_t get_num_mip_levels() const { return mip_levels_; } void set_base_mip_level(size_t value) { glTextureParameteri(handle_, GL_TEXTURE_BASE_LEVEL, base_mip_level_ = value); } size_t get_base_mip_level() const { return base_mip_level_; } void set_max_mip_level(size_t value) { glTextureParameteri(handle_, GL_TEXTURE_MAX_LEVEL, max_mip_level_ = value); } size_t get_max_mip_level() const { return max_mip_level_; } void set_lod_min(float value) { glTextureParameterf(handle_, GL_TEXTURE_MIN_LOD, lod_min_ = value); } float get_lod_min() const { return lod_min_; } void set_lod_max(float value) { glTextureParameterf(handle_, GL_TEXTURE_MAX_LOD, lod_max_ = value); } float get_lod_max() const { return lod_max_; } void set_lod_range(float min, float max) { set_lod_min(min); set_lod_max(max); } void set_lod_bias(float value) { glTextureParameterf(handle_, GL_TEXTURE_LOD_BIAS, lod_bias_ = value); } float get_lod_bias() const { return lod_bias_; } void generate_mipmaps() { glGenerateTextureMipmap(handle_); } // Filtering ----------------------------------------------------------------------------------------------------------- void set_min_filter(enum_t value) { glTextureParameteri(handle_, GL_TEXTURE_MIN_FILTER, min_filter_ = value); } enum_t get_min_filter() const { return min_filter_; } void set_mag_filter(enum_t value) { glTextureParameteri(handle_, GL_TEXTURE_MAG_FILTER, mag_filter_ = value); } enum_t get_mag_filter() const { return mag_filter_; } // Swizzling ----------------------------------------------------------------------------------------------------------- void set_swizzle_r(enum_t swizzle) { glTextureParameteri(handle_, GL_TEXTURE_SWIZZLE_R, swizzle_.r = swizzle); } enum_t get_swizzle_r() const { return swizzle_.r; } void set_swizzle_g(enum_t swizzle) { glTextureParameteri(handle_, GL_TEXTURE_SWIZZLE_G, swizzle_.g = swizzle); } enum_t get_swizzle_g() const { return swizzle_.g; } void set_swizzle_b(enum_t swizzle) { glTextureParameteri(handle_, GL_TEXTURE_SWIZZLE_B, swizzle_.b = swizzle); } enum_t get_swizzle_b() const { return swizzle_.b; } void set_swizzle_a(enum_t swizzle) { glTextureParameteri(handle_, GL_TEXTURE_SWIZZLE_A, swizzle_.a = swizzle); } enum_t get_swizzle_a() const { return swizzle_.a; } void set_swizzle(const swizzle_t& swizzle) { glTextureParameterIuiv(handle_, GL_TEXTURE_SWIZZLE_RGBA, &(swizzle_ = swizzle).x); } const swizzle_t& get_swizzle() const { return swizzle_; } // Wrapping ------------------------------------------------------------------------------------------------------------ void set_wrapping_s(enum_t value) { glTextureParameteri(handle_, GL_TEXTURE_WRAP_S, wrapping_.x = value); } enum_t get_wrapping_s() const { return wrapping_.x; } void set_wrapping_t(enum_t value) { glTextureParameteri(handle_, GL_TEXTURE_WRAP_T, wrapping_.y = value); } enum_t get_wrapping_t() const { return wrapping_.y; } void set_wrapping_r(enum_t value) { glTextureParameteri(handle_, GL_TEXTURE_WRAP_R, wrapping_.z = value); } enum_t get_wrapping_r() const { return wrapping_.z; } void set_wrapping(const wrapping_t& value) { glTextureParameteri(handle_, GL_TEXTURE_WRAP_S, wrapping_.x = value.x); glTextureParameteri(handle_, GL_TEXTURE_WRAP_T, wrapping_.y = value.y); glTextureParameteri(handle_, GL_TEXTURE_WRAP_R, wrapping_.z = value.z); } const wrapping_t& get_wrapping() const { return wrapping_; } void set_border_color(const glm::vec4& value) { glTextureParameterfv(handle_, GL_TEXTURE_BORDER_COLOR, &(border_color_ = value).x); } const glm::vec4& get_border_color() const { return border_color_; } // Variables =========================================================================================================== private: handle_t handle_; glm::ivec3 size_; size_t samples_, mip_levels_; enum_t depth_stencil_mode_; size_t base_mip_level_, max_mip_level_; glm::vec4 border_color_; enum_t compare_mode_, compare_func_; float lod_min_, lod_max_, lod_bias_; enum_t min_filter_, mag_filter_; swizzle_t swizzle_; wrapping_t wrapping_; }; template void texture::get_current_params_() { glGetTextureParameterIuiv(handle_, GL_DEPTH_STENCIL_TEXTURE_MODE, &depth_stencil_mode_); glGetTextureParameteriv(handle_, GL_TEXTURE_BASE_LEVEL, &base_mip_level_); glGetTextureParameteriv(handle_, GL_TEXTURE_MAX_LEVEL, &max_mip_level_); glGetTextureParameterfv(handle_, GL_TEXTURE_BORDER_COLOR, &border_color_.x); glGetTextureParameterIuiv(handle_, GL_TEXTURE_COMPARE_MODE, &compare_mode_); glGetTextureParameterIuiv(handle_, GL_TEXTURE_COMPARE_FUNC, &compare_func_); glGetTextureParameterfv(handle_, GL_TEXTURE_MIN_LOD, &lod_min_); glGetTextureParameterfv(handle_, GL_TEXTURE_MAX_LOD, &lod_max_); glGetTextureParameterfv(handle_, GL_TEXTURE_LOD_BIAS, &lod_bias_); glGetTextureParameterIuiv(handle_, GL_TEXTURE_MIN_FILTER, &min_filter_); glGetTextureParameterIuiv(handle_, GL_TEXTURE_MAG_FILTER, &mag_filter_); glGetTextureParameterIuiv(handle_, GL_TEXTURE_SWIZZLE_RGBA, &swizzle_.x); glGetTextureParameterIuiv(handle_, GL_TEXTURE_WRAP_S, &wrapping_.x); glGetTextureParameterIuiv(handle_, GL_TEXTURE_WRAP_T, &wrapping_.y); glGetTextureParameterIuiv(handle_, GL_TEXTURE_WRAP_R, &wrapping_.z); } template texture::texture(size_t size) : handle_(NULL) , size_(size, size, size) , samples_(1), mip_levels_(0) , depth_stencil_mode_() , base_mip_level_(), max_mip_level_() , border_color_() , compare_mode_(), compare_func_() , lod_min_(), lod_max_(), lod_bias_() , min_filter_(), mag_filter_() , swizzle_(), wrapping_() { static_assert(contains_enum::value); mip_levels_ = static_cast(glm::log2(static_cast(size))); mip_levels_ = std::max(mip_levels_ - 6, 1); glCreateTextures(type, 1, &handle_); glTextureStorage1D(handle_, mip_levels_, format, size); get_current_params_(); } template texture::texture(glm::ivec2 size) : handle_(NULL) , size_(size, 1) , samples_(1), mip_levels_(0) , depth_stencil_mode_() , base_mip_level_(), max_mip_level_() , border_color_() , compare_mode_(), compare_func_() , lod_min_(), lod_max_(), lod_bias_() , min_filter_(), mag_filter_() , swizzle_(), wrapping_() { static_assert(contains_enum::value); mip_levels_ = static_cast(glm::log2(static_cast(min(size.x, size.y)))); mip_levels_ = std::max(mip_levels_ - 6, 1); glCreateTextures(type, 1, &handle_); glTextureStorage2D(handle_, mip_levels_, format, size.x, size.y); get_current_params_(); } template texture::texture(glm::ivec2 size, size_t samples) : handle_(NULL) , size_(size, 1) , samples_(samples), mip_levels_(0) , depth_stencil_mode_() , base_mip_level_(), max_mip_level_() , border_color_() , compare_mode_(), compare_func_() , lod_min_(), lod_max_(), lod_bias_() , min_filter_(), mag_filter_() , swizzle_(), wrapping_() { static_assert(contains_enum::value); glCreateTextures(type, 1, &handle_); glTextureStorage2DMultisample(handle_, samples, format, size.x, size.y, true); get_current_params_(); } template texture::texture(glm::ivec3 size) : handle_(NULL) , size_(size) , samples_(1), mip_levels_(0) , depth_stencil_mode_() , base_mip_level_(), max_mip_level_() , border_color_() , compare_mode_(), compare_func_() , lod_min_(), lod_max_(), lod_bias_() , min_filter_(), mag_filter_() , swizzle_(), wrapping_() { static_assert(contains_enum::value); mip_levels_ = static_cast(glm::log2(static_cast(min(size.x, size.y, size.z)))); mip_levels_ = std::max(mip_levels_ - 6, 1); glCreateTextures(type, 1, &handle_); glTextureStorage3D(handle_, mip_levels_, format, size.x, size.y, size.z); get_current_params_(); } template texture::texture(glm::ivec3 size, size_t samples) : handle_(NULL) , size_(size) , samples_(samples), mip_levels_(0) , depth_stencil_mode_() , base_mip_level_(), max_mip_level_() , border_color_() , compare_mode_(), compare_func_() , lod_min_(), lod_max_(), lod_bias_() , min_filter_(), mag_filter_() , swizzle_(), wrapping_() { static_assert(contains_enum::value); glCreateTextures(type, 1, &handle_); glTextureStorage3DMultisample(handle_, samples, format, size.x, size.y, size.z, true); get_current_params_(); } template texture::texture(texture&& move) : handle_(move.handle_) , size_(move.size_) , samples_(move.samples_) , mip_levels_(move.mip_levels_) , depth_stencil_mode_(move.depth_stencil_mode_) , base_mip_level_(move.base_mip_level_) , max_mip_level_(move.max_mip_level_) , border_color_(move.border_color_) , compare_mode_(move.compare_mode_) , compare_func_(move.compare_func_) , lod_min_(move.lod_min_) , lod_max_(move.lod_max_) , lod_bias_(move.lod_bias_) , min_filter_(move.min_filter_) , mag_filter_(move.mag_filter_) , swizzle_(move.swizzle_) , wrapping_(move.wrapping_) { move.handle_ = 0; } template texture::~texture() { if(handle_) glDeleteTextures(1, &handle_); } template void texture::resize(size_t size) { static_assert(contains_enum::value); if(size == size_.x) return; glDeleteTextures(1, &handle_); glCreateTextures(type, 1, &handle_); glTextureStorage1D(handle_, mip_levels_, format, size); size_ = glm::ivec3(size, 1, 1); } template void texture::resize(glm::ivec2 size) { static_assert(contains_enum::value); if(size.x == size_.x && size.y == size_.y) return; glDeleteTextures(1, &handle_); glCreateTextures(type, 1, &handle_); glTextureStorage2D(handle_, mip_levels_, format, size.x, size.y); size_ = glm::ivec3(size, 1); } template void texture::resize(glm::ivec2 size, size_t samples) { static_assert(contains_enum::value); if(size.x == size_.x && size.y == size_.y && samples == samples_) return; glDeleteTextures(1, &handle_); glCreateTextures(type, 1, &handle_); glTextureStorage2DMultisample(handle_, samples_ = samples, format, size.x, size.y, true); size_ = glm::ivec3(size, 1); } template void texture::resize(glm::ivec3 size) { static_assert(contains_enum::value); if(size.x == size_.x && size.y == size_.y && size.z == size_.z) return; glDeleteTextures(1, &handle_); glCreateTextures(type, 1, &handle_); glTextureStorage3D(handle_, mip_levels_, format, size.x, size.y, size.z); size_ = glm::ivec3(size); } template void texture::resize(glm::ivec3 size, size_t samples) { static_assert(contains_enum::value); if(size.x == size_.x && size.y == size_.y && size.z == size_.z && samples == samples_) return; glDeleteTextures(1, &handle_); glCreateTextures(type, 1, &handle_); glTextureStorage3DMultisample(handle_, samples_ = samples, format, size.x, size.y, size.z, true); size_ = glm::ivec3(size); } } #endif // GLW_TEXTURE_H