521 lines
13 KiB
C++
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;
|
|
}
|
|
|
|
}
|