attributes object for holding generic gl vertices
This commit is contained in:
parent
a6bcc2a4d9
commit
1690bb5f19
|
@ -0,0 +1,131 @@
|
|||
/* /\ +--------------------------------------------------------------+
|
||||
____/ \____ /| - zlib/MIT/Unlicenced game framework licensed to freely use, |
|
||||
\ / / | copy, modify and sell without restriction |
|
||||
+--\ ^__^ /--+ | |
|
||||
| ~/ \~ | | - originally created at [http://nugget.fun] |
|
||||
| ~~~~~~~~~~~~ | +--------------------------------------------------------------+
|
||||
| SPACE ~~~~~ | /
|
||||
| ~~~~~~~ BOX |/
|
||||
+--------------+ */
|
||||
|
||||
#include "Attributes.hpp"
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
std::visit([&] (const auto& vector) {
|
||||
if constexpr (!std::is_same_v<std::decay_t<decltype(vector)>, std::monostate>)
|
||||
{
|
||||
out << vector;
|
||||
}
|
||||
}, attributes.vertices);
|
||||
return out;
|
||||
}
|
|
@ -0,0 +1,222 @@
|
|||
/* /\ +--------------------------------------------------------------+
|
||||
____/ \____ /| - zlib/MIT/Unlicenced game framework licensed to freely use, |
|
||||
\ / / | copy, modify and sell without restriction |
|
||||
+--\ ^__^ /--+ | |
|
||||
| ~/ \~ | | - originally created at [http://nugget.fun] |
|
||||
| ~~~~~~~~~~~~ | +--------------------------------------------------------------+
|
||||
| SPACE ~~~~~ | /
|
||||
| ~~~~~~~ BOX |/
|
||||
+--------------+
|
||||
|
||||
[Attributes.hpp]
|
||||
|
||||
An Attributes object is a container that acts as a limited, but dynamically typed vector for
|
||||
vertex properties to be passed to a GL buffer object and described by a vertex array object.
|
||||
It uses the variant library and type inference to determine the vertex type for an object. The
|
||||
appropriate variant will be applied based on the first vertices submitted to the object.
|
||||
|
||||
The object can contain float, int, or unsigned int vertices of any dimension from 1D to 4D. It
|
||||
can also contain bool vertices from 2D to 4D. Vertices can be submitted at initialization through
|
||||
the constructor or added and appended later using the add function. If the object is initialized
|
||||
without any vertices, it will be in an empty state indicated by the std::monostate variant
|
||||
alternative.
|
||||
|
||||
The constructor accepts initializer lists, so unknown types can be submitted as long as a single type
|
||||
can be inferred. For example,
|
||||
|
||||
Attributes({1.0f, 0.5f, -1.0f}); // glm::vec3 attributes containing one vertex
|
||||
Attributes({ // glm::vec3 attributes containing two vertices
|
||||
{1.0f, 0.5f, -1.0f},
|
||||
{0.0f, 1.0f, 1.0f}
|
||||
});
|
||||
Attributes({1, 2}); // glm::ivec2 attributes containing one vertex
|
||||
Attributes(glm::uvec2{1, 2}); // glm::uvec2 attributes containing one vertex
|
||||
Attributes(1, 2); // same as Attributes({1, 2}), each argument is treated as a
|
||||
// coordinate
|
||||
Attributes({ // inferred as glm::uvec3, so the -5, -555, and the
|
||||
{-5, 55, -555}, // decimal precision are lost
|
||||
glm::uvec3{8, 88, 888},
|
||||
{9.9, 9.99, 9.999}
|
||||
});
|
||||
|
||||
The add function also accepts initializer lists and any type that can be converted into the original.
|
||||
|
||||
sb::Attributes attr, attr2, attr3, attr4;
|
||||
attr.add(420);
|
||||
attr.add({69, 9000});
|
||||
attr.add({});
|
||||
attr.add({{1}});
|
||||
std::cout << attr << std::endl;
|
||||
attr2.add(glm::ivec4{1, 1, 2, 3});
|
||||
attr2.add({{5.0f, 8.0f, 13.0f, 21.0f}, {34.0f, 55.0f, 89.0f, 144.0f}});
|
||||
attr2.add({glm::uvec4{5, 8, 13, 21}, glm::uvec4{34, 55, 89, 144}});
|
||||
attr2.add({0.0f, 0.0f, 0.0f, 0.0f});
|
||||
std::cout << attr2 << std::endl;
|
||||
attr3.add(1.1f);
|
||||
attr3.add(2);
|
||||
attr3.add(std::vector<std::uint32_t>{3, 4});
|
||||
std::cout << attr3 << std::endl;
|
||||
attr3 = attr2;
|
||||
attr2 = attr4;
|
||||
std::cout << attr3 << std::endl;
|
||||
std::cout << attr2 << std::endl;
|
||||
attr4 = sb::Attributes({{-5, 55, -555}, glm::ivec3{8, 88, 888}, {9.99, 9.9, 9.0}});
|
||||
attr4.add(1000, 10000, 100000);
|
||||
attr4.add(1, 11);
|
||||
std::cout << attr4 << std::endl;
|
||||
attr2 = sb::Attributes(std::vector<float>{5.5, 6.6, 7.7, 8.8});
|
||||
attr2.add(glm::vec2{9.9f, 10.10f});
|
||||
std::cout << attr2 << std::endl;
|
||||
|
||||
This prints
|
||||
|
||||
warning: was not added to the attributes because its type is incompatible
|
||||
{ 420 69 9000 1 }
|
||||
warning: { 0 0 0 0 } was not added to the attributes because its type is incompatible
|
||||
{ {1, 1, 2, 3} {5, 8, 13, 21} {34, 55, 89, 144} {5, 8, 13, 21} {34, 55, 89, 144} }
|
||||
{ 1.1 2 3 4 }
|
||||
{ {1, 1, 2, 3} {5, 8, 13, 21} {34, 55, 89, 144} {5, 8, 13, 21} {34, 55, 89, 144} }
|
||||
|
||||
warning: { {1, 11} } was not added to the attributes because its type is incompatible
|
||||
{ {-5, 55, -555} {8, 88, 888} {9, 9, 9} {1000, 10000, 100000} }
|
||||
warning: { {9.9, 10.1} } was not added to the attributes because its type is incompatible
|
||||
{ 5.5 6.6 7.7 8.8 }
|
||||
|
||||
*/
|
||||
|
||||
#ifndef SB_ATTRIBUTES_H_
|
||||
#define SB_ATTRIBUTES_H_
|
||||
|
||||
#include <any>
|
||||
#include <ostream>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
#include <typeinfo>
|
||||
#include <typeindex>
|
||||
#include <initializer_list>
|
||||
#include <variant>
|
||||
#include "glm/glm.hpp"
|
||||
#include "Log.hpp"
|
||||
#include "extension.hpp"
|
||||
|
||||
namespace sb
|
||||
{
|
||||
|
||||
/* Calls to the stream operator from within this namespace will search the global namespace as well as the current */
|
||||
using ::operator<<;
|
||||
|
||||
class Attributes;
|
||||
|
||||
std::ostream& operator<<(std::ostream&, const Attributes&);
|
||||
|
||||
class Attributes
|
||||
{
|
||||
|
||||
private:
|
||||
|
||||
/* Every type of vertex in the glm vertex types (bool, unsigned int, int, float) from 1D to 4D is included in this variant.
|
||||
* Each variant alternative is a vector of vertex type, so only homogenous vectors can be used. Index 0 is the std::monospace
|
||||
* alternative, which is used to default initialize Attributes to an empty state where no variant alternative is selected.
|
||||
* 1D vertices are specified by vectors of scalars, rather than the 1D glm vertex types. The std::vector<bool> alternative is
|
||||
* not included because its specialization doesn't include a data member function */
|
||||
using Vertices = std::variant<std::monostate, std::vector<std::uint32_t>, std::vector<std::int32_t>, std::vector<float>,
|
||||
std::vector<glm::bvec2>, std::vector<glm::uvec2>, std::vector<glm::ivec2>, std::vector<glm::vec2>,
|
||||
std::vector<glm::bvec3>, std::vector<glm::uvec3>, std::vector<glm::ivec3>, std::vector<glm::vec3>,
|
||||
std::vector<glm::bvec4>, std::vector<glm::uvec4>, std::vector<glm::ivec4>, std::vector<glm::vec4>>;
|
||||
Vertices vertices;
|
||||
|
||||
public:
|
||||
|
||||
/* The default constructor creates an empty set of attributes. An empty set of attributes is indicated by
|
||||
* the class's vertex variant having the std::monostate variant. */
|
||||
Attributes() {};
|
||||
|
||||
/* Construct a new Attributes object with vertices set to the vertices contained in this vector */
|
||||
template<typename Type>
|
||||
Attributes(const std::vector<Type>& vertices) : vertices(vertices)
|
||||
{
|
||||
/* debug message */
|
||||
std::ostringstream message;
|
||||
message << "added vertex ";
|
||||
for (const Type& vertex : vertices)
|
||||
{
|
||||
message << vertex << " ";
|
||||
}
|
||||
sb::Log::log(message, sb::Log::DEBUG);
|
||||
}
|
||||
|
||||
/* Add a vertex. The vertex can be any of the variant types. */
|
||||
template<typename Type>
|
||||
Attributes(const Type& vertex) : Attributes({vertex}) {}
|
||||
|
||||
/* Add a vertex by specifying each coordinate as a separate argument. All arguments must have idential type. */
|
||||
template<typename XType, typename... CoordinateTypes,
|
||||
std::enable_if_t<(... && std::is_same_v<CoordinateTypes, XType>), std::nullptr_t> = nullptr>
|
||||
Attributes(const XType& coordinate_0, const CoordinateTypes&... coordinates) :
|
||||
Attributes({std::initializer_list<XType>({coordinate_0, coordinates ...})}) {}
|
||||
|
||||
/* Add vertices by passing an uninitialized list of vertices. This template applies to a list of
|
||||
* vertices where the list type is undeclared but the containing vertices have been initialized
|
||||
* or the type can be inferred because the vertices are 1D scalars */
|
||||
template<typename VertexType>
|
||||
Attributes(const std::initializer_list<VertexType>& vertices) :
|
||||
Attributes(std::vector<VertexType>(vertices.begin(), vertices.end())) {}
|
||||
|
||||
/* Add vertices by passing a two-dimensional initializer list, a list of uninitialized vertices.
|
||||
* The appropriate glm vertex size is applied by looking at the length of the first uninitialized
|
||||
* vertex in the initializer list. */
|
||||
template<typename CoordinateType>
|
||||
Attributes(const std::initializer_list<std::initializer_list<CoordinateType>>& vertices)
|
||||
{
|
||||
for (auto& vertex : vertices)
|
||||
{
|
||||
const CoordinateType* coordinate = vertex.begin();
|
||||
if (vertex.size() == 1)
|
||||
{
|
||||
add(vertex);
|
||||
}
|
||||
else if (vertex.size() == 2)
|
||||
{
|
||||
add(glm::vec<2, CoordinateType, glm::defaultp>({*coordinate++, *coordinate}));
|
||||
}
|
||||
else if (vertex.size() == 3)
|
||||
{
|
||||
add(glm::vec<3, CoordinateType, glm::defaultp>({*coordinate++, *coordinate++, *coordinate}));
|
||||
}
|
||||
else if (vertex.size() == 4)
|
||||
{
|
||||
add(glm::vec<4, CoordinateType, glm::defaultp>({*coordinate++, *coordinate++, *coordinate++, *coordinate}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void add(const Attributes&);
|
||||
|
||||
/* Add a 2D, 3D or 4D vertex by specifying each coordinate as a separate argument. All arguments must have
|
||||
* identical type. */
|
||||
template<typename XType, typename YType, typename... CoordinateTypes,
|
||||
std::enable_if_t<std::is_same_v<XType, YType> && (... && std::is_same_v<CoordinateTypes, XType>),
|
||||
std::nullptr_t> = nullptr>
|
||||
void add(const XType& coordinate_0, const YType& coordinate_1, const CoordinateTypes&... coordinates)
|
||||
{
|
||||
/* The arguments are converted to an initializer list of initializer lists, a 2D initializer list with
|
||||
* the new vertex as its only element, and passed to the overloaded add function where the vertex is
|
||||
* converted into an Attributes object */
|
||||
add({std::initializer_list<XType>({coordinate_0, coordinate_1, coordinates ...})});
|
||||
}
|
||||
|
||||
void extend(const Attributes&, std::size_t = 1);
|
||||
void index(int);
|
||||
int index() const;
|
||||
void enable() const;
|
||||
std::size_t size() const;
|
||||
std::size_t count() const;
|
||||
friend std::ostream& operator<<(std::ostream&, const Attributes&);
|
||||
operator const void*() const;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -190,21 +190,9 @@ int SDL_SetRenderDrawColor(SDL_Renderer*, const Color&);
|
|||
int SDL_RenderFillRect(SDL_Renderer*, const Box&);
|
||||
int lineColor(SDL_Renderer*, const Segment&, const Color&, std::uint8_t = 1);
|
||||
|
||||
template <typename T>
|
||||
std::ostream& operator<<(std::ostream& out, const std::vector<T>& members)
|
||||
{
|
||||
out << "{ ";
|
||||
for (const T& member : members)
|
||||
{
|
||||
out << member << " ";
|
||||
}
|
||||
out << "}";
|
||||
return out;
|
||||
}
|
||||
|
||||
/* Stream a text representation of a glm::vec of any type or dimension */
|
||||
template<int dimensions, typename Type>
|
||||
std::ostream& operator<<(std::ostream& out, const glm::vec<dimensions, Type, glm::defaultp> vec)
|
||||
std::ostream& operator<<(std::ostream& out, const glm::vec<dimensions, Type, glm::defaultp>& vec)
|
||||
{
|
||||
out << "{" << vec.x << ", " << vec.y;
|
||||
if constexpr (dimensions > 2)
|
||||
|
@ -217,9 +205,21 @@ std::ostream& operator<<(std::ostream& out, const glm::vec<dimensions, Type, glm
|
|||
}
|
||||
out << "}";
|
||||
return out;
|
||||
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream&, const SDL_Color&);
|
||||
|
||||
/* Add the contents of a vector to the output stream */
|
||||
template <typename Type>
|
||||
std::ostream& operator<<(std::ostream& out, const std::vector<Type>& members)
|
||||
{
|
||||
out << "{ ";
|
||||
for (const Type& member : members)
|
||||
{
|
||||
out << member << " ";
|
||||
}
|
||||
out << "}";
|
||||
return out;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue