282 lines
9.4 KiB
C++
282 lines
9.4 KiB
C++
/* +------------------------------------------------------+
|
|
____/ \____ /| - Open source game framework licensed to freely use, |
|
|
\ / / | copy, modify and sell without restriction |
|
|
+--\ ^__^ /--+ | |
|
|
| ~/ \~ | | - created for <https://foam.shampoo.ooo> |
|
|
| ~~~~~~~~~~~~ | +------------------------------------------------------+
|
|
| SPACE ~~~~~ | /
|
|
| ~~~~~~~ BOX |/
|
|
+-------------*/
|
|
|
|
#include "Attributes.hpp"
|
|
|
|
void sb::Attributes::index(GLint index)
|
|
{
|
|
_index = index;
|
|
}
|
|
|
|
GLint sb::Attributes::index() const
|
|
{
|
|
try
|
|
{
|
|
return std::get<GLint>(_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::decay_t<decltype(vector)>, 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::decay_t<decltype(vector)>, 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<GLvoid*>(_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<decltype(vector)>;
|
|
/* omit size check for the monostate (uninitialized attributes) variant */
|
|
if constexpr (!std::is_same_v<VectorType, std::monostate>)
|
|
{
|
|
using VertexType = typename VectorType::value_type;
|
|
/* For 1D vertices, the vertex type will be scalar */
|
|
if constexpr (std::is_scalar_v<VertexType>)
|
|
{
|
|
using ScalarType = VertexType;
|
|
if constexpr (std::is_floating_point_v<ScalarType>) return GL_FLOAT;
|
|
else if constexpr (std::is_unsigned_v<ScalarType>)
|
|
{
|
|
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<ScalarType>) return GL_FLOAT;
|
|
else if constexpr (std::is_unsigned_v<ScalarType>)
|
|
{
|
|
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<decltype(vector)>;
|
|
if constexpr (!std::is_same_v<VectorType, std::monostate>)
|
|
{
|
|
using VertexType = typename VectorType::value_type;
|
|
if constexpr (std::is_scalar_v<VertexType>)
|
|
{
|
|
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::decay_t<decltype(vector)>, 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<std::monostate>(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<decltype(vector)>;
|
|
if constexpr (!std::is_same_v<Type, std::monostate>)
|
|
{
|
|
using VertexType = typename Type::value_type;
|
|
std::visit([&] (auto& other_vector) {
|
|
using OtherType = std::decay_t<decltype(other_vector)>;
|
|
/* Check if current vertex type and current other vertex type are compatible */
|
|
if constexpr (!std::is_same_v<OtherType, std::monostate>)
|
|
{
|
|
using OtherVertexType = typename OtherType::value_type;
|
|
if constexpr (std::is_convertible_v<OtherVertexType, VertexType>)
|
|
{
|
|
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;
|
|
* // <Attributes 2D, 32 bytes, { {0, 0} {5, 0} {5, 5} {0, 5} }>
|
|
* // <Attributes 2D, 64 bytes, { {0, 0} {5, 0} {5, 5} {0, 5} {0, 0} {5, 0} {5, 5} {0, 5} }>
|
|
* // <Attributes 2D, 72 bytes, { {0, 0} {5, 0} {5, 5} {0, 5} {0, 0} {5, 0} {5, 5} {0, 5} } + (1 more)>
|
|
*/
|
|
std::ostream& sb::operator<<(std::ostream& out, const sb::Attributes& attributes)
|
|
{
|
|
out << "<Attributes " << attributes.dimensions() << "D, " << attributes.size() << " bytes, ";
|
|
std::visit([&] (const auto& vector) {
|
|
if constexpr (!std::is_same_v<std::decay_t<decltype(vector)>, std::monostate>)
|
|
{
|
|
std::size_t max = 8;
|
|
out << std::vector<std::decay_t<decltype(*vector.begin())>>(vector.begin(), vector.begin() + std::min(vector.size(), max));
|
|
if (vector.size() > max)
|
|
{
|
|
out << " + (" << (vector.size() - max) << " more)";
|
|
}
|
|
}
|
|
}, attributes.vertices);
|
|
out << ">";
|
|
return out;
|
|
}
|