OGRE  1.12.13
Object-Oriented Graphics Rendering Engine
Manual mesh creation

There are two ways to create your own mesh. The first way is to create a Ogre::Mesh instance and provide it with the vertex and index buffers directly.

The second way is the high level Ogre::ManualObject interface. Instead of filling position and color buffers, you simply call the "position" and "colour" functions.

Using Manual Object

Building one-off geometry objects manually usually requires getting down and dirty with the vertex buffer and vertex declaration API, which some people find a steep learning curve. This class gives you a simpler interface specifically for the purpose of building a 3D object simply and quickly. Note that if you intend to instance your object you will still need to become familiar with the Mesh class.

This class draws heavily on the interface for OpenGL immediate-mode (glBegin, glVertex, glNormal etc), since this is generally well-liked by people. There are a couple of differences in the results though - internally this class still builds hardware buffers which can be re-used, so you can render the resulting object multiple times without re-issuing all the same commands again. Secondly, the rendering is not immediate, it is still queued just like all OGRE objects. This makes this object more efficient than the equivalent GL immediate-mode commands, so it's feasible to use it for large objects if you really want to.

To construct some geometry with this object:

  1. If you know roughly how many vertices (and indices, if you use them) you're going to submit, call estimateVertexCount() and estimateIndexCount(). This is not essential but will make the process more efficient by saving memory reallocations.
  2. Call begin() to begin entering data
  3. For each vertex, call position(), normal(), textureCoord(), colour() to define your vertex data. Note that each time you call position() you start a new vertex. Note that the first vertex defines the components of the vertex - you can't add more after that. For example if you didn't call normal() in the first vertex, you cannot call it in any others. You ought to call the same combination of methods per vertex.
  4. If you want to define triangles (or lines/points) by indexing into the vertex list, you can call index() as many times as you need to define them. If you don't do this, the class will assume you want triangles drawn directly as defined by the vertex list, i.e. non-indexed geometry. Note that stencil shadows are only supported on indexed geometry, and that indexed geometry is a little faster; so you should try to use it.
  5. Call end() to finish entering data.
  6. Optionally repeat the begin-end cycle if you want more geometry using different rendering operation types, or different materials After calling end(), the class will organise the data for that section internally and make it ready to render with. Like any other MovableObject you should attach the object to a SceneNode to make it visible. Other aspects like the relative render order can be controlled using standard MovableObject methods like setRenderQueueGroup.

You can also use beginUpdate() to alter the geometry later on if you wish. If you do this, you should call setDynamic(true) before your first call to begin(), and also consider using estimateVertexCount() / estimateIndexCount() if your geometry is going to be growing, to avoid buffer recreation during growth.

Note
like all OGRE geometry, triangles should be specified in anti-clockwise winding order (whether you're doing it with just vertices, or using indexes too). That is to say that the front of the face is the one where the vertices are listed in anti-clockwise order.

Example

We will use the ManualObject to create a single textured plane. After creating the object, we start a new geometry block that will use the given material

Ogre::ManualObject* man = mSceneMgr->createManualObject("test");
man->begin("Examples/OgreLogo", Ogre::RenderOperation::OT_TRIANGLE_LIST);

Next we specify the vertices of the plane

man->position(-20, 20, 20);
man->normal(0, 0, 1);
man->textureCoord(0, 0);
man->position(-20, -20, 20);
man->normal(0, 0, 1);
man->textureCoord(0, 1);
man->position(20, -20, 20);
man->normal(0, 0, 1);
man->textureCoord(1, 1);
man->position(20, 20, 20);
man->normal(0, 0, 1);
man->textureCoord(1, 0);

Now we can define the face. Ogre will split the quad into triangles for us.

man->quad(0, 1, 2, 3);

Calling end() creates the actual Hardware Buffers to be used for rendering and we can attach the Object to a Ogre::SceneNode.

man->end();
mSceneMgr->getRootSceneNode()->createChildSceneNode()->attachObject(man);

In case you need multiple Ogre::Entities of the plane, you should call Ogre::ManualObject::convertToMesh first and then use Ogre::SceneManager::createEntity as usual.

Using vertex and index buffers directly

This time we are going to create a plane using the lower level Ogre::HardwareBuffer primitives.

We start by creating a Mesh object. As this is a manual Mesh, we have to set the bounds of it explicitly.

using namespace Ogre;
mesh->_setBounds(AxisAlignedBox({-100,-100,0}, {100,100,0});

Next we define what should end up in our vertex and index buffer. We will store all data interleaved in one buffer. This typically has some advantages due to cache coherency and also is what ManualObject does automatically for us.

float vertices[32] = {
-100, -100, 0, // pos
0,0,1, // normal
0,1, // texcoord
100, -100, 0,
0,0,1,
1,1,
100, 100, 0,
0,0,1,
1,0,
-100, 100, 0 ,
0,0,1,
0,0
};
uint16 faces[6] = {0,1,2,
0,2,3 };

However we could also split the data into multiple buffers with lower precision to save some bytes on texture coordinates and normals.

To describe the vertex sources, we have to create a Ogre::VertexData object. Notably it stores how many vertices we have.

mesh->sharedVertexData = new VertexData();
VertexDeclaration* decl = mesh->sharedVertexData->vertexDeclaration;
VertexBufferBinding* bind = mesh->sharedVertexData->vertexBufferBinding;

The actual description of our vertex buffer however is stored inside the Ogre::VertexDeclaration.

size_t offset = 0;
offset += decl->addElement(0, offset, VET_FLOAT3, VES_POSITION).getSize();
offset += decl->addElement(0, offset, VET_FLOAT3, VES_NORMAL).getSize();
offset += decl->addElement(0, offset, VET_FLOAT2, VES_TEXTURE_COORDINATES, 0).getSize();

Now we can continue to create the Hardware Buffers and upload our data.

HardwareBufferManager::getSingleton().createVertexBuffer(offset, 4, HBU_GPU_ONLY);
vbuf->writeData(0, vbuf->getSizeInBytes(), vertices, true);
bind->setBinding(0, vbuf);
HardwareIndexBufferSharedPtr ibuf = HardwareBufferManager::getSingleton().createIndexBuffer(
HardwareIndexBuffer::IT_16BIT, 6, HBU_GPU_ONLY);
ibuf->writeData(0, ibuf->getSizeInBytes(), faces, true);

Note how we used the symbolical constant 0 to link the Ogre::HardwareVertexBuffer to the Ogre::VertexDeclaration. This allows the underlying RenderSystem to swap VertexBuffers without changing the VertexDeclaration. i.e. render different Meshes that share the same vertex layout, without changing the state.

Finally we create the Ogre::SubMesh that will be ultimately rendered.

SubMesh* sub = mesh->createSubMesh();
sub->useSharedVertices = true;
sub->indexData->indexBuffer = ibuf;
sub->indexData->indexCount = 6;
sub->indexData->indexStart = 0;

Note that while our VertexBuffer is shared, the IndexBuffer is not. This allows rendering different faces of the same object using different Materials. Here, each SubMesh links the faces (IndexBuffer) to the according material.

Finally, we have to update the loading state of the mesh as

mesh->load();

If you have registered a Ogre::ManualResourceLoader, the resource loading would only happen now.

Note
Using the Ogre::ManualResourceLoader is highly recommended. It allows lazy-loading the data on demand as well as unloading and re-loading resources when running out of memory.
Ogre::RenderOperation::OT_TRIANGLE_LIST
@ OT_TRIANGLE_LIST
A list of triangles, 3 vertices per triangle.
Definition: OgreRenderOperation.h:56
Ogre::HardwareBuffer::writeData
virtual void writeData(size_t offset, size_t length, const void *pSource, bool discardWholeBuffer=false)
Writes data to the buffer from an area of system memory; note that you must ensure that your buffer i...
Definition: OgreHardwareBuffer.h:298
Ogre::ManualObject::position
void position(const Vector3 &pos)
Add a vertex position, starting a new vertex at the same time.
Definition: OgreManualObject.h:203
Ogre::Mesh::createSubMesh
SubMesh * createSubMesh(void)
Creates a new SubMesh.
Ogre::VertexData::vertexCount
size_t vertexCount
The number of vertices to process in this particular rendering group.
Definition: OgreVertexIndexData.h:100
Ogre::ManualObject
Class providing a much simplified interface to generating manual objects with custom geometry.
Definition: OgreManualObject.h:106
Ogre::HardwareVertexBufferSharedPtr
SharedPtr< HardwareVertexBuffer > HardwareVertexBufferSharedPtr
Definition: OgrePrerequisites.h:298
Ogre::MeshManager::getSingleton
static MeshManager & getSingleton(void)
Get the singleton instance.
Ogre::ManualObject::normal
void normal(const Vector3 &norm)
Add a vertex normal to the current vertex.
Definition: OgreManualObject.h:237
Ogre::HBU_GPU_ONLY
@ HBU_GPU_ONLY
Device-local GPU (video) memory.
Definition: OgreHardwareBuffer.h:71
Ogre::VES_NORMAL
@ VES_NORMAL
Normal, typically VET_FLOAT3.
Definition: OgreHardwareVertexBuffer.h:99
Ogre::ManualObject::quad
void quad(uint32 i1, uint32 i2, uint32 i3, uint32 i4)
Add a set of 4 vertex indices to construct a quad (out of 2 triangles); this is a shortcut to calling...
Definition: OgreManualObject.h:408
Ogre::Resource::load
virtual void load(bool backgroundThread=false)
Loads the resource, if it is not already.
Ogre::VET_FLOAT3
@ VET_FLOAT3
Definition: OgreHardwareVertexBuffer.h:129
Ogre::Mesh::sharedVertexData
VertexData * sharedVertexData
Shared vertex data.
Definition: OgreMesh.h:309
Ogre::VES_TEXTURE_COORDINATES
@ VES_TEXTURE_COORDINATES
Texture coordinates, typically VET_FLOAT2.
Definition: OgreHardwareVertexBuffer.h:105
Ogre::SharedPtr< Mesh >
Ogre::ManualObject::textureCoord
void textureCoord(float u)
Add a texture coordinate to the current vertex.
Definition: OgreManualObject.h:277
Ogre::VES_POSITION
@ VES_POSITION
Position, typically VET_FLOAT3.
Definition: OgreHardwareVertexBuffer.h:93
Ogre::RGN_DEFAULT
const _OgreExport char *const RGN_DEFAULT
Default resource group name.
Ogre::VertexData::vertexDeclaration
VertexDeclaration * vertexDeclaration
Declaration of the the format of the vertex input.
Definition: OgreVertexIndexData.h:90
Ogre::SubMesh::useSharedVertices
bool useSharedVertices
Indicates if this submesh shares vertex data with other meshes or whether it has it's own vertices.
Definition: OgreSubMesh.h:72
Ogre::Mesh::_setBounds
void _setBounds(const AxisAlignedBox &bounds, bool pad=true)
Manually set the bounding box for this Mesh.
Ogre::AxisAlignedBox
A 3D box aligned with the x/y/z axes.
Definition: OgreAxisAlignedBox.h:55
Ogre::uint16
unsigned short uint16
Definition: OgrePlatform.h:288
Ogre
Definition: OgreAlignedAllocator.h:34
Ogre::MeshManager::createManual
MeshPtr createManual(const String &name, const String &groupName, ManualResourceLoader *loader=0)
Creates a new Mesh specifically for manual definition rather than loading from an object file.
Ogre::ManualObject::begin
virtual void begin(const String &materialName, RenderOperation::OperationType opType=RenderOperation::OT_TRIANGLE_LIST, const String &groupName=ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME)
Start defining a part of the object.
Ogre::VertexData::vertexBufferBinding
VertexBufferBinding * vertexBufferBinding
Defines which vertex buffers are bound to which sources.
Definition: OgreVertexIndexData.h:94
Ogre::ManualObject::end
virtual ManualObjectSection * end(void)
Finish defining the object and compile the final renderable version.
Ogre::HardwareIndexBufferSharedPtr
SharedPtr< HardwareIndexBuffer > HardwareIndexBufferSharedPtr
Definition: OgrePrerequisites.h:294
Ogre::VET_FLOAT2
@ VET_FLOAT2
Definition: OgreHardwareVertexBuffer.h:128