UPDATE: Be careful when using this code for model storage or attribute binding. Models must be stroed with proper alignment, but my container have none.
Ok, here is my implementation.
It's inline&template-only, so you should put it in a header.
Keep in mind that it expects that locations of vertex attribs are specified in a shader with layout(location = ..)
and start with 0 (then 1,2,3...).
Features:
Attribute setup:
SetVertexAttribs<AttribLayout<Vec<float, 4>, Vec<int, 2>, vec<double, 3>>>();
// It enables attrib arrays (here: 0, 1 and 2), and then sets attrib pointers for them.
// It also selects correct glVertexAttrib{|I|L}Pointer function depending on type of each parametr.
Class Vec:
Vec<float, 4> a {1, 2, 3, 4}, b;
// First parametr can be any type. Second parametr is
// number of objects of that type in the vector.
// First parametr can be any default scalar type.
// Second parametr can only be 1, 2, 3 or 4.
a.x = 2; // you can use .x or .r - they have same meaning
a.y = 3; // you can use .y or .g - they have same meaning
a.z = 4; // you can use .z or .b - they have same meaning
a.w = 4; // you can use .w or .a - they have same meaning
a = a + a - a + -a; // Overloaded operators
a = {1, 2, 3, 4}; b = a; // Overloaded assignment
a += {0, 1, 2, 3}; b += a;
a -= {0, 1, 0, 1}; b -= a;
Vec<float, 4>::type var; // here means `float var;`
// Vec<...>::type means type of a single element
std::cout << a.dim; // here prints 4 -- number of dimensions of a vector
Class AttribLayout:
Arrays of this type are useful for storing models.
AttribLayout<Vec<float, 4>, Vec<int, 2>, Vec<double, 3>>
obj1, obj2({0,0,0,0},{1,2},{10,20,30}); // Constructors
// This class uses trick with char array - don't worry about padding, it does not have it.
std::cout << obj1.elements; // Here prints 3 -- number of vectors in layout
std::cout << obj1.byte_len; // Same as `sizeof obj1`
Vec<int, 2> var = obj1.get<1>(); // Returns vector with a specific number in a layout
double dvar = obj2.get<2,1>; // Here dvar == 20. First parametr means number of vector in a layout, second parametr means number of element in a vector.
obj1.set<1>({1,2}); // Parametrs mean same things as in `get()`.
obj1.set<2,0>(0.0); // I think you understood what `set()` does.
AttribLayout<Vec<float, 4>, Vec<int, 2>, vec<double, 3>>::type_at<1> var;
// Here means `Vec<int, 2> var;`
Useful functions:
type2glconst<int>::value // == GL_INT
// I think you understand what it's for.
type2glattribfunc<float> // same as glVertexAttribPointer
type2glattribfunc<double> // same as glVertexAttribLPointer
type2glattribfunc</*any integer type*/> // same as glVertexAttribIPointer
Code:
template <typename T>
struct type2glconst
{
static_assert(std::is_same<T, void>::value, "Invalid type!");
static constexpr GLenum value = 0;
};
template <> struct type2glconst<unsigned char> {static constexpr GLenum value = GL_UNSIGNED_BYTE;};
template <> struct type2glconst<signed char> {static constexpr GLenum value = GL_BYTE;};
template <> struct type2glconst<char> {static constexpr GLenum value = Utils::is_char_signed ? GL_BYTE : GL_UNSIGNED_BYTE;};
template <> struct type2glconst<unsigned short> {static constexpr GLenum value = GL_UNSIGNED_SHORT;};
template <> struct type2glconst<signed short> {static constexpr GLenum value = GL_SHORT;};
template <> struct type2glconst<unsigned int> {static constexpr GLenum value = GL_UNSIGNED_INT;};
template <> struct type2glconst<signed int> {static constexpr GLenum value = GL_INT;};
template <> struct type2glconst<float> {static constexpr GLenum value = GL_FLOAT;};
template <> struct type2glconst<double> {static constexpr GLenum value = GL_DOUBLE;};
template <typename T>
inline void type2glattribfunc(GLuint index, GLint size, GLsizei stride, const GLvoid *pointer)
{
static_assert(type2glconst<T>::value, "Invalid type!");
glVertexAttribIPointer(index, size, type2glconst<T>::value, stride, pointer);
}
template <>
inline void type2glattribfunc<float>(GLuint index, GLint size, GLsizei stride, const GLvoid *pointer)
{
glVertexAttribPointer(index, size, type2glconst<float>::value, GL_FALSE, stride, pointer);
}
template <>
inline void type2glattribfunc<double>(GLuint index, GLint size, GLsizei stride, const GLvoid *pointer)
{
glVertexAttribLPointer(index, size, type2glconst<double>::value, stride, pointer);
}
template <typename T, unsigned int D>
struct Vec
{
static_assert((std::is_void<T>::value || type2glconst<T>::value) && D <= 4, "Invalid dimension for vector!");
static constexpr int dim = 0;
using type = void;
};
template <typename T>
struct Vec<T, 1>
{
using type = T;
static constexpr int dim = 1;
union {T x, r;};
Vec<T, 1> operator+(const Vec<T, 1> &o) const {return {x + o.x};}
Vec<T, 1> &operator+=(const Vec<T, 1> &o) {x += o.x; return *this;}
Vec<T, 1> operator-() const {return {-x};}
Vec<T, 1> operator-(const Vec<T, 1> &o) const {return {x - o.x};}
Vec<T, 1> &operator-=(const Vec<T, 1> &o) {x -= o.x; return *this;}
};
template <typename T>
struct Vec<T, 2>
{
using type = T;
static constexpr int dim = 2;
union {T x, r;};
union {T y, g;};
Vec<T, 2> operator+(const Vec<T, 2> &o) const {return {x + o.x, y + o.y};}
Vec<T, 2> &operator+=(const Vec<T, 2> &o) {x += o.x; y += o.y; return *this;}
Vec<T, 2> operator-() const {return {-x, -y};}
Vec<T, 2> operator-(const Vec<T, 2> &o) const {return {x - o.x, y - o.y};}
Vec<T, 2> &operator-=(const Vec<T, 2> &o) {x -= o.x; y -= o.y; return *this;}
};
template <typename T>
struct Vec<T, 3>
{
using type = T;
static constexpr int dim = 3;
union {T x, r;};
union {T y, g;};
union {T z, b;};
Vec<T, 3> operator+(const Vec<T, 3> &o) const {return {x + o.x, y + o.y, z + o.z};}
Vec<T, 3> &operator+=(const Vec<T, 3> &o) {x += o.x; y += o.y; z += o.z; return *this;}
Vec<T, 3> operator-() const {return {-x, -y, -z};}
Vec<T, 3> operator-(const Vec<T, 3> &o) const {return {x - o.x, y - o.y, z - o.z};}
Vec<T, 3> &operator-=(const Vec<T, 3> &o) {x -= o.x; y -= o.y; z -= o.z; return *this;}
Vec<T, 3> operator*(const Vec<T, 3> &o) const {return {y * o.z - z * o.y, z * o.x - x * o.z, x * o.y - y * o.x};}
Vec<T, 3> &operator*=(const Vec<T, 3> &o) {*this = *this * o; return *this;}
};
template <typename T>
struct Vec<T, 4>
{
using type = T;
static constexpr int dim = 4;
union {T x, r;};
union {T y, g;};
union {T z, b;};
union {T w, a;};
Vec<T, 4> operator+(const Vec<T, 4> &o) const {return {x + o.x, y + o.y, z + o.z, w + o.w};}
Vec<T, 4> &operator+=(const Vec<T, 4> &o) {x += o.x; y += o.y; z += o.z; w += o.w; return *this;}
Vec<T, 4> operator-() const {return {-x, -y, -z, -w};}
Vec<T, 4> operator-(const Vec<T, 4> &o) const {return {x - o.x, y - o.y, z - o.z, w - o.w};}
Vec<T, 4> &operator-=(const Vec<T, 4> &o) {x -= o.x; y -= o.y; z -= o.z; w -= o.w; return *this;}
};
template <typename T, typename ...P>
struct AttribLayout_InternalValue
{
static_assert(!std::is_same<T, void>::value, "Void vector is used in layout!");
using curtype = T;
};
template <typename T, typename ...P>
struct AttribLayout_InternalContainer
{
static_assert(!std::is_same<T, void>::value, "Void vector is used in layout!");
using curtype = T;
using nexttype = typename std::conditional<(sizeof...(P) > 1), AttribLayout_InternalContainer<P...>, AttribLayout_InternalValue<P...>>::type;
};
template <typename ...P>
class AttribLayout
{
protected:
static_assert(sizeof...(P) > 0, "Zero-length attrib layout!");
using cont_type = typename std::conditional<(sizeof...(P) > 1), AttribLayout_InternalContainer<P...>, AttribLayout_InternalValue<P...>>::type;
public:
static constexpr int elements = sizeof...(P);
protected:
template <unsigned int N, typename T> struct type_at_internal
{
using type = typename type_at_internal<(N - 1), typename T::nexttype>::type;
};
template <typename T> struct type_at_internal<0, T>
{
using type = T;
};
template <unsigned int N> using cont_type_at = typename type_at_internal<N, cont_type>::type;
public:
template <unsigned int N> using type_at = typename type_at_internal<N, cont_type>::type::curtype;
template <unsigned int N, typename T> struct bytes_internal
{
static constexpr unsigned int var = bytes_internal<(N - 1), T>::var + (type_at<(N - 1)>::dim * sizeof(typename type_at<(N - 1)>::type));
};
template <typename T> struct bytes_internal<0, T>
{
static constexpr unsigned int var = 0;
};
static constexpr unsigned int byte_len = bytes_internal<(sizeof...(P)), void>::var;
unsigned char bytearr[byte_len];
template <unsigned int N> type_at<N> get()
{
static_assert(N >= 0 && N < sizeof...(P), "Element is out of range!");
type_at<N> ret;
std::memcpy(&ret, bytearr + bytes_internal<N, void>::var, sizeof (type_at<N>));
return ret;
}
template <unsigned int N> void set(const type_at<N> &ref)
{
static_assert(N >= 0 && N < sizeof...(P), "Element is out of range!");
std::memcpy(bytearr + bytes_internal<N, void>::var, &ref, sizeof (type_at<N>));
}
template <unsigned int N, unsigned int X> typename type_at<N>::type get()
{
static_assert(N >= 0 && N < sizeof...(P), "Element is out of range!");
static_assert(X > 0 && X <= type_at<N>::dim, "Vector element is out of range!");
typename type_at<N>::type ret;
std::memcpy(&ret, bytearr + bytes_internal<N, void>::var + sizeof(typename type_at<N>::type) * (X - 1), sizeof (typename type_at<N>::type));
return ret;
}
template <unsigned int N, unsigned int X> void set(typename type_at<N>::type obj)
{
static_assert(N >= 0 && N < sizeof...(P), "Element is out of range!");
static_assert(X > 0 && X <= type_at<N>::dim, "Vector element is out of range!");
std::memcpy(bytearr + bytes_internal<N, void>::var + sizeof(typename type_at<N>::type) * (X - 1), &obj, sizeof (typename type_at<N>::type));
}
protected:
template <unsigned int N, unsigned int M> struct ctor_internal
{
static void func(void **ptr, unsigned char *arr)
{
std::memcpy(arr + bytes_internal<(M - N), void>::var, *ptr, sizeof (type_at<(M - N)>));
ctor_internal<(N - 1), M>::func(ptr + 1, arr);
}
};
template <unsigned int M> struct ctor_internal<0, M>
{
static void func(void **ptr, unsigned char *arr)
{
std::memcpy(arr + bytes_internal<M, void>::var, *ptr, sizeof (type_at<M>));
}
};
public:
AttribLayout()
{
static_assert(sizeof (decltype(*this)) == byte_len, "Crappy compiler have added padding to AttribLayout!");
static_assert(alignof (decltype(*this)) == 1, "Crappy compiler have added alignment to AttribLayout!");
}
AttribLayout(P ...params)
{
void *ptrs[sizeof...(P)] {(void *)¶ms...};
ctor_internal<(sizeof...(P) - 1), (sizeof...(P) - 1)>::func(ptrs, bytearr);
}
};
namespace InternalStuff
{
template <class T, unsigned N> struct SetVertexAttribs_internal
{
static void func()
{
SetVertexAttribs_internal<T, (N - 1)>::func();
glEnableVertexAttribArray(N);
type2glattribfunc<typename T::template type_at<N>::type>(N, T::template type_at<N>::dim, T::byte_len, (void *)T::template bytes_internal<N, void>::var);
}
};
template <class T> struct SetVertexAttribs_internal<T, 0>
{
static void func()
{
glEnableVertexAttribArray(0);
type2glattribfunc<typename T::template type_at<0>::type>(0, T::template type_at<0>::dim, T::byte_len, (void *)T::template bytes_internal<0, void>::var);
}
};
}
template <class T>
void SetVertexAttribs()
{
InternalStuff::SetVertexAttribs_internal<T, (T::elements - 1)>::func();
}