ManualObject is a class for easy creation of custom 2D/3D objects by code. They can be created and modified "on the fly".
Table of contents
A Crash Course in 3D Objects
This section was taken from the Intermediate Tutorial 4.
Before we start diving directly into making a mesh, it would probably be useful to talk about what a mesh is, and what it is made up of. Though this is a gross oversimplification, a mesh consists of roughly two parts: the vertex buffer and the index buffer.
Vertex buffers define points in 3D space. Each element in the vertex buffer is defined by several attributes you can set. The only attribute you must set is the position of the vertex. Aside from that, there are many optional properties you can set, such as the color of the vertex, the texture coordinates, and so on. Which ones you will actually need to use is dependent on what you are trying to do with the mesh.
Index buffers "connect the dots" by selecting points from the vertex buffer. Every three indexes specified in the index buffer defines a single triangle to be drawn by the GPU. The order in which you select vertices in the index buffer tells the graphics card which way the triangle faces. A triangle which is drawn counter-clockwise is facing you, one drawn clockwise is facing away from you. Normally only the front of a triangle is rendered, so it is important to be sure that your triangles are setup properly.
Though all meshes have a vertex buffer, not all meshes will have an index buffer. For example, the mesh we are about to create will not have an index buffer since we want to create an empty rectangle (as opposed to a filled rectangle). Lastly, note that vertex and index buffers are usually stored in the video card's own memory, so your software can just send the card one simple, discrete set of commands to tell it to use those predefined buffers to render an entire 3D mesh in one go.
How to create 3D objects (by code)
This section was taken from the Intermediate Tutorial 4.
There are two ways to create your own mesh within Ogre. The first way is to subclass the SimpleRenderable object and provide it with the vertex and index buffers directly. This is the most direct way to create one, but it's also the most cryptic. The Generating A Mesh code snippet shows an example of this.
To make things easier, Ogre provides a much nicer interface called ManualObject, which allows you to use some simple functions to define a mesh instead of writing raw data to the buffer objects. Instead of dropping the position, color, and so on into a buffer, you simply call the "position" and "colour" functions.
Basics of ManualObject
Using a ManualObject you don't need to load resources and it's nice to create dynamic objects or simple ones.
To do so, you define the needed points (vertices), rendering type (points, lines, surfaces) and assign a material. To add it to the scene, it has to be attached to a SceneNode (e.g. the RootSceneNode).
ManualObjects are composed by sections (similar to SubMeshes of a Mesh). Each section is a "definition block" begin() ...content... end(). Often it's enough to create one section.
If you define more than one section, each of them can use individual materials and rendering types. As a result you can create ManualObjects that contain lines, surfaces or a combination of it. Transparency is also possible (by use of materials with transparency property).
If several instances (copies) of a ManualObject are needed, you either create them multiple times or just create a Mesh from it by use of ManualObject::convertToMesh(). One mesh can be loaded to multiple times to a scene (and becomes an Entity). An other advantage of the convertToMesh() conversion is, that you can apply different materials to the same shape.
In some cases the identity projection can be interesting. With this the coordinates are in 2D screen space of the current camera. Useful for overlay rendering.
- If you want to convert a ManualObject to a Mesh, it's not enough to define positions. Additionally you have to define indexes.
- Similarly, defining indexes is also a requirement if you wish to generate tangent vectors (e.g., for use in normal mapping).
- For good viewing reflections you need to calculate/define normals for each vertex.
- For usage with textures you also have to calculate/define UV coordinates for each vertex.
- Avoid to create many sections with only less content (e.g. 1 triangle per section). This could decrease the performance. (Benchmark result: Avoid to have many sections with less than 30 triangles.)
A very minimal example that creates a line in 3D space.
// create ManualObject ManualObject* manual = mSceneMgr->createManualObject("manual"); // specify the material (by name) and rendering type manual->begin("BaseWhiteNoLighting", RenderOperation::OT_LINE_LIST); // define start and end point manual->position(-100, -100, -100); manual->position(100, 100, 100); // tell Ogre, your definition has finished manual->end(); // add ManualObject to the RootSceneNode (so it will be visible) mSceneMgr->getRootSceneNode()->attachObject(manual);
This example gives an outlined square (or quad):
ManualObject* manual = mSceneMgr->createManualObject("manual"); manual->begin("BaseWhiteNoLighting", RenderOperation::OT_LINE_STRIP); manual->position(-100.0, -100.0, 0.0); // start position manual->position( 100.0, -100.0, 0.0); // draw first line manual->position( 100.0, 100.0, 0.0); manual->position(-100.0, 100.0, 0.0); manual->position(-100.0, -100.0, 0.0); // draw fourth line manual->end(); mSceneMgr->getRootSceneNode()->createChildSceneNode()->attachObject(manual);
This example is similar to the previous one, but uses indexes.
- Vertex positions and the usage order are defined seperatly
- Vertex positions can be updated (changed) later without re-build of the whole ManualObject
- Index definitions are needed if you want to create a Mesh out of the ManualObject
ManualObject* manual = mSceneMgr->createManualObject("manual"); manual->begin("BaseWhiteNoLighting", RenderOperation::OT_LINE_STRIP); // define vertex position of index 0..3 manual->position(-100.0, -100.0, 0.0); manual->position( 100.0, -100.0, 0.0); manual->position( 100.0, 100.0, 0.0); manual->position(-100.0, 100.0, 0.0); // define usage of vertices by refering to the indexes manual->index(0); manual->index(1); manual->index(2); manual->index(3); manual->index(0); manual->end(); mSceneMgr->getRootSceneNode()->createChildSceneNode()->attachObject(manual);
When you want to move, rotate, scale or make temporarily invisible the ManualObject, attach it to a dedicated SceneNode.
Then you can apply the wanted operations to its SceneNode.
Here you see an example for moving:
SceneNode mySceneNode = mSceneMgr->getRootSceneNode()->createChildSceneNode("mySceneNode"); // attach ManualObject mySceneNode->attachObject(manual); // move it mySceneNode->setPosition(0, 10, 0);
If you want to clamp the ManualObject to a movable object (e.g. a vehicle), just attach the ManualObject (or its dedicated SceneNode) to the SceneNode of the movable object.
|OT_POINT_LIST||A list of points||1 vertex per point|
|OT_LINE_LIST||A list of lines||2 vertices per line|
|OT_LINE_STRIP||A strip of connected lines||1 start vertex and 1 vertex per line|
|OT_TRIANGLE_LIST||A list of triangles||3 vertices per triangle|
|OT_TRIANGLE_STRIP||A strip of triangles||3 vertices for the first triangle and 1 per triangle after that|
|OT_TRIANGLE_FAN||A fan of triangles||3 vertices for the first triangle and 1 per triangle after that|
A triangle strip is a series of connected triangles, sharing vertices, allowing for faster rendering and more efficient memory usage. They are optimized on most graphics cards, making them the most efficient way of describing an object.
More details you find in this Wikipedia page.
A triangle fan describes a set of connected triangles that share one central vertex.
More details you find in this Wikipedia page.
Using a triangle strip or triangle fan you need just N+2 vertices for describing N triangles (instead of 3N as for seperately defined triangles).
The size of one point is 1 pixel, independent of its distance to the camera. The point size can be changed by the material settings. Use Material::setPointSize() for the whole material or more differentiated Technique::setPointSize() or Pass::setPointSize().
It's also possible to render the line thickness dependent to the distance. For details look to setPointAttenuation, setPointMinSize, setPointMaxSize and
setPointSpritesEnabled of the Pass class.
- For large ManualObjects it's suggested to use indices. This reduces redundant vertices in the vertex buffer.
- Strips and fans (without index), also reduce vertex redundancy, but have the disadvantage that they need one batch for each strip. This is bad for performance, especially when there are many short strips. Consider if you really need to save GPU memory. (Calculation example for 1000 triangles: Needs ~100KB as strip/fan or ~300KB as triangle list)
This detailed description was copied from the Ogre 1.7.1 class reference of ManualObject.
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 that 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.
ManualObjects can always be altered and extended.
As described above, a ManualObject can contain several sections begin(), ...add content..., end().
To add more vertices, just create a new section, using begin(). To alter a section, use beginUpdate() instead.
BeginUpdate() - API description
void Ogre::ManualObject::beginUpdate(size_t sectionIndex)
Start the definition of an update to a part of the object.
Using this method, you can update an existing section of the object efficiently. You do not have the option of changing the operation type obviously, since it must match the one that was used before.
If your sections are changing size, particularly growing, use estimateVertexCount and estimateIndexCount to pre-size the buffers a little larger than the initial needs to avoid buffer reconstruction.
sectionIndex - The index of the section you want to update. The first call to begin() would have created section 0, the second section 1, etc.
- Class reference for ManualObject
- Other ManualObject examples: Line3D, DynamicLineDrawing, Circle3D, ManualObject 2D
- MOGRE: Line 3D, Create Tetrahedron, Generating a grid
- MadMarx Tutorial 3 - ManualObject Quad
- MadMarx Tutorial 4 - ManualObject to Mesh
- Important remarks are in its API description of ManualObject
- API description of convertToMesh()
- API description of SimpleRenderable
- API description of identity projection
- Ogre Procedural Geometry Library - A library to quickly create geometric primitives
- DynamicLineDrawing - create a line by the class SimpleRenderable, which can be modified with better performance (e.g. change size of a circle)
- Debug Drawing Utility Class
- Read raw data from ManualObject - MOGRE
- TerrainMeshDecal - ManualObject example: Using a mesh decal to show a marker on the terrain for terrain selection
- Generating A Mesh - creating a mesh in memory using an index and vertex buffer