ArrayBuffer.hh 10.5 KB
Newer Older
1 2
#pragma once

Philip Trettner's avatar
Philip Trettner committed
3
#include "Buffer.hh"
4

5
#include <vector>
6

7 8 9 10
#include <glow/fwd.hh>
#include <glow/common/gltypeinfo.hh>
#include <glow/common/log.hh>
#include <glow/common/nodiscard.hh>
11

12 13
#include "ArrayBufferAttribute.hh"
#include "raii/BoundArrayBuffer.hh"
14

15 16 17 18
namespace glow
{
GLOW_SHARED(class, ArrayBuffer);

19 20 21 22 23 24 25 26
/**
 * An ArrayBuffer holds an array of per-vertex data. In its simplest form an
 * array of one attribute, for example the vertex position or texture-coordinate.
 * An ArrayBuffer however can also hold multiple attributes in an interleaved way.
 *
 * An ArrayBuffer can be drawn directly or indexed in combination with an ArrayBuffer.
 *
 * The combination of (multiple) attributes of (multiple) ArrayBuffers
27
 * and one (optional) ArrayBuffer is a VertexBufferObject or VertexArray.
28 29 30 31
 *
 * Note: In some documents ArrayBuffers (and sometimes ArrayBuffers) are
 *       called VertexBufferObjects, VBOs. The original extension that introduced
 *       these two new buffer types was called ARB_vertex_buffer_object but the buffers
32
 *       itself are called ArrayBuffer and ElementArrayBuffer.
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
 *
 ***************************************************************************************************************
 * Attributes:
 *************
 *
 * _type is the GL type
 * _size the number of elements in this attribute (1..4)
 * _normalized is the attribute normalization for int types
 *
 * Want to add tightly packed attributes in order?
 *  -> use defineAttribute()
 *
 * Want to add attributes with individual padding in order?
 *  -> use defineAttributeWithPadding()
 *
 * Want to add attributes out-of-order?
 *  -> use defineAttributeWithOffset()
 *
 * The stride size gets always set to the minimal stride size that covers all defined attributes (/w padding).
 * All define methods can get mixed!
 *
 * ab->defineAttribute(            "pos",       GL_FLOAT, 3    ); // stride: 12 bytes
 * ab->defineAttributeWithPadding( "color",     GL_CHAR,  3, 1 ); // stride: 12 + 3 + 1 = 16 bytes
 * ab->defineAttributeWithOffset(  "colorNorm", GL_CHAR,  3, 12, GL_TRUE ); // stride is still 16 as 12+3 <= 16!
 *
 * Convenience: most basic C++ and glm types can be used as template argument
59 60
 * (Automatically uses an appropriate AttributeMode! Float for floats, Integer for ints, Double for doubles,
 *  NormalizedInteger is not supported)
61
 *
Philip Trettner's avatar
Philip Trettner committed
62
 * ab->defineAttribute<tg::pos3>( "aPosition" );
63
 *
64 65 66 67
 * Convenience #2: (Automatically uses an appropriate AttributeMode! Float for floats, Integer for ints, Double for
 *                  doubles, NormalizedInteger is not supported)
 *
 * struct MyVertex {
Philip Trettner's avatar
Philip Trettner committed
68 69
 *  tg::pos3 aPosition;
 *  tg::pos2 texCoord;
70 71
 * };
 *
72 73 74
 * ab->defineAttribute(&MyVertex::aPosition, "aPosition");
 * ab->defineAttributes({{&MyVertex::aPosition, "aPosition"},
 *                       {&MyVertex::texCoord, "aTexCoord"}});
75
 *
76
 **************************************************************************************************************/
77
class ArrayBuffer final : public Buffer
78
{
79 80 81 82
private: // member
    /// Buffer stride in bytes
    GLuint mStride = 0;

83 84 85
    /// Number of elements
    GLuint mElementCount = 0;

86
    /// Attributes
87
    std::vector<ArrayBufferAttribute> mAttributes;
88 89 90

public: // getter
    /// Gets the currently bound AB (nullptr if none)
91
    static BoundArrayBuffer* getCurrentBuffer();
92 93

    GLuint getStride() const { return mStride; }
94
    GLuint getElementCount() const { return mElementCount; }
95
    std::vector<ArrayBufferAttribute> const& getAttributes() const { return mAttributes; }
96
    void setStride(GLuint stride) { mStride = stride; }
97

98 99
public: // unbound functions
    /// Adds a fully specified attribute
100 101 102 103
    void defineAttribute(ArrayBufferAttribute const& a);

    /// Adds a set of fully specified attributes
    void defineAttributes(std::vector<ArrayBufferAttribute> const& attrs);
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129

    /// Adds the attribute at the end of the existing attributes, stride gets computed automatically
    void defineAttribute(std::string const& name, GLenum type, GLint size, AttributeMode mode = AttributeMode::Float, GLuint divisor = 0);

    /// Adds the attribute at the end of the existing attributes, stride gets computed automatically
    /// + extra padding in bytes at the end
    void defineAttributeWithPadding(std::string const& name,
                                    GLenum type,
                                    GLint size,
                                    GLuint padding,
                                    AttributeMode mode = AttributeMode::Float,
                                    GLuint divisor = 0);

    /// Adds an attribute defined by an offset: this way an attribute can get added at arbitrary
    /// locations in the stride. If it's added at the end, the stride gets resized.
    /// This way attributes can even overlap, hope you know what you're doing...
    void defineAttributeWithOffset(std::string const& name,
                                   GLenum type,
                                   GLint size,
                                   GLuint offset,
                                   AttributeMode mode = AttributeMode::Float,
                                   GLuint divisor = 0);

    /// Attribute definition via template
    /// e.g. defineAttribute<glm::vec3>("aPosition");
    template <typename DataT>
130 131
    void defineAttribute(std::string const& name)
    {
132 133
        defineAttribute(name, glTypeOf<DataT>::type, glTypeOf<DataT>::size,
                        ArrayBufferAttribute::naturalModeOf(glTypeOf<DataT>::type), 0);
134 135 136
    }
    template <typename DataT>
    void defineAttribute(std::string const& name, AttributeMode mode, GLuint divisor = 0)
137 138 139 140 141
    {
        defineAttribute(name, glTypeOf<DataT>::type, glTypeOf<DataT>::size, mode, divisor);
    }
    /// Attribute definition with padding via template
    template <typename DataT>
142 143 144
    void defineAttributeWithPadding(std::string const& name, GLuint padding)
    {
        defineAttributeWithPadding(name, glTypeOf<DataT>::type, glTypeOf<DataT>::size, padding,
145
                                   ArrayBufferAttribute::naturalModeOf(glTypeOf<DataT>::type), 0);
146 147 148
    }
    template <typename DataT>
    void defineAttributeWithPadding(std::string const& name, GLuint padding, AttributeMode mode, GLuint divisor = 0)
149 150 151 152 153
    {
        defineAttributeWithPadding(name, glTypeOf<DataT>::type, glTypeOf<DataT>::size, padding, mode, divisor);
    }
    /// Attribute definition with offset via template
    template <typename DataT>
154 155 156
    void defineAttributeWithOffset(std::string const& name, GLuint offset)
    {
        defineAttributeWithOffset(name, glTypeOf<DataT>::type, glTypeOf<DataT>::size, offset,
157
                                  ArrayBufferAttribute::naturalModeOf(glTypeOf<DataT>::type), 0);
158 159 160
    }
    template <typename DataT>
    void defineAttributeWithOffset(std::string const& name, GLuint offset, AttributeMode mode, GLuint divisor = 0)
161 162 163 164
    {
        defineAttributeWithOffset(name, glTypeOf<DataT>::type, glTypeOf<DataT>::size, offset, mode, divisor);
    }

165 166 167
    /// Attribute definition via pointer-to-member
    /// Usage:
    ///   ab->defineAttribute(&Vertex::pos, "aPosition");
168 169 170
    ///
    /// Tip for 32bit colors:
    ///   ab->defineAttribute(&Point::color, "aColor", GL_UNSIGNED_BYTE, 4, AttributeMode::NormalizedInteger);
171 172 173 174
    template <class DataT, class VertexT>
    void defineAttribute(DataT VertexT::*member, std::string const& name)
    {
        if (getStride() > 0 && getStride() != sizeof(VertexT))
175 176
            ::glow::warning() << "Stride mismatch! Expected " << sizeof(VertexT) << ", got " << getStride() << " "
                              << to_string(this);
177 178 179 180
        setStride(sizeof(VertexT));

        auto offset = reinterpret_cast<std::ptrdiff_t>(&(reinterpret_cast<VertexT const volatile*>(0)->*member));
        defineAttributeWithOffset(name, glTypeOf<DataT>::type, glTypeOf<DataT>::size, offset,
181
                                  ArrayBufferAttribute::naturalModeOf(glTypeOf<DataT>::type), 0);
182
    }
Philip Trettner's avatar
Philip Trettner committed
183
    template <class DataT, class VertexT>
184 185 186
    void defineAttribute(DataT VertexT::*member, std::string const& name, AttributeMode mode, GLuint divisor = 0)
    {
        if (getStride() > 0 && getStride() != sizeof(VertexT))
187 188
            ::glow::warning() << "Stride mismatch! Expected " << sizeof(VertexT) << ", got " << getStride() << " "
                              << to_string(this);
189 190 191 192 193
        setStride(sizeof(VertexT));

        auto offset = reinterpret_cast<std::ptrdiff_t>(&(reinterpret_cast<VertexT const volatile*>(0)->*member));
        defineAttributeWithOffset(name, glTypeOf<DataT>::type, glTypeOf<DataT>::size, offset, mode, divisor);
    }
194 195 196 197
    template <class DataT, class VertexT>
    void defineAttribute(DataT VertexT::*member, std::string const& name, GLenum type, int elements, AttributeMode mode, GLuint divisor = 0)
    {
        if (getStride() > 0 && getStride() != sizeof(VertexT))
198 199
            ::glow::warning() << "Stride mismatch! Expected " << sizeof(VertexT) << ", got " << getStride() << " "
                              << to_string(this);
200 201 202
        setStride(sizeof(VertexT));

        auto offset = reinterpret_cast<std::ptrdiff_t>(&(reinterpret_cast<VertexT const volatile*>(0)->*member));
Philip Trettner's avatar
Philip Trettner committed
203
        defineAttributeWithOffset(name, type, elements, GLuint(offset), mode, divisor);
204
    }
205

206
    /// Sets the divisor of _all_ attributes
Philip Trettner's avatar
Philip Trettner committed
207
    /// CAUTION: Only modifies already defined attributes
208 209
    void setDivisor(int div);

210 211
public:
    ArrayBuffer();
212

Philip Trettner's avatar
Philip Trettner committed
213
    /// Binds this array buffer.
214
    /// Unbinding is done when the returned object runs out of scope.
215
    GLOW_NODISCARD BoundArrayBuffer bind() { return {this}; }
216
    friend struct BoundArrayBuffer;
217

218 219 220 221
public: // static construction
    /// Creates an empty array buffer
    /// Same as std::make_shared<ArrayBuffer>();
    static SharedArrayBuffer create();
222
    /// Creates an empty array buffer with the given attribute definitions
Philip Trettner's avatar
Philip Trettner committed
223 224 225
    /// Example:
    ///   ArrayBuffer::create({{&MyVertex::aPosition, "aPosition"},
    ///                        {&MyVertex::texCoord, "aTexCoord"}});
226
    static SharedArrayBuffer create(std::vector<ArrayBufferAttribute> const& attrs);
227 228 229 230 231 232 233 234
    /// Same as create(attrs) but also sets vertices immediately
    template <typename DataT>
    static SharedArrayBuffer create(std::vector<ArrayBufferAttribute> const& attrs, std::vector<DataT> const& vertices)
    {
        auto ab = create(attrs);
        ab->bind().setData(vertices);
        return ab;
    }
235 236 237 238 239 240 241 242
    template <typename DataT>
    static SharedArrayBuffer create(std::string const& name, std::vector<DataT> const& vertices)
    {
        auto ab = create();
        ab->defineAttribute<DataT>(name);
        ab->bind().setData(vertices);
        return ab;
    }
243 244
};
}