VertexArrayObject.hh 18.4 KB
Newer Older
1 2 3 4 5
/***********************************************************************
 * Copyright 2011-2012 Computer Graphics Group RWTH Aachen University. *
 * All rights reserved.                                                *
 * Distributed under the terms of the MIT License (see LICENSE.TXT).   *
 **********************************************************************/
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24

#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).
Robert Menzel's avatar
Robert Menzel committed
25 26 27
 *
 * Note: Primitive restart state (enable/disable, primitive restart index) is NOT
 *       part of the VAO state!
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
 */

#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{

46
// if a VAO is present either because GL 3.0+ is present or because extensions are expected, signal this using this define
47 48
// for classes that expect VAO to be present!
#define ACGL_SUPPORT_VAO
49 50 51 52 53 54 55 56 57 58
class VertexArrayObject
{
    ACGL_NOT_COPYABLE(VertexArrayObject)

    // ==================================================================================================== \/
    // ============================================================================================ STRUCTS \/
    // ==================================================================================================== \/
public:
    struct Attribute
    {
59 60
        ConstSharedArrayBuffer arrayBuffer; // the ArrayBuffer to use
        int32_t                attributeID; // the attribute from that ArrayBuffer
61 62 63
        // more Attribute properties can be looked up in the ArrayBuffer (like the name)
    };

64 65 66 67
    // ===================================================================================================== \/
    // ============================================================================================ TYPEDEFS \/
    // ===================================================================================================== \/
public:
Robert Menzel's avatar
Robert Menzel committed
68
    typedef std::vector< Attribute > AttributeVec; // position in the vector == attrib location
69

70 71 72 73
    // ========================================================================================================= \/
    // ============================================================================================ CONSTRUCTORS \/
    // ========================================================================================================= \/
public:
74
    VertexArrayObject( GLenum _mode = GL_TRIANGLES );
75 76 77 78 79 80 81

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

82 83 84 85
    // ==================================================================================================== \/
    // ============================================================================================ SETTERS \/
    // ==================================================================================================== \/
public:
Robert Menzel's avatar
Robert Menzel committed
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
    /**
     * Sets the default mode of drawing.
     * Must be one of: GL_POINTS
     *                 GL_LINE_STRIP
     *                 GL_LINE_LOOP
     *                 GL_LINES
     *                 GL_TRIANGLE_STRIP
     *                 GL_TRIANGLES_FAN
     *                 GL_TRIANGLES
     * OpenGL >= 3.2 : GL_LINE_STRIP_ADJACENCY
     *                 GL_LINES_ADJACENCY
     *                 GL_TRIANGLE_STRIP_ADJACENCY
     *                 GL_TRIANGLES_ADJACENCY
     * OpenGL >= 4.0 : GL_PATCHES
     *
     */
102 103
    inline void setMode(GLenum _mode) { mMode = _mode; }

104 105 106 107
    // ==================================================================================================== \/
    // ============================================================================================ GETTERS \/
    // ==================================================================================================== \/
public:
108 109 110 111 112
    inline       GLuint                        getObjectName()         const { return mObjectName; }
    inline const AttributeVec&                 getAttributes()         const { return mAttributes; }
    inline       GLenum                        getMode()               const { return mMode;       }
    inline       ConstSharedElementArrayBuffer getElementArrayBuffer() const { return mpElementArrayBuffer; }
    inline       GLuint                        getElements()           const { return mAttributes[0].arrayBuffer->getElements(); }
113 114 115 116 117 118 119 120 121

    // ==================================================================================================== \/
    // ============================================================================================ METHODS \/
    // ==================================================================================================== \/
public:
    /**
     * Set the given ElementArrayBuffer, if a NULL pointer is given, an existing EAB will get unset.
     * Will restore the previously bound VAO (DSA style)
     */
122
    void attachElementArrayBuffer( const ConstSharedElementArrayBuffer& _elementArrayBuffer );
123

Robert Menzel's avatar
Robert Menzel committed
124
    //! sets the EAB to NULL if there was one
125
    inline void detachElementArrayBuffer() { attachElementArrayBuffer( ConstSharedElementArrayBuffer() ); }
126 127 128 129 130

    /**
     * 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
Robert Menzel's avatar
Robert Menzel committed
131
     * An attribute location of -1 indicates that the attribute should get the next free location
132
     */
133
    inline void attachAttribute( const ConstSharedArrayBuffer& _arrayBuffer,
Robert Menzel's avatar
Robert Menzel committed
134 135
                                 uint32_t _arrayBufferAttribute,
                                 GLint    _attributeLocation = -1)
136
    {
Robert Menzel's avatar
Robert Menzel committed
137
        Attribute newAttribute = { _arrayBuffer, (int32_t) _arrayBufferAttribute };
Robert Menzel's avatar
Robert Menzel committed
138
        attachAttribute( newAttribute, _attributeLocation );
139 140
    }

141 142 143 144
    /**
     * 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
Robert Menzel's avatar
Robert Menzel committed
145
     * An attribute location of -1 indicates that the attribute should should get the next free location
146
     */
147 148 149
    void attachAttribute( const ConstSharedArrayBuffer& _arrayBuffer,
                          const std::string& _arrayBufferAttributeName,
                                GLint        _attributeLocation = -1);
150

Robert Menzel's avatar
Robert Menzel committed
151 152 153
    //! if the location was already in use, the new attribute will replace it, if a free loc should be used but no location is free,
    //! nothing will get attached.
    void attachAttribute( const Attribute &_attribute, GLint _location );
154

155 156
    /**
     * Attaches all attributes defined by an ArrayBuffer
Robert Menzel's avatar
Robert Menzel committed
157
     * The attributes are attached to the next free location.
158 159
     * Afterwards, you might want to automatically wire up the attributes with a ShaderProgram, using setAttributeLocationsByShaderProgram
     */
160
    void attachAllAttributes( const ConstSharedArrayBuffer& _arrayBuffer );
161 162

    /**
Robert Menzel's avatar
Robert Menzel committed
163
     * Will detach the Attribute with the given attribute location.
164
     */
Robert Menzel's avatar
Robert Menzel committed
165
    void detachAttribute( GLuint _location );
166 167 168 169

    /**
     * Will detach the first found Attribute with the given name.
     */
Robert Menzel's avatar
Robert Menzel committed
170
    void detachAttribute( const std::string &_name );
171

172 173 174 175 176
    /**
     * Will detach all currently attached Attributes
     */
    void detachAllAttributes();

177
    /**
178 179 180
     * Query the attribute names from the ArrayBuffers.
     * If they match a name from _locationMappings, use the location from _locationMappings.
     * All remaining locations will get a unique location as well (how this is calculated is undefined).
181
     */
Robert Menzel's avatar
Robert Menzel committed
182
    void setAttributeLocations( ConstSharedLocationMappings _locationMappings );
183

184
    //! get a list of attribute locations and names of this VAO. That list can then be used to set up a ShaderProgram.
185
    SharedLocationMappings getAttributeLocations() const;
Robert Menzel's avatar
Robert Menzel committed
186

187
private:
Robert Menzel's avatar
Robert Menzel committed
188
    //! Sets the vertex attribute pointer for the current VAO according to the specified attribute (index into mAttributes)
189 190
    //! Note: expects that this VAO is currently bound
    //! Note: will bind the ArrayBuffer referenced by _attribute.arrayBuffer
Robert Menzel's avatar
Robert Menzel committed
191 192 193 194
    void setAttributePointer( GLuint _index );

    //! returns a free attribute location between 0 .. MAX_ATTRIB_LOCATIONS unless no location is free, in that case returns -1
    GLint getFreeAttributeLocation();
195

196 197 198 199 200
    // ===================================================================================================== \/
    // ============================================================================================ WRAPPERS \/
    // ===================================================================================================== \/
public:
    //! Bind this VAO
Robert Menzel's avatar
Robert Menzel committed
201
    //! some consistancy checks are made
202
    void bind() const;
203 204

    //! Nothing has to be prepared for a render call
Robert Menzel's avatar
Robert Menzel committed
205
    inline void render (void)
206
    {
Robert Menzel's avatar
Robert Menzel committed
207
        storeOldVAOandBind();
208
        draw();
Robert Menzel's avatar
Robert Menzel committed
209
        restoreOldVAO();
210 211
    }

Robert Menzel's avatar
Robert Menzel committed
212
    //! Will select the matching draw call. Remember to bind() first!
213
    void draw( GLsizei _primcount = 1 ) const
214
    {
Robert Menzel's avatar
Robert Menzel committed
215
        if (mpElementArrayBuffer) {
216
            drawElements( _primcount );
Robert Menzel's avatar
Robert Menzel committed
217
        } else {
218
            drawArrays( _primcount );
Robert Menzel's avatar
Robert Menzel committed
219
        }
220 221
    }

222 223 224 225 226 227 228 229 230 231 232 233 234
    // ===================================================================================================== \/
    // ========================================================================================= DRAW ARRAYS \/
    // ===================================================================================================== \/

    //! stores parameters for drawArraysIndirect
    typedef struct {
        GLuint count;
        GLuint primCount;
        GLuint first;
        GLuint baseInstance; // must be 0 pre OpenGL 4.2
    } DrawArraysIndirectCommand;

    //! returns the number of vertices in one of the array buffers. NOTE: only useful if all array buffer have the same number of elements!
235 236 237 238
    inline GLsizei getArrayVertexCount() const {
        int firstAttribute = getFirstAttributeIndex(); // will be -1 if no attribte is set.
        return ( (firstAttribute == -1)?0:mAttributes[firstAttribute].arrayBuffer->getElements());
    }
239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259

    //! draws the whole VAO
    inline void drawArrays(void) const {
        glDrawArrays( mMode, 0, getArrayVertexCount() );
    }

    //! draws the whole VAO, primcount is the number used for instancing
    //! baseinstance does not modify gl_InstanceID
    inline void drawArrays(GLsizei primcount, GLuint baseinstance = 0) const
    {
#if (ACGL_OPENGL_VERSION >= 42)
        glDrawArraysInstancedBaseInstance( mMode, 0, getArrayVertexCount(), primcount, baseinstance );
#elif (ACGL_OPENGL_VERSION >= 31)
        if (baseinstance != 0) ACGL::Utils::error() << "baseinstance is only supported on OpenGL 4.2 and later!" << std::endl;
        glDrawArraysInstanced( mMode, 0, getArrayVertexCount(), primcount );
#else
        if ((baseinstance != 0) || (primcount != 1)) ACGL::Utils::error() << "baseinstance and primcount are only supported on OpenGL 3.1 and later!" << std::endl;
        glDrawArrays( mMode, 0, getArrayVertexCount() );
#endif
    }

Robert Menzel's avatar
Robert Menzel committed
260 261
    //! dispatch multiple draw commands with different parameters, for drawing the same object multiple times, use drawArrays to use instancing
    //! first and count are arrays of the size primcount (so primcount chunks are beeing rendered)
262
    inline void multiDrawArrays( GLint *first, GLsizei *count, GLsizei primcount ) const
263
    {
264 265 266 267 268 269 270 271 272 273 274
        glMultiDrawArrays( mMode, first, count, primcount );
    }

#if (ACGL_OPENGL_VERSION >= 40)
    //! with a parameter struct:
    inline void drawArraysIndirect( const DrawArraysIndirectCommand *_indirect ) const {
        glDrawArraysIndirect( mMode, (void*) _indirect );
    }
    //! with an offset assuming a buffer is bound to GL_DRAW_INDIRECT_BUFFER
    inline void drawArraysIndirectBufferOffset( GLsizeiptr _offset = 0 ) const {
        glDrawArraysIndirect( mMode, (void*) _offset );
275
    }
276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300
#endif

    // ===================================================================================================== \/
    // ======================================================================================= DRAW ELEMENTS \/
    // ===================================================================================================== \/
    /*
      NOTE: no wrappers yet for:
      glDrawElementsBaseVertex
      glDrawRangeElements
      glDrawRangeElementsBaseVertex
     */

    typedef struct {
        GLuint count;
        GLuint primCount;
        GLuint firstIndex;
        GLint  baseVertex;
        GLuint baseInstance; // must be 0 pre OpenGL 4.2
    } DrawElementsIndirectCommand;

    //! returns the number of vertices indexed by the element array buffer (0 if there is none)
    inline GLsizei getIndexCount() const { return (mpElementArrayBuffer)?mpElementArrayBuffer->getElements():0; }

    //! returns the index data type: GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT, GL_UNSIGNED_INT or GL_INVALID_ENUM if no index is set
    inline GLenum getIndexType() const { return (mpElementArrayBuffer)?mpElementArrayBuffer->getType():GL_INVALID_ENUM; }
301 302 303

    //! Can be called directly instead of draw() iff the caller knows this is the correct call!
    //! Draws all elements
304 305
    inline void drawElements(void) const
    {
306
        glDrawElements(mMode, getIndexCount(), getIndexType(), (GLvoid*)NULL);
307 308
    }

Robert Menzel's avatar
Robert Menzel committed
309
    //! count and indices are arrays of the size primcount (so primcount chunks are beeing rendered)
310
    inline void multiDrawElements( GLsizei *count, GLenum type, const GLvoid **indices, GLsizei primcount ) const
311
    {
312
        glMultiDrawElements( mMode, count, type, indices, primcount );
313 314
    }

315 316 317
    inline void drawRangeElements( GLuint start,
                                   GLsizei count )
    {
Robert Menzel's avatar
Robert Menzel committed
318
        glDrawRangeElements( mMode, start, getIndexCount(), count, getIndexType(), NULL);
319 320
    }

321
#if (ACGL_OPENGL_VERSION >= 32)
322
    inline void multiDrawElements( GLsizei *count, GLenum type, GLvoid **indices, GLsizei primcount, GLint *basevertex ) const
323
    {
324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352
        glMultiDrawElementsBaseVertex( mMode, count, type, indices, primcount, basevertex );
    }
#endif

    //! for instanced rendering: primcount is the number of instances
    //! baseinstance does not modify gl_InstanceID
    inline void drawElements( GLsizei primcount, GLint basevertex = 0, GLuint baseinstance = 0 ) const {
#if (ACGL_OPENGL_VERSION >= 42)
        glDrawElementsInstancedBaseVertexBaseInstance( mMode, getIndexCount(), getIndexType(), (GLvoid*)NULL, primcount, basevertex, baseinstance );
#elif (ACGL_OPENGL_VERSION >= 32)
        if (baseinstance != 0) ACGL::Utils::error() << "baseinstance is only supported on OpenGL 4.2 and later!" << std::endl;
        glDrawElementsInstancedBaseVertex( mMode, getIndexCount(), getIndexType(), (GLvoid*)NULL, primcount, basevertex );
#elif (ACGL_OPENGL_VERSION >= 31)
        if ((baseinstance != 0) || (basevertex != 0)) ACGL::Utils::error() << "baseinstance and basevertex are only supported on OpenGL 3.2 and later!" << std::endl;
        glDrawElementsInstanced( mMode, getIndexCount(), getIndexType(), (GLvoid*)NULL, primcount );
#else
        if ((baseinstance != 0) || (basevertex != 0) || (primcount != 1)) ACGL::Utils::error() << "drawElements not supported in this OpenGL version!" << std::endl;
        drawElements();
#endif
    }

#if (ACGL_OPENGL_VERSION >= 40)
    //! with a parameter struct:
    inline void drawElementsIndirect( const DrawElementsIndirectCommand *_indirect ) const {
        glDrawElementsIndirect( mMode, getIndexType(), (void*) _indirect );
    }
    //! with an offset assuming a buffer is bound to GL_DRAW_INDIRECT_BUFFER
    inline void drawElementsIndirect( GLsizeiptr _offset = 0) const {
        glDrawElementsIndirect( mMode, getIndexType(), (void*) _offset );
353
    }
354
#endif
355

Robert Menzel's avatar
Robert Menzel committed
356 357 358
private:
    //! Bind this VAO and remember the previously bound VAO
    //! Note: every call to this method must be paired with a corresponding call to disable()
Robert Menzel's avatar
Robert Menzel committed
359
    inline void storeOldVAOandBind(void)
Robert Menzel's avatar
Robert Menzel committed
360 361 362 363 364 365 366
    {
        // remember old VAO
        glGetIntegerv( GL_VERTEX_ARRAY_BINDING, &mPreviousVAOName );
        if (mObjectName != (GLuint)mPreviousVAOName) bind();
    }

    //! Bind the VAO that was bound before the most recent enable() call
Robert Menzel's avatar
Robert Menzel committed
367
    inline void restoreOldVAO(void)
Robert Menzel's avatar
Robert Menzel committed
368 369 370 371
    {
        if (mObjectName != (GLuint)mPreviousVAOName) glBindVertexArray((GLuint)mPreviousVAOName);
    }

372 373 374 375
    //! Returns the first attribute that has a non-NULL ArrayBuffer attached
    inline int_t getFirstAttributeIndex() const
    {
        // TODO: cache this value?
Robert Menzel's avatar
Robert Menzel committed
376 377
        for (unsigned int index = 0; index < mAttributes.size(); ++index)
            if (mAttributes[index].arrayBuffer)
378 379 380 381
                return index;
        return -1;
    }

Robert Menzel's avatar
Robert Menzel committed
382 383 384 385
    // =================================================================================================== \/
    // ============================================================================================ FIELDS \/
    // =================================================================================================== \/
private:
386 387 388 389 390
    ConstSharedElementArrayBuffer mpElementArrayBuffer; // optional EAB
    AttributeVec                  mAttributes;          // vertex attributes
    GLuint                        mObjectName;          // OpenGL object name
    GLenum                        mMode;                // primitive type to render (e.g. GL_TRIANGLES)
    GLint                         mPreviousVAOName;     // the VAO that was bound before the last enable() call
391 392
};

Robert Menzel's avatar
Robert Menzel committed
393
ACGL_SMARTPOINTER_TYPEDEFS(VertexArrayObject)
394 395 396 397 398 399 400

} // OpenGL
} // ACGL

#endif // OpenGL 3.0

#endif // ACGL_OPENGL_OBJECTS_VERTEXARRAYOBJECT_HH