Implement instanced meshes & matrix attributes
This commit is contained in:
parent
aedd41ae7f
commit
64751170cb
2 changed files with 359 additions and 15 deletions
|
|
@ -5,6 +5,7 @@
|
||||||
#include <psemek/geom/vector.hpp>
|
#include <psemek/geom/vector.hpp>
|
||||||
#include <psemek/geom/point.hpp>
|
#include <psemek/geom/point.hpp>
|
||||||
#include <psemek/geom/simplex.hpp>
|
#include <psemek/geom/simplex.hpp>
|
||||||
|
#include <psemek/geom/matrix.hpp>
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
|
@ -18,7 +19,9 @@ namespace psemek::gfx
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct normalized
|
struct normalized
|
||||||
{};
|
{
|
||||||
|
using type = T;
|
||||||
|
};
|
||||||
|
|
||||||
// skips a single attribute index
|
// skips a single attribute index
|
||||||
struct skip
|
struct skip
|
||||||
|
|
@ -31,6 +34,13 @@ namespace psemek::gfx
|
||||||
static constexpr std::size_t size = N;
|
static constexpr std::size_t size = N;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// instance attribute, for instanced meshes only
|
||||||
|
template <typename T>
|
||||||
|
struct instanced
|
||||||
|
{
|
||||||
|
using type = T;
|
||||||
|
};
|
||||||
|
|
||||||
template <typename Attrib>
|
template <typename Attrib>
|
||||||
struct attrib_traits;
|
struct attrib_traits;
|
||||||
|
|
||||||
|
|
@ -154,6 +164,16 @@ namespace psemek::gfx
|
||||||
static constexpr GLboolean normalized = gl::TRUE_;
|
static constexpr GLboolean normalized = gl::TRUE_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct attrib_traits<instanced<T>>
|
||||||
|
{
|
||||||
|
using attrib_type = T;
|
||||||
|
|
||||||
|
static constexpr GLint size = attrib_traits<T>::size;
|
||||||
|
static constexpr GLenum type = attrib_traits<T>::type;
|
||||||
|
static constexpr GLboolean normalized = attrib_traits<T>::normalized;
|
||||||
|
};
|
||||||
|
|
||||||
namespace detail
|
namespace detail
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
@ -165,6 +185,34 @@ namespace psemek::gfx
|
||||||
struct is_padding<padding<N>> : std::true_type
|
struct is_padding<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<geom::matrix<T, R, C>> : std::true_type
|
||||||
|
{};
|
||||||
|
|
||||||
template <typename Attr>
|
template <typename Attr>
|
||||||
std::size_t attr_size()
|
std::size_t attr_size()
|
||||||
{
|
{
|
||||||
|
|
@ -182,11 +230,37 @@ namespace psemek::gfx
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename ... Attribs>
|
template <bool InstanceSetup, typename ... Attribs>
|
||||||
struct mesh_setup;
|
struct stride_helper;
|
||||||
|
|
||||||
template <>
|
template <bool InstanceSetup>
|
||||||
struct mesh_setup<>
|
struct stride_helper<InstanceSetup>
|
||||||
|
{
|
||||||
|
static std::size_t stride()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <bool InstanceSetup, typename Attr1, typename ... Attribs>
|
||||||
|
struct stride_helper<InstanceSetup, Attr1, Attribs...>
|
||||||
|
{
|
||||||
|
static std::size_t stride()
|
||||||
|
{
|
||||||
|
std::size_t s = 0;
|
||||||
|
if constexpr (is_instanced<Attr1>::value == InstanceSetup)
|
||||||
|
{
|
||||||
|
s = attr_size<Attr1>();
|
||||||
|
}
|
||||||
|
return s + stride_helper<InstanceSetup, Attribs...>::stride();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <bool IsInstanced, bool InstanceSetup, typename ... Attribs>
|
||||||
|
struct mesh_setup_base;
|
||||||
|
|
||||||
|
template <bool IsInstanced, bool InstanceSetup>
|
||||||
|
struct mesh_setup_base<IsInstanced, InstanceSetup>
|
||||||
{
|
{
|
||||||
static std::size_t setup()
|
static std::size_t setup()
|
||||||
{
|
{
|
||||||
|
|
@ -197,12 +271,12 @@ namespace psemek::gfx
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Attr1, typename ... Attribs>
|
template <bool IsInstanced, bool InstanceSetup, typename Attr1, typename ... Attribs>
|
||||||
struct mesh_setup<Attr1, Attribs...>
|
struct mesh_setup_base<IsInstanced, InstanceSetup, Attr1, Attribs...>
|
||||||
{
|
{
|
||||||
static std::size_t setup()
|
static std::size_t setup()
|
||||||
{
|
{
|
||||||
std::size_t const stride = attr_size<Attr1>() + (0 + ... + attr_size<Attribs>());
|
std::size_t const stride = stride_helper<InstanceSetup, Attr1, Attribs...>::stride();
|
||||||
setup_impl(0, 0, stride);
|
setup_impl(0, 0, stride);
|
||||||
return stride;
|
return stride;
|
||||||
}
|
}
|
||||||
|
|
@ -211,24 +285,74 @@ namespace psemek::gfx
|
||||||
{
|
{
|
||||||
if constexpr (std::is_same_v<Attr1, skip>)
|
if constexpr (std::is_same_v<Attr1, skip>)
|
||||||
{
|
{
|
||||||
mesh_setup<Attribs...>::setup_impl(index + 1, offset, stride);
|
mesh_setup_base<IsInstanced, InstanceSetup, Attribs...>::setup_impl(index + 1, offset, stride);
|
||||||
}
|
}
|
||||||
else if constexpr (is_padding<Attr1>::value)
|
else if constexpr (is_padding<Attr1>::value)
|
||||||
{
|
{
|
||||||
mesh_setup<Attribs...>::setup_impl(index, offset + Attr1::size, stride);
|
mesh_setup_base<IsInstanced, InstanceSetup, Attribs...>::setup_impl(index, offset + Attr1::size, stride);
|
||||||
|
}
|
||||||
|
else if constexpr (!IsInstanced && is_instanced<Attr1>::value)
|
||||||
|
{
|
||||||
|
static_assert("cannot use instanced attribute for non-instanced mesh");
|
||||||
|
}
|
||||||
|
else if constexpr (!IsInstanced || (is_instanced<Attr1>::value == InstanceSetup))
|
||||||
|
{
|
||||||
|
using attr = typename remove_instanced<Attr1>::type;
|
||||||
|
|
||||||
|
if constexpr (is_matrix<attr>::value)
|
||||||
|
{
|
||||||
|
using T = typename attr::scalar_type;
|
||||||
|
using traits = attrib_traits<geom::vector<T, attr::columns>>;
|
||||||
|
|
||||||
|
for (std::size_t row = 0; row < attr::rows; ++row)
|
||||||
|
{
|
||||||
|
gl::EnableVertexAttribArray(index + row);
|
||||||
|
gl::VertexAttribPointer(index + row, traits::size, traits::type, traits::normalized, stride, reinterpret_cast<char const *>(offset + row * attr::columns * sizeof(T)));
|
||||||
|
|
||||||
|
if (InstanceSetup)
|
||||||
|
gl::VertexAttribDivisor(index + row, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
mesh_setup_base<IsInstanced, InstanceSetup, Attribs...>::setup_impl(index + attr::rows, offset + attr_size<Attr1>(), stride);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (InstanceSetup)
|
||||||
|
gl::VertexAttribDivisor(index, 1);
|
||||||
|
|
||||||
|
using traits = attrib_traits<Attr1>;
|
||||||
|
|
||||||
|
gl::EnableVertexAttribArray(index);
|
||||||
|
gl::VertexAttribPointer(index, traits::size, traits::type, traits::normalized, stride, reinterpret_cast<char const *>(offset));
|
||||||
|
|
||||||
|
mesh_setup_base<IsInstanced, InstanceSetup, Attribs...>::setup_impl(index + 1, offset + attr_size<Attr1>(), stride);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
using traits = attrib_traits<Attr1>;
|
using attr = typename remove_instanced<Attr1>::type;
|
||||||
|
|
||||||
gl::EnableVertexAttribArray(index);
|
if constexpr (is_matrix<attr>::value)
|
||||||
gl::VertexAttribPointer(index, traits::size, traits::type, traits::normalized, stride, reinterpret_cast<char const *>(offset));
|
{
|
||||||
|
mesh_setup_base<IsInstanced, InstanceSetup, Attribs...>::setup_impl(index + attr::rows, offset, stride);
|
||||||
mesh_setup<Attribs...>::setup_impl(index + 1, offset + attr_size<Attr1>(), stride);
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mesh_setup_base<IsInstanced, InstanceSetup, Attribs...>::setup_impl(index + 1, offset, stride);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename ... Attribs>
|
||||||
|
using mesh_setup = mesh_setup_base<false, false, Attribs...>;
|
||||||
|
|
||||||
|
template <typename ... Attribs>
|
||||||
|
using instanced_mesh_vertex_setup = mesh_setup_base<true, false, Attribs...>;
|
||||||
|
|
||||||
|
template <typename ... Attribs>
|
||||||
|
using instanced_mesh_instance_setup = mesh_setup_base<true, true, Attribs...>;
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct gl_type;
|
struct gl_type;
|
||||||
|
|
||||||
|
|
@ -496,4 +620,150 @@ namespace psemek::gfx
|
||||||
indexed_mesh(int);
|
indexed_mesh(int);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct instanced_mesh
|
||||||
|
{
|
||||||
|
static instanced_mesh null();
|
||||||
|
|
||||||
|
instanced_mesh();
|
||||||
|
instanced_mesh(instanced_mesh &&);
|
||||||
|
instanced_mesh(instanced_mesh const &) = delete;
|
||||||
|
|
||||||
|
instanced_mesh & operator = (instanced_mesh &&);
|
||||||
|
instanced_mesh & operator = (instanced_mesh const &) = delete;
|
||||||
|
|
||||||
|
~instanced_mesh();
|
||||||
|
|
||||||
|
template <typename ... Attribs>
|
||||||
|
void setup()
|
||||||
|
{
|
||||||
|
gl::BindVertexArray(array_);
|
||||||
|
|
||||||
|
gl::BindBuffer(gl::ARRAY_BUFFER, buffer_);
|
||||||
|
stride_ = detail::instanced_mesh_vertex_setup<Attribs...>::setup();
|
||||||
|
|
||||||
|
gl::BindBuffer(gl::ARRAY_BUFFER, instance_buffer_);
|
||||||
|
instance_stride_ = detail::instanced_mesh_instance_setup<Attribs...>::setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Vertex>
|
||||||
|
void load(Vertex const * vertices, std::size_t count, GLenum primitive_type, GLenum usage = gl::STREAM_DRAW)
|
||||||
|
{
|
||||||
|
if (sizeof(Vertex) != stride_)
|
||||||
|
throw std::runtime_error("Vertex size not equal to sum of attribute sizes");
|
||||||
|
|
||||||
|
switch (primitive_type)
|
||||||
|
{
|
||||||
|
case gl::LINES:
|
||||||
|
if ((count % 2) != 0) throw std::runtime_error("Vertex count for GL_LINES should be a multiple of 2");
|
||||||
|
break;
|
||||||
|
case gl::TRIANGLES:
|
||||||
|
if ((count % 3) != 0) throw std::runtime_error("Vertex count for GL_TRIANGLES should be a multiple of 3");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
gl::BindBuffer(gl::ARRAY_BUFFER, buffer_);
|
||||||
|
gl::BufferData(gl::ARRAY_BUFFER, count * sizeof(Vertex), vertices, usage);
|
||||||
|
|
||||||
|
count_ = count;
|
||||||
|
primitive_type_ = primitive_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Vertex>
|
||||||
|
void load(std::vector<Vertex> const & vertices, GLenum primitive_type, GLenum usage = gl::STREAM_DRAW)
|
||||||
|
{
|
||||||
|
load(vertices.data(), vertices.size(), primitive_type, usage);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Vertex, std::size_t N>
|
||||||
|
void load(geom::simplex<Vertex, N> const * simplices, std::size_t count, GLenum usage = gl::STREAM_DRAW)
|
||||||
|
{
|
||||||
|
static_assert(sizeof(geom::simplex<Vertex, N>) == (N + 1) * sizeof(Vertex));
|
||||||
|
|
||||||
|
GLenum primitive_type;
|
||||||
|
|
||||||
|
if constexpr (N == 0)
|
||||||
|
{
|
||||||
|
primitive_type = gl::POINTS;
|
||||||
|
}
|
||||||
|
else if constexpr (N == 1)
|
||||||
|
{
|
||||||
|
primitive_type = gl::LINES;
|
||||||
|
}
|
||||||
|
else if constexpr (N == 2)
|
||||||
|
{
|
||||||
|
primitive_type = gl::TRIANGLES;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
static_assert("unknown primitive type");
|
||||||
|
}
|
||||||
|
|
||||||
|
load(reinterpret_cast<Vertex const *>(simplices), count * (N + 1), primitive_type, usage);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Vertex, std::size_t N>
|
||||||
|
void load(std::vector<geom::simplex<Vertex, N>> const & simplices, GLenum usage = gl::STREAM_DRAW)
|
||||||
|
{
|
||||||
|
load(simplices.data(), simplices.size(), usage);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Instance>
|
||||||
|
void load_instance(Instance const * instances, std::size_t count, GLenum usage = gl::STREAM_DRAW)
|
||||||
|
{
|
||||||
|
if (sizeof(Instance) != instance_stride_)
|
||||||
|
throw std::runtime_error("Instance size not equal to sum of instanced attribute sizes");
|
||||||
|
|
||||||
|
gl::BindBuffer(gl::ARRAY_BUFFER, instance_buffer_);
|
||||||
|
gl::BufferData(gl::ARRAY_BUFFER, count * sizeof(Instance), instances, usage);
|
||||||
|
instance_count_ = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Instance>
|
||||||
|
void load_instance(std::vector<Instance> const & instances, GLenum usage = gl::STREAM_DRAW)
|
||||||
|
{
|
||||||
|
load_instance(instances.data(), instances.size(), usage);
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw() const
|
||||||
|
{
|
||||||
|
if (count_ == 0) return;
|
||||||
|
if (instance_count_ == 0) return;
|
||||||
|
|
||||||
|
gl::BindVertexArray(array_);
|
||||||
|
gl::DrawArraysInstanced(primitive_type_, 0, count_, instance_count_);
|
||||||
|
}
|
||||||
|
|
||||||
|
GLsizei count() const
|
||||||
|
{
|
||||||
|
return count_;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLsizei instance_count() const
|
||||||
|
{
|
||||||
|
return count_;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLenum primitive_type() const
|
||||||
|
{
|
||||||
|
return primitive_type_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
GLuint array_;
|
||||||
|
GLuint buffer_;
|
||||||
|
GLuint instance_buffer_;
|
||||||
|
|
||||||
|
GLsizei count_ = 0;
|
||||||
|
GLsizei instance_count_ = 0;
|
||||||
|
|
||||||
|
std::size_t stride_ = 0;
|
||||||
|
std::size_t instance_stride_ = 0;
|
||||||
|
|
||||||
|
GLenum primitive_type_;
|
||||||
|
|
||||||
|
instanced_mesh(int);
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -137,5 +137,79 @@ namespace psemek::gfx
|
||||||
index_buffer_ = 0;
|
index_buffer_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
instanced_mesh instanced_mesh::null()
|
||||||
|
{
|
||||||
|
return instanced_mesh(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
instanced_mesh::instanced_mesh()
|
||||||
|
{
|
||||||
|
gl::GenVertexArrays(1, &array_);
|
||||||
|
gl::GenBuffers(1, &buffer_);
|
||||||
|
gl::GenBuffers(1, &instance_buffer_);
|
||||||
|
}
|
||||||
|
|
||||||
|
instanced_mesh::instanced_mesh(instanced_mesh && other)
|
||||||
|
{
|
||||||
|
array_ = other.array_;
|
||||||
|
buffer_ = other.buffer_;
|
||||||
|
instance_buffer_ = other.instance_buffer_;
|
||||||
|
count_ = other.count_;
|
||||||
|
instance_count_ = other.instance_count_;
|
||||||
|
stride_ = other.stride_;
|
||||||
|
instance_stride_ = other.instance_stride_;
|
||||||
|
primitive_type_ = other.primitive_type_;
|
||||||
|
|
||||||
|
other.array_ = 0;
|
||||||
|
other.buffer_ = 0;
|
||||||
|
other.instance_buffer_ = 0;
|
||||||
|
other.count_ = 0;
|
||||||
|
other.instance_count_ = 0;
|
||||||
|
other.stride_ = 0;
|
||||||
|
other.instance_stride_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
instanced_mesh & instanced_mesh::operator = (instanced_mesh && other)
|
||||||
|
{
|
||||||
|
if (this == &other)
|
||||||
|
return *this;
|
||||||
|
|
||||||
|
gl::DeleteVertexArrays(1, &array_);
|
||||||
|
gl::DeleteBuffers(1, &buffer_);
|
||||||
|
gl::DeleteBuffers(1, &instance_buffer_);
|
||||||
|
|
||||||
|
array_ = other.array_;
|
||||||
|
buffer_ = other.buffer_;
|
||||||
|
instance_buffer_ = other.instance_buffer_;
|
||||||
|
count_ = other.count_;
|
||||||
|
instance_count_ = other.instance_count_;
|
||||||
|
stride_ = other.stride_;
|
||||||
|
instance_stride_ = other.instance_stride_;
|
||||||
|
primitive_type_ = other.primitive_type_;
|
||||||
|
|
||||||
|
other.array_ = 0;
|
||||||
|
other.buffer_ = 0;
|
||||||
|
other.instance_buffer_ = 0;
|
||||||
|
other.count_ = 0;
|
||||||
|
other.instance_count_ = 0;
|
||||||
|
other.stride_ = 0;
|
||||||
|
other.instance_stride_ = 0;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
instanced_mesh::~instanced_mesh()
|
||||||
|
{
|
||||||
|
gl::DeleteVertexArrays(1, &array_);
|
||||||
|
gl::DeleteBuffers(1, &buffer_);
|
||||||
|
gl::DeleteBuffers(1, &instance_buffer_);
|
||||||
|
}
|
||||||
|
|
||||||
|
instanced_mesh::instanced_mesh(int)
|
||||||
|
{
|
||||||
|
array_ = 0;
|
||||||
|
buffer_ = 0;
|
||||||
|
instance_buffer_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue