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 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
|
#pragma once
#include <GL/glew.h>
#include "VBO.h"
#include "VertexTraits.h"
namespace render
{
/**
* \brief
* Receiver of vertex geometry for rendering
*
* A VertexBuffer provides methods to accept batches of vertices for rendering.
* Vertices can be submitted in several batches, and each batch can be rendered
* separately (and more than once) without recreating the vertex buffer. The
* VertexBuffer may make use of an OpenGL VBO for improved performance.
*/
template<typename Vertex_T> class VertexBuffer
{
public:
typedef std::vector<Vertex_T> Vertices;
private:
typedef VertexTraits<Vertex_T> Traits;
// OpenGL VBO information
mutable GLuint _vboID;
// Initial non-VBO based vertex storage
Vertices _vertices;
// Batch start index and size
struct Batch
{
std::size_t start;
std::size_t size;
};
// All batches
std::vector<Batch> _batches;
private:
// Create the VBO and copy all vertex data into it
void initialiseVBO() const
{
_vboID = makeVBOFromArray(GL_ARRAY_BUFFER, _vertices);
if (_vboID == 0)
{
std::runtime_error("Could not create vertex buffer");
}
}
public:
/// Default construct with no initial resource allocation
VertexBuffer()
: _vboID(0)
{ }
/// Destroy all resources
~VertexBuffer()
{
deleteVBO(_vboID);
}
/**
* \brief
* Add a batch of vertices
*
* \param begin
* Iterator pointing to the first Vertex_T in the batch.
*
* \param count
* Number of vertices in the batch.
*
* \param stride
* How much to increment the input iterator between each vertex (defaults
* to 1).
*/
template<typename Iter_T>
void addBatch(Iter_T begin, std::size_t count, std::size_t stride = 1)
{
if (count < 1)
{
throw std::logic_error("Batch must contain at least one vertex");
}
// Store batch information
Batch newBatch = { _vertices.size(), count };
_batches.push_back(newBatch);
// Append all vertices
_vertices.reserve(_vertices.size() + count);
Iter_T i = begin;
while (count > 0)
{
_vertices.push_back(*i);
if (--count > 0)
{
i += stride;
}
else
{
break;
}
}
}
/**
* \brief
* Replace data with that from another VertexBuffer
*
* If the other VertexBuffer is the same size or smaller than this one and
* has not yet had its own VBO allocated, this may improve performance by
* avoiding unnecessary re-allocations of GPU memory.
*
* This method may call GL functions so requires a valid GL context.
*/
void replaceData(const VertexBuffer& other)
{
replaceVBODataIfPossible(GL_ARRAY_BUFFER, _vboID,
_vertices, other._vertices);
_vertices = other._vertices;
_batches = other._batches;
}
/// Render all batches with the given primitive type
void renderAllBatches(GLenum primitiveType) const
{
if (_vboID == 0)
{
initialiseVBO();
}
glBindBuffer(GL_ARRAY_BUFFER, _vboID);
// Vertex pointer is always at the start of the whole buffer (the start
// and count parameters to glDrawArrays separate batches).
glVertexPointer(3, GL_DOUBLE, sizeof(Vertex_T),
Traits::VERTEX_OFFSET());
// For each batch
for (typename std::vector<Batch>::const_iterator i = _batches.begin();
i != _batches.end();
++i)
{
glDrawArrays(primitiveType, static_cast<GLint>(i->start),
static_cast<GLsizei>(i->size));
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
};
}
|