Unofficial OpenGL Software Development Kit  0.5.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
Classes | Typedefs | Enumerations | Functions | Variables
Immediate Mesh Drawing

API for immediate mode-style rendering.

It is often useful to send vertex data for objects on the fly. Immediate mode in OpenGL was the original way to do this, but OpenGL 3.1 core and above removed that.

These classes provide a means for having functionality similar to immediate mode.

Before rendering can begin, some setup work needs to be done. Core OpenGL 3.1 and above can only render from buffer objects. Therefore, a buffer object must be used.

This is provided by the StreamBuffer class. It provides an interface for sequentially adding vertex data to a buffer. It can invalidate the buffer automatically if there is not enough room left in the buffer to write a particular set of vertex data.

The next setup is to declare a format for the vertex data. Unlike the original immediate mode, this drawing API requires an explicit format for vertex data. This is provided by the VertexFormat class, which defines a series of vertex attributes.

These two classes are used by the glmesh::Draw class. The Draw class is what provides the actual drawing API. It uses a StreamBuffer to stream its data into, and it expects a specific VertexFormat for its vertex data.

You can use immediate mode drawing just to fill a buffer object with data, rather than actually rendering with it. This is done by using CpuDataWriter to write the data to system memory. After writing as much as you like, you call CpuDataWriter::TransferToBuffer to transfer the data to a buffer object. From there, you can bind the buffer to GL_ARRAY_BUFFER and use the VertexFormat::BindAttributes to fill a VAO and render with it. You can even put data from multiple meshes in the same buffer (so long as they use the same vertex format).

Note that the OpenGL immediate mode API allows you to put as many vertices in as you want, and only checks for errors when you call glEnd. The glmesh::Draw API is more strict: you must specify how many vertices you want up front, and if you try to provide too many attributes, an exception is thrown. CpuDataWriter is more lenient (since it is writing to arbitrary CPU memory storage); you can provide a guesstimate of the number of vertices, but it will expand that as needed.

Also, the old OpenGL immediate mode has the concept of constant data; you did not have to set every attribute for each vertex. This drawing API is more restrictive; you must set each attribute for each vertex. And each attribute must be provided in the order specified by the VertexFormat.

Here is an example of how to use the API:

#include <memory>
#include <glload/gl_all.h>
#include <glload/gll.hpp>
#include <glmesh/glmesh.h>
void RenderScene(glmesh::VertexFormat &vfmt, glmesh::StreamBuffer &streamBuf);
int main(int argc, char *argv[])
{
//Initialize OpenGL and bind the context
//glload must be initialized for glmesh to work.
if(glload::LoadFunctions() == glload::LS_LOAD_FAILED)
//exit in some way
//Loading succeeded. Create the vertex format.
//First attribute is attribute index 0, a vec4 of floats.
//Second attribute is attribute index 1, a vec4 of normalized, unsigned bytes.
glmesh::VertexFormat vfmt(attribs);
//attribs is no longer necessary; all of the info is stored in vfmt.
//Create the StreamBuffer.
//Must be large enough to hold the longest single run of vertex
//data that we submit. For best performance, we shouldn't run
//past the end of the buffer more than once per frame.
glmesh::StreamBuffer streamBuf(1024*1024);
//...
while(true)
{
//check for exit; otherwise, draw frame.
RenderScene(vfmt, streamBuf);
}
}
void RenderScene(glmesh::VertexFormat &vfmt, glmesh::StreamBuffer &streamBuf)
{
//Clear the screen and set up shaders, textures, etc.
{
//Draw a single triangle
glmesh::Draw imm(gl::GL_TRIANGLES, 3, m_vertFmt, m_streamBuf);
imm.Attrib(0.75f, 0.75f, 0.0f, 1.0f);
imm.Attrib<GLubyte>(153, 204, 0, 255);
imm.Attrib(0.75f, -0.75f, 0.0f, 1.0f);
imm.Attrib<GLubyte>(230, 51, 0, 255);
imm.Attrib(-0.75f, -0.75f, 0.0f, 1.0f);
imm.Attrib<GLubyte>(26, 51, 179, 255);
//The destructor causes the actual rendering.
}
{
//Draw a triangle strip.
glmesh::Draw imm(gl::GL_TRIANGLE_STRIP, 4, m_vertFmt, m_streamBuf);
imm.Attrib(30.0f, 0.0f, 30.0f, 1.0f);
imm.Attrib<GLubyte>(51, 255, 51, 255);
imm.Attrib(30.0f, 0.0f, -30.0f, 1.0f);
imm.Attrib<GLubyte>(51, 255, 51, 255);
imm.Attrib(-30.0f, 0.0f, 30.0f, 1.0f);
imm.Attrib<GLubyte>(51, 255, 51, 255);
imm.Attrib(-30.0f, 0.0f, -30.0f, 1.0f);
imm.Attrib<GLubyte>(51, 255, 51, 255);
//No need to wait for the destructor.
//This also causes exceptions that the destructor would otherwise
//swallow, thus making code safer.
imm.Render();
}
}

If you are inclined to use Boost directly, a Boost.Fusion-capable function exists which is compatible with any glmesh::VertexWriter-based class (such as glmesh::Draw). It takes a Boost.Tuple or other Fusion-compatible sequence type and writes each attribute in order. This allows you to use arbitrary structs directly, rather than having to write each vertex's attributes in order.

This function, glmesh::Attrib, has example code in its documentation.

Classes

class  glmesh::CpuDataWriter
 Allows immediate mode drawing to a CPU buffer, rather than a buffer object. More...
 
class  glmesh::Draw
 RAII-style class for immediate-mode type rendering through a VertexFormat and StreamBuffer. More...
 
class  glmesh::StreamBuffer
 A class for streaming vertex data to buffer objects on the GPU. More...
 
class  glmesh::StreamBuffer::Map
 A RAII-style class for mapping a StreamBuffer. More...
 
class  glmesh::AttribDesc
 Describes the storage for a single vertex attribute. More...
 
struct  glmesh::SeparateAttribFormatTag
 Used in VertexFormat::Enable to differentiate constructors. More...
 
class  glmesh::VertexFormat
 Describes the layout for a sequence of vertex attributes, to be used for rendering. More...
 
class  glmesh::VertexFormat::Enable
 RAII-style class for binding a VertexFormat to the OpenGL context. More...
 
class  glmesh::VertexWriter< Sink >
 Base class, using CRTP, that provides a framework for writing vertex attributes to arbitrary locations. More...
 

Typedefs

typedef std::vector< AttribDesc > glmesh::AttributeList
 Convenience typedef for std::vector's of attributes.
 

Enumerations

enum  glmesh::VertexDataType {
  glmesh::VDT_HALF_FLOAT, glmesh::VDT_SINGLE_FLOAT, glmesh::VDT_DOUBLE_FLOAT, glmesh::VDT_SIGN_BYTE,
  glmesh::VDT_UNSIGN_BYTE, glmesh::VDT_SIGN_SHORT, glmesh::VDT_UNSIGN_SHORT, glmesh::VDT_SIGN_INT,
  glmesh::VDT_UNSIGN_INT
}
 The C data type that you will be providing the vertex attribute data in. More...
 
enum  glmesh::AttribDataType { glmesh::ADT_FLOAT, glmesh::ADT_NORM_FLOAT, glmesh::ADT_INTEGER, glmesh::ADT_DOUBLE }
 The expected interpretation of the attribute data by GLSL. More...
 

Functions

template<typename Sink , typename VertexSequence >
void glmesh::Attrib (VertexWriter< Sink > &drawable, const VertexSequence &vertexData)
 A Boost.Fusion-based function for drawing attribute structs. More...
 

Variables

const SeparateAttribFormatTag glmesh::sepFormat
 Use this in VertexFormat::Enable to use the separate attribute format constructor.
 

Enumeration Type Documentation

The expected interpretation of the attribute data by GLSL.

This type must match its corresponding glmesh::VertexDataType or an error will result.

  • ADT_FLOAT can be used with anything.
  • ADT_NORM_FLOAT can only be used with the integer types, signed or unsigned.
  • ADT_INTEGER can only be used with the integer types, signed or unsigned.
  • ADT_DOUBLE can only be used with VDT_DOUBLE_FLOAT.
Enumerator
ADT_FLOAT 

Values are used directly as floats. Integer types like 24 are converted to 24.0f floats.

ADT_NORM_FLOAT 

Integer values are normalized. So 128 as an unsigned byte becomes 0.502.

ADT_INTEGER 

Integer values are taken as integers.

ADT_DOUBLE 

Values are used as double-precision. The shader must use double or dvec attributes.

The C data type that you will be providing the vertex attribute data in.

Enumerator
VDT_HALF_FLOAT 

16-bit half-floats.

VDT_SINGLE_FLOAT 

32-bit single-precision floats.

VDT_DOUBLE_FLOAT 

64-bit double-precision floats.

VDT_SIGN_BYTE 

8-bit signed integers.

VDT_UNSIGN_BYTE 

8-bit unsigned integers.

VDT_SIGN_SHORT 

16-bit signed integers.

VDT_UNSIGN_SHORT 

16-bit unsigned integers.

VDT_SIGN_INT 

32-bit signed integers.

VDT_UNSIGN_INT 

32-bit unsigned integers.

Function Documentation

template<typename Sink , typename VertexSequence >
void glmesh::Attrib ( VertexWriter< Sink > &  drawable,
const VertexSequence &  vertexData 
)

A Boost.Fusion-based function for drawing attribute structs.

Boost.Fusion makes it possible to compile-time iterate over boost::tuples, fusion::sequences, and similar types. With the use of certain Boost.Fusion preprocessor commands, it is also possible to iterate over plain structs.

When using glmesh::Draw, if you have some struct that has vertex data, you normally must either write a function to send its vertex data to a glmesh::Draw object, or you must write it out manually at the location where you send vertex data. But if you store your vertex data in a struct that is a valid Boost.Fusion sequence, you can use this function to iterate over them.

Here is an example:

#include <boost/tuple/tuple.hpp>
#include <boost/range.hpp>
#include <boost/foreach.hpp>
typedef boost::tuple<glm::hvec4, glm::detail::tvec4<GLubyte> > VertexType;
VertexType vertexData[] = ... //Fill in array.
//Draw the mesh.
glmesh::Draw immMode(gl::TRIANGLES, boost::size(g_vertexData), vertexFormat, streamBuffer);
BOOST_FOREACH(const VertexType &vertex, vertexData)
{
glmesh::Attrib(immMode, vertex);
}

Here is a version that uses Boost.Fusion's struct adaptor utilities:

#include <boost/fusion/adapted/struct/adapt_struct.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/range.hpp>
#include <boost/foreach.hpp>
struct VertexType
{
glm::hvec4 position;
glm::tvec4<GLubyte> color;
};
BOOST_FUSION_ADAPT_STRUCT(
VertexType,
(glm::hvec4, position)
(glm::detail::tvec4<GLubyte>, color))
VertexType vertexData[] = ... //Fill in array.
glmesh::Draw immMode(gl::TRIANGLES, boost::size(g_vertexData), vertexFormat, streamBuffer);
BOOST_FOREACH(const VertexType &vertex, vertexData)
{
glmesh::Attrib(immMode, vertex);
}

You can change the vertex type to match whatever you use. In the future, there may be a function that takes a Boost.Fusion type and converts it into a vertex format directly.