376 lines
9.6 KiB
C++
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}
|
|
{}
|
|
|
|
}
|