Browse Source

Mesh rework, part 2: reworked internals.

* VAOs are used only if the extension is supported.
 * Removed finalize() and other already useless stuff.
 * Preparation for DSA and state tracking.
pull/7/head
Vladimír Vondruš 14 years ago
parent
commit
10e64d2d27
  1. 4
      src/Context.cpp
  2. 65
      src/IndexedMesh.cpp
  3. 80
      src/IndexedMesh.h
  4. 157
      src/Mesh.cpp
  5. 132
      src/Mesh.h

4
src/Context.cpp

@ -23,6 +23,8 @@
#include "AbstractTexture.h"
#include "Buffer.h"
#include "Extensions.h"
#include "IndexedMesh.h"
#include "Mesh.h"
#include "Implementation/State.h"
#include "BufferedTexture.h"
@ -205,6 +207,8 @@ Context::Context() {
AbstractTexture::initializeContextBasedFunctionality(this);
Buffer::initializeContextBasedFunctionality(this);
BufferedTexture::initializeContextBasedFunctionality(this);
IndexedMesh::initializeContextBasedFunctionality(this);
Mesh::initializeContextBasedFunctionality(this);
}
Context::~Context() {

65
src/IndexedMesh.cpp

@ -17,21 +17,28 @@
#include <Utility/Debug.h>
#include "Context.h"
#include "Extensions.h"
namespace Magnum {
void IndexedMesh::draw() {
/* Vertex array must be bound before finalization */
#ifndef MAGNUM_TARGET_GLES
bind();
#endif
IndexedMesh::CreateIndexedImplementation IndexedMesh::createIndexedImplementation = &IndexedMesh::createIndexedImplementationDefault;
IndexedMesh::BindIndexedImplementation IndexedMesh::bindIndexedImplementation = &IndexedMesh::bindIndexedImplementationDefault;
IndexedMesh::IndexedMesh(Mesh::Primitive primitive): Mesh(primitive), _indexCount(0), _indexType(Type::UnsignedShort) {
_indexBuffer.setTargetHint(Buffer::Target::ElementArray);
(this->*createIndexedImplementation)();
}
finalize();
IndexedMesh::IndexedMesh(Mesh::Primitive primitive, GLsizei vertexCount, GLsizei indexCount, Type indexType): Mesh(primitive, vertexCount), _indexCount(indexCount), _indexType(indexType) {
_indexBuffer.setTargetHint(Buffer::Target::ElementArray);
/* Buffers must be bound after initialization */
#ifdef MAGNUM_TARGET_GLES
(this->*createIndexedImplementation)();
}
void IndexedMesh::draw() {
bind();
_indexBuffer.bind(Buffer::Target::ElementArray);
#endif
/** @todo Start at given index */
glDrawElements(static_cast<GLenum>(primitive()), _indexCount, static_cast<GLenum>(_indexType), nullptr);
@ -39,20 +46,40 @@ void IndexedMesh::draw() {
unbind();
}
#ifndef DOXYGEN_GENERATING_OUTPUT
void IndexedMesh::finalize() {
if(isFinalized()) return;
void IndexedMesh::bind() {
CORRADE_ASSERT(_indexCount, "IndexedMesh: the mesh has zero index count!", );
/* Finalize attribute positions */
Mesh::finalize();
Mesh::bind();
(this->*bindIndexedImplementation)();
}
void IndexedMesh::initializeContextBasedFunctionality(Context* context) {
if(context->isExtensionSupported<Extensions::GL::APPLE::vertex_array_object>()) {
#ifndef MAGNUM_TARGET_GLES
Debug() << "IndexedMesh: using" << Extensions::GL::APPLE::vertex_array_object::string() << "features";
createIndexedImplementation = &IndexedMesh::createIndexedImplementationVAO;
bindIndexedImplementation = &IndexedMesh::bindIndexedImplementationVAO;
#endif
}
}
void IndexedMesh::createIndexedImplementationDefault() {}
#ifndef MAGNUM_TARGET_GLES
void IndexedMesh::createIndexedImplementationVAO() {
glBindVertexArray(vao);
/* Bind index buffer to VAO too */
#ifndef MAGNUM_TARGET_GLES
_indexBuffer.bind(Buffer::Target::ElementArray);
#endif
}
#endif
void IndexedMesh::bindIndexedImplementationDefault() {
_indexBuffer.bind(Buffer::Target::ElementArray);
}
#ifndef MAGNUM_TARGET_GLES
void IndexedMesh::bindIndexedImplementationVAO() {}
#endif
}

80
src/IndexedMesh.h

@ -25,21 +25,39 @@
namespace Magnum {
/**
* @brief Indexed mesh
*/
@brief Indexed mesh
@section IndexedMesh-configuration Indexed mesh configuration
Next to @ref Mesh-configuration "everything needed for non-indexed mesh" you
have to specify also index count and type (either in constructor or using
setIndexCount() and setIndexType()). Then fill index buffer or use
MeshTools::compressIndices() to conveniently fill the index buffer and set
index count and type.
@section IndexedMesh-drawing Rendering meshes
From user point-of-view the operation is the same as for
@ref Mesh-drawing "non-indexed meshes".
@section IndexedMesh-performance-optimization Performance optimizations
If @extension{APPLE,vertex_array_object} is supported, next to
@ref Mesh-performance-optimization "optimizations in Mesh itself" the index
buffer is bound on object construction instead of in every draw() call.
*/
class MAGNUM_EXPORT IndexedMesh: public Mesh {
friend class Context;
public:
/**
* @brief Implicit constructor
* @param primitive Primitive type
*
* Allows creating the object without knowing anything about mesh data.
* Note that you have to call setVertexCount(), setIndexCount() and
* setIndexType() manually for mesh to draw properly.
* @see @fn_gl{BindVertexArray} (if @extension{APPLE,vertex_array_object}
* is available)
*/
inline IndexedMesh(Primitive primitive = Primitive::Triangles): Mesh(primitive), _indexCount(0), _indexType(Type::UnsignedShort) {
_indexBuffer.setTargetHint(Buffer::Target::ElementArray);
}
IndexedMesh(Primitive primitive = Primitive::Triangles);
/**
* @brief Constructor
@ -47,10 +65,12 @@ class MAGNUM_EXPORT IndexedMesh: public Mesh {
* @param vertexCount Count of unique vertices
* @param indexCount Count of indices
* @param indexType Type of indices (indexable, see TypeTraits)
*
* @see setPrimitive(), setVertexCount(), setIndexCount(),
* setIndexType(), @fn_gl{BindVertexArray} (if
* @extension{APPLE,vertex_array_object} is available)
*/
inline IndexedMesh(Primitive primitive, GLsizei vertexCount, GLsizei indexCount, Type indexType = Type::UnsignedShort): Mesh(primitive, vertexCount), _indexCount(indexCount), _indexType(indexType) {
_indexBuffer.setTargetHint(Buffer::Target::ElementArray);
}
IndexedMesh(Primitive primitive, GLsizei vertexCount, GLsizei indexCount, Type indexType = Type::UnsignedShort);
/** @brief Index count */
inline GLsizei indexCount() const { return _indexCount; }
@ -60,7 +80,6 @@ class MAGNUM_EXPORT IndexedMesh: public Mesh {
* @return Pointer to self (for method chaining)
*
* @see MeshTools::compressIndices()
* @todo definalize after that?
*/
inline IndexedMesh* setIndexCount(GLsizei count) {
_indexCount = count;
@ -73,6 +92,8 @@ class MAGNUM_EXPORT IndexedMesh: public Mesh {
/**
* @brief Set index type
* @return Pointer to self (for method chaining)
*
* @see MeshTools::compressIndices()
*/
inline IndexedMesh* setIndexType(Type type) {
_indexType = type;
@ -90,20 +111,35 @@ class MAGNUM_EXPORT IndexedMesh: public Mesh {
/**
* @brief Draw the mesh
*
* Expects an active shader with all uniforms set.
* @see Buffer::bind(), bind(), unbind(), finalize(), @fn_gl{DrawElements}
* Expects an active shader with all uniforms set. See
* @ref AbstractShaderProgram-rendering-workflow "AbstractShaderProgram documentation"
* for more information.
* @see @fn_gl{EnableVertexAttribArray}, @fn_gl{BindBuffer},
* @fn_gl{VertexAttribPointer}, @fn_gl{DisableVertexAttribArray}
* or @fn_gl{BindVertexArray} (if @extension{APPLE,vertex_array_object}
* is available), @fn_gl{DrawElements}
*/
void draw();
protected:
/**
* @brief Finalize the mesh
*
* @see Mesh::finalize(), Buffer::bind()
*/
MAGNUM_LOCAL void finalize();
private:
static void MAGNUM_LOCAL initializeContextBasedFunctionality(Context* context);
void MAGNUM_LOCAL bind();
typedef void(IndexedMesh::*CreateIndexedImplementation)();
void MAGNUM_LOCAL createIndexedImplementationDefault();
#ifndef MAGNUM_TARGET_GLES
void MAGNUM_LOCAL createIndexedImplementationVAO();
#endif
static MAGNUM_LOCAL CreateIndexedImplementation createIndexedImplementation;
typedef void(IndexedMesh::*BindIndexedImplementation)();
void MAGNUM_LOCAL bindIndexedImplementationDefault();
#ifndef MAGNUM_TARGET_GLES
void MAGNUM_LOCAL bindIndexedImplementationVAO();
#endif
static MAGNUM_LOCAL BindIndexedImplementation bindIndexedImplementation;
Buffer _indexBuffer;
GLsizei _indexCount;
Type _indexType;

157
src/Mesh.cpp

@ -18,59 +18,38 @@
#include <Utility/Debug.h>
#include "Buffer.h"
#include "Context.h"
#include "Extensions.h"
using namespace std;
namespace Magnum {
Mesh::Mesh(Mesh&& other):
#ifndef MAGNUM_TARGET_GLES
vao(other.vao),
#endif
_primitive(other._primitive), _vertexCount(other._vertexCount), finalized(other.finalized), attributes(other.attributes)
{
#ifndef MAGNUM_TARGET_GLES
other.vao = 0;
#endif
}
Mesh::CreateImplementation Mesh::createImplementation = &Mesh::createImplementationDefault;
Mesh::DestroyImplementation Mesh::destroyImplementation = &Mesh::destroyImplementationDefault;
Mesh::BindAttributeImplementation Mesh::bindAttributeImplementation = &Mesh::bindAttributeImplementationDefault;
Mesh::BindImplementation Mesh::bindImplementation = &Mesh::bindImplementationDefault;
Mesh::UnbindImplementation Mesh::unbindImplementation = &Mesh::unbindImplementationDefault;
void Mesh::destroy() {
#ifndef MAGNUM_TARGET_GLES
glDeleteVertexArrays(1, &vao);
#endif
Mesh::Mesh(Mesh&& other): vao(other.vao), _primitive(other._primitive), _vertexCount(other._vertexCount), attributes(other.attributes) {
other.vao = 0;
}
Mesh& Mesh::operator=(Mesh&& other) {
destroy();
(this->*destroyImplementation)();
#ifndef MAGNUM_TARGET_GLES
vao = other.vao;
#endif
_primitive = other._primitive;
_vertexCount = other._vertexCount;
finalized = other.finalized;
attributes = other.attributes;
#ifndef MAGNUM_TARGET_GLES
other.vao = 0;
#endif
return *this;
}
void Mesh::draw() {
/* Vertex array must be bound before finalization */
#ifndef MAGNUM_TARGET_GLES
bind();
#endif
/* Finalize, if not already */
finalize();
/* Buffers must be bound after initialization */
#ifdef MAGNUM_TARGET_GLES
bind();
#endif
/** @todo Start at given index */
glDrawArrays(static_cast<GLenum>(_primitive), 0, _vertexCount);
@ -78,65 +57,99 @@ void Mesh::draw() {
unbind();
}
#ifndef DOXYGEN_GENERATING_OUTPUT
void Mesh::bind() {
#ifndef MAGNUM_TARGET_GLES
glBindVertexArray(vao);
#else
bindBuffers();
#endif
}
CORRADE_ASSERT((_vertexCount == 0) == attributes.empty(), "Mesh: vertex count is non-zero, but no attributes are bound", );
void Mesh::unbind() {
#ifndef MAGNUM_TARGET_GLES
glBindVertexArray(0);
#else
for(const Attribute& attribute: attributes)
glDisableVertexAttribArray(attribute.location);
#endif
(this->*bindImplementation)();
}
void Mesh::finalize() {
/* Already finalized */
if(finalized) return;
void Mesh::addVertexAttribute(Buffer* buffer, GLuint location, GLint count, Type type, GLintptr offset, GLsizei stride) {
CORRADE_ASSERT(_vertexCount != 0, "Mesh: vertex count must be set before binding attributes", );
CORRADE_ASSERT((_vertexCount == 0) == attributes.empty(), "Mesh: vertex count is non-zero, but no attributes are bound", );
attributes.push_back({
buffer,
location,
count,
type,
offset,
stride
});
finalized = true;
(this->*bindAttributeImplementation)(attributes.back());
}
void Mesh::vertexAttribPointer(const Mesh::Attribute& attribute) {
glEnableVertexAttribArray(attribute.location);
attribute.buffer->bind(Buffer::Target::Array);
#ifndef MAGNUM_TARGET_GLES
bindBuffers();
if(TypeInfo::isIntegral(attribute.type))
glVertexAttribIPointer(attribute.location, attribute.count, static_cast<GLenum>(attribute.type), attribute.stride, reinterpret_cast<const GLvoid*>(attribute.offset));
else
#endif
glVertexAttribPointer(attribute.location, attribute.count, static_cast<GLenum>(attribute.type), GL_FALSE, attribute.stride, reinterpret_cast<const GLvoid*>(attribute.offset));
}
void Mesh::bindBuffers() {
/* Bind all attributes to this buffer */
for(const Attribute& attribute: attributes) {
glEnableVertexAttribArray(attribute.location);
attribute.buffer->bind(Buffer::Target::Array);
void Mesh::initializeContextBasedFunctionality(Context* context) {
if(context->isExtensionSupported<Extensions::GL::APPLE::vertex_array_object>()) {
#ifndef MAGNUM_TARGET_GLES
if(TypeInfo::isIntegral(attribute.type))
glVertexAttribIPointer(attribute.location, attribute.count, static_cast<GLenum>(attribute.type), attribute.stride, reinterpret_cast<const GLvoid*>(attribute.offset));
else
Debug() << "Mesh: using" << Extensions::GL::APPLE::vertex_array_object::string() << "features";
createImplementation = &Mesh::createImplementationVAO;
destroyImplementation = &Mesh::destroyImplementationVAO;
bindAttributeImplementation = &Mesh::bindAttributeImplementationVAO;
bindImplementation = &Mesh::bindImplementationVAO;
unbindImplementation = &Mesh::unbindImplementationVAO;
#endif
glVertexAttribPointer(attribute.location, attribute.count, static_cast<GLenum>(attribute.type), GL_FALSE, attribute.stride, reinterpret_cast<const GLvoid*>(attribute.offset));
}
}
void Mesh::createImplementationDefault() {}
#ifndef MAGNUM_TARGET_GLES
void Mesh::createImplementationVAO() {
glGenVertexArrays(1, &vao);
}
#endif
void Mesh::addVertexAttribute(Buffer* buffer, GLuint location, GLint count, Type type, GLintptr offset, GLsizei stride) {
CORRADE_ASSERT(_vertexCount != 0, "Mesh: vertex count must be set before binding attributes", );
void Mesh::destroyImplementationDefault() {}
attributes.push_back({
buffer,
location,
count,
type,
offset,
stride
});
#ifndef MAGNUM_TARGET_GLES
void Mesh::destroyImplementationVAO() {
glDeleteVertexArrays(1, &vao);
}
#endif
void Mesh::bindAttributeImplementationDefault(const Attribute&) {}
#ifndef MAGNUM_TARGET_GLES
void Mesh::bindAttributeImplementationVAO(const Attribute& attribute) {
glBindVertexArray(vao);
vertexAttribPointer(attribute);
}
#endif
void Mesh::bindImplementationDefault() {
for(const Attribute& attribute: attributes)
vertexAttribPointer(attribute);
}
#ifndef MAGNUM_TARGET_GLES
void Mesh::bindImplementationVAO() {
glBindVertexArray(vao);
}
#endif
void Mesh::unbindImplementationDefault() {
for(const Attribute& attribute: attributes)
glDisableVertexAttribArray(attribute.location);
}
#ifndef MAGNUM_TARGET_GLES
void Mesh::unbindImplementationVAO() {
glBindVertexArray(0);
}
#endif
}

132
src/Mesh.h

@ -27,6 +27,7 @@
namespace Magnum {
class Buffer;
class Context;
/**
@brief Base class for managing non-indexed meshes
@ -77,8 +78,12 @@ mesh->setPrimitive(plane.primitive())
Basic workflow is to set up respective shader (see @ref AbstractShaderProgram-rendering-workflow "AbstractShaderProgram documentation" for more infromation) and call Mesh::draw().
VAOs are used for desktop OpenGL (not in OpenGL ES).
@requires_gl30 Extension @extension{APPLE,vertex_array_object}
@section Mesh-performance-optimization Performance optimizations
If @extension{APPLE,vertex_array_object} is supported, VAOs are used instead
of binding the buffers and specifying vertex attribute pointers in each
draw() call.
@requires_gl30 Extension @extension{EXT,gpu_shader4} (for unsigned integer attributes)
@todo Support for normalized values (e.g. for color as char[4] passed to
@ -90,6 +95,9 @@ VAOs are used for desktop OpenGL (not in OpenGL ES).
@todo Redo in a way that allows glMultiDrawArrays, glDrawArraysInstanced etc.
*/
class MAGNUM_EXPORT Mesh {
friend class IndexedMesh;
friend class Context;
Mesh(const Mesh& other) = delete;
Mesh& operator=(const Mesh& other) = delete;
@ -331,12 +339,11 @@ class MAGNUM_EXPORT Mesh {
* @param primitive Primitive type
* @param vertexCount Vertex count
*
* @see @fn_gl{GenVertexArrays}, setPrimitive(), setVertexCount()
* @see setPrimitive(), setVertexCount(), @fn_gl{GenVertexArrays} (if
* @extension{APPLE,vertex_array_object} is available)
*/
inline Mesh(Primitive primitive = Primitive::Triangles, GLsizei vertexCount = 0): _primitive(primitive), _vertexCount(vertexCount), finalized(false) {
#ifndef MAGNUM_TARGET_GLES
glGenVertexArrays(1, &vao);
#endif
inline Mesh(Primitive primitive = Primitive::Triangles, GLsizei vertexCount = 0): _primitive(primitive), _vertexCount(vertexCount) {
(this->*createImplementation)();
}
/** @brief Move constructor */
@ -345,20 +352,16 @@ class MAGNUM_EXPORT Mesh {
/**
* @brief Destructor
*
* @see @fn_gl{DeleteVertexArrays}
* @see @fn_gl{DeleteVertexArrays} (if
* @extension{APPLE,vertex_array_object} is available)
*/
inline virtual ~Mesh() { destroy(); }
inline virtual ~Mesh() {
(this->*destroyImplementation)();
}
/** @brief Move assignment */
Mesh& operator=(Mesh&& other);
/**
* @brief Whether the mesh is finalized
*
* When the mesh is finalized, no new attributes can be bound.
*/
inline bool isFinalized() const { return finalized; }
/** @brief Primitive type */
inline Primitive primitive() const { return _primitive; }
@ -385,7 +388,6 @@ class MAGNUM_EXPORT Mesh {
*/
inline Mesh* setVertexCount(GLsizei vertexCount) {
_vertexCount = vertexCount;
finalized = false;
attributes.clear();
return this;
}
@ -428,7 +430,10 @@ class MAGNUM_EXPORT Mesh {
* mesh, you must ensure it will exist for whole lifetime of the
* mesh and delete it afterwards.
*
* @see addInterleavedVertexBuffer()
* @see addInterleavedVertexBuffer(), @fn_gl{BindVertexArray},
* @fn_gl{EnableVertexAttribArray}, @fn_gl{BindBuffer},
* @fn_gl{VertexAttribPointer} (if
* @extension{APPLE,vertex_array_object} is available)
*/
template<class ...T> inline Mesh* addVertexBuffer(Buffer* buffer, const T&... attributes) {
addVertexBufferInternal(buffer, 0, attributes...);
@ -485,7 +490,10 @@ class MAGNUM_EXPORT Mesh {
* mesh, you must ensure it will exist for whole lifetime of the
* mesh and delete it afterwards.
*
* @see addVertexBufferStride(), addVertexBuffer()
* @see addVertexBufferStride(), addVertexBuffer(),
* @fn_gl{BindVertexArray}, @fn_gl{EnableVertexAttribArray},
* @fn_gl{BindBuffer}, @fn_gl{VertexAttribPointer} (if
* @extension{APPLE,vertex_array_object} is available)
*/
template<class ...T> inline Mesh* addInterleavedVertexBuffer(Buffer* buffer, GLintptr offset, const T&... attributes) {
addInterleavedVertexBufferInternal(buffer, offset, strideOfInterleaved(attributes...), attributes...);
@ -505,42 +513,16 @@ class MAGNUM_EXPORT Mesh {
/**
* @brief Draw the mesh
*
* Expects an active shader with all uniforms set.
* @see bind(), unbind(), finalize(), @fn_gl{DrawArrays}
* Expects an active shader with all uniforms set. See
* @ref AbstractShaderProgram-rendering-workflow "AbstractShaderProgram documentation"
* for more information.
* @see @fn_gl{EnableVertexAttribArray}, @fn_gl{BindBuffer},
* @fn_gl{VertexAttribPointer}, @fn_gl{DisableVertexAttribArray}
* or @fn_gl{BindVertexArray} (if @extension{APPLE,vertex_array_object}
* is available), @fn_gl{DrawArrays}
*/
virtual void draw();
protected:
/**
* @brief Bind all buffers
*
* @see @fn_gl{EnableVertexAttribArray}, @fn_gl{VertexAttribPointer}
*/
void bindBuffers();
/**
* @brief Bind vertex array or all buffers
*
* @see @fn_gl{BindVertexArray} or bindBuffers()
*/
void bind();
/**
* @brief Unbind vertex array or all buffers
*
* @see @fn_gl{BindVertexArray} or @fn_gl{DisableVertexAttribArray}
*/
void unbind();
/**
* @brief Finalize the mesh
*
* Computes location and stride of each attribute in its buffer. After
* this function is called, no new attribute can be bound.
* @see bindBuffers()
*/
MAGNUM_LOCAL void finalize();
private:
struct MAGNUM_LOCAL Attribute {
Buffer* buffer;
@ -551,6 +533,8 @@ class MAGNUM_EXPORT Mesh {
GLsizei stride;
};
static void MAGNUM_LOCAL initializeContextBasedFunctionality(Context* context);
/* Adding non-interleaved vertex attributes */
template<GLuint location, class T, class ...U> inline void addVertexBufferInternal(Buffer* buffer, GLintptr offset, const AbstractShaderProgram::Attribute<location, T>&, const U&... attributes) {
addVertexAttribute(buffer, location, TypeTraits<T>::count(), TypeTraits<T>::type(), offset, 0);
@ -588,14 +572,52 @@ class MAGNUM_EXPORT Mesh {
void MAGNUM_EXPORT addVertexAttribute(Buffer* buffer, GLuint location, GLint count, Type type, GLintptr offset, GLsizei stride);
void destroy();
void MAGNUM_LOCAL bind();
inline void unbind() {
(this->*unbindImplementation)();
}
void MAGNUM_LOCAL vertexAttribPointer(const Attribute& attribute);
typedef void(Mesh::*CreateImplementation)();
void MAGNUM_LOCAL createImplementationDefault();
#ifndef MAGNUM_TARGET_GLES
GLuint vao;
void MAGNUM_LOCAL createImplementationVAO();
#endif
static CreateImplementation createImplementation;
typedef void(Mesh::*DestroyImplementation)();
void MAGNUM_LOCAL destroyImplementationDefault();
#ifndef MAGNUM_TARGET_GLES
void MAGNUM_LOCAL destroyImplementationVAO();
#endif
static DestroyImplementation destroyImplementation;
typedef void(Mesh::*BindAttributeImplementation)(const Attribute&);
void MAGNUM_LOCAL bindAttributeImplementationDefault(const Attribute& attribute);
#ifndef MAGNUM_TARGET_GLES
void MAGNUM_LOCAL bindAttributeImplementationVAO(const Attribute& attribute);
#endif
static MAGNUM_LOCAL BindAttributeImplementation bindAttributeImplementation;
typedef void(Mesh::*BindImplementation)();
void MAGNUM_LOCAL bindImplementationDefault();
#ifndef MAGNUM_TARGET_GLES
void MAGNUM_LOCAL bindImplementationVAO();
#endif
static MAGNUM_LOCAL BindImplementation bindImplementation;
typedef void(Mesh::*UnbindImplementation)();
void MAGNUM_LOCAL unbindImplementationDefault();
#ifndef MAGNUM_TARGET_GLES
void MAGNUM_LOCAL unbindImplementationVAO();
#endif
static MAGNUM_LOCAL UnbindImplementation unbindImplementation;
GLuint vao;
Primitive _primitive;
GLsizei _vertexCount;
bool finalized;
std::vector<Attribute> attributes;
};

Loading…
Cancel
Save