Introduction

Recently I have been working on a small project using the Ogre3D rendering engine and needed to dynamically create a mesh at runtime. There is an example on the Ogre3D wiki but it assumes you have some underlying knowledge on the way meshes are represented by lower-level APIs such as OpenGL and Direct3D so I decided to write this blog post to give some more detail.

Vertices and triangles

Most of the time in a 3d game, you are looking at a bunch of triangles which make up more complicated shapes. Each of these triangles is defined by a set of three points - its vertices.

In order for the graphics card to render these shapes, all you need to do is send it the coordinates of the vertices and then tell it how to join them up. Really old graphics cards used to be sent the vertices they needed to render each frame, however, this isn’t the best way of doing it (nevertheless, you still see it in many OpenGL tutorials around the web). Your application becomes limited by the capacity of the bus for little reason, especially considering that most of the models in the scene are static.

This has been improved by the creation of “vertex buffers”. Vertex buffers allow you to send the coordinates of the vertices to the card once. They are then kept in video memory, so that you don’t need to clog up the bus each frame. Because you are no longer limited by the bus, you can also render many more models.

Vertex buffers in OGRE

In OGRE, the HardwareBuffer class represents a vertex buffer. The HardwareBufferManager singleton allows you to create new hardware buffers.

Creating a vertex buffer is quite simple, you call the createVertexBuffer method and tell it the following pieces of information:

  • How you plan to use the buffer
  • The number of bytes that represent each vertex
  • The number of vertices in the buffer

I’ll focus on the first point to begin with. You can either mark the buffer as being HBU_STATIC or HBU_DYNAMIC. Static buffers are ones you don’t plan to modify very often (you can still modify them, though). Dynamic buffers are ones you plan to modify frequently.

You can also mark a buffer as being HBU_WRITE_ONLY which means you don’t plan to read data back from the buffer.

These allow the graphics card/driver to pick the best way to store your buffer.

The number of vertices in the buffer is fairly self explanatory. However, the number of bytes that represent each vertex requires more explaining. You need to pass this value because there is no fixed way to represent a vertex. As well as the position, your application might need normals, texture coordinates, colours, etc.

This leads us into the next section - vertex declarations.

Vertex declarations in OGRE

The VertexDeclaration class holds a list of VertexElements. These are used to describe the structure of each vertex.

For example, if you wanted each vertex to hold its position, a 2d texture coordinate and a normal, you would need to create three elements for each of these.

Each element has a type and semantic. The type is the data type - e.g. a 2d texture coordinate is usually represented by two floating point numbers, so its type would be VET_FLOAT2. The semantic is VES_TEXCOORD, as it is a texture coordinate.

So for the example earlier, we’d need to have:

  • VET_FLOAT3 VES_POSITION for the position
  • VET_FLOAT2 VES_TEXCOORD for the texture coordinate
  • VET_FLOAT3 VES_NORMAL for the normal

Index buffers

The final thing we need is to do is to tell the graphics card how to connect the buffers into triangles. There are several ways to do this, but the easiest and most versatile is to use OT_TRIANGLE_LIST. With this, you supply a list of indices which point to the vertices from earlier. Each three indices describe a triangle. You must specify the indices in anti-clockwise order in order for the triangle to face you. The other side of the triangle will be ‘culled’ and invisible, so for two-sided triangles you must specify the indices twice.

Diagram of anti-clockwise indices of a triangle

The code

The following code is a commented example on how to create a simple mesh, which should be fairly easy to understand if you read the theory above.

/* create the mesh and a single sub mesh */
Ogre::MeshPtr mesh = Ogre::MeshManager::getSingleton().createManual("CustomMesh", "General")
Ogre::SubMesh *subMesh = mesh->createSubMesh();

/* create the vertex data structure */
mesh->sharedVertexData = new Ogre::VertexData;
mesh->sharedVertexData->vertexCount = 3;

/* declare how the vertices will be represented */
Ogre::VertexDeclaration *decl = mesh->sharedVertexData->vertexDeclaration;
size_t offset = 0;

/* the first three floats of each vertex represent the position */
decl->addElement(0, offset, Ogre::VET_FLOAT3, Ogre::VES_POSITION);
offset += Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3);

/* the second three floats of each vertex represent the colour */
decl->addElement(0, offset, Ogre::VET_FLOAT3, Ogre::VES_COLOUR);
offset += Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3);

/* create the vertex buffer */
Ogre::HardwareBuffer *vertexBuffer = Ogre::HardwareBufferManager::getSingleton().
    createVertexBuffer(offset, mesh->sharedVertexData->vertexCount, Ogre::HardwareBuffer::HBU_STATIC);

/* lock the buffer so we can get exclusive access to its data */
float *vertices = static_cast<float *>(vertexBuffer->lock(Ogre::HardwareBuffer::HBL_NORMAL));

/* populate the buffer with some data */
vertices[0] = 0; vertices[1] = 1; vertices[2] = 0; /* position */
vertices[3] = 1; vertices[4] = 0; vertices[5] = 0; /* colour */

vertices[6] = -1; vertices[7] = -1; vertices[8] = 0; /* position */
vertices[9] = 0; vertices[10] = 1; vertices[11] = 0; /* colour */

vertices[12] = 1; vertices[13] = -1; vertices[14] = 0; /* position */
vertices[15] = 0; vertices[16] = 0; vertices[17] = 1; /* colour */

/* unlock the buffer */
vertexBuffer->unlock();

/* create the index buffer */
Ogre::HardwareBuffer *indexBuffer = Ogre::HardwareBufferManager::getSingleton().
    createIndexBuffer(Ogre::HardwareIndexBuffer::IT_16BIT, mesh->sharedVertexData->vertexCount, Ogre::HardwareBuffer::HBU_STATIC);

/* lock the buffer so we can get exclusive access to its data */
uint16_t *indices = static_cast<uint16_t *>(indexBuffer->lock(Ogre::HardwareBuffer::HBL_NORMAL));

/* define our triangle */
indices[0] = 0;
indices[1] = 1;
indices[2] = 2;

/* unlock the buffer */
indexBuffer->unlock();

/* attach the buffers to the mesh */
mesh->sharedVertexData->vertexBufferBinding->setBinding(0, vertexBuffer);
subMesh->useSharedVertices = true;
subMesh->indexData->indexBuffer = indexBuffer;
subMesh->indexData->indexCount = mesh->sharedVertexData->vertexCount;
subMesh->indexData->indexStart = 0;

/* set the bounds of the mesh */
mesh->_setBounds(Ogre::AxisAlignedBox(-1, -1, -1, 1, 1, 1));

/* notify the mesh that we're all ready */
mesh->load();

/* you can now create an entity/scene node based on your mesh, e.g. */
Ogre::Entity *entity = sceneManager->createEntity("CustomEntity", "CustomMesh", "General");
entity->setMaterialName("YourMaterial", "General");
Ogre::SceneNode *node = rootNode->createChildSceneNode();
node->attachObject(entity);