Skip to main content
MadMarx Tutorial 5         Basic Mesh Loading

Foreword.

If you prefer tutorials that come with a framework => check the other wiki tutorial series.
If you prefer tutorials that go step by step without a framework => this page should be ok.

I assume you know C++. If not, this tutorial will probably be hard to understand !

This tutorial presents only a few elements of Ogre3D.

You can download the code and media for this tutorial at the bottom of this wiki page.
This little tutorial is an extract of a bigger project which contains more tutorials & helper classes.
This bigger project is avaible there :
https://sourceforge.net/projects/so3dtools/

Also, make sure you read these tutorials in order!

Tutorial Description

In this program a .mesh file will be loaded from the hard drive disk.

In this basic version, I will load everything that lies in a directory.
When loading loadable data with ogre3D, the 'ogre3D way' to do it is to
1/ create a ResourceGroup.
2/ declare the path of the directory containing the resource you want in that ResourceGroup.
3/ initialise and load the ResourceGroup.
There are other ways to do it, some are covered in other tutorials.

How to get a .mesh file? You can code a program to do it, or use Blender / 3DSMAX / Maya / ...
that will export (using plugins) either a .mesh, either a .xml that can be converted
to a .mesh. (Note : the 'dtd' of this xml + the OgreXMLConverter program sources are in
the source of ogre3D : Tools/XMLConverter/docs).

After viewport creation

Here I choose a name for a resource group. Then I create it.
Often, a resourcegroup is a good way to store the data corresponding
to a level in a game.

Copy to clipboard
¤ Ogre::String lNameOfResourceGroup = "Mission 1 : Deliver Tom"; { Ogre::ResourceGroupManager& lRgMgr = Ogre::ResourceGroupManager::getSingleton(); lRgMgr.createResourceGroup(lNameOfResourceGroup);

We say which directories will be loaded by this resourcegroup.
I can add many directories, which will be loaded in same the ORDER.
This ORDER is extremely important : if a material is loaded <i>after</i> a mesh using this material,
this mesh won't be able to find the material during its loading!
I advise you not to use a recursive load (which load the full directory tree).
Seriously, 'recursive loading' often leads to problems.

Copy to clipboard
¤ Ogre::String lDirectoryToLoad = "../../media/mesh"; bool lIsRecursive = false; lRgMgr.addResourceLocation(lDirectoryToLoad, "FileSystem", lNameOfResourceGroup, lIsRecursive);

The function 'initialiseResourceGroup' parses scripts if any in the locations.

Copy to clipboard
¤ lRgMgr.initialiseResourceGroup(lNameOfResourceGroup);

Files that can be loaded are loaded.

Copy to clipboard
¤ lRgMgr.loadResourceGroup(lNameOfResourceGroup);

Now the loaded Mesh is available from its ResourceGroup,
as well as from the Ogre::MeshManager. A shared pointer to
it can be accessed by : Ogre::MeshManager::getSingleton().getByName(name_of_the_mesh);

Now I can create Entities using that mesh.

Copy to clipboard
¤ Ogre::String lNameOfTheMesh = "MonsterHead.mesh"; int lNumberOfEntities = 5; for(int iter = 0; iter < lNumberOfEntities; ++iter) { Ogre::Entity* lEntity = lScene->createEntity(lNameOfTheMesh);

Now I attach it to a scenenode, so that it becomes present in the scene.

Copy to clipboard
¤ Ogre::SceneNode* lNode = lRootSceneNode->createChildSceneNode(); lNode->attachObject(lEntity);

I move the SceneNode so that it is visible to the camera.

Copy to clipboard
¤ float lPositionOffset = float(1+ iter * 2) - (float(lNumberOfEntities)); lPositionOffset = lPositionOffset * 20; lNode->translate(lPositionOffset, lPositionOffset, -200.0f);

The loaded mesh will be white. This is normal.

main.cpp

Copy to clipboard
// In this program a .mesh file will be loaded from the hard drive disk. // // In this basic version, I will load everything that lies in a directory. // When loading loadable data with ogre3D, the 'ogre3D way' to do it is to // 1/ create a ResourceGroup. // 2/ declare the path of the directory containing the resource you want in that ResourceGroup. // 3/ initialise and load the ResourceGroup. // There are other ways to do it, some are covered in other tutorials. // // How to get a .mesh file? You can code a program to do it, or use Blender / 3DSMAX / Maya / ... // that will export (using plugins) either a .mesh, either a .xml that can be converted // to a .mesh. (Note : the 'dtd' of this xml + the OgreXMLConverter program sources are in // the source of ogre3D : Tools/XMLConverter/docs). // I will use std::auto_ptr so I need to include 'memory'. // If you don't know std::auto_ptr, you should check some C++ tutorials/lesson on this matter. #include <memory> // I will check for std::exception. If you don't know what exception/try/catch means, you should learn C++ first. #include <exception> // These are some files that we need to include to use Ogre3D. Note that you can at the beginnings use directly "Ogre.h", to include lots of commonly used classes. #include "OGRE/OgreRoot.h" #include "OGRE/OgreRenderSystem.h" #include "OGRE/OgreRenderWindow.h" #include "OGRE/OgreWindowEventUtilities.h" #include "OGRE/OgreManualObject.h" #include "OGRE/OgreEntity.h" //Here I include my other files, like the one for SimpleOgreInit... #include "SimpleOgreInit.h" #include "EasyDefines.h" // I declare a function in which I will make my whole application. // This is easy then to add more things later in that function. // The main will call this function and take care of the global try/catch. void AnOgreApplication() { // I construct my object that will allow me to initialise Ogre easily. OgreEasy::SimpleOgreInit lOgreInit; if(!lOgreInit.initOgre()) { std::cout<<"Impossible to init Ogre correctly."<<std::endl; return; } //I prefer to be able to access my variables directly. Ogre::Root* lRoot = lOgreInit.mRoot.get(); Ogre::RenderWindow* lWindow = lOgreInit.mWindow; // I create a scenemanager. This is like a 'Scene', in which I can put lights, 3d objects, etc... // The scenemanager contains an arborescent graph of 'SceneNodes'. To manage elements of the scene, // I will create SceneNodes in the SceneManager, and attach the elements to the scenenodes. // First parameter : I select a kind of SceneManager. This may have a huge impact on performance. // Depending on your scene, some are better than other. The default one does no optimization at all. // Second parameter : I give a name to the scenemanager. // Note : It is easy to have more than one scenemanager (If you got 2 different scenes for example). Ogre::SceneManager* lScene = lRoot->createSceneManager(Ogre::ST_GENERIC, "MyFirstSceneManager"); // The 'root SceneNode' is the only scenenode at the beginning in the SceneManager. // The SceneNodes can be seen as 'transformation' containers <=> it contains scale/position/rotation // of the objects. There is only 1 root scenenode, and all other scenenode are // its direct or indirect children. Ogre::SceneNode* lRootSceneNode = lScene->getRootSceneNode(); // I create a camera. It represent a 'point of view' in the scene. Ogre::Camera* lCamera = lScene->createCamera("MyFirstCamera"); // I attach the camera to a new SceneNode. It will be easier then to move it in the scene. Ogre::SceneNode* lCameraNode = lRootSceneNode->createChildSceneNode("MyFirstCameraNode"); lCameraNode->attachObject(lCamera); // We create a viewport on a part of the window. // A viewport is the link between 1 camera and 1 drawing surface (here the window). // I can then call 'update();' on it to make it draw the Scene from the camera. // You can have several viewports on 1 window. // Check API for details on parameters. float lViewportWidth = 0.88f; float lViewportHeight = 0.88f; float lViewportLeft = (1.0f - lViewportWidth) * 0.5f; float lViewportTop = (1.0f - lViewportHeight) * 0.5f; unsigned short lMainViewportZOrder = 100; Ogre::Viewport * vp = lWindow->addViewport(lCamera, lMainViewportZOrder, lViewportLeft, lViewportTop, lViewportWidth, lViewportHeight); // I want the viewport to draw the scene automatically // when I will call lWindow->update(); vp->setAutoUpdated(true); // I choose a color for this viewport. // I prefer to have a bright color, to detect holes in geometry etc... vp->setBackgroundColour(Ogre::ColourValue(1,0,1)); // I choose the visual ratio of the camera. To make it looks real, I want it the same as the viewport. float ratio = float(vp->getActualWidth()) / float(vp->getActualHeight()); lCamera->setAspectRatio(ratio); // I choose the clipping far& near planes. if far/near>2000, you can get z buffer problem. // eg : far/near = 10000/5 = 2000 . it's ok. // If (far/near)>2000 then you will likely get 'z fighting' issues. lCamera->setNearClipDistance(1.5f); lCamera->setFarClipDistance(3000.0f); // I want my window to be active lWindow->setActive(true); // I want to update myself the content of the window, not automatically. lWindow->setAutoUpdated(false); // Here I choose a name for a resource group. Then I create it. // Often, a resourcegroup is a good way to store the data corresponding // to a level in a game. Ogre::String lNameOfResourceGroup = "Mission 1 : Deliver Tom"; { Ogre::ResourceGroupManager& lRgMgr = Ogre::ResourceGroupManager::getSingleton(); lRgMgr.createResourceGroup(lNameOfResourceGroup); // We say which directories will be loaded by this resourcegroup. // I can add many directories, which will be loaded in same the ORDER. // This ORDER is extremely important : if a material is loaded <i>after</i> a mesh using this material, // this mesh won't be able to find the material during its loading! // I advise you not to use a recursive load (which load the full directory tree). // Seriously, 'recursive loading' often leads to problems. Ogre::String lDirectoryToLoad = "../../media/mesh"; bool lIsRecursive = false; lRgMgr.addResourceLocation(lDirectoryToLoad, "FileSystem", lNameOfResourceGroup, lIsRecursive); // The function 'initialiseResourceGroup' parses scripts if any in the locations. lRgMgr.initialiseResourceGroup(lNameOfResourceGroup); // Files that can be loaded are loaded. lRgMgr.loadResourceGroup(lNameOfResourceGroup); // Now the loaded Mesh is available from its ResourceGroup, // as well as from the Ogre::MeshManager. A shared pointer to // it can be accessed by : Ogre::MeshManager::getSingleton().getByName(name_of_the_mesh); // Now I can create Entities using that mesh. Ogre::String lNameOfTheMesh = "MonsterHead.mesh"; int lNumberOfEntities = 5; for(int iter = 0; iter < lNumberOfEntities; ++iter) { Ogre::Entity* lEntity = lScene->createEntity(lNameOfTheMesh); // Now I attach it to a scenenode, so that it becomes present in the scene. Ogre::SceneNode* lNode = lRootSceneNode->createChildSceneNode(); lNode->attachObject(lEntity); // I move the SceneNode so that it is visible to the camera. float lPositionOffset = float(1+ iter * 2) - (float(lNumberOfEntities)); lPositionOffset = lPositionOffset * 20; lNode->translate(lPositionOffset, lPositionOffset, -200.0f); // The loaded mesh will be white. This is normal. } } // cleaning of windows events managed by Ogre::WindowEventUtilities::... // I call it after a 'pause in window updating', in order to maintain smoothness. // Explanation : if you clicked 2000 times when the windows was being created, there are // at least 2000 messages created by the OS to listen to. This is made to clean them. lRoot->clearEventTimes(); // I wait until the window is closed. // The "message pump" thing is something you will see in most GUI application. // It allow the binding of messages between the application and the OS. // These messages are most of the time : keystroke, mouse moved, ... or window closed. // If I don't do this, the message are never caught, and the window won't close. while(!lOgreInit.mWindow->isClosed()) { // Drawings // the window update its content. // each viewport that is 'autoupdated' will be redrawn now, // in order given by its z-order. lWindow->update(false); // The drawn surface is then shown on the screen // (google "double buffering" if you want more details). // I always use vertical synchro. bool lVerticalSynchro = true; lWindow->swapBuffers(lVerticalSynchro); // This update some internal counters and listeners. // Each render surface (window/rtt/mrt) that is 'auto-updated' has got its 'update' function called. lRoot->renderOneFrame(); Ogre::WindowEventUtilities::messagePump(); } // Let's cleanup! { lWindow->removeAllViewports(); } { lScene->destroyAllCameras(); lScene->destroyAllManualObjects(); lScene->destroyAllEntities(); lRootSceneNode->removeAndDestroyAllChildren(); } { Ogre::ResourceGroupManager& lRgMgr = Ogre::ResourceGroupManager::getSingleton(); lRgMgr.destroyResourceGroup(lNameOfResourceGroup); } return; } int main() { try { AnOgreApplication(); std::cout<<"end of the program"<<std::endl; }catch(Ogre::Exception &e) { MWARNING("!!!!Ogre::Exception!!!!\n"<<e.what()); }catch(std::exception &e) { MWARNING("!!!!std::exception!!!!\n"<<e.what()); } return 0; }

Full program sources:
http://sourceforge.net/projects/so3dtools/files/Ogre3DWiki/05_BasicMeshLoading.7z/download