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:
- 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.
- Call begin() to begin entering data
- 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.
- 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.
- Call end() to finish entering data.
- 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
Class providing a much simplified interface to generating manual objects with custom geometry.
Definition OgreManualObject.h:107
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.
@ OT_TRIANGLE_LIST
A list of triangles, 3 vertices per triangle.
Definition OgreRenderOperation.h:56
Next we specify the vertices of the plane
void position(const Vector3 &pos)
Add a vertex position, starting a new vertex at the same time.
Definition OgreManualObject.h:199
void textureCoord(float u)
Add a texture coordinate to the current vertex.
Definition OgreManualObject.h:273
void normal(const Vector3 &norm)
Add a vertex normal to the current vertex.
Definition OgreManualObject.h:233
Now we can define the face. Ogre will split the quad into triangles for us.
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:400
Calling end()
creates the actual Hardware Buffers to be used for rendering and we can attach the Object to a Ogre::SceneNode.
mSceneMgr->getRootSceneNode()->createChildSceneNode()->attachObject(man);
virtual ManualObjectSection * end(void)
Finish defining the object and compile the final renderable version.
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.
A 3D box aligned with the x/y/z axes.
Definition OgreAxisAlignedBox.h:56
void _setBounds(const AxisAlignedBox &bounds, bool pad=true)
Manually set the bounding box for this Mesh.
Definition OgreAlignedAllocator.h:34
Definition OgreDeprecated.h:54
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.
-100, -100, 0,
0,0,1,
0,1,
100, -100, 0,
0,0,1,
1,1,
100, 100, 0,
0,0,1,
1,0,
-100, 100, 0 ,
0,0,1,
0,0
};
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.
VertexData * sharedVertexData
Shared vertex data.
Definition OgreMesh.h:310
void createVertexData(HardwareBufferManagerBase *mgr=nullptr)
Creates a new shared vertex data object.
Definition OgreMesh.h:320
Records the state of all the vertex buffer bindings required to provide a vertex declaration with the...
Definition OgreHardwareVertexBuffer.h:480
VertexBufferBinding * vertexBufferBinding
Defines which vertex buffers are bound to which sources.
Definition OgreVertexIndexData.h:95
uint32 vertexCount
The number of vertices to process in this particular rendering group.
Definition OgreVertexIndexData.h:101
VertexDeclaration * vertexDeclaration
Declaration of the the format of the vertex input.
Definition OgreVertexIndexData.h:91
This class declares the format of a set of vertex inputs, which can be issued to the rendering API th...
Definition OgreHardwareVertexBuffer.h:278
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);
HardwareBufferManager::getSingleton().createIndexBuffer(HardwareIndexBuffer::IT_16BIT, 6, HBU_GPU_ONLY);
void setBinding(unsigned short index, const HardwareVertexBufferSharedPtr &buffer)
Set a binding, associating a vertex buffer with a given index.
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.
HardwareIndexBufferSharedPtr indexBuffer
Pointer to the HardwareIndexBuffer to use, must be specified if useIndexes = true.
Definition OgreVertexIndexData.h:254
uint32 indexStart
Index in the buffer to start from for this operation.
Definition OgreVertexIndexData.h:257
uint32 indexCount
The number of indexes to use from the buffer.
Definition OgreVertexIndexData.h:260
SubMesh * createSubMesh(void)
Creates a new SubMesh.
Defines a part of a complete mesh.
Definition OgreSubMesh.h:61
bool useSharedVertices
Indicates if this submesh shares vertex data with other meshes or whether it has it's own vertices.
Definition OgreSubMesh.h:143
IndexData * indexData
Face index data.
Definition OgreSubMesh.h:91
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
virtual void load(bool backgroundThread=false)
Loads the resource, if it is not already.
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.