VertexArrayObject.hh 16.9 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
59
60
61
62
63
64
65
////////////////////////////////////////////////////////////////////////////////
// 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)
    };

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

81
82
83
84
85
86
    // ==================================================================================================== \/
    // ============================================================================================ SETTERS \/
    // ==================================================================================================== \/
public:
    inline void setMode(GLenum _mode) { mMode = _mode; }

87
88
89
90
91
92
    // ==================================================================================================== \/
    // ============================================================================================ GETTERS \/
    // ==================================================================================================== \/
public:
    inline GLuint operator()   (void) const { return mObjectName; }
    inline GLuint getObjectName(void) const { return mObjectName; }
93
    inline GLenum getMode(void)       const { return mMode;       }
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111

    // ==================================================================================================== \/
    // ============================================================================================ 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 )
    {
112
        enable();
113
114
115

        mpElementArrayBuffer = _elementArrayBuffer;

116
117
        if (mpElementArrayBuffer) // could be set to NULL!
        {
118
            mpElementArrayBuffer->bind();
119
120
121
        }
        else
        {
122
123
124
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
        }

125
        disable();
126
127
128
129
130
131
132
133
    }

    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
134
     * An attribute location of -1 indicates that the attribute should remain unbound for now
135
     */
136
    inline void attachAttribute( const SharedArrayBuffer& _arrayBuffer,
137
                          uint32_t _arrayBufferAttribute,
138
                          GLint   _attributeLocation = -1)
139
140
141
142
143
    {
        Attribute newAttribute = { _arrayBuffer, _arrayBufferAttribute, _attributeLocation };
        attachAttribute( newAttribute );
    }

144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
    /**
     * 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);
    }

159
160
161
162
    void attachAttribute( const Attribute &_attribute )
    {
        GLint maxAttributes;
        glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxAttributes); // TODO: clever caching
163
164
165

        if(mAttributes.size() >= (uint32_t) maxAttributes)
        {
166
167
168
169
170
171
172
            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 );

173
174
175
176
        enable();
        setAttributePointer( _attribute );
        disable();
    }
177

178
179
180
181
182
183
184
185
186
187
188
189
    /**
     * 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);
        }
190
191
192
193
194
195
196
197
198
    }

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

259
260
            if (shaderLocation == -1)
            {
261
262
263
264
                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
            }

265
266
            if (mAttributes[i].location != shaderLocation)
            {
267
268
269
270
271
272
273
                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...
274
275
276
        if (fullUpdateNeeded)
        {
            enable();
277

278
            // disable all attributes
279
280
            GLint maxAttributes;
            glGetIntegerv( GL_MAX_VERTEX_ATTRIBS, &maxAttributes );
281
282
            for (GLint i = 0; i < maxAttributes; ++i)
                glDisableVertexAttribArray( i );
283
284

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

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

320
321
322
323
324
325
326
327
328
329
    // ===================================================================================================== \/
    // ============================================================================================ WRAPPERS \/
    // ===================================================================================================== \/
public:
    //! Bind this VAO
    inline void bind(void) const
    {
        glBindVertexArray( mObjectName );
    }

330
331
332
333
334
335
336
337
338
339
340
341
342
343
    //! 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);
    }
344
345
346
347
348
349
350
351
352
353
354
355

    //! 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
    {
356
357
358
359
360
361
362
        // TODO: fail silently?
        if(mAttributes.empty())
        {
            ACGL::Utils::error() << "cannot draw VAO with no attributes attached" << std::endl;
            return;
        }

363
364
365
366
367
368
369
370
371
        if(mpElementArrayBuffer)
            drawElements();
        else
            drawArrays();

        openGLRareError();
    }

    //! Can be called directly instead of draw() iff the caller knows this is the correct call!
372
373
374
375
376
377
378
    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
379
380
    inline void drawElements(void) const
    {
381
        drawElements(mpElementArrayBuffer->getElements());
382
383
384
    }

    //! Can be called directly instead of draw() iff the caller knows this is the correct call!
385
386
387
388
389
390
391
    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
392
393
    inline void drawArrays(void) const
    {
394
        drawArrays(mAttributes[0].arrayBuffer->getElements());
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
    }

    // ===================================================================================================== \/
    // ============================================================================================ TYPEDEFS \/
    // ===================================================================================================== \/
protected:
    typedef std::vector< Attribute > AttributeVec;

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

private:
    static GLint             sPreviousVAOName;     // the VAO that was bound before the last enable() call
414
415
416
417
418
419
420
421
422
423
};

ACGL_SHARED_TYPEDEF(VertexArrayObject)

} // OpenGL
} // ACGL

#endif // OpenGL 3.0

#endif // ACGL_OPENGL_OBJECTS_VERTEXARRAYOBJECT_HH