Unofficial OpenGL Software Development Kit: Immediate Mesh Drawing

Unofficial OpenGL Software Development Kit 0.5.0

- Main Page
- Related Pages
- Modules
- Namespaces
- Classes
- Files

All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
Classes | Typedefs | Enumerations | Functions | Variables
Immediate Mesh Drawing
GL Mesh

##

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.
glmesh::AttributeList attribs;
//First attribute is attribute index 0, a vec4 of floats.
attribs.push_back(glmesh::AttribDesc(0, 4, glmesh::VDT_SINGLE_FLOAT, glmesh::ADT_FLOAT));
//Second attribute is attribute index 1, a vec4 of normalized, unsigned bytes.
attribs.push_back(glmesh::AttribDesc(1, 4, glmesh::VDT_UNSIGN_BYTE, glmesh::ADT_NORM_FLOAT));
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

enum glmesh::AttribDataType

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.

enum glmesh::VertexDataType

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.