psemek/libs/gfx/include/psemek/gfx/attribs.hpp

521 lines
13 KiB
C++

#pragma once
#include <psemek/gfx/gl.hpp>
#include <psemek/math/vector.hpp>
#include <psemek/math/point.hpp>
#include <psemek/math/matrix.hpp>
#include <psemek/math/quaternion.hpp>
#include <vector>
#include <optional>
#include <array>
#include <cstdint>
namespace psemek::gfx
{
struct attrib_description
{
GLuint index;
GLint size;
GLenum type;
bool integer = false;
GLboolean normalized;
void const * pointer;
GLuint divisor;
};
struct attribs_description
{
std::vector<attrib_description> attribs;
GLuint index_count = 0;
GLuint vertex_size = 0;
GLuint instance_size = 0;
attribs_description & operator += (attribs_description const & d);
};
inline attribs_description operator + (attribs_description d1, attribs_description const & d2)
{
for (auto a : d2.attribs)
{
a.index += d1.index_count;
a.pointer = reinterpret_cast<char const *>(a.pointer) + (a.divisor == 0 ? d1.vertex_size : d1.instance_size);
d1.attribs.push_back(a);
}
d1.index_count += d2.index_count;
d1.vertex_size += d2.vertex_size;
d1.instance_size += d2.instance_size;
return d1;
}
inline attribs_description & attribs_description::operator += (attribs_description const & d)
{
(*this) = (*this) + d;
return *this;
}
template <typename T>
struct normalized
{
using type = T;
};
template <typename T>
struct integer
{
using type = T;
};
// skips a single attribute index
struct skip
{};
// skips bytes in the vertex
template <std::size_t N>
struct padding
{
static constexpr std::size_t size = N;
};
// instance attribute, for instanced meshes only
template <typename T>
struct instanced
{
using type = T;
};
template <std::size_t N>
struct instanced<padding<N>>
{
using type = padding<N>;
static constexpr std::size_t size = N;
};
template <typename ... Attribs>
attribs_description make_attribs_description();
namespace detail
{
template <typename Attrib>
struct attrib_traits;
template <>
struct attrib_traits<std::uint8_t>
{
using attrib_type = std::uint8_t;
static constexpr GLint size = 1;
static constexpr GLenum type = gl::UNSIGNED_BYTE;
static constexpr bool integer = false;
static constexpr GLboolean normalized = gl::FALSE;
};
template <>
struct attrib_traits<std::int8_t>
{
using attrib_type = std::int8_t;
static constexpr GLint size = 1;
static constexpr GLenum type = gl::BYTE;
static constexpr bool integer = false;
static constexpr GLboolean normalized = gl::FALSE;
};
template <>
struct attrib_traits<std::uint16_t>
{
using attrib_type = std::uint16_t;
static constexpr GLint size = 1;
static constexpr GLenum type = gl::UNSIGNED_SHORT;
static constexpr bool integer = false;
static constexpr GLboolean normalized = gl::FALSE;
};
template <>
struct attrib_traits<std::int16_t>
{
using attrib_type = std::int16_t;
static constexpr GLint size = 1;
static constexpr GLenum type = gl::SHORT;
static constexpr bool integer = false;
static constexpr GLboolean normalized = gl::FALSE;
};
template <>
struct attrib_traits<std::uint32_t>
{
using attrib_type = std::uint32_t;
static constexpr GLint size = 1;
static constexpr GLenum type = gl::UNSIGNED_INT;
static constexpr bool integer = false;
static constexpr GLboolean normalized = gl::FALSE;
};
template <>
struct attrib_traits<std::int32_t>
{
using attrib_type = std::int32_t;
static constexpr GLint size = 1;
static constexpr GLenum type = gl::INT;
static constexpr bool integer = false;
static constexpr GLboolean normalized = gl::FALSE;
};
template <>
struct attrib_traits<float>
{
using attrib_type = float;
static constexpr GLint size = 1;
static constexpr GLenum type = gl::FLOAT;
static constexpr bool integer = false;
static constexpr GLboolean normalized = gl::FALSE;
};
#ifndef PSEMEK_GLES
template <>
struct attrib_traits<double>
{
using attrib_type = double;
static constexpr GLint size = 1;
static constexpr GLenum type = gl::DOUBLE;
static constexpr bool integer = false;
static constexpr GLboolean normalized = gl::FALSE;
};
#endif
template <typename T, std::size_t N>
struct attrib_traits<std::array<T, N>>
{
using attrib_type = std::array<T, N>;
static constexpr GLint size = N;
static constexpr GLenum type = attrib_traits<T>::type;
static constexpr bool integer = attrib_traits<T>::integer;
static constexpr GLboolean normalized = gl::FALSE;
};
template <typename T, std::size_t N>
struct attrib_traits<math::vector<T, N>>
{
using attrib_type = math::vector<T, N>;
static constexpr GLint size = N;
static constexpr GLenum type = attrib_traits<T>::type;
static constexpr bool integer = attrib_traits<T>::integer;
static constexpr GLboolean normalized = gl::FALSE;
};
template <typename T, std::size_t N>
struct attrib_traits<math::point<T, N>>
{
using attrib_type = math::point<T, N>;
static constexpr GLint size = N;
static constexpr GLenum type = attrib_traits<T>::type;
static constexpr bool integer = attrib_traits<T>::integer;
static constexpr GLboolean normalized = gl::FALSE;
};
template <typename T, std::size_t R, std::size_t C>
struct attrib_traits<math::matrix<T, R, C>>
{
using attrib_type = math::matrix<T, R, C>;
};
template <typename T>
struct attrib_traits<math::quaternion<T>>
{
using attrib_type = math::quaternion<T>;
static constexpr GLint size = 4;
static constexpr GLenum type = attrib_traits<T>::type;
static constexpr bool integer = attrib_traits<T>::integer;
static constexpr GLboolean normalized = gl::FALSE;
};
template <typename T>
struct attrib_traits<normalized<T>>
{
using attrib_type = T;
static constexpr GLint size = attrib_traits<T>::size;
static constexpr GLenum type = attrib_traits<T>::type;
static constexpr bool integer = false;
static constexpr GLboolean normalized = gl::TRUE;
};
template <typename T>
struct attrib_traits<integer<T>>
{
using attrib_type = T;
static constexpr GLint size = attrib_traits<T>::size;
static constexpr GLenum type = attrib_traits<T>::type;
static constexpr bool integer = true;
static constexpr GLboolean normalized = gl::FALSE;
};
template <typename T>
struct attrib_traits<instanced<T>>
{
using attrib_type = typename attrib_traits<T>::attrib_type;
static constexpr GLint size = attrib_traits<T>::size;
static constexpr GLenum type = attrib_traits<T>::type;
static constexpr bool integer = attrib_traits<T>::integer;
static constexpr GLboolean normalized = attrib_traits<T>::normalized;
};
template <typename T>
struct is_padding : std::false_type
{};
template <std::size_t N>
struct is_padding<padding<N>> : std::true_type
{};
template <std::size_t N>
struct is_padding<instanced<padding<N>>> : std::true_type
{};
template <typename T>
struct is_instanced : std::false_type
{};
template <typename T>
struct is_instanced<instanced<T>> : std::true_type
{};
template <typename T>
struct remove_instanced
{
using type = T;
};
template <typename T>
struct remove_instanced<instanced<T>>
{
using type = T;
};
template <typename T>
struct is_matrix : std::false_type
{};
template <typename T, std::size_t R, std::size_t C>
struct is_matrix<math::matrix<T, R, C>> : std::true_type
{};
template <typename Attr>
std::size_t attr_size()
{
if constexpr (std::is_same_v<Attr, skip>)
{
return 0;
}
else if constexpr (is_padding<Attr>::value)
{
return Attr::size;
}
else
{
return sizeof(typename attrib_traits<Attr>::attrib_type);
}
}
template <bool Instance, typename ... Attribs>
struct make_attribs_description_impl;
template <bool Instance>
struct make_attribs_description_impl<Instance>
{
static void make(attribs_description &)
{}
static void make_impl(attribs_description & result, std::size_t index, std::size_t offset)
{
result.index_count = index;
if constexpr (Instance)
{
result.instance_size = offset;
}
else
{
result.vertex_size = offset;
}
}
};
template <bool Instance, typename Attr1, typename ... Attribs>
struct make_attribs_description_impl<Instance, Attr1, Attribs...>
{
static void make(attribs_description & result)
{
make_impl(result, 0, 0);
}
static void make_impl(attribs_description & result, std::size_t index, std::size_t offset)
{
if constexpr (std::is_same_v<Attr1, skip>)
{
make_attribs_description_impl<Instance, Attribs...>::make_impl(result, index + 1, offset);
}
else if constexpr (is_padding<Attr1>::value)
{
if constexpr (is_instanced<Attr1>::value == Instance)
make_attribs_description_impl<Instance, Attribs...>::make_impl(result, index, offset + Attr1::size);
else
make_attribs_description_impl<Instance, Attribs...>::make_impl(result, index, offset);
}
else if constexpr (is_instanced<Attr1>::value == Instance)
{
using attr = typename remove_instanced<Attr1>::type;
if constexpr (is_matrix<attr>::value)
{
using T = typename attr::scalar_type;
using traits = attrib_traits<math::vector<T, attr::static_columns>>;
for (std::size_t row = 0; row < attr::static_rows; ++row)
{
attrib_description attr;
attr.index = index + row;
attr.size = traits::size;
attr.type = traits::type;
attr.integer = traits::integer;
attr.normalized = traits::normalized;
attr.pointer = reinterpret_cast<char const *>(offset + row * attr::static_columns * sizeof(T));
attr.divisor = Instance ? 1 : 0;
result.attribs.push_back(attr);
}
make_attribs_description_impl<Instance, Attribs...>::make_impl(result, index + attr::static_rows, offset + attr_size<Attr1>());
}
else
{
using traits = attrib_traits<Attr1>;
attrib_description attr;
attr.index = index;
attr.size = traits::size;
attr.type = traits::type;
attr.integer = traits::integer;
attr.normalized = traits::normalized;
attr.pointer = reinterpret_cast<char const *>(offset);
attr.divisor = Instance ? 1 : 0;
result.attribs.push_back(attr);
make_attribs_description_impl<Instance, Attribs...>::make_impl(result, index + 1, offset + attr_size<Attr1>());
}
}
else
{
using attr = typename remove_instanced<Attr1>::type;
if constexpr (is_matrix<attr>::value)
{
make_attribs_description_impl<Instance, Attribs...>::make_impl(result, index + attr::static_rows, offset);
}
else
{
make_attribs_description_impl<Instance, Attribs...>::make_impl(result, index + 1, offset);
}
}
}
};
}
template <typename ... Attribs>
attribs_description make_attribs_description()
{
attribs_description result;
detail::make_attribs_description_impl<false, Attribs...>::make(result);
detail::make_attribs_description_impl<true, Attribs...>::make(result);
return result;
}
inline void setup(attribs_description const & attribs, bool instance)
{
for (auto const & a : attribs.attribs)
{
if (instance != (a.divisor != 0)) continue;
gl::EnableVertexAttribArray(a.index);
gl::VertexAttribDivisor(a.index, a.divisor);
GLuint stride = (a.divisor == 0) ? attribs.vertex_size : attribs.instance_size;
if (a.integer)
gl::VertexAttribIPointer(a.index, a.size, a.type, stride, a.pointer);
else
gl::VertexAttribPointer(a.index, a.size, a.type, a.normalized, stride, a.pointer);
}
}
inline std::int8_t to_signed_8bit(float x)
{
return static_cast<std::int8_t>(math::clamp((0.5f * x + 0.5f) * 255.f - 128.f, {-128.f, 127.f}));
}
inline std::uint8_t to_unsigned_8bit(float x)
{
return static_cast<std::uint8_t>(math::clamp(x * 255.f, {0.f, 255.f}));
}
inline std::int16_t to_signed_16bit(float x)
{
return static_cast<std::int16_t>(math::clamp((0.5f * x + 0.5f) * 65535.f - 32768.f, {-32768.f, 32767.f}));
}
inline std::uint16_t to_unsigned_16bit(float x)
{
return static_cast<std::uint16_t>(math::clamp(x * 65535.f, {0.f, 65535.f}));
}
template <std::size_t D>
math::vector<std::int8_t, D> to_signed_8bit(math::vector<float, D> const & v)
{
math::vector<std::int8_t, D> result;
for (std::size_t i = 0; i < D; ++i)
result[i] = to_signed_8bit(v[i]);
return result;
}
template <std::size_t D>
math::vector<std::uint8_t, D> to_unsigned_8bit(math::vector<float, D> const & v)
{
math::vector<std::uint8_t, D> result;
for (std::size_t i = 0; i < D; ++i)
result[i] = to_unsigned_8bit(v[i]);
return result;
}
template <std::size_t D>
math::vector<std::int16_t, D> to_signed_16bit(math::vector<float, D> const & v)
{
math::vector<std::int16_t, D> result;
for (std::size_t i = 0; i < D; ++i)
result[i] = to_signed_16bit(v[i]);
return result;
}
template <std::size_t D>
math::vector<std::uint16_t, D> to_unsigned_16bit(math::vector<float, D> const & v)
{
math::vector<std::uint16_t, D> result;
for (std::size_t i = 0; i < D; ++i)
result[i] = to_unsigned_16bit(v[i]);
return result;
}
}