401 lines
18 KiB
C++
401 lines
18 KiB
C++
|
/***********************************************************************
|
||
|
* Copyright 2011-2012 Computer Graphics Group RWTH Aachen University. *
|
||
|
* All rights reserved. *
|
||
|
* Distributed under the terms of the MIT License (see LICENSE.TXT). *
|
||
|
**********************************************************************/
|
||
|
|
||
|
#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).
|
||
|
*
|
||
|
* Note: Primitive restart state (enable/disable, primitive restart index) is NOT
|
||
|
* part of the VAO state!
|
||
|
*/
|
||
|
|
||
|
#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{
|
||
|
|
||
|
// if a VAO is present either because GL 3.0+ is present or because extensions are expected, signal this using this define
|
||
|
// for classes that expect VAO to be present!
|
||
|
#define ACGL_SUPPORT_VAO
|
||
|
class VertexArrayObject
|
||
|
{
|
||
|
ACGL_NOT_COPYABLE(VertexArrayObject)
|
||
|
|
||
|
// ==================================================================================================== \/
|
||
|
// ============================================================================================ STRUCTS \/
|
||
|
// ==================================================================================================== \/
|
||
|
public:
|
||
|
struct Attribute
|
||
|
{
|
||
|
ConstSharedArrayBuffer arrayBuffer; // the ArrayBuffer to use
|
||
|
int32_t attributeID; // the attribute from that ArrayBuffer
|
||
|
// more Attribute properties can be looked up in the ArrayBuffer (like the name)
|
||
|
};
|
||
|
|
||
|
// ===================================================================================================== \/
|
||
|
// ============================================================================================ TYPEDEFS \/
|
||
|
// ===================================================================================================== \/
|
||
|
public:
|
||
|
typedef std::vector< Attribute > AttributeVec; // position in the vector == attrib location
|
||
|
|
||
|
// ========================================================================================================= \/
|
||
|
// ============================================================================================ CONSTRUCTORS \/
|
||
|
// ========================================================================================================= \/
|
||
|
public:
|
||
|
VertexArrayObject( GLenum _mode = GL_TRIANGLES );
|
||
|
|
||
|
virtual ~VertexArrayObject(void)
|
||
|
{
|
||
|
// as always, OpenGL will ignore object name 0
|
||
|
glDeleteVertexArrays(1, &mObjectName);
|
||
|
}
|
||
|
|
||
|
// ==================================================================================================== \/
|
||
|
// ============================================================================================ SETTERS \/
|
||
|
// ==================================================================================================== \/
|
||
|
public:
|
||
|
/**
|
||
|
* 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
|
||
|
*
|
||
|
*/
|
||
|
inline void setMode(GLenum _mode) { mMode = _mode; }
|
||
|
|
||
|
// ==================================================================================================== \/
|
||
|
// ============================================================================================ GETTERS \/
|
||
|
// ==================================================================================================== \/
|
||
|
public:
|
||
|
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(); }
|
||
|
|
||
|
// ==================================================================================================== \/
|
||
|
// ============================================================================================ 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)
|
||
|
*/
|
||
|
void attachElementArrayBuffer( const ConstSharedElementArrayBuffer& _elementArrayBuffer );
|
||
|
|
||
|
//! sets the EAB to NULL if there was one
|
||
|
inline void detachElementArrayBuffer() { attachElementArrayBuffer( ConstSharedElementArrayBuffer() ); }
|
||
|
|
||
|
/**
|
||
|
* 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
|
||
|
* An attribute location of -1 indicates that the attribute should get the next free location
|
||
|
*/
|
||
|
inline void attachAttribute( const ConstSharedArrayBuffer& _arrayBuffer,
|
||
|
uint32_t _arrayBufferAttribute,
|
||
|
GLint _attributeLocation = -1)
|
||
|
{
|
||
|
Attribute newAttribute = { _arrayBuffer, (int32_t) _arrayBufferAttribute };
|
||
|
attachAttribute( newAttribute, _attributeLocation );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 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 should get the next free location
|
||
|
*/
|
||
|
void attachAttribute( const ConstSharedArrayBuffer& _arrayBuffer,
|
||
|
const std::string& _arrayBufferAttributeName,
|
||
|
GLint _attributeLocation = -1);
|
||
|
|
||
|
//! 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 );
|
||
|
|
||
|
/**
|
||
|
* Attaches all attributes defined by an ArrayBuffer
|
||
|
* The attributes are attached to the next free location.
|
||
|
* Afterwards, you might want to automatically wire up the attributes with a ShaderProgram, using setAttributeLocationsByShaderProgram
|
||
|
*/
|
||
|
void attachAllAttributes( const ConstSharedArrayBuffer& _arrayBuffer );
|
||
|
|
||
|
/**
|
||
|
* Will detach the Attribute with the given attribute location.
|
||
|
*/
|
||
|
void detachAttribute( GLuint _location );
|
||
|
|
||
|
/**
|
||
|
* Will detach the first found Attribute with the given name.
|
||
|
*/
|
||
|
void detachAttribute( const std::string &_name );
|
||
|
|
||
|
/**
|
||
|
* Will detach all currently attached Attributes
|
||
|
*/
|
||
|
void detachAllAttributes();
|
||
|
|
||
|
/**
|
||
|
* 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).
|
||
|
*/
|
||
|
void setAttributeLocations( ConstSharedLocationMappings _locationMappings );
|
||
|
|
||
|
//! get a list of attribute locations and names of this VAO. That list can then be used to set up a ShaderProgram.
|
||
|
SharedLocationMappings getAttributeLocations() const;
|
||
|
|
||
|
private:
|
||
|
//! Sets the vertex attribute pointer for the current VAO according to the specified attribute (index into mAttributes)
|
||
|
//! Note: expects that this VAO is currently bound
|
||
|
//! Note: will bind the ArrayBuffer referenced by _attribute.arrayBuffer
|
||
|
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();
|
||
|
|
||
|
// ===================================================================================================== \/
|
||
|
// ============================================================================================ WRAPPERS \/
|
||
|
// ===================================================================================================== \/
|
||
|
public:
|
||
|
//! Bind this VAO
|
||
|
//! some consistancy checks are made
|
||
|
void bind() const;
|
||
|
|
||
|
//! Nothing has to be prepared for a render call
|
||
|
inline void render (void)
|
||
|
{
|
||
|
storeOldVAOandBind();
|
||
|
draw();
|
||
|
restoreOldVAO();
|
||
|
}
|
||
|
|
||
|
//! Will select the matching draw call. Remember to bind() first!
|
||
|
void draw( GLsizei _primcount = 1 ) const
|
||
|
{
|
||
|
if (mpElementArrayBuffer) {
|
||
|
drawElements( _primcount );
|
||
|
} else {
|
||
|
drawArrays( _primcount );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ===================================================================================================== \/
|
||
|
// ========================================================================================= 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!
|
||
|
inline GLsizei getArrayVertexCount() const {
|
||
|
int firstAttribute = getFirstAttributeIndex(); // will be -1 if no attribte is set.
|
||
|
return ( (firstAttribute == -1)?0:mAttributes[firstAttribute].arrayBuffer->getElements());
|
||
|
}
|
||
|
|
||
|
//! 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
|
||
|
}
|
||
|
|
||
|
//! 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)
|
||
|
inline void multiDrawArrays( GLint *first, GLsizei *count, GLsizei primcount ) const
|
||
|
{
|
||
|
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 );
|
||
|
}
|
||
|
#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; }
|
||
|
|
||
|
//! Can be called directly instead of draw() iff the caller knows this is the correct call!
|
||
|
//! Draws all elements
|
||
|
inline void drawElements(void) const
|
||
|
{
|
||
|
glDrawElements(mMode, getIndexCount(), getIndexType(), (GLvoid*)NULL);
|
||
|
}
|
||
|
|
||
|
//! count and indices are arrays of the size primcount (so primcount chunks are beeing rendered)
|
||
|
inline void multiDrawElements( GLsizei *count, GLenum type, const GLvoid **indices, GLsizei primcount ) const
|
||
|
{
|
||
|
glMultiDrawElements( mMode, count, type, indices, primcount );
|
||
|
}
|
||
|
|
||
|
inline void drawRangeElements( GLuint start,
|
||
|
GLsizei count )
|
||
|
{
|
||
|
glDrawRangeElements( mMode, start, getIndexCount(), count, getIndexType(), NULL);
|
||
|
}
|
||
|
|
||
|
#if (ACGL_OPENGL_VERSION >= 32)
|
||
|
inline void multiDrawElements( GLsizei *count, GLenum type, GLvoid **indices, GLsizei primcount, GLint *basevertex ) const
|
||
|
{
|
||
|
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 );
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
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()
|
||
|
inline void storeOldVAOandBind(void)
|
||
|
{
|
||
|
// 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
|
||
|
inline void restoreOldVAO(void)
|
||
|
{
|
||
|
if (mObjectName != (GLuint)mPreviousVAOName) glBindVertexArray((GLuint)mPreviousVAOName);
|
||
|
}
|
||
|
|
||
|
//! Returns the first attribute that has a non-NULL ArrayBuffer attached
|
||
|
inline int_t getFirstAttributeIndex() const
|
||
|
{
|
||
|
// TODO: cache this value?
|
||
|
for (unsigned int index = 0; index < mAttributes.size(); ++index)
|
||
|
if (mAttributes[index].arrayBuffer)
|
||
|
return index;
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
// =================================================================================================== \/
|
||
|
// ============================================================================================ FIELDS \/
|
||
|
// =================================================================================================== \/
|
||
|
private:
|
||
|
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
|
||
|
};
|
||
|
|
||
|
ACGL_SMARTPOINTER_TYPEDEFS(VertexArrayObject)
|
||
|
|
||
|
} // OpenGL
|
||
|
} // ACGL
|
||
|
|
||
|
#endif // OpenGL 3.0
|
||
|
|
||
|
#endif // ACGL_OPENGL_OBJECTS_VERTEXARRAYOBJECT_HH
|