How to-list
Written by Oogst, last update on 25-05-2004, check the Oogst-site for more Oogst-stuff.
See also: OogstsHowTo2 and Snippets
Table of contents
- How to-list
- About this list
- Why I made this list
- ExampleApplication
- How to set up an application using ExampleApplication
- How to put a 3D-model in the scene
- How to remove an Entity from the scene
- How to move, reposition, scale and rotate a SceneNode
- How to put a light in the scene
- How to set the ambient lighting
- How to control the camera
- How to add a billboard/sprite to the scene
- How to create a basic FrameListener using ExampleFrameListener
- How to control some object from your scene in a FrameListener
- How to get the time since the previous frame
- How to react to key-presses
- How to make sure key-presses are not reacted to too shortly after each other
- How to quit your application
- How to efficiently add and remove 3D-objects from the scene during run-time (like rockets being fired)
- How to show an -Overlay (and hide it again)
- How to change the text in a TextArea
- How to show the mouse-cursor
- How to create a working button
- How to find out which button was pressed
- How to quit the application using an ActionListener
- How to get a different SceneManager
- How to efficiently get a list of all possible collisions
- How to exclude objects from collision detection
- How to find out to which of your own objects a -MovableObject belongs
About this list
This is a list of how to do simple things in Ogre. It contains mainly simple code examples, which is something I myself really missed while learning the basics of Ogre. The explanations provided here are all kept brief. For more details on all the things mentioned, see:
- the official manual for the Ogre-basics and the script-definitions;
- the API for descriptions of all classes and functions in Ogre;
- the tutorials provided with Ogre;
- the search-function of the Ogre-forum.
Why I made this list
I made this list for four reasons:
- I am working on a group project at school and the rest of the team would like to be able to find code examples quickly;
- I really missed this kind of thing when learning Ogre myself and it would have saved me a lot of time;
- I keep forgetting function-names and this way I can look them up quickly;
- I wanted to do something in return for the fantastic engine the Ogre-team has provided and which I can use for free without any expectation from the creators of getting something back for it.
ExampleApplication
This list works from the ExampleApplication and supposes the user uses ExampleApplication and ExampleFrameListener. Some variables, like mSceneMgr, are only available with this name through ExampleApplication. If you are not using ExampleApplication, you will have to fill in the different variables yourself.
Furthermore, all examples use arbitrary names for variables and such; of course you should change these to what you find appropriate for your own application.
How to set up an application using ExampleApplication
If you do not know how this works already, you MUST read more about it in Ogre's tutorial-section: it is really important to understand this properly. In short it requires creating a class that is derived from ExampleApplication. This class can implement the functions createScene() and createFrameListener(). It will probably look something like this:
#include "ExampleApplication.h" class MyClass: public ExampleApplication, { public: MyClass(void); ~MyClass(void); protected: void createScene(void); void createFrameListener(void); };
You will also need a .cpp-file that creates one instance of MyClass and runs it. In general it will look something like this (taken from the "Guide To Setting Up Application Project Files"-tutorial):
#include "Ogre.h" #include "MyClass.h" #if OGRE_PLATFORM == OGRE_PLATFORM_WIN32 #define WIN32_LEAN_AND_MEAN #include "windows.h" INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT ) #else int main(int argc, char **argv) #endif { MyClass app; try { app.go(); } catch( Exception& e ) { #if OGRE_PLATFORM == OGRE_PLATFORM_WIN32 MessageBox( NULL, e.getFullDescription().c_str(), "An exception has occurred!", MB_OK | MB_ICONERROR | MB_TASKMODAL); #else fprintf(stderr, "An exception has occured: %s\n", e.getFullDescription().c_str()); #endif } return 0; }
How to put a 3D-model in the scene
A 3D-model is an Entity and it must be attached to a SceneNode to be placed in the scene. The SceneNode can be taken from the rootSceneNode in the SceneManager. Creating an Entity and SceneNode and attaching the Entity to the SceneNode will look something like this:
Entity* thisEntity = mSceneMgr->createEntity("nameInTheScene", "FileName.mesh"); SceneNode* thisSceneNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(); thisSceneNode->attachObject(thisEntity);
How to remove an Entity from the scene
To remove an Entity, you must detach the Entity from its SceneNode, delete it and if needed destroy the SceneNode as well, which must be done using the SceneManager. If you have a SceneNode* called myNode you can completely destroy all its contents (both MovableObjects and child SceneNodes) and the node itself using the following code:
SceneNode* parent = entity->getParentSceneNode(); parent->detachObject(entity); mSceneMgr->destroyEntity(entity->getName()); // entity is now destroyed, don't try to use the pointer anymore! // optionally destroy node mSceneMgr->destroySceneNode(parent->getName());
How to move, reposition, scale and rotate a SceneNode
If you have a SceneNode with some MovableObjects, like Entities, Lights and Cameras, attached to it, you can move it using a lot of different functions, see the API for all of them. The following functions respectively move it, reposition it, scale it and rotate it over its X-, Y- and Z-axis:
thisSceneNode->translate(10, 20, 30); thisSceneNode->setPosition(1.8, 20.1, 10.5); thisSceneNode->scale(0.5, 0.8, 1.3); thisSceneNode->pitch(45); thisSceneNode->yaw(90); thisSceneNode->roll(180);
How to put a light in the scene
To add a light to the scene, you must ask the SceneManager for one. You can then set its settings, of which some examples are given below:
Light* myLight = mSceneMgr->createLight("nameOfTheLight"); myLight->setType(Light::LT_POINT); myLight->setPosition(200, 300, 400); myLight->setDiffuseColour(1, 1, 0.7); myLight->setSpecularColour(1, 1, 0.7);
You can also attach a light to a SceneNode. The following code creates a SceneNode and attaches myLight to it:
SceneNode* thisSceneNode = static_cast<SceneNode*>(mSceneMgr->getRootSceneNode()->createChild()); thisSceneNode->attachObject(myLight);
How to set the ambient lighting
The ambient lighting is controlled by the Scenemanager, so that is where you can set it:
mSceneMgr->setAmbientLight(ColourValue(0.2, 0.2, 0.2));
How to control the camera
The standard camera in ExampleApplication is called mCamera and is available in the class that is derived from ExampleApplication. The following code changes its position, changes the point it looks at, creates a SceneNode and attaches the camera to it:
mCamera->setPosition(0, 130, -400); mCamera->lookAt(0, 40, 0); SceneNode* camNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(); camNode->attachObject(mCamera);
Control the camera with the mouse
This code makes it easy to have a "standard" camera movement interface for RTS styled games. The camera moves around a fixed point when the left button is pressed. Zooming is done with either the wheel or by pressing the middle button and moving the mouse up and down.
Real MoveFactor = 60.0 * evt.timeSinceLastFrame; // Move the camera around with the left button if (mInputDevice->getMouseButton(1)) { SceneNode *camNode = mCamera->getParentSceneNode(); if (camNode == 0) { std::cerr << "mCamera isn't attached to any SceneNode !" << std::endl; } camNode->yaw(Degree(mInputDevice->getMouseRelativeX() * MoveFactor * -0.1)); camNode->pitch(Degree(mInputDevice->getMouseRelativeY() * MoveFactor * -0.1)); } // Zoom with the middle button... if (mInputDevice->getMouseButton(2)) { mCamera->moveRelative(Vector3(0.0, 0.0, -0.5) * mInputDevice->getMouseRelativeY() * MoveFactor); } // ... and the wheel ;-) mCamera->moveRelative(Vector3(0.0, 0.0, -0.1) * mInputDevice->getMouseRelativeZ() * MoveFactor);
How to add a billboard/sprite to the scene
A {LEX()}Billboard{LEX} is a square polygon that is always pointed at the camera. It is also known as a sprite. To make one, you must first make a BillboardSet. Then the billboard can be added to it on a given position. The BillboardSet is a {LEX()}MovableObject{LEX} and should therefore be added to a SceneNode. The whole procedure is as follows:
SceneNode* myNode = static_cast<SceneNode*>(mSceneMgr->getRootSceneNode()->createChild()); BillboardSet* mySet = mSceneMgr->createBillboardSet("mySet"); Billboard* myBillboard = mySet->createBillboard(Vector3(100, 0, 200)); myNode->attachObject(mySet);
How to create a basic FrameListener using ExampleFrameListener
A FrameListener gives you the opportunity to do something at the start and the end of every frame. You must first create a class that is derived from ExampleFrameListener and in this class you can implement frameStarted() and frameEnded(). This will look something like this:
#include "ExampleFrameListener.h" <br /> class myFrameListener: public ExampleFrameListener { public: myFrameListener(RenderWindow* win, Camera* cam); ~myFrameListener(void); bool frameStarted(const FrameEvent& evt); bool frameEnded(const FrameEvent& evt); };
The constructor should call its parent-constructor, which will look something like this:
myFrameListener::myFrameListener(RenderWindow* win, Camera* cam): ExampleFrameListener(win, cam){}
You must also register your FrameListener to the Root. This can be done in the createFrameListener()-function of the class that is derived from ExampleApplication. You can register as many FrameListeners to the root as you want. It will look something like this:
void createFrameListener(void) { MyFrameListener listener = new MyFrameListener(mWindow, mCamera); mRoot->addFrameListener(listener); }
How to control some object from your scene in a FrameListener
If you want to control some object you have in your scene in a FrameListener, the FrameListener must have access to it. An easy way to do this, is by providing a pointer to it in the constructor of the FrameListener. Its constructor will now look something like this:
myFrameListener(RenderWindow* win, Camera* cam, Car* car);
How to get the time since the previous frame
In the functions frameEnded() and frameStarted() of a FrameListener you can get the time in seconds (this is a float) in the following way:
bool frameStarted(const FrameEvent& evt) { float time = evt.timeSinceLastFrame; return true; }
You can now for instance multiply the speed per second with this float in order to get the movement since the last frame. This will make the pace of the game framerate-independent.
How to react to key-presses
In the functions frameEnded() and frameStarted() of a FrameListener you can react to key-presses by first ordering Ogre to capture them and then checking which key is being pressed. You only have to capture the InputDevice once per frame. This will look something like this:
mInputDevice->capture(); if (mInputDevice->isKeyDown(Ogre::KC_DOWN)) { //react however you like } if (mInputDevice->isKeyDown(Ogre::KC_UP)) { //react however you like }
How to make sure key-presses are not reacted to too shortly after each other
If you implement reactions to key-presses in the above way, they will happen each frame. If you want them to happen, for instance, at most twice per second, you can achieve this by setting a timer. This timer must be kept in the class itself and not in the function in order to be able to access it through different calls of the function. Implementing this will look something like this:
class myFrameListener: public ExampleFrameListener { protected: float buttonTimer; public: myFrameListener(RenderWindow* win, Camera* cam): ExampleFrameListener(win, cam) { buttonTimer = 0; } bool frameStarted(const FrameEvent& evt) { float time = evt.timeSinceLastFrame; buttonTimer -= time; mInputDevice->capture(); if (mInputDevice->isKeyDown(Ogre::KC_DOWN) && buttonTimer <= 0) { buttonTimer = 0.5; //react however you like } return true; } };
How to quit your application
You can quit you application in the frameStarted() or frameEnded()-function of the FrameListener by returning false. If you want to tie this to pressing the escape-button on the keyboard, this will look as follows:
bool frameStarted(const FrameEvent& evt) { mInputDevice->capture(); if (mInputDevice->isKeyDown(Ogre::KC_ESCAPE)) return false; return true; }
How to efficiently add and remove 3D-objects from the scene during run-time (like rockets being fired)
In the createScene()-function you can load meshes from a file, but this is too slow to do if new objects must be added in run-time (though in a very simple application you will not see the difference in speed). To add objects faster, you can load a lot of meshes in the createScene()-function and then take them from a stack in run-time. After the objects are not used anymore, you can put them back on the stack for later use. A good example of the usefulness of this is a canon that fires rockets: these must be created when fired and removed when exploded.
In order to always have access to this stack of rockets, you can make it a global variable (outside any class) or access it through a singleton. The latter is much nicer, but takes more code so I will not do so in this example. So you keep a stack of Entities somewhere:
stack rocketEntities;
In createScene() you fill this stack with a lot of rockets. Each Entity is set invisible for now and must have a unique name, which is achieved using the sprintf()-function. All in all it looks like this:
for (unsigned int t = 0; t < 100; t++) { char tmp[20]; sprintf(tmp, "rocket_%d", t); Entity* newRocketEntity = mSceneMgr->createEntity(tmp, "Rocket.mesh"); newRocketEntity->setVisible(false); rocketEntities.push(newRocketEntity); }
Now when creating a new Rocket we can take a mesh from rocketEntities. In this example I do so in the constructor of the Rocket and put it pack in the destructor of the Rocket. In order to get the Entity back from the SceneNode in the destructor, I store its name in rocketEntityName. I also position the rocket correctly in the constructor. To make this all work, the SceneManager must be available throughout your program, in this case this is achieved by having it be a global variable (outside any class). I also handle creation and destruction of the SceneNode in the constructor and destructor. The Rocket-class will now look like this:
class Rocket { protected: SceneNode* rocketNode; string rocketEntityName; public: Rocket(const Vector3& position, const Quaternion& direction) { rocketNode = static_cast<SceneNode*>(sceneMgr->getRootSceneNode()->createChild()); Entity* rocketEntity = rocketEntities.top(); rocketEntities.pop(); rocketEntity->setVisible(true); rocketEntityName = rocketEntity->getName(); rocketNode->attachObject(rocketEntity); rocketNode->setOrientation(direction); rocketNode->setPosition(position); } ~Rocket() { Entity* rocketEntity = static_cast<Entity*>(rocketNode->detachObject(rocketEntityName)); rocketEntity->setVisible(false); rocketEntities.push(rocketEntity); sceneMgr->getRootSceneNode()->removeAndDestroyChild(rocketNode->getName()); } };
If you use this construction, you should be aware of the fact that this crashes if no rockets are left. You can solve this by checking whether the stack of Entities is empty and if it is, load new meshes to add to the stack.
How to show an -Overlay (and hide it again)
To show and hide an -Overlay, you must first get a pointer to it using the OverlayManager, which is a singleton. If you have an -Overlay that is defined in a .overlay-script with the name "myOverlay" you can get it, show it and hide it again using the following code:
Overlay* thisOverlay = static_cast<Overlay*>( OverlayManager::getSingleton().getByName("myOverlay")); thisOverlay->show(); thisOverlay->hide();
How to change the text in a TextArea
You can change all parameters of Containers and Elements in the GUI in runtime. In order to do so, you must first get a pointer to the Element or Container you want and, if needed for what you want to do, cast it to the type it is. Getting a pointer to a TextArea that is defined in a .overlay-script as "myTextArea" and changing its caption will look something like this:
OverlayElement* thisTextArea = OverlayManager::getSingleton().getOverlayElement("myTextArea"); thisTextArea->setCaption("blaat");
In this case no casting was needed, as every OverlayElement has a caption. If you want to set a setting that is specific for one type of OverlayElement, you will have to cast it to that type. Changing the font-name of a TextArea will look something like this:
TextAreaGuiElement* thisTextArea = static_cast<TextAreaOverlayElement*>( OverlayManager::getSingleton().getOverlayElement("myTextArea")); thisTextArea->setFontName("RealCoolFont");
How to show the mouse-cursor
This item is outdated. The new CEGUI method is detailed here: How To Show The Mouse Cursor, and here: Intermediate Tutorial 3
If you want to show the mouse-cursor on the screen, you have to do two things: set it to be shown and tell a FrameListener to track it. Telling it to be shown can be done as follows:
GuiContainer* cursor = OverlayManager::getSingleton().getCursorGui(); cursor->setMaterialName("Cursor/default"); cursor->setDimensions(32.0/640.0, 32.0/480.0); cursor->show();
Telling a FrameListener to track it should be done in its parents’ constructor by setting its last boolean-parameter to true. The constructor will now look something like this:
myFrameListener::myFrameListener(RenderWindow* win, Camera* cam) : ExampleFrameListener(win, cam, false, true){}
Beware though that after doing this, this specific FrameListener will not react to button-presses anymore as the capture()- and isKeyPressed()-functions of mInputDevice now do not work properly anymore. A different FrameListener should be made to handle keyboard input now.
How to create a working button
This item is outdated and should be replaced with the cegui solution.
You can define a button in an Overlay-script using something like the following syntax, where it is important to set all these materials:
container Button(myButton) { metrics_mode relative horz_align left vert_align top top 0.1 left 0.1 width 0.18 height 0.1 material NCG/GuiSession/RedMaterial button_down_material NCG/GuiSession/RedMaterial button_up_material NCG/GuiSession/RedMaterial button_hilite_down_material NCG/GuiSession/RedMaterial button_hilite_up_material NCG/GuiSession/RedMaterial button_disabled_material NCG/GuiSession/RedMaterial }
Buttons are not standard in Ogre and therefore you must make sure they are found by the compiler in the folder ogrenew\PlugIns\GuiElements\Include. Now you can include it:
#include "OgreButtonGuiElement.h"
To make the button work, an ActionListener must be registered to it, which can be done as follows:
ActionTarget* thisButton = static_cast<ButtonGuiElement*>( GuiManager::getSingleton().getGuiElement("myButton")); thisButton->addActionListener(this);
This examples supposes ‘this’ is an ActionListener, which is not true by default. Of cource any ActionListener will do, it does not have to be ‘this’. You can make a class an ActionListener by deriving it from ActionListener and implementing actionPerformed(). This will look something like this:
class Listener: public ActionListener { public: void actionPerformed(ActionEvent *e); };
How to find out which button was pressed
This item is outdated and should be replaced with the cegui solution.
If you register one ActionListener to several buttons, they will all call the same function actionPerformed(). You can find out which button was actually being pressed by comparing its name with the name in the ActionEvent e. This will look something like this:
#include <br /> void actionPerformed(ActionEvent *e) { std::string action = e->getActionCommand(); if (action == "myButton") { //handle the button-press } }
How to quit the application using an ActionListener
This item is outdated and should be replaced with the cegui solution.
Quiting an application can be done in the frameStarted()- and frameEnded()-functions of a FrameListener, not in the actionPerformed()-function. So how do we get there? For this we can introduce a simple FrameListener that does nothing more than quit if asked to. This FrameListener will look something like this (from the Gui-demo provided with Ogre):
#include "ExampleFrameListener.h" <br /> class QuitListener: public ExampleFrameListener { public: QuitListener(RenderWindow* win, Camera* cam): ExampleFrameListener(win, cam, false, false) { quit = false; } bool frameStarted(const FrameEvent& evt) { if (quit) return false; return true; } void scheduleQuit(void) { quit = true; } protected: bool quit; };
Now if your ActionListener has a pointer to this QuitListener, it can simply schedule a quit by calling scheduleQuit() and the QuitListener will perform it before the next frame starts.
How to get a different SceneManager
The SceneManager is chosen automatically using an identifier that tells what kind of scene it is. This is done in ExampleApplication, so to change this, you will have to change ExampleApplication itself. Normally, ExampleApplication contains the following piece of code:
virtual void chooseSceneManager(void) { // Get the SceneManager, in this case a generic one mSceneMgr = mRoot->getSceneManager(ST_GENERIC); }
You can change ST_GENERIC to any of the following identifiers to get a SceneManager that is appropriate for your specific scene:
- ST_GENERIC
- ST_EXTERIOR_CLOSE
- ST_EXTERIOR_FAR
- ST_EXTERIOR_REAL_FAR
- ST_INTERIOR
More informations about this types you find here.
Generally informations are in SceneManagersFAQ.
How to efficiently get a list of all possible collisions
Ogre can provide you with a list of all objects that are possible colliding. Objects that are close but not in a collision might be in this list as well, so you will have to make sure which one is a collision yourself afterwards. You can ask for this list of collisions using an IntersectionSceneQuery, which you can get using the following code:
IntersectionSceneQuery* intersectionQuery = sceneMgr->createIntersectionQuery();
Now you can ask it for a list of all possible collisions:
IntersectionSceneQueryResult& queryResult = intersectionQuery->execute();
If you intend to get this list several times before updating your scene, there is no need to have Ogre calculate it again over and over. You can get back the same list again without a new calculation using the following line:
IntersectionSceneQueryResult& queryResult = intersectionQuery->getLastResults();
After use, you can store the IntersectionSceneQuery for later use or remove it. If you remove it, you must tell the SceneManager to do so using the following code:
mSceneMgr->destroyQuery(intersectionQuery);
The IntersectionSceneQueryResult is a list of pairs of MovableObjects. An example of its use is getting the name of the first of the two colliding objects:
queryResult.movables2movables.begin()->first->getName();
See the Ogre-API of IntersectionSceneQueryResult for more details on the types of the results.
How to exclude objects from collision detection
You can exclude certain objects from collision detection by using flags. You can add a flag to any -MovableObject, for instance 100 in the following example:
Entity* ground = mSceneMgr->createEntity("ground", "Ground.mesh"); ground->setQueryFlags(100);
Now you can tell your IntersectionSceneQuery to exclude objects with this flag from its list of intersections by setting its mask. This will look like this:
IntersectionSceneQuery* intersectionQuery = sceneMgr->createIntersectionQuery(); intersectionQuery->setQueryMask(~100);
How to find out to which of your own objects a -MovableObject belongs
The list of colliding objects IntersectionSceneQuery delivers is of MovableObjects, but in general you will want to relate this to some custom object in your own scene. To do so, you can attach a pointer to your own object to a -MovableObject and get this pointer back later on. If you have an Entity (which is a MovableObject) and a custom object (like myRocket here below), you can attach your custom object to the Entity as follows:
Entity* myEntity = sceneMgr->createEntity("myEntity", "Rocket.mesh"); Rocket* myRocket = new Rocket(); myEntity->setUserAny(Any(myRocket));
Now you can get a pointer to myRocket back from the Entity using the following code:
Rocket* myRocket = Ogre::any_cast<Rocket*>(myEntity->getUserAny());
Ogre::Any can wrap any type, and any_cast will throw an exception if you try to cast to the wrong type.
Now after having detected which MovableObjects are involved in a collision, you can also find out which of your own objects are involved in this collision.