/* +------------------------------------------------------+ ____/ \____ /| - Open source game framework licensed to freely use, | \ / / | copy, modify and sell without restriction | +--\ ^__^ /--+ | | | ~/ \~ | | - created for | | ~~~~~~~~~~~~ | +------------------------------------------------------+ | SPACE ~~~~~ | / | ~~~~~~~ BOX |/ +-------------*/ #include "Attributes.hpp" void sb::Attributes::index(GLint index) { _index = index; } GLint sb::Attributes::index() const { try { return std::get(_index); } catch (const std::bad_variant_access&) { std::ostringstream message; message << "Trying to get index for " << *this << ", but no index has been set yet. Assign an index with glBindAttribLocation and" << " copy it to Attributes::index(GLint) or look up the index automatically at bind time by passing the location name and" << " linked shader program index to Attributes::bind(const std::string&, GLuint)."; throw std::runtime_error(message.str()); } } void sb::Attributes::offset(GLintptr offset) { _offset = offset; } std::size_t sb::Attributes::count() const { return std::visit([] (const auto& vector) -> std::size_t { /* omit size check for the monostate (uninitialized attributes) variant */ if constexpr (!std::is_same_v, std::monostate>) { return vector.size(); } else { return 0; } }, vertices); } std::size_t sb::Attributes::size() const { return std::visit([] (const auto& vector) -> std::size_t { /* omit size check for the monostate (uninitialized attributes) variant */ if constexpr (!std::is_same_v, std::monostate>) { return vector.size() * sizeof(vector.front()); } else { return 0; } }, vertices); } void sb::Attributes::bind() const { /* Define an array of vertex attributes that have data stored at the specified offset. */ glVertexAttribPointer(index(), dimensions(), type(), normalized(), 0, reinterpret_cast(_offset)); /* Debug */ std::ostringstream message; message << "After binding " << *this; sb::Log::gl_errors(message.str()); } void sb::Attributes::bind(const std::string& name, GLuint program) { GLint index = glGetAttribLocation(program, name.c_str()); /* A return value of -1 indicates an error finding the given attribute name */ if (index == -1) { std::ostringstream message; message << "Error getting attribute location of \"" << name << "\" in shader program. Did the program compile?" << " Is the attribute present and not optimized out?"; throw std::runtime_error(message.str()); } this->index(index); /* Debug */ std::ostringstream message; message << "After getting attribute location of " << name; sb::Log::gl_errors(message.str()); /* Call glVertexAttribPointer with the index */ bind(); } void sb::Attributes::enable() const { glEnableVertexAttribArray(*this); } void sb::Attributes::disable() const { glDisableVertexAttribArray(*this); } GLenum sb::Attributes::type() const { return std::visit([] (const auto& vector) -> GLenum { using VectorType = std::decay_t; /* omit size check for the monostate (uninitialized attributes) variant */ if constexpr (!std::is_same_v) { using VertexType = typename VectorType::value_type; /* For 1D vertices, the vertex type will be scalar */ if constexpr (std::is_scalar_v) { using ScalarType = VertexType; if constexpr (std::is_floating_point_v) return GL_FLOAT; else if constexpr (std::is_unsigned_v) { if constexpr (sizeof(ScalarType) > 8) return GL_UNSIGNED_INT; else return GL_UNSIGNED_BYTE; } else return GL_INT; } /* For dimensions greater than 1, the scalar type will be the value_type of the vertex type */ else { using ScalarType = typename VertexType::value_type; if constexpr (std::is_floating_point_v) return GL_FLOAT; else if constexpr (std::is_unsigned_v) { if constexpr (sizeof(ScalarType) > 1) return GL_UNSIGNED_INT; else return GL_BOOL; } else return GL_INT; } } else { return GL_INVALID_ENUM; } }, vertices); } std::size_t sb::Attributes::dimensions() const { return std::visit([] (const auto& vector) -> std::size_t { using VectorType = std::decay_t; if constexpr (!std::is_same_v) { using VertexType = typename VectorType::value_type; if constexpr (std::is_scalar_v) { return 1; } else { return VertexType::length(); } } else { return 0; } }, vertices); } bool sb::Attributes::normalized() const { return false; } sb::Attributes::operator const void*() const { return std::visit([] (const auto& vector) -> const void* { /* return nullptr for the monostate (uninitialized attributes) variant */ if constexpr (!std::is_same_v, std::monostate>) { return vector.data(); } else { return nullptr; } }, vertices); } sb::Attributes::operator int() const { return index(); } void sb::Attributes::add(const Attributes& other) { /* If the variant is std::monostate, these are the first attributes, so set vertices to these */ if (std::holds_alternative(vertices)) { vertices = other.vertices; } else { /* Visit each of the current variants and in each, visit each of the other Attributes's variants. * In every possible combination between the variants of both attributes, check to see if the variant * types are compatible. If so, add the insert code to this version of the templated function */ bool found = false; std::visit([&] (auto& vector) { using Type = std::decay_t; if constexpr (!std::is_same_v) { using VertexType = typename Type::value_type; std::visit([&] (auto& other_vector) { using OtherType = std::decay_t; /* Check if current vertex type and current other vertex type are compatible */ if constexpr (!std::is_same_v) { using OtherVertexType = typename OtherType::value_type; if constexpr (std::is_convertible_v) { vector.insert(vector.end(), other_vector.begin(), other_vector.end()); found = true; } } }, other.vertices); } }, vertices); if (!found) { std::ostringstream message; message << "warning: " << other << " was not added to " << *this << " because types are incompatible"; sb::Log::log(message); } } } void sb::Attributes::extend(const Attributes& other, std::size_t count) { while (count--) { add(other); } } /*! * Add string representation of attributes to output stream, truncated to at most 8 vertices. * * sb::Attributes attr {{0, 0}, {5, 0}, {5, 5}, {0, 5}}; * std::cout << attr << std::endl; * sb::Attributes attr2 {{0, 0}, {5, 0}, {5, 5}, {0, 5}, {0, 0}, {5, 0}, {5, 5}, {0, 5}}; * std::cout << attr2 << std::endl; * sb::Attributes attr3 {{0, 0}, {5, 0}, {5, 5}, {0, 5}, {0, 0}, {5, 0}, {5, 5}, {0, 5}, {-5, -5}}; * std::cout << attr3 << std::endl; * // * // * // */ std::ostream& sb::operator<<(std::ostream& out, const sb::Attributes& attributes) { out << ", std::monostate>) { std::size_t max = 8; out << std::vector>(vector.begin(), vector.begin() + std::min(vector.size(), max)); if (vector.size() > max) { out << " + (" << (vector.size() - max) << " more)"; } } }, attributes.vertices); out << ">"; return out; }