#include #include #include namespace psemek::gfx { mesh::mesh(mesh && other) : array_{std::move(other.array_)} , vertex_buffer_{std::move(other.vertex_buffer_)} , index_buffer_{std::move(other.index_buffer_)} , instance_buffer_{std::move(other.instance_buffer_)} , info_{other.info_} { other.info_ = mesh_info{}; } mesh & mesh::operator = (mesh && other) { if (this == &other) return *this; array_ = std::move(other.array_); vertex_buffer_ = std::move(other.vertex_buffer_); index_buffer_ = std::move(other.index_buffer_); instance_buffer_ = std::move(other.instance_buffer_); info_ = other.info_; other.info_ = mesh_info{}; return *this; } void mesh::bind() const { array_.bind(); } void mesh::setup(attribs_description const & attribs) { assert(!attribs.attribs.empty()); bind(); for (std::size_t i = 0; i < info_.max_attribute_index_; ++i) gl::DisableVertexAttribArray(i); std::size_t max_index = 0; bool instanced = false; for (auto const & a : attribs.attribs) { max_index = std::max(max_index, a.index); if (a.divisor != 0) { assert(a.divisor == 1); instanced = true; } } info_.max_attribute_index_ = max_index; info_.stride_ = attribs.vertex_size; info_.instance_stride_ = attribs.instance_size; info_.instanced_ = instanced; if (!vertex_buffer_) vertex_buffer_ = buffer{}; vertex_buffer_.bind(); for (auto const & a : attribs.attribs) { if (a.divisor != 0) continue; gl::EnableVertexAttribArray(a.index); gl::VertexAttribPointer(a.index, a.size, a.type, a.normalized, attribs.vertex_size, a.pointer); } if (instanced) { if (!instance_buffer_) instance_buffer_ = buffer{}; instance_buffer_.bind(); for (auto const & a : attribs.attribs) { if (a.divisor == 0) continue; gl::EnableVertexAttribArray(a.index); gl::VertexAttribPointer(a.index, a.size, a.type, a.normalized, attribs.instance_size, a.pointer); gl::VertexAttribDivisor(a.index, a.divisor); } } } void mesh::load_raw(void const * vertices, std::size_t vertex_size, std::size_t count, GLenum primitive_type, GLenum usage) { assert(info_.stride_ == vertex_size); if (auto n = detail::get_primitive_type_vertex_count(primitive_type); n) assert((count % (*n)) == 0); assert(vertex_buffer_); vertex_buffer_.load(vertices, vertex_size * count, usage); info_.vertex_count_ = count; info_.index_count_ = 0; info_.indexed_ = false; info_.primitive_type_ = primitive_type; } static std::size_t index_size(GLenum type) { switch (type) { case gl::UNSIGNED_BYTE: return 1; case gl::UNSIGNED_SHORT: return 2; case gl::UNSIGNED_INT: return 4; default: throw std::runtime_error("Unknown undex type"); } } void mesh::load_raw(void const * vertices, std::size_t vertex_size, std::size_t vertex_count, void const * indices, GLenum index_type, std::size_t index_count, GLenum primitive_type, GLenum usage) { assert(info_.stride_ == vertex_size); if (auto n = detail::get_primitive_type_vertex_count(primitive_type); n) assert((index_count % (*n)) == 0); assert(vertex_buffer_); if (!index_buffer_) { index_buffer_ = buffer{}; array_.bind(); gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, index_buffer_.id()); } vertex_buffer_.load(vertices, vertex_size * vertex_count, usage); index_buffer_.load(indices, index_size(index_type) * index_count, usage); info_.vertex_count_ = vertex_count; info_.index_count_ = index_count; info_.indexed_ = true; info_.primitive_type_ = primitive_type; info_.index_type_ = index_type; } void mesh::load_raw(imported_mesh const & m) { load_raw(m.vertices.data(), m.attribs.vertex_size, m.vertices.size() / m.attribs.vertex_size, m.indices.data(), gl::UNSIGNED_INT, m.indices.size(), gl::TRIANGLES, gl::STATIC_DRAW); } void mesh::draw() const { draw(0, is_indexed() ? index_count() : vertex_count(), instance_count()); } void mesh::draw(std::size_t first, std::size_t count, std::size_t instance_count) const { if (is_indexed()) { assert(first + count <= index_count()); if (is_instanced()) { assert(instance_count <= this->instance_count()); if (count != 0 && instance_count != 0) { bind(); gl::DrawElementsInstanced(primitive_type(), count, index_type(), reinterpret_cast(detail::index_size(index_type()) * first), instance_count); } } else { if (count != 0) { bind(); gl::DrawElements(primitive_type(), count, index_type(), reinterpret_cast(detail::index_size(index_type()) * first)); } } } else { assert(first + count <= vertex_count()); if (is_instanced()) { assert(instance_count <= this->instance_count()); if (count != 0 && instance_count != 0) { bind(); gl::DrawArraysInstanced(primitive_type(), first, count, instance_count); } } else { if (count != 0) { bind(); gl::DrawArrays(primitive_type(), first, count); } } } } imported_mesh load_mesh(std::string_view data) { // These should be in sync with convert-mesh.py static std::uint32_t const POSITION_MASK = 1; static std::uint32_t const NORMALS_MASK = 2; static std::uint32_t const COLORS_MASK = 4; static std::uint32_t const TEXCOORDS_MASK = 8; util::binary_istream s{data}; imported_mesh result; auto vertex_format = s.read(); if (vertex_format & POSITION_MASK) result.attribs += make_attribs_description>(); if (vertex_format & NORMALS_MASK) result.attribs += make_attribs_description>(); if (vertex_format & COLORS_MASK) result.attribs += make_attribs_description>>(); if (vertex_format & TEXCOORDS_MASK) result.attribs += make_attribs_description>(); auto vertex_count = s.read(); auto vertex_ptr = s.read_raw(vertex_count * result.attribs.vertex_size); auto index_count = s.read(); auto index_ptr = reinterpret_cast(s.read_raw(index_count * sizeof(std::uint32_t))); result.vertices = {vertex_ptr, vertex_ptr + vertex_count * result.attribs.vertex_size}; result.indices = {index_ptr, index_ptr + index_count}; return result; } }