History: MadMarx Tutorial 5
Source of version: 9 (current)
Copy to clipboard
!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!
{maketoc}
!!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.
{CODE(wrap="1",colors="c++")}
¤ Ogre::String lNameOfResourceGroup = "Mission 1 : Deliver Tom";
{
Ogre::ResourceGroupManager& lRgMgr = Ogre::ResourceGroupManager::getSingleton();
lRgMgr.createResourceGroup(lNameOfResourceGroup);
{CODE}
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|material)) is loaded <i>after</i> a mesh using this ((-Material|material)),
this mesh won't be able to find the ((-Material|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.
{CODE(wrap="1",colors="c++")}
¤ Ogre::String lDirectoryToLoad = "../../media/mesh";
bool lIsRecursive = false;
lRgMgr.addResourceLocation(lDirectoryToLoad, "FileSystem", lNameOfResourceGroup, lIsRecursive);
{CODE}
The function 'initialiseResourceGroup' parses scripts if any in the locations.
{CODE(wrap="1",colors="c++")}
¤ lRgMgr.initialiseResourceGroup(lNameOfResourceGroup);
{CODE}
Files that can be loaded are loaded.
{CODE(wrap="1",colors="c++")}
¤ lRgMgr.loadResourceGroup(lNameOfResourceGroup);
{CODE}
Now the loaded ((-Mesh|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.
{CODE(wrap="1",colors="c++")}
¤ Ogre::String lNameOfTheMesh = "MonsterHead.mesh";
int lNumberOfEntities = 5;
for(int iter = 0; iter < lNumberOfEntities; ++iter)
{
Ogre::Entity* lEntity = lScene->createEntity(lNameOfTheMesh);
{CODE}
Now I attach it to a scenenode, so that it becomes present in the scene.
{CODE(wrap="1",colors="c++")}
¤ Ogre::SceneNode* lNode = lRootSceneNode->createChildSceneNode();
lNode->attachObject(lEntity);
{CODE}
I move the ((-SceneNode|SceneNode)) so that it is visible to the ((-Camera|camera)).
{CODE(wrap="1",colors="c++")}
¤ float lPositionOffset = float(1+ iter * 2) - (float(lNumberOfEntities));
lPositionOffset = lPositionOffset * 20;
lNode->translate(lPositionOffset, lPositionOffset, -200.0f);
{CODE}
The loaded mesh will be white. This is normal.
! main.cpp
{CODE(wrap="1",colors="c++")}
// 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;
}
{CODE}
Full program sources:
http://sourceforge.net/projects/so3dtools/files/Ogre3DWiki/05_BasicMeshLoading.7z/download