VertexArrayObject.hh 18.7 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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
////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2011, Computer Graphics Group RWTH Aachen University         //
// All rights reserved.                                                       //
////////////////////////////////////////////////////////////////////////////////

#ifndef ACGL_OPENGL_OBJECTS_VERTEXARRAYOBJECT_HH
#define ACGL_OPENGL_OBJECTS_VERTEXARRAYOBJECT_HH

/**
 * A VertexArrayObject is a predefined combination of (multiple) attributes of
 * (multiple) ArrayBuffers and optionally one ElementArrayBuffer.
 *
 * It's only present in OpenGL 3.0 onwards. For older implementations (or
 * embedded systems) see VertexBufferObject which is a softwareimplementation
 * of the same idea).
 * Alternatively, there are the GL_APPLE_vertex_array_object and
 * GL_ARB_vertex_array_object extensions for OpenGL 2.1.
 * OES_vertex_array_object for OpenGL ES (e.g. iOS 4.0+)
 *
 * A VAO will cache the enabled vertex attributes (set with glEnableVertexAttribArray)
 * and vertex attribute pointer calls (glVertexAttribPointer).
 * Binding a VAO will restore that state (saving a lot of gl calls to do that
 * manually).
 */

#include <ACGL/ACGL.hh>
#include <ACGL/OpenGL/GL.hh>
#if (ACGL_OPENGL_VERSION >= 30)

#include <ACGL/Base/Macros.hh>
#include <ACGL/OpenGL/Tools.hh>

#include <ACGL/OpenGL/Objects/ArrayBuffer.hh>
#include <ACGL/OpenGL/Objects/ElementArrayBuffer.hh>
#include <ACGL/OpenGL/Objects/ShaderProgram.hh>

#include <vector>

namespace ACGL{
namespace OpenGL{

class VertexArrayObject
{
    ACGL_NOT_COPYABLE(VertexArrayObject)

    // ==================================================================================================== \/
    // ============================================================================================ STRUCTS \/
    // ==================================================================================================== \/
public:
    struct Attribute
    {
        SharedArrayBuffer arrayBuffer; // the ArrayBuffer to use
        int32_t           attributeID; // the attribute from that ArrayBuffer
        GLint             location;    // a location the in-attribute from a shader is bound to
        // more Attribute properties can be looked up in the ArrayBuffer (like the name)
    };

58
59
60
61
62
63
    // ===================================================================================================== \/
    // ============================================================================================ TYPEDEFS \/
    // ===================================================================================================== \/
public:
    typedef std::vector< Attribute > AttributeVec;

64
65
66
67
68
69
70
    // ========================================================================================================= \/
    // ============================================================================================ CONSTRUCTORS \/
    // ========================================================================================================= \/
public:
    VertexArrayObject()
    :   mObjectName(0),
        mpElementArrayBuffer(),
71
72
        mAttributes(),
        mMode(GL_TRIANGLES)
73
74
75
76
77
78
79
80
81
82
83
84
85
    {
        glGenVertexArrays(1, &mObjectName);
        if (openGLCriticalErrorOccured() ) {
            ACGL::Utils::error() << "could not generate vertex array object!" << std::endl;
        }
    }

    virtual ~VertexArrayObject(void)
    {
        // as always, OpenGL will ignore object name 0
        glDeleteVertexArrays(1, &mObjectName);
    }

86
87
88
89
90
91
    // ==================================================================================================== \/
    // ============================================================================================ SETTERS \/
    // ==================================================================================================== \/
public:
    inline void setMode(GLenum _mode) { mMode = _mode; }

92
93
94
95
    // ==================================================================================================== \/
    // ============================================================================================ GETTERS \/
    // ==================================================================================================== \/
public:
96
97
98
99
    inline       GLuint        operator()   (void) const { return mObjectName; }
    inline       GLuint        getObjectName(void) const { return mObjectName; }
    inline const AttributeVec& getAttributes(void) const { return mAttributes; }
    inline       GLenum        getMode      (void) const { return mMode;       }
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117

    // ==================================================================================================== \/
    // ============================================================================================ METHODS \/
    // ==================================================================================================== \/
public:
    /**
     * Will check if the VAO looks ok (e.g. there is at least one ArrayBuffer and all ArrayBuffers
     * have the same number of elements).
     * A failed test will output an error but won't have other consequences.
     */
    bool isValid(void) const;

    /**
     * Set the given ElementArrayBuffer, if a NULL pointer is given, an existing EAB will get unset.
     * Will restore the previously bound VAO (DSA style)
     */
    void attachElementArrayBuffer( const SharedElementArrayBuffer& _elementArrayBuffer )
    {
118
        enable();
119
120
121

        mpElementArrayBuffer = _elementArrayBuffer;

122
123
        if (mpElementArrayBuffer) // could be set to NULL!
        {
124
            mpElementArrayBuffer->bind();
125
126
127
        }
        else
        {
128
129
130
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
        }

131
        disable();
132
133
134
135
136
137
138
139
    }

    inline void detachElementArrayBuffer() { attachElementArrayBuffer( SharedElementArrayBuffer() ); }

    /**
     * Will set the attribute _arrayBufferAttribute of ArrayBuffer _arrayBuffer to the given attribute location.
     * If that location was already used it will get overwritten.
     * The _attributeLocation has to be lower than GL_MAX_VERTEX_ATTRIBS
140
     * An attribute location of -1 indicates that the attribute should remain unbound for now
141
     */
142
    inline void attachAttribute( const SharedArrayBuffer& _arrayBuffer,
143
                          uint32_t _arrayBufferAttribute,
144
                          GLint   _attributeLocation = -1)
145
146
147
148
149
    {
        Attribute newAttribute = { _arrayBuffer, _arrayBufferAttribute, _attributeLocation };
        attachAttribute( newAttribute );
    }

150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
    /**
     * Will set the attribute named _arrayBufferAttributeName of ArrayBuffer _arrayBuffer to the given attribute location.
     * If that location was already used it will get overwritten.
     * The _attributeLocation has to be lower than GL_MAX_VERTEX_ATTRIBS
     * An attribute location of -1 indicates that the attribute should remain unbound for now
     */
    inline void attachAttribute( const SharedArrayBuffer& _arrayBuffer,
                          const std::string& _arrayBufferAttributeName,
                          GLint   _attributeLocation = -1)
    {
        attachAttribute(_arrayBuffer,
                        _arrayBuffer->getAttributeIndexByName(_arrayBufferAttributeName),
                        _attributeLocation);
    }

165
166
167
168
    void attachAttribute( const Attribute &_attribute )
    {
        GLint maxAttributes;
        glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxAttributes); // TODO: clever caching
169
170
171

        if(mAttributes.size() >= (uint32_t) maxAttributes)
        {
172
173
174
175
176
177
178
            ACGL::Utils::error() << "can't attach attribute " << _attribute.arrayBuffer->getAttributes()[_attribute.attributeID].name
                                 << " - maximum number of attributes reached: " << maxAttributes << std::endl;
            return;
        }

        mAttributes.push_back( _attribute );

179
180
181
182
        enable();
        setAttributePointer( _attribute );
        disable();
    }
183

184
185
186
187
188
189
190
191
192
193
194
195
    /**
     * Attaches all attributes defined by an ArrayBuffer
     * The attributes are attached to the default location of -1, indicating they should remain unbound for now
     * Afterwards, you might want to automatically wire up the attributes with a ShaderProgram, using setAttributeLocationsByShaderProgram
     */
    void attachAllAttributes( const SharedArrayBuffer& _arrayBuffer )
    {
        ArrayBuffer::AttributeVec attributes = _arrayBuffer->getAttributes();
        for(ArrayBuffer::AttributeVec::size_type i = 0; i < attributes.size(); ++i)
        {
            attachAttribute(_arrayBuffer, i);
        }
196
197
198
199
200
201
202
203
204
    }

    /**
     * Will detach the first found Attribute with the given attribute location.
     */
    void detachAttribute( GLint _location )
    {
        for (AttributeVec::size_type i = 0; i < mAttributes.size(); ++i)
        {
205
206
            if (mAttributes[i].location == _location)
            {
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
                // the other pointer data is still set, but that isn't relevant if the attribute itself is deactivated
                glDisableVertexArrayAttribEXT( mObjectName, mAttributes[i].location );
                mAttributes.erase( mAttributes.begin()+i );
                return;
            }
        }
        // if we got here, no Attribute of the given name exists
        ACGL::Utils::warning() << "can't detach attribute with location " << _location << " - no such Attribute" << std::endl;
    }

    /**
     * Will detach the first found Attribute with the given name.
     */
    void detachAttribute( const std::string &_name )
    {
        for (AttributeVec::size_type i = 0; i < mAttributes.size(); ++i)
        {
224
225
            if (mAttributes[i].arrayBuffer->getAttributes()[ mAttributes[i].attributeID ].name == _name)
            {
226
227
228
229
230
231
232
233
234
235
236
237
238
239
                // the other pointer data is still set, but that isn't relevant if the attribute itself is deactivated
                glDisableVertexArrayAttribEXT( mObjectName, mAttributes[i].location );
                mAttributes.erase( mAttributes.begin()+i );
                return;
            }
        }
        // if we got here, no Attribute of the given name exists
        ACGL::Utils::warning() << "can't detach attribute " << _name << " - no such Attribute" << std::endl;
    }

    /**
     * Return true if setAttributeMappingsByShaderProgram(_shaderProgram) would do nothing.
     * That means the VAO and ShaderProgram have already matching layouts.
     */
240
    bool mappingsMatchShaderProgram( const SharedShaderProgram &_shaderProgram ) const
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
    {
        for (AttributeVec::size_type i = 0; i < mAttributes.size(); ++i)
        {
            std::string attributeName  = mAttributes[i].arrayBuffer->getAttributes()[ mAttributes[i].attributeID ].name;
            GLint       shaderLocation = _shaderProgram->getAttributeLocation( attributeName );

            if (mAttributes[i].location != shaderLocation) return false;
        }
        return true;
    }

    /**
     * Query the attribute locations based on the attribute names in the ArrayBuffers and the ShaderProgram
     * If they match, use the location reported from the ShaderProgram.
     */
256
    void setAttributeLocationsByShaderProgram( const SharedShaderProgram &_shaderProgram )
257
    {
258
        // TODO: deprecate
259
260
261
262
263
264
265
        bool fullUpdateNeeded = false;

        for (AttributeVec::size_type i = 0; i < mAttributes.size(); ++i)
        {
            std::string attributeName  = mAttributes[i].arrayBuffer->getAttributes()[ mAttributes[i].attributeID ].name;
            GLint       shaderLocation = _shaderProgram->getAttributeLocation( attributeName );

266
267
            if (shaderLocation == -1)
            {
268
269
270
271
                ACGL::Utils::error() << "can't update VAO mappings, attribute " << attributeName << " does not exist in shader" << std::endl;
                continue; // try to match as much as possible
            }

272
273
            if (mAttributes[i].location != shaderLocation)
            {
274
275
276
277
278
279
280
                mAttributes[i].location  = shaderLocation;
                fullUpdateNeeded = true;
            }
        }

        // why the full update? setting the new location right when a change is detected will get problamatic
        // if two attributes exchange there position...
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
        if (fullUpdateNeeded)
        {
            enable();

            // disable all attributes
            GLint maxAttributes;
            glGetIntegerv( GL_MAX_VERTEX_ATTRIBS, &maxAttributes );
            for (GLint i = 0; i < maxAttributes; ++i)
                glDisableVertexAttribArray( i );

            // set all attributes:
            for (uint32_t i = 0; i < mAttributes.size(); ++i)
            {
                setAttributePointer(mAttributes[i]);
            }

            disable();
        }
    }

    /**
     * Query the attribute locations based on the attribute names in the ArrayBuffers and the ShaderProgram
     * If they match, use the location reported from the ShaderProgram.
     */
    void setAttributeLocations( ConstSharedLocationMappings _locationMappings )
    {
        bool fullUpdateNeeded = false;

        for (AttributeVec::size_type i = 0; i < mAttributes.size(); ++i)
        {
            std::string attributeName = mAttributes[i].arrayBuffer->getAttributes()[ mAttributes[i].attributeID ].name;
            int_t       location      = _locationMappings->getLocation(attributeName);

            if(location == -1)
            {
                // TODO: fail silently?
                ACGL::Utils::error() << "can't update VAO mappings: attribute " << attributeName << " not specified" << std::endl;
                continue; // try to match as much as possible
            }

            if(mAttributes[i].location != location)
            {
                mAttributes[i].location = location;
                fullUpdateNeeded = true;
            }
        }

        // why the full update? setting the new location right when a change is detected will get problamatic
        // if two attributes exchange there position...
330
331
332
        if (fullUpdateNeeded)
        {
            enable();
333

334
            // disable all attributes
335
336
            GLint maxAttributes;
            glGetIntegerv( GL_MAX_VERTEX_ATTRIBS, &maxAttributes );
337
338
            for (GLint i = 0; i < maxAttributes; ++i)
                glDisableVertexAttribArray( i );
339
340

            // set all attributes:
341
342
343
            for (uint32_t i = 0; i < mAttributes.size(); ++i)
            {
                setAttributePointer(mAttributes[i]);
344
345
            }

346
            disable();
347
348
349
        }
    }

350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
private:
    //! Sets the vertex attribute pointer for the current VAO according to the specified attribute data
    //! Note: expects that this VAO is currently bound
    //! Note: will bind the ArrayBuffer referenced by _attribute.arrayBuffer
    void setAttributePointer(const Attribute& _attribute)
    {
        if(_attribute.location == -1)
        {
            // An attribute location of -1 indicates that this attribute should remain unbound for now
            return;
        }

        SharedArrayBuffer arrayBuffer = _attribute.arrayBuffer;
        arrayBuffer->bind();

        ArrayBuffer::Attribute arrayBufferAttribute = arrayBuffer->getAttributes()[_attribute.attributeID];
        glVertexAttribPointer(_attribute.location,
                              arrayBufferAttribute.size,
                              arrayBufferAttribute.type,
                              arrayBufferAttribute.normalized,
                              arrayBuffer->getStride(),
                              reinterpret_cast<GLvoid*>(arrayBufferAttribute.offset)
                              );
        glEnableVertexAttribArray(_attribute.location);
    }

376
377
378
379
380
381
382
383
384
385
    // ===================================================================================================== \/
    // ============================================================================================ WRAPPERS \/
    // ===================================================================================================== \/
public:
    //! Bind this VAO
    inline void bind(void) const
    {
        glBindVertexArray( mObjectName );
    }

386
387
388
389
390
391
392
393
394
395
396
397
398
399
    //! Bind this VAO and remember the previously bound VAO
    //! Note: every call to this method must be paired with a corresponding call to disable()
    inline void enable(void) const
    {
        // remember old VAO
        glGetIntegerv( GL_VERTEX_ARRAY_BINDING, &sPreviousVAOName );
        bind();
    }

    //! Bind the VAO that was bound before the most recent enable() call
    inline void disable(void) const
    {
        glBindVertexArray(sPreviousVAOName);
    }
400
401
402
403
404
405
406
407
408
409
410
411

    //! Nothing has to be prepared for a render call
    inline void render (void)  const
    {
        enable();
        draw();
        disable();
    }

    //! Will select the matching draw call. Remember to enable first!
    void draw(void) const
    {
412
413
414
415
416
417
418
        // TODO: fail silently?
        if(mAttributes.empty())
        {
            ACGL::Utils::error() << "cannot draw VAO with no attributes attached" << std::endl;
            return;
        }

419
420
421
422
423
424
425
426
427
        if(mpElementArrayBuffer)
            drawElements();
        else
            drawArrays();

        openGLRareError();
    }

    //! Can be called directly instead of draw() iff the caller knows this is the correct call!
428
429
430
431
432
433
434
    inline void drawElements(GLsizei _elements, const GLvoid* _offset = 0) const
    {
        glDrawElements(mMode, _elements, mpElementArrayBuffer->getType(), _offset);
    }

    //! Can be called directly instead of draw() iff the caller knows this is the correct call!
    //! Draws all elements
435
436
    inline void drawElements(void) const
    {
437
        drawElements(mpElementArrayBuffer->getElements());
438
439
440
    }

    //! Can be called directly instead of draw() iff the caller knows this is the correct call!
441
442
443
444
445
446
447
    inline void drawArrays(GLsizei _elements, GLint _offset = 0) const
    {
        glDrawArrays(mMode, _offset, _elements);
    }

    //! Can be called directly instead of draw() iff the caller knows this is the correct call!
    //! Draws all elements
448
449
    inline void drawArrays(void) const
    {
450
        drawArrays(mAttributes[0].arrayBuffer->getElements());
451
452
453
454
455
456
457
458
459
    }

    // =================================================================================================== \/
    // ============================================================================================ FIELDS \/
    // =================================================================================================== \/
protected:
    GLuint                   mObjectName;          // OpenGL object name
    SharedElementArrayBuffer mpElementArrayBuffer; // optional EAB
    AttributeVec             mAttributes;          // vertex attributes
460
461
462
463
    GLenum                   mMode;                // primitive type to render (e.g. GL_TRIANGLES)

private:
    static GLint             sPreviousVAOName;     // the VAO that was bound before the last enable() call
464
465
466
467
468
469
470
471
472
473
};

ACGL_SHARED_TYPEDEF(VertexArrayObject)

} // OpenGL
} // ACGL

#endif // OpenGL 3.0

#endif // ACGL_OPENGL_OBJECTS_VERTEXARRAYOBJECT_HH