psemek/libs/gfx/include/psemek/gfx/texture.hpp

376 lines
9.6 KiB
C++

#pragma once
#include <psemek/gfx/gl.hpp>
#include <psemek/gfx/pixel.hpp>
#include <psemek/gfx/pixmap.hpp>
#include <psemek/geom/vector.hpp>
#include <optional>
namespace psemek::gfx
{
template <std::size_t D, GLenum Target>
struct basic_texture
{
static_assert(D >= 1 && D <= 3);
basic_texture();
basic_texture(basic_texture &&);
basic_texture & operator = (basic_texture &&);
~basic_texture();
basic_texture(basic_texture const &) = delete;
basic_texture & operator = (basic_texture const &) = delete;
static basic_texture null();
GLuint id() const { return id_; }
explicit operator bool() const { return id() != 0; }
void reset();
void bind() const;
geom::vector<std::size_t, D> size() const { return size_; }
std::size_t width() const;
std::size_t height() const;
std::size_t depth() const;
void load(GLint internal_format, geom::vector<std::size_t, D> const & size, GLenum format, GLenum type, const void * data);
template <typename Pixel>
void load(geom::vector<std::size_t, D> const & size, Pixel const * data = nullptr);
template <typename Pixel>
void load(util::array<Pixel, D> const & p);
void pixels(GLenum format, GLenum type, void * data) const;
template <typename Pixmap>
Pixmap pixels() const;
static basic_texture from_data(GLint internal_format, geom::vector<std::size_t, D> const & size, GLenum format, GLenum type, const void * data);
template <typename Pixmap>
static basic_texture from_pixmap(Pixmap const & p);
void generate_mipmap();
void nearest_filter();
void linear_filter();
void linear_mipmap_filter();
void anisotropy();
void repeat();
void clamp();
protected:
GLuint id_;
geom::vector<std::size_t, D> size_;
explicit basic_texture(std::nullptr_t);
};
using texture_1d = basic_texture<1, gl::TEXTURE_1D>;
using texture_1d_array = basic_texture<2, gl::TEXTURE_1D_ARRAY>;
using texture_2d = basic_texture<2, gl::TEXTURE_2D>;
using texture_2d_array = basic_texture<3, gl::TEXTURE_2D_ARRAY>;
using texture_3d = basic_texture<3, gl::TEXTURE_3D>;
extern template struct basic_texture<1, gl::TEXTURE_1D>;
extern template struct basic_texture<2, gl::TEXTURE_1D_ARRAY>;
extern template struct basic_texture<2, gl::TEXTURE_2D>;
extern template struct basic_texture<3, gl::TEXTURE_2D_ARRAY>;
extern template struct basic_texture<3, gl::TEXTURE_3D>;
extern template struct basic_texture<2, gl::TEXTURE_CUBE_MAP>;
struct texture_cubemap
: basic_texture<2, gl::TEXTURE_CUBE_MAP>
{
texture_cubemap() = default;
texture_cubemap(texture_cubemap &&) = default;
texture_cubemap & operator = (texture_cubemap &&) = default;
~texture_cubemap() = default;
texture_cubemap(texture_cubemap const &) = delete;
texture_cubemap & operator = (texture_cubemap const &) = delete;
static texture_cubemap null()
{
return basic_texture<2, gl::TEXTURE_CUBE_MAP>::null();
}
static GLenum face_to_gl(int f);
void load(int f, GLint internal_format, geom::vector<std::size_t, 2> const & size, GLenum format, GLenum type, const void * data);
template <typename Pixel>
void load(int f, geom::vector<std::size_t, 2> const & size, Pixel const * data = nullptr)
{
using traits = pixel_traits<Pixel>;
load(f, traits::internal_format, size, traits::format, traits::type, data);
}
template <typename Pixel>
void load(int f, util::array<Pixel, 2> const & p)
{
geom::vector<std::size_t, 2> size;
for (std::size_t i = 0; i < 2; ++i) size[i] = p.dim(i);
load(f, size, p.data());
}
void pixels(int f, GLenum format, GLenum type, void * data) const;
template <typename Pixmap>
Pixmap pixels(int f) const
{
using traits = pixel_traits<typename Pixmap::value_type>;
std::array<std::size_t, 2> size;
for (std::size_t i = 0; i < 2; ++i) size[i] = size_[i];
Pixmap p(size);
pixels(f, traits::format, traits::type, p.data());
return p;
}
private:
texture_cubemap(basic_texture<2, gl::TEXTURE_CUBE_MAP> t)
: basic_texture<2, gl::TEXTURE_CUBE_MAP>(std::move(t))
{}
};
namespace detail
{
std::optional<float> max_anisotropy();
}
template <std::size_t D, GLenum Target>
basic_texture<D, Target>::basic_texture()
{
gl::GenTextures(1, &id_);
}
template <std::size_t D, GLenum Target>
basic_texture<D, Target>::basic_texture(basic_texture && other)
: id_{other.id_}
{
other.id_ = 0;
}
template <std::size_t D, GLenum Target>
basic_texture<D, Target> & basic_texture<D, Target>::operator = (basic_texture && other)
{
if (this == &other) return *this;
reset();
std::swap(id_, other.id_);
return *this;
}
template <std::size_t D, GLenum Target>
basic_texture<D, Target>::~basic_texture()
{
reset();
}
template <std::size_t D, GLenum Target>
basic_texture<D, Target> basic_texture<D, Target>::null()
{
return basic_texture(nullptr);
}
template <std::size_t D, GLenum Target>
void basic_texture<D, Target>::reset()
{
if (id_ != 0)
gl::DeleteTextures(1, &id_);
id_ = 0;
}
template <std::size_t D, GLenum Target>
void basic_texture<D, Target>::bind() const
{
gl::BindTexture(Target, id());
}
template <std::size_t D, GLenum Target>
std::size_t basic_texture<D, Target>::width() const
{
return size_[0];
}
template <std::size_t D, GLenum Target>
std::size_t basic_texture<D, Target>::height() const
{
if constexpr (D >= 2)
{
return size_[1];
}
else
{
return 1;
}
}
template <std::size_t D, GLenum Target>
std::size_t basic_texture<D, Target>::depth() const
{
if constexpr (D >= 3)
{
return size_[2];
}
else
{
return 1;
}
}
template <std::size_t D, GLenum Target>
void basic_texture<D, Target>::load(GLint internal_format, geom::vector<std::size_t, D> const & size, GLenum format, GLenum type, const void * data)
{
bind();
if constexpr (D == 1)
{
gl::TexImage1D(Target, 0, internal_format, size[0], 0, format, type, data);
}
else if (D == 2)
{
gl::TexImage2D(Target, 0, internal_format, size[0], size[1], 0, format, type, data);
}
else if (D == 3)
{
gl::TexImage3D(Target, 0, internal_format, size[0], size[1], size[2], 0, format, type, data);
}
size_ = size;
}
template <std::size_t D, GLenum Target>
template <typename Pixel>
void basic_texture<D, Target>::load(geom::vector<std::size_t, D> const & size, Pixel const * data)
{
using traits = pixel_traits<Pixel>;
load(traits::internal_format, size, traits::format, traits::type, data);
}
template <std::size_t D, GLenum Target>
template <typename Pixel>
void basic_texture<D, Target>::load(util::array<Pixel, D> const & p)
{
geom::vector<std::size_t, D> size;
for (std::size_t i = 0; i < D; ++i) size[i] = p.dim(i);
load(size, p.data());
}
template <std::size_t D, GLenum Target>
void basic_texture<D, Target>::pixels(GLenum format, GLenum type, void * data) const
{
bind();
gl::GetTexImage(Target, 0, format, type, data);
}
template <std::size_t D, GLenum Target>
template <typename Pixmap>
Pixmap basic_texture<D, Target>::pixels() const
{
using traits = pixel_traits<typename Pixmap::value_type>;
std::array<std::size_t, D> size;
for (std::size_t i = 0; i < D; ++i) size[i] = size_[i];
Pixmap p(size);
pixels(traits::format, traits::type, p.data());
return p;
}
template <std::size_t D, GLenum Target>
basic_texture<D, Target> basic_texture<D, Target>::from_data(GLint internal_format, geom::vector<std::size_t, D> const & size, GLenum format, GLenum type, const void * data)
{
basic_texture result;
result.load(internal_format, size, format, type, data);
return result;
}
template <std::size_t D, GLenum Target>
template <typename Pixmap>
basic_texture<D, Target> basic_texture<D, Target>::from_pixmap(Pixmap const & p)
{
basic_texture result;
result.load(p);
return result;
}
template <std::size_t D, GLenum Target>
void basic_texture<D, Target>::generate_mipmap()
{
bind();
gl::GenerateMipmap(Target);
}
template <std::size_t D, GLenum Target>
void basic_texture<D, Target>::nearest_filter()
{
bind();
gl::TexParameteri(Target, gl::TEXTURE_MIN_FILTER, gl::NEAREST);
gl::TexParameteri(Target, gl::TEXTURE_MAG_FILTER, gl::NEAREST);
}
template <std::size_t D, GLenum Target>
void basic_texture<D, Target>::linear_filter()
{
bind();
gl::TexParameteri(Target, gl::TEXTURE_MIN_FILTER, gl::LINEAR);
gl::TexParameteri(Target, gl::TEXTURE_MAG_FILTER, gl::LINEAR);
}
template <std::size_t D, GLenum Target>
void basic_texture<D, Target>::linear_mipmap_filter()
{
bind();
gl::TexParameteri(Target, gl::TEXTURE_MIN_FILTER, gl::LINEAR_MIPMAP_LINEAR);
gl::TexParameteri(Target, gl::TEXTURE_MAG_FILTER, gl::LINEAR);
}
template <std::size_t D, GLenum Target>
void basic_texture<D, Target>::anisotropy()
{
auto level = detail::max_anisotropy();
if (level)
{
bind();
gl::TexParameterf(Target, gl::TEXTURE_MAX_ANISOTROPY, *level);
}
}
template <std::size_t D, GLenum Target>
void basic_texture<D, Target>::repeat()
{
bind();
if constexpr (D >= 1) gl::TexParameteri(Target, gl::TEXTURE_WRAP_S, gl::REPEAT);
if constexpr (D >= 2) gl::TexParameteri(Target, gl::TEXTURE_WRAP_T, gl::REPEAT);
if constexpr (D >= 3) gl::TexParameteri(Target, gl::TEXTURE_WRAP_R, gl::REPEAT);
}
template <std::size_t D, GLenum Target>
void basic_texture<D, Target>::clamp()
{
bind();
if constexpr (D >= 1) gl::TexParameteri(Target, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE);
if constexpr (D >= 2) gl::TexParameteri(Target, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE);
if constexpr (D >= 3) gl::TexParameteri(Target, gl::TEXTURE_WRAP_R, gl::CLAMP_TO_EDGE);
}
template <std::size_t D, GLenum Target>
basic_texture<D, Target>::basic_texture(std::nullptr_t)
: id_{0}
{}
}