275 lines
11 KiB
C++
275 lines
11 KiB
C++
/*!<pre>
|
|
* /\ +------------------------------------------------------+
|
|
* ____/ \____ /| - Open source game framework licensed to freely use, |
|
|
* \ / / | copy, modify and sell without restriction |
|
|
* +--\ ^__^ /--+ | |
|
|
* | ~/ \~ | | - created for <https://foam.shampoo.ooo> |
|
|
* | ~~~~~~~~~~~~ | +------------------------------------------------------+
|
|
* | SPACE ~~~~~ | /
|
|
* | ~~~~~~~ BOX |/
|
|
* +--------------+ </pre>*/
|
|
|
|
#include "Attributes.hpp"
|
|
|
|
/* Set the generic vertex attribute index for the attributes. It must be the same index that is
|
|
* returned when glGetAttribLocation is given the shader program and variable name the attributes
|
|
* will be associated with. The bind class member functions can automatically do that, and they
|
|
* should be used instead of this function if possible.
|
|
*
|
|
* The index may be specified to GL in the shader source code using the location layout qualifier
|
|
* in GLSL 3.3 and above. It can also be set automatically by GL when a shader program is linked.
|
|
* In both cases, the return value of glGetAttribLocation should be passed to this function.
|
|
* Otherwise, this index can be an arbitrary choice that is not greater than GL_MAX_VERTEX_ATTRIBS,
|
|
* in which case it would be explicitly passed to glBindAttribLocation.
|
|
*
|
|
* Once assigned, the index can be passed to glVertexAttribPointer to set the properties of the
|
|
* attributes on the GPU, either manually or automatically through the add member function in the
|
|
* VBO class. */
|
|
void sb::Attributes::index(GLint index)
|
|
{
|
|
attribute_index = index;
|
|
}
|
|
|
|
/* Returns the generic vertex attribute index for the attributes. This index can be passed to
|
|
* glBindAttribLocation to assign the index to a shader input variable if the index isn't already
|
|
* assigned by a layout qualifier in the GLSL source code. The index should be passed to
|
|
* glVertexAttribPointer to specify the location to use when rendering. */
|
|
GLint sb::Attributes::index() const
|
|
{
|
|
return attribute_index;
|
|
}
|
|
|
|
/* Returns the count of attributes */
|
|
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);
|
|
}
|
|
|
|
/* Enable the attributes in the VAO. */
|
|
void sb::Attributes::enable() const
|
|
{
|
|
glEnableVertexAttribArray(*this);
|
|
}
|
|
|
|
/* Disable the attributes in the VAO. */
|
|
void sb::Attributes::disable() const
|
|
{
|
|
glDisableVertexAttribArray(*this);
|
|
}
|
|
|
|
/* Returns the size in bytes of the object's underlying vector of vertices. This can be passed to OpenGL
|
|
* along with the memory pointer when copying vertices to the GPU. */
|
|
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);
|
|
}
|
|
|
|
/* Set the index to the location of attributes with given name in a shader program. The program
|
|
* must already be linked to get the location. */
|
|
void sb::Attributes::bind(GLuint program, const std::string& name)
|
|
{
|
|
index(glGetAttribLocation(program, name.c_str()));
|
|
std::ostringstream message;
|
|
message << "index for " << name << " is " << index();
|
|
sb::Log::log(message, sb::Log::DEBUG);
|
|
}
|
|
|
|
/* Set the index and bind it to the given name in a shader program. This should be called before
|
|
* the shader program is linked, otherwise the name will already have been assigned a location
|
|
* index by GL. */
|
|
void sb::Attributes::bind(std::uint32_t index, GLuint program, const std::string& name)
|
|
{
|
|
glBindAttribLocation(program, index, name.c_str());
|
|
this->index(index);
|
|
}
|
|
|
|
/* Return the GLenum that corresponds to the type of scalar being held in the vertices. This
|
|
* will return either GL_FLOAT, GL_INT, GL_UNSIGNED_INT, GL_UNSIGNED_BYTE, GL_BOOL, or
|
|
* GL_INVALID_ENUM. A return value of GL_INVALID_ENUM indicates an error meaning there are no
|
|
* attributes and no type exists yet. */
|
|
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);
|
|
}
|
|
|
|
/* Return the number of dimensions in the vertices. If there are no vertices, return 0. */
|
|
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);
|
|
}
|
|
|
|
/* Normalization isn't supported, so this always returns false */
|
|
bool sb::Attributes::normalized() const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
/* Returns a pointer to the first vertex in the object's underlying vector of vertices. This can be used
|
|
* along with the size function to pass the raw bytes of the vertices to the GPU. */
|
|
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);
|
|
}
|
|
|
|
/* Attributes can be represented by their GL index when an int is requested. */
|
|
sb::Attributes::operator int() const
|
|
{
|
|
return index();
|
|
}
|
|
|
|
/* Extend to include these attributes at the end. The argument will be automatically converted to an
|
|
* Attributes object, so the vertices can be in any of the Attributes constructor formats. If no attributes
|
|
* have been constructed or initialized, the current vertices will be set to these vertices. Otherwise,
|
|
* vertices will be inserted at the end of the current vertices. */
|
|
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 the attributes because its type is incompatible";
|
|
sb::Log::log(message);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Add attributes to the end count number of times. If count is 1, this has the same effect as the
|
|
* add function. */
|
|
void sb::Attributes::extend(const Attributes& other, std::size_t count)
|
|
{
|
|
while (count--)
|
|
{
|
|
add(other);
|
|
}
|
|
}
|
|
|
|
/* Overload the stream operator to support attributes. Add a string representation of the vertices to
|
|
* the output stream. Since this is defined as a friend function and isn't in the global scope, it should
|
|
* prevent it being looked up with arguments other than attributes. */
|
|
std::ostream& sb::operator<<(std::ostream& out, const Attributes& attributes)
|
|
{
|
|
out << "<Attributes #" << attributes.index() << ", " << attributes.dimensions() << "D, " <<
|
|
attributes.size() << " bytes, ";
|
|
std::visit([&] (const auto& vector) {
|
|
if constexpr (!std::is_same_v<std::decay_t<decltype(vector)>, std::monostate>)
|
|
{
|
|
out << vector;
|
|
}
|
|
}, attributes.vertices);
|
|
out << ">";
|
|
return out;
|
|
}
|