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
58
////////////////////////////////////////////////////////////////////////////////
// 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>
#include <tr1/memory>

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)
    };

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

65
66
67
68
69
70
71
    // ========================================================================================================= \/
    // ============================================================================================ CONSTRUCTORS \/
    // ========================================================================================================= \/
public:
    VertexArrayObject()
    :   mObjectName(0),
        mpElementArrayBuffer(),
72
73
        mAttributes(),
        mMode(GL_TRIANGLES)
74
75
76
77
78
79
80
81
82
83
84
85
86
    {
        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);
    }

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

93
94
95
96
    // ==================================================================================================== \/
    // ============================================================================================ GETTERS \/
    // ==================================================================================================== \/
public:
97
98
99
100
    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;       }
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118

    // ==================================================================================================== \/
    // ============================================================================================ 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 )
    {
119
        enable();
120
121
122

        mpElementArrayBuffer = _elementArrayBuffer;

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

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

    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
141
     * An attribute location of -1 indicates that the attribute should remain unbound for now
142
     */
143
    inline void attachAttribute( const SharedArrayBuffer& _arrayBuffer,
144
                          uint32_t _arrayBufferAttribute,
145
                          GLint   _attributeLocation = -1)
146
147
148
149
150
    {
        Attribute newAttribute = { _arrayBuffer, _arrayBufferAttribute, _attributeLocation };
        attachAttribute( newAttribute );
    }

151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
    /**
     * 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);
    }

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

        if(mAttributes.size() >= (uint32_t) maxAttributes)
        {
173
174
175
176
177
178
179
            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 );

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

185
186
187
188
189
190
191
192
193
194
195
196
    /**
     * 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);
        }
197
198
199
200
201
202
203
204
205
    }

    /**
     * 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)
        {
206
207
            if (mAttributes[i].location == _location)
            {
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
                // 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)
        {
225
226
            if (mAttributes[i].arrayBuffer->getAttributes()[ mAttributes[i].attributeID ].name == _name)
            {
227
228
229
230
231
232
233
234
235
236
237
238
239
240
                // 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.
     */
241
    bool mappingsMatchShaderProgram( const SharedShaderProgram &_shaderProgram ) const
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
    {
        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.
     */
257
    void setAttributeLocationsByShaderProgram( const SharedShaderProgram &_shaderProgram )
258
    {
259
        // TODO: deprecate
260
261
262
263
264
265
266
        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 );

267
268
            if (shaderLocation == -1)
            {
269
270
271
272
                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
            }

273
274
            if (mAttributes[i].location != shaderLocation)
            {
275
276
277
278
279
280
281
                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...
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
330
        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...
331
332
333
        if (fullUpdateNeeded)
        {
            enable();
334

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

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

347
            disable();
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
376
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);
    }

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

387
388
389
390
391
392
393
394
395
396
397
398
399
400
    //! 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);
    }
401
402
403
404
405
406
407
408
409
410
411
412

    //! 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
    {
413
414
415
416
417
418
419
        // TODO: fail silently?
        if(mAttributes.empty())
        {
            ACGL::Utils::error() << "cannot draw VAO with no attributes attached" << std::endl;
            return;
        }

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

        openGLRareError();
    }

    //! Can be called directly instead of draw() iff the caller knows this is the correct call!
429
430
431
432
433
434
435
    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
436
437
    inline void drawElements(void) const
    {
438
        drawElements(mpElementArrayBuffer->getElements());
439
440
441
    }

    //! Can be called directly instead of draw() iff the caller knows this is the correct call!
442
443
444
445
446
447
448
    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
449
450
    inline void drawArrays(void) const
    {
451
        drawArrays(mAttributes[0].arrayBuffer->getElements());
452
453
454
455
456
457
458
459
460
    }

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

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

ACGL_SHARED_TYPEDEF(VertexArrayObject)

} // OpenGL
} // ACGL

#endif // OpenGL 3.0

#endif // ACGL_OPENGL_OBJECTS_VERTEXARRAYOBJECT_HH