[转]Ogre之基本框架
Basic Tutorial 0
From Ogre Wiki
Beginner Tutorial 0: Behind the scenes - Example framework demystified.
Original version by --Jacmoe 12:35, 17 July 2006 (CDT)
Introduction
If you looked at the code for the Ogre demos, and read the Wiki Tutorials, you probably noticed the use of ExampleApplication and ExampleFramelistener.
And you were probably wondering: What does the example framework do behind the scenes? There must be more to Ogre programming than
ExampleApp theApp; theApp.go();
and overriding the createScene() function?
This tutorial will describe exactly how the example framework works.
We will work our way through the framework - and end up with an alternative Ogre application class, containing all the example framework code OgreSteined together.
Note: If you really, really want to copy and paste code, please choose the right code: the code at the very end. ;- )
We will gradually work our way through the framework, adding code to our 'OgreStein class' as we go.
Note 2: If you are using Mac OS X, here are some hints on compiling from the Mac OS X command line.
Contents[hide] |
The example framework
The example framework consists of two classes: ExampleApplication (in ExampleApplication.h) and ExampleFramelistener (in ExampleFramelistener.h). They 'live' in the Samples/Include directory, and are used by the Ogre demos.
In fact, the framework is created to allow for quick and dirty demo writing. Nothing fancy, not an example of great design. And probably not what you want to use in a bigger, more serious setting.
But it saves a lot of time. And it allows you to do some very quick Ogre tests.
An application using the example framework starts with a class which derives from ExampleApplication:
class GroovieDemo : public ExampleApplication { // details ... };
And that means that everything ExampleApplication does, GroovieDemo does too.
GroovieDemo needs to define the ExampleApplication::createScene() function, because that is a pure virtual function, so GroovieDemo becomes:
class GroovieDemo : public ExampleApplication { virtual void createScene() { // details ... } };
And then you just create a GroovieDemo instance and call its go() function. And you will have the most basic of Ogre demos.
But... What does ExampleApplication actually do?
ExampleApplication
Here is what happens:
go() setup() new Ogre::Root setupResources() // parses resources.cfg configure() // shows the Ogre config dialog which configures the render system. // constructs a render window. chooseSceneManager(); // the scenemanager decides what to render createCamera(); // we need a camera to render from createViewports(); // a viewport to render to createResourceListener(); // Create any resource listeners (for loading screens) loadResources(); // Now we can load the resources: all systems are on-line. createScene(); // Now that the system is up and running: create a scene to render. mRoot->startRendering(); // Kick off Ogre
As you can see it first creates an instance of Ogre::Root. Then it parses the resources.cfg, shows the config dialog, loads a rendersystem, constructs a renderwindow, chooses a scenemanager, creates a camera and a viewport, loads the resources and starts rendering.
A basic skeleton
Create a file called OgreApplication.h and paste the following code:
#ifndef __OgreApplication_h__ #define __OgreApplication_h__ #include <ogre.h> using namespace Ogre; class OgreApplication { public: OgreApplication(); virtual ~OgreApplication(); virtual void go(); protected: virtual bool initialise(); virtual bool initOgreCore(); virtual void createSceneManager(); virtual void createCamera(); virtual void createViewports(); virtual void createResourceListener(); virtual void addResourceLocations(); virtual void initResources(); virtual void createScene() = 0; // I am pure virtual, override me! virtual void destroyScene(); Root *mRoot; Camera* mCamera; SceneManager* mSceneMgr; RenderWindow* mWindow; }; #endif // __OgreApplication_h__
One thing you'll notice is that some of the functions are named differently than the example framework. That's because (IMO) the new function names more accurately reflects what the functions are doing.
Okay. Here is everything we need for our OgreApplication, so let's get started!
Step by step
OK. Let's create a new file, called OgreApplication.cpp, and paste the following into it:
#include "OgreApplication.h" //------------------------------------------------------------------------------------- OgreApplication::OgreApplication() : mRoot(0) { } //------------------------------------------------------------------------------------- OgreApplication::~OgreApplication() { delete mRoot; }
As you can see, mRoot is zeroed out in the constructor, and deleted in the destructor.
We will be adding code to this file as we go.
Let's go!
Add the following code to OgreApplication.cpp:
//------------------------------------------------------------------------------------- void OgreApplication::go() { if (!initialise()) return; mRoot->startRendering(); // clean up destroyScene(); }
This piece of code starts off the Ogre application by calling 'initialise()'.
If initialise() fails, the application returns and the game is over.
Then it calls startRendering() on mRoot.
Finally destroyScene() is called to do domestic household routines, like cleaning up the mess we made in createScene().
Here is what Root::startRendering() does:
void Root::startRendering(void) { assert(mActiveRenderer != 0); mActiveRenderer->_initRenderTargets(); // Clear event times for(int i=0; i!=3; ++i) mEventTimes[i].clear(); // Infinite loop, until broken out of by frame listeners // or break out by calling queueEndRendering() mQueuedEnd = false; while( !mQueuedEnd ) { //Allow platform to pump/create/etc messages/events once per frame mPlatformManager->messagePump(mAutoWindow); if (!renderOneFrame()) break; } }
It initialises the render targets, clears the event times and starts the renderloop, which will be run until mQueueEnd is set to false by callling queueEndRendering().
More about Root::renderOneFrame() later.
initialise
Add the following code to OgreApplication.cpp:
//------------------------------------------------------------------------------------- bool OgreApplication::initialise() { mRoot = new Root(); // add resource locations addResourceLocations(); // if we cannot initialise Ogre, just abandon the whole deal if ( !initOgreCore() ) return false; createSceneManager(); createCamera(); createViewports(); // Set default mipmap level (NB some APIs ignore this) TextureManager::getSingleton().setDefaultNumMipmaps(5); // Create any resource listeners (for loading screens) createResourceListener(); // Initialise resources initResources(); // Create the scene createScene(); return true; };
This is the meat of the example application.
- Ogre::Root is created.
- The resource locations are parsed and added.
- The configuration screen is shown (initOgreCore).
- The chosen RenderSystem is created, along with a window.
- A SceneManager is created.
- Camera is created and setup.
- Viewports are created and hooked up with the camera.
- Resources are parsed and loaded.
- The scene is created.
addResourceLocations
Add the following code to OgreApplication.cpp:
//------------------------------------------------------------------------------------- void OgreApplication::addResourceLocations() { // Load resource paths from config file ConfigFile cf; cf.load("resources.cfg"); // Go through all sections & settings in the file ConfigFile::SectionIterator seci = cf.getSectionIterator(); String secName, typeName, archName; while (seci.hasMoreElements()) { secName = seci.peekNextKey(); ConfigFile::SettingsMultiMap *settings = seci.getNext(); ConfigFile::SettingsMultiMap::iterator i; for (i = settings->begin(); i != settings->end(); ++i) { typeName = i->first; archName = i->second; ResourceGroupManager::getSingleton().addResourceLocation(archName, typeName, secName); } } }
This function simply parses resources.cfg and adds the resource locations.
It uses the Ogre::ConfigFile class for the job.
initOgreCore
Add the following code to OgreApplication.cpp:
//------------------------------------------------------------------------------------- bool OgreApplication::initOgreCore() { // Show the configuration dialog and initialise the system // You can skip this and use root.restoreConfig() to load configuration // settings if you were sure there are valid ones saved in ogre.cfg if(mRoot->restoreConfig() || mRoot->showConfigDialog()) { // If returned true, user clicked OK so initialise // Here we choose to let the system create a default rendering window by passing 'true' mWindow = mRoot->initialise(true); return true; } else { return false; } }
This function does one of two things:
If Root::restoreConfig() succeeds, it means that there is a valid configuration present, and that information is used to configure Ogre. If there isn't, the configuration dialog is shown.
And how does this initialise the Ogre core, you ask?
All it does is present the configure dialog, or is there something happening behind the scenes?
Yes, you're right.
The configuration dialog calls Root::restoreConfig() first.
Then it calls Root::getRenderSystem().
Root::getAvailableRenderers() returns a list of available renderers (surprise!)
RenderSystem::getConfigOptions() returns a ConfigOptionMap with the available RenderSystem options.
RenderSystem->setConfigOption(value, value) will set the options.
An example:
selectedRenderSystem->setConfigOption("Full Screen","No"); selectedRenderSystem->setConfigOption("Video Mode","800 x 600 @ 16-bit colour"); selectedRenderSystem->setConfigOption("Allow NVPerfHUD","No"); selectedRenderSystem->setConfigOption("Anti aliasing","None"); selectedRenderSystem->setConfigOption("Floating-point mode","Fastest"); selectedRenderSystem->setConfigOption("Rendering Device","RADEON 9200"); selectedRenderSystem->setConfigOption("VSync","No");
RenderSystem::validateConfigOptions() is used to validate the configuration options, before Root::setRenderSystem(selectedRenderSystem) is called.
Then Root saves the configuration: Root::saveConfig()
When that's done, it's time to call Root::initialise.
It looks like this:
RenderWindow* Root::initialise(bool autoCreateWindow, const String& windowTitle) { if (!mActiveRenderer) OGRE_EXCEPT(Exception::ERR_NO_RENDERSYSTEM_SELECTED, "Cannot initialise - no render " "system has been selected.", "Root::initialise"); if (!mControllerManager) mControllerManager = new ControllerManager(); mAutoWindow = mActiveRenderer->initialise(autoCreateWindow, windowTitle); mResourceBackgroundQueue->initialise(); if (autoCreateWindow && !mFirstTimePostWindowInit) { oneTimePostWindowInit(); mAutoWindow->_setPrimary(); } // Initialise timer mTimer->reset(); // Init plugins initialisePlugins(); mIsInitialised = true; return mAutoWindow; }
For examples of out-in-the-open Ogre initialisation, see other parts of this Ogre Wiki.
createSceneManager
Add the following code to OgreApplication.cpp:
//------------------------------------------------------------------------------------- void OgreApplication::createSceneManager() { // Create the SceneManager, in this case a generic one mSceneMgr = mRoot->createSceneManager(ST_GENERIC); }
Root is asked to create an instance of the generic SceneManager.
Here we choose to create it from a SceneType, and ignore the second parameter, which is the instance name.
In Ogre, we can create many SceneManager instances. Each camera belongs to a SceneManager instance, so creating multiple cameras on different SceneManagers will allow us to switch between them just by changing the active camera.
We could also choose to create the SceneManger like this:
mSceneMgr = mRoot->createSceneManager("DefaultSceneManager");
And, if we wanted multiple scenemanagers, we need to pass the instance name:
mSceneMgr = mRoot->createSceneManager("DefaultSceneManager", "MainOgreApplicationInstance");
Getting a SceneManager instance can be done through Ogre::Root or Ogre::Camera:
mCamera->getSceneManager("MainOgreApplicationInstance");
or
mRoot->getSceneManager("MainOgreApplicationInstance");
createCamera
Add the following code to OgreApplication.cpp:
//------------------------------------------------------------------------------------- void OgreApplication::createCamera() { // Create the camera mCamera = mSceneMgr->createCamera("PlayerCam"); // Position it at 500 in Z direction mCamera->setPosition(Vector3(0,0,500)); // Look back along -Z mCamera->lookAt(Vector3(0,0,-300)); mCamera->setNearClipDistance(5); }
The SceneManager is asked to create a camera named "PlayerCam".
The camera is then configured with some default values.
This is probably one function which you will want to override in your own application.
createViewports
Add the following code to OgreApplication.cpp:
//------------------------------------------------------------------------------------- void OgreApplication::createViewports() { // Create one viewport, entire window Viewport* vp = mWindow->addViewport(mCamera); vp->setBackgroundColour(ColourValue(0,0,0)); // Alter the camera aspect ratio to match the viewport mCamera->setAspectRatio(Real(vp->getActualWidth()) / Real(vp->getActualHeight())); }
createResourceListener
Add the following code to OgreApplication.cpp:
//------------------------------------------------------------------------------------- void OgreApplication::createResourceListener() { }
Here you can create custom resourcegroup listeners, for things like loadingbars, etc.
Look at the ExampleLoadingBar in samples/common and you'll notice that it derives from ResourceGroupListener.
Resourcegrouplisteners receive callbacks during resource group loading, see API docs for specifics.
initResources
Add the following code to OgreApplication.cpp:
//------------------------------------------------------------------------------------- void OgreApplication::initResources() { // Initialise, parse scripts etc ResourceGroupManager::getSingleton().initialiseAllResourceGroups(); }
Here all resource groups are initialised in one go.
You can initialise them manually if you want.
The ExampleLoadingBar does initialise the "Bootstrap" ResourceGroup before Ogre is fully configured, because it needs the media in OgreCore.zip:
ResourceGroupManager::getSingleton().initialiseResourceGroup("Bootstrap");
createScene
Since this function is pure virtual, we don't define it here.
Later, when we derive from OgreApplication, we need to deal with it.
destroyScene
Add the following code to OgreApplication.cpp:
//------------------------------------------------------------------------------------- void OgreApplication::destroyScene() { }
This function does nothing, but it is defined. That means that users of the OgreApplication class can choose not to override it, if they don't want to clean up after themselves.
Adding a Framelistener
Right now our OgreApplication is quite limited.
It sets up the Ogre system and starts rendering - and that's about it.
It would be great if we had the ability to run tasks each frame, like responding to input, updating logic, etc.
Fortunately, Ogre has a FrameListener.
What does it look like?
Well, if you look at OgreFrameListener.h, you will see the following:
struct FrameEvent { Real timeSinceLastEvent; Real timeSinceLastFrame; }; class _OgreExport FrameListener { /* Note that this could have been an abstract class, but I made the explicit choice not to do this, because I wanted to give people the option of only implementing the methods they wanted, rather than having to create 'do nothing' implementations for those they weren't interested in. As such this class follows the 'Adapter' classes in Java rather than pure interfaces. */ public: virtual bool frameStarted(const FrameEvent& evt) { return true; } virtual bool frameEnded(const FrameEvent& evt) { return true; } virtual ~FrameListener() {} };
It means that adding framelistening support to our OgreApplication only requires us to derive it from FrameListener and overriding the two functions: frameStarted and frameEnded.
Note: We don't need to override those functions, if we don't want to. But there is no point in deriving from FrameListener if we didn't override at least one of them.
How does it work?
Remember the renderOneFrame function from Root::startRendering() ?
Here it is:
bool Root::renderOneFrame(void) { if(!_fireFrameStarted()) return false; _updateAllRenderTargets(); return _fireFrameEnded(); }
_fireFrameStarted() and _fireFrameEnded() are what interests us.
Let's look at _fireFrameStarted:
bool Root::_fireFrameStarted(FrameEvent& evt) { // Increment frame number ++mCurrentFrame; // Remove all marked listeners std::set<FrameListener*>::iterator i; for (i = mRemovedFrameListeners.begin(); i != mRemovedFrameListeners.end(); i++) { mFrameListeners.erase(*i); } mRemovedFrameListeners.clear(); // Tell all listeners for (i= mFrameListeners.begin(); i != mFrameListeners.end(); ++i) { if (!(*i)->frameStarted(evt)) return false; } return true; }
Just before returning true, it iterates over the registered framelisteners and calls frameStarted(evt) on them.
OK. Now we know how it works. Let's add some code.
Adding the code
Look at the updated OgreApplication.h:
#ifndef __OgreApplication_h__ #define __OgreApplication_h__ #include <ogre.h> using namespace Ogre; class OgreApplication : public FrameListener { public: OgreApplication(void); virtual ~OgreApplication(void); virtual void go(void); protected: virtual bool initialise(); virtual bool initOgreCore(); virtual void createSceneManager(); virtual void createCamera(); virtual void createViewports(); virtual void createResourceListener(); virtual void createFrameListener(); virtual void addResourceLocations(); virtual void initResources(); virtual void createScene() = 0; // I am pure virtual, override me! virtual void destroyScene(); // FrameListener overrides virtual bool frameStarted(const FrameEvent& evt); virtual bool frameEnded(const FrameEvent& evt); Root *mRoot; Camera* mCamera; SceneManager* mSceneMgr; RenderWindow* mWindow; }; #endif // __OgreApplication_h__
Notice how OgreApplication now is deriving from FrameListener, and overriding the two framelistener functions.
Also notice the new function: createFrameListener().
Now we need to add the function definitions in OgreApplication.cpp:
//------------------------------------------------------------------------------------- bool OgreApplication::frameStarted(const FrameEvent& evt) { return true; } //------------------------------------------------------------------------------------- bool OgreApplication::frameEnded(const FrameEvent& evt) { return true; } //------------------------------------------------------------------------------------- void OgreApplication::createFrameListener() { mRoot->addFrameListener(this); }
The createFrameListener function registers the frame listener with Root, passing this because OgreApplication is a FrameListener.
We need to register our FrameListener with Root if we want our frameStarted and frameEnded functions to be called each frame.
Now, we need to go back to our OgreApplication::initialise() function, and add the call to createFrameListener:
// Create the scene createScene(); createFrameListener(); return true; };
We are not adding any code to the frame listener functions, yet.
We need to add input to our OgreApplication first!
Adding basic Ogre input
Alright. Let's add some basic input, shall we?
Unbuffered Input
We need to add an Ogre::InputReader to our class:
InputReader* mInputDevice;
Just add it after mWindow in OgreApplication.h.
We also need two input processing functions:
virtual bool processUnbufferedKeyInput(const FrameEvent& evt); virtual bool processUnbufferedMouseInput(const FrameEvent& evt);
Add them to the OgreApplication interface.
Now we need to modify the OgreApplication destructor, in OgreApplication.cpp:
//------------------------------------------------------------------------------------- OgreApplication::~OgreApplication() { PlatformManager::getSingleton().destroyInputReader( mInputDevice ); delete Root::getSingletonPtr(); }
We need to destroy the InputReader to avoid memory-leaks.
createFrameListener changes
Add these two lines to OgreApplication::createFrameListener:
mInputDevice = PlatformManager::getSingleton().createInputReader(); mInputDevice->initialise(mWindow,true, true);
The PlatformManager is asked to create an InputReader, and then we initialise it. The input device is asked to capture inputs from mWindow, the two boolean arguments are useKeyboard and useMouse - we say yes to both questions.
processUnbufferedKeyInput
Add the following code to OgreApplication.cpp:
//------------------------------------------------------------------------------------- bool OgreApplication::processUnbufferedKeyInput(const FrameEvent& evt) { if( mInputDevice->isKeyDown( KC_ESCAPE) ) { return false; } // Return true to continue rendering return true; }
Here we ask the InputReader if the Escape key is down. If it is, we return false.
Returning false from a FrameListener will break out of the Root::startRendering() loop, and exit our application.
processUnbufferedMouseInput
//------------------------------------------------------------------------------------- bool OgreApplication::processUnbufferedMouseInput(const FrameEvent& evt) { /* If the second mouse button is pressed, then the mouse movement results in sliding the camera, otherwise we rotate. */ if( mInputDevice->getMouseButton( 1 ) ) { mTranslateVector.x += mInputDevice->getMouseRelativeX() * 0.13; mTranslateVector.y -= mInputDevice->getMouseRelativeY() * 0.13; } else { mRotX = Degree(-mInputDevice->getMouseRelativeX() * 0.13); mRotY = Degree(-mInputDevice->getMouseRelativeY() * 0.13); } return true; }
Here we use InputReader::getMouseButton to check whether or not the second mouse button is pressed.
InputReader::getMouseRelativeX/getMouseRelativeY is then used to either translate or rotate the camera.
As you probably noticed, we need to declare mRotX, mRotY and mTranslateVector in our OgreApplication interface:
Vector3 mTranslateVector; Radian mRotX, mRotY;
Just add them to OgreApplication.h.
We'll deal with them later on.
frameStarted changes
Unbuffered (aka immediate) input is not buffered (hence the name), so we need to capture the current state of the keyboard and mouse each frame.
Any inputs between calls to InputDevice::capture are simply ignored.
Now we need to add the function definitions in OgreApplication.cpp:
Change OgreApplication::frameStarted in OgreApplication.cpp, like this:
//------------------------------------------------------------------------------------- bool OgreApplication::frameStarted(const FrameEvent& evt) { mInputDevice->capture(); mRotX = 0; mRotY = 0; mTranslateVector = Vector3::ZERO; if (processUnbufferedKeyInput(evt) == false) { return false; } if (processUnbufferedMouseInput(evt) == false) { return false; } return true; }
First we capture the state of the input devices, then we initialise mRotX, mRotY and mTranslateVector. They are used for moving the camera, but we'll get to that soon.
After that we process buffered keyboard input and buffered mouse input before returning true.
Buffered Input
This tutorial will not cover buffered input, see Basic Tutorial 5#Buffered_Input_Interfaces for a rundown on that.
We want to keep the code simple and short.
If you understand what's happening here, adding buffered input to your own application should be a walk in the park:
Derive OgreApplication from KeyListener, MouseMotionListener and MouseListener, add an EventProcessor and initialise it.
Then you need to implement the keyPressed, keyClicked, keyReleased, mouseMoved, mouseDragged, mousePressed and mouseReleased functions. Look in the API docs.
If you want to switch between input modes, you need to keep track of the current mode and act accordingly. Look at the standard ExampleFrameListener to see how it's done.
Moving the camera
Now that the input system is up and running, it's time to put it to good use: moving our camera.
moveCamera
Add the following function declaration to OgreApplication.h:
virtual void moveCamera();
Add the function definition to OgreApplication.cpp:
//------------------------------------------------------------------------------------- void OgreApplication::moveCamera() { // Make all the changes to the camera // Note that YAW direction is around a fixed axis (freelook style) rather than a natural YAW (e.g. airplane) mCamera->yaw(mRotX); mCamera->pitch(mRotY); mCamera->moveRelative(mTranslateVector); }
Aha, you say: mRotX and mRotY is for rotating the camera, mTranslateVector is for translating it!
OgreApplication additions
We need to add a couple member variables to our OgreApplication:
Real mMoveSpeed; Degree mRotateSpeed; float mMoveScale; Degree mRotScale;
Just add them near mTranslateVector and the mouse rotation variables, in OgreApplication.h.
These are used to calculate movement speed: mMoveScale is calculated by multiplying mMoveSpeed by the time since last frame, the same goes for mRotScale.
frameStarted revised
Let's modify frameStarted a bit:
// If this is the first frame, pick a speed if (evt.timeSinceLastFrame == 0) { mMoveScale = 1; mRotScale = 0.1; } // Otherwise scale movement units by time passed since last frame else { // Move about 100 units per second, mMoveScale = mMoveSpeed * evt.timeSinceLastFrame; // Take about 10 seconds for full rotation mRotScale = mRotateSpeed * evt.timeSinceLastFrame; }
This should be added to the beginning of frameStarted.
The variables are set to a sane value if this is the first frame.
The movement scale is movement speed times time since last frame.
Now we need to put a call to moveCamera() in our frameStarted function.
Add it at the end of it, just before return true;:
moveCamera(); return true;
createFrameListener modifications
Add this to OgreApplication::createFrameListener in OgreApplication.cpp:
mRotateSpeed = 36; mMoveSpeed = 100; mMoveScale = 0.0f; mRotScale = 0.0f;
Yes, they could have been initialised in the OgreApplication constructor instead, but ... (I will do that, it's a TODO - or an exercise for the reader :-)) )
The values for mRotateSpeed and mMoveSpeed works for most scenes.
processUnbufferedInput updated
Add the following code to OgreApplication::processUnbufferedInput (OgreApplication.cpp):
if (mInputDevice->isKeyDown(KC_A)) { // Move camera left mTranslateVector.x = -mMoveScale; } if (mInputDevice->isKeyDown(KC_D)) { // Move camera RIGHT mTranslateVector.x = mMoveScale; } /* Move camera forward by keypress. */ if (mInputDevice->isKeyDown(KC_UP) || mInputDevice->isKeyDown(KC_W) ) { mTranslateVector.z = -mMoveScale; } /* Move camera backward by keypress. */ if (mInputDevice->isKeyDown(KC_DOWN) || mInputDevice->isKeyDown(KC_S) ) { mTranslateVector.z = mMoveScale; } if (mInputDevice->isKeyDown(KC_PGUP)) { // Move camera up mTranslateVector.y = mMoveScale; } if (mInputDevice->isKeyDown(KC_PGDOWN)) { // Move camera down mTranslateVector.y = -mMoveScale; } if (mInputDevice->isKeyDown(KC_RIGHT)) { mCamera->yaw(-mRotScale); } if (mInputDevice->isKeyDown(KC_LEFT)) { mCamera->yaw(mRotScale); }
This is regular WASD movement, with the addition of arrow key movement and PageUp and PageDown for vertical camera movement.
The modifications are mostly done to mTranslateVector by mMoveScale. The left and right arrow keys will yaw the camera by mRotScale.
Various Enhancements
TOBE done.
The final code
Here's the completed BaseApplication Framework:
OgreApplication.h
OgreApplication.cpp
One note about usage:
If you don't intend to subclass OgreApplication, and want to utilise it directly in your app, you need to implement createScene in OgreApplication.cpp, and remove the = 0 bit in the declaration (to make it virtual rather than pure virtual).
Example Usage
Keep in mind that you can freely override functions.
Notice that your application *is* a FrameListener as well, so you can override frameStarted without the need to pass anything to anything, because it all happens within the same class.
You derive your class from OgreApplication, and override the OgreApplication::createScene().
You can override other functions as well, but that's optional.
MyApplication.h
#ifndef __MyApplication_h__ #define __MyApplication_h__ #include "OgreApplication.h" class MyApplication : public OgreApplication { public: MyApplication(void); virtual ~MyApplication(); protected: virtual void createScene(); virtual bool frameStarted(const FrameEvent& evt); Entity* ogreHead; SceneNode* headNode; }; #endif // #ifndef __MyApplication_h__
MyApplication.cpp
#include "MyApplication.h" //------------------------------------------------------------------------------------- MyApplication::MyApplication() : ogreHead(0), headNode(0) { } //------------------------------------------------------------------------------------- MyApplication::~MyApplication() { } //------------------------------------------------------------------------------------- void MyApplication::createScene() { ogreHead = mSceneMgr->createEntity("Head", "ogrehead.mesh"); headNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(); headNode->attachObject(ogreHead); // Set ambient light mSceneMgr->setAmbientLight(ColourValue(0.5, 0.5, 0.5)); // Create a light Light* l = mSceneMgr->createLight("MainLight"); l->setPosition(20,80,50); } //------------------------------------------------------------------------------------- bool MyApplication::frameStarted(const FrameEvent& evt) { // Just a silly example to demonstrate how much easier this is than passing objects to an external framelistener headNode->translate(0.0f, 0.005f, 0.0f); return OgreApplication::frameStarted(evt); } //------------------------------------------------------------------------------------- #if OGRE_PLATFORM == OGRE_PLATFORM_WIN32 #define WIN32_LEAN_AND_MEAN #include "windows.h" #endif #ifdef __cplusplus extern "C" { #endif #if OGRE_PLATFORM == OGRE_PLATFORM_WIN32 INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT ) #else int main(int argc, char *argv[]) #endif { // Create application object MyApplication app; SET_TERM_HANDLER; try { app.go(); } catch( Ogre::Exception& e ) { #if OGRE_PLATFORM == OGRE_PLATFORM_WIN32 MessageBox( NULL, e.getFullDescription().c_str(), "An exception has occured!", MB_OK | MB_ICONERROR | MB_TASKMODAL); #else std::cerr << "An exception has occured: " << e.getFullDescription().c_str() << std::endl; #endif } return 0; } #ifdef __cplusplus } #endif