// =====================================================================================================================
// 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