Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/main_reto/src/RunManager.cpp @ 141

Last change on this file since 141 was 141, checked in by rgrieder, 16 years ago

RunManager.cpp completely commented in Doxygen style.
One or two comments are yet inappropriate since a few changes to the code have to be made anyway. coming soon.

File size: 16.5 KB
Line 
1/*
2*   ORXONOX - the hottest 3D action shooter ever to exist
3*
4*
5*   License notice:
6*
7*   This program is free software: you can redistribute it and/or modify
8*   it under the terms of the GNU General Public License as published by
9*   the Free Software Foundation, either version 3 of the License, or
10*   (at your option) any later version.
11*
12*   This program is distributed in the hope that it will be useful,
13*   but WITHOUT ANY WARRANTY; without even the implied warranty of
14*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15*   GNU General Public License for more details.
16*
17*   You should have received a copy of the GNU General Public License
18*   along with this program.  If not, see <http://www.gnu.org/licenses/>.
19*
20*
21*   Author:
22*      Reto Grieder
23*   Co-authors:
24*      ...
25*/
26
27/**
28* RunManager is the basic control object during the game.
29*
30* The RunManger class is designed to actually "run" the main part of the
31* game. The Idea is, that you could derive from the RunManager in order
32* to distinguish between a first person shooter or a space craft shooter.
33* RunManager loads and initialises everything in the scene (like the ship,
34* the enemies in the scene, any scripts, the physics, window events,
35* environment, HUD, etc.).
36* It also captures any input from keyboard, mous, joystick (optional) or
37* Ogre (window events).
38*/
39
40#include "RunManager.h"
41
42
43/**
44* Contructor only needs the render window and the Root object which are both
45* the OgreControl object.
46* Right now the constructor does all the initialisation work. This could also
47* be done in a new method "initialize()", for whatever purpose.
48*
49*
50* @param mOgre The OgreControl object holding the render window and the Root
51*/
52RunManager::RunManager(OgreControl * mOgre, bool bufferedKeys,
53                       bool bufferedMouse, bool bufferedJoy )
54                       : mOgre(mOgre), mWindow(mOgre->getRenderWindow()), leftButtonDown(false),
55                       mStatsOn(true), mNumScreenShots(0), mTimeUntilNextToggle(0),
56                       mFiltering(TFO_BILINEAR), mAniso(1), mSceneDetailIndex(0),
57                       mDebugOverlay(0), mInputManager(0), mMouse(0), mKeyboard(0), mJoy(0)
58{
59
60  // SETTING UP THE SCENE
61
62  // create one new SceneManger
63  mSceneMgr = mOgre->getRoot()->createSceneManager(ST_GENERIC, "mScene");
64
65  // background scene (world objects, skybox, lights, etc.)
66  mScene = new OrxonoxScene(mSceneMgr);
67
68  // PLAYER SPACESHIP
69
70  // create a steerable SceneNode (not derived!) object. The idea is that this
71  // object only receives the mouse and the keyboard input (not specifi keys,
72  // more like up, down, left, right, roll left, roll right, move down,
73  // move up). The steering class can then decide how to control the node for
74  // the spaceship. This gives a certain flexibility.
75  // It should also be considered, that this class should provide another Node
76  // for a camera to be attached (otherwise the spaceship in front of the
77  // would be very static, never moving at all.
78  mShipNode = mSceneMgr->getRootSceneNode()->createChildSceneNode("ShipNode",
79    Vector3(20, 20, 20));
80
81  // Construct a new spaceship and attach it to the node
82  mShip = new OrxonoxShip(mSceneMgr, mShipNode);
83
84
85  // RESOURCE LOADING (using ResourceGroups if implemented)
86
87  // load all resources and create the entities by calling the initialise()
88  // methods for each object (don't initialise in the constructor!).
89  mScene->initialise();
90  mShip->initialise();
91
92
93  // CAMERA AND VIEWPORT
94  // TODO: create a camera manager. It should be able to change its position
95  // around the space ship (predefined states would be nice too). And it should
96  // also be able to switch between different locations (like ship, spactator,
97  // certain fixed positions (e.g. finish line, etc.)). These are just ideas.
98
99  // create camera and viewport
100  createCamera();
101  createViewports();
102
103
104  // Set default mipmap level (NB some APIs ignore this)
105  TextureManager::getSingleton().setDefaultNumMipmaps(5);
106
107 
108  // BULLET LIST FOR THE TEST APPLICATION
109
110  // TODO: Use STL to make life easier. But it works this way too..
111  mBullets = new Bullet*[10];
112  mBulletsPosition = 0;
113  mBulletsSize = 10;
114
115
116  // HUMAN INTERFACE
117
118  using namespace OIS;
119
120  mDebugOverlay = OverlayManager::getSingleton()
121    .getByName("Core/DebugOverlay");
122
123  LogManager::getSingletonPtr()->logMessage("*** Initializing OIS ***");
124  ParamList pl;
125  size_t windowHnd = 0;
126  std::ostringstream windowHndStr;
127
128  mWindow->getCustomAttribute("WINDOW", &windowHnd);
129  windowHndStr << windowHnd;
130  pl.insert(std::make_pair(std::string("WINDOW"), windowHndStr.str()));
131
132  mInputManager = InputManager::createInputSystem( pl );
133
134  // Create all devices (We only catch joystick exceptions here,
135  // as, most people have Key/Mouse)
136  mKeyboard = static_cast<Keyboard*>(mInputManager
137    ->createInputObject( OISKeyboard, bufferedKeys ));
138  mMouse = static_cast<Mouse*>(mInputManager
139    ->createInputObject( OISMouse, bufferedMouse ));
140  try {
141    mJoy = static_cast<JoyStick*>(mInputManager
142      ->createInputObject( OISJoyStick, bufferedJoy ));
143  }
144  catch(...) {
145    mJoy = 0;
146  }
147
148  //Set initial mouse clipping size
149  windowResized(mWindow);
150
151  showDebugOverlay(true);
152
153  // REGISTER THIS OBJECT AS A WINDOW EVENT LISTENER IN OGRE
154  // It will then receive events liek windowClosed, windowResized, etc.
155  WindowEventUtilities::addWindowEventListener(mWindow, this);
156}
157
158/**
159* Standard destructor.
160* Removes this object as a window event listener and deletes all created
161* variables.
162*/
163RunManager::~RunManager()
164{
165  //Remove ourself as a Window listener
166  WindowEventUtilities::removeWindowEventListener(mWindow, this);
167  windowClosed(mWindow);
168
169  if (mScene)
170    delete mScene;
171  if (mShip)
172    delete mShip;
173
174  // clean up the bullet list
175  for (int i = 0; i < mBulletsPosition; i++)
176    delete mBullets[i];
177  delete mBullets;
178}
179
180
181/**
182* Method to compute anyting between 2 frames.
183*
184* Everything that needs to be computed during the games happens right here.
185* The only exception are the listeners (which should only set variables,
186* not actually do something).
187*
188* @param time Absolute play time
189* @param deltaTime Time passed since last frame
190* @return Return false to end rendering
191*/
192bool RunManager::tick(unsigned long time, float deltaTime)
193{
194  // synchronize with internal class timer
195  mTime = time;
196
197  // Call tick() for every object
198  // This could be done by registering (needs a factory..)
199  mScene->tick(time, deltaTime);
200  mShip->tick(time, deltaTime);
201
202
203  // Update the 'HUD'
204  updateStats();
205
206  // update the bullet positions
207  for (int i = 0; i < mBulletsPosition; i++)
208  {
209    mBullets[i]->mNode->translate(mBullets[i]->mSpeed*deltaTime);
210    mBullets[i]->mNode->yaw(Degree(deltaTime*100));
211    mBullets[i]->mNode->roll(Degree(deltaTime*300));
212  }
213
214  // HUMAN INTERFACE
215
216  using namespace OIS;
217
218  if(mWindow->isClosed())       return false;
219
220  //Need to capture/update each device
221  mKeyboard->capture();
222  mMouse->capture();
223  if( mJoy ) mJoy->capture();
224
225  bool buffJ = (mJoy) ? mJoy->buffered() : true;
226
227  //Check if one of the devices is not buffered
228  if( !mMouse->buffered() || !mKeyboard->buffered() || !buffJ )
229  {
230    // one of the input modes is immediate, so setup what
231    // is needed for immediate movement
232    if (mTimeUntilNextToggle >= 0)
233      mTimeUntilNextToggle -= deltaTime;
234  }
235
236  //Check to see which device is not buffered, and handle it
237  if( !mKeyboard->buffered() )
238    if( processUnbufferedKeyInput() == false )
239      return false;
240  if( !mMouse->buffered() )
241    if( processUnbufferedMouseInput() == false )
242      return false;
243
244  // keep rendering
245  return true;
246}
247
248
249/**
250* Adjust mouse clipping area.
251* This method is called by Ogre without regards of tick()!
252* Avoid doing too much in this call.
253* @param rw render window
254*/
255void RunManager::windowResized(RenderWindow* rw)
256{
257  unsigned int width, height, depth;
258  int left, top;
259  rw->getMetrics(width, height, depth, left, top);
260
261  const OIS::MouseState &ms = mMouse->getMouseState();
262  ms.width = width;
263  ms.height = height;
264}
265
266
267/**
268* Unattach OIS before window shutdown (very important under Linux).
269* Again, avoid computing a lot in this function.
270* @param rw Render Window
271*/
272void RunManager::windowClosed(RenderWindow* rw)
273{
274  //Only close for window that created OIS (the main window in these demos)
275  if( rw == mWindow )
276  {
277    if( mInputManager )
278    {
279      mInputManager->destroyInputObject( mMouse );
280      mInputManager->destroyInputObject( mKeyboard );
281      mInputManager->destroyInputObject( mJoy );
282
283      OIS::InputManager::destroyInputSystem(mInputManager);
284      mInputManager = 0;
285    }
286  }
287}
288
289/**
290* Processes the Keyboard input.
291* TODO: Use listeners to improve performance.
292* A lookup table should be implemented to bind any key to a specific action.
293* @return Return true to keep rendering
294*/
295bool RunManager::processUnbufferedKeyInput()
296{
297  using namespace OIS;
298
299  if(mKeyboard->isKeyDown(KC_A) || mKeyboard->isKeyDown(KC_LEFT))
300    mShip->setSideThrust(1);
301  else if(mKeyboard->isKeyDown(KC_D) || mKeyboard->isKeyDown(KC_RIGHT))
302    mShip->setSideThrust(-1);
303  else
304    mShip->setSideThrust(0);
305
306  if(mKeyboard->isKeyDown(KC_UP) || mKeyboard->isKeyDown(KC_W) )
307    mShip->setThrust(1);
308  else if(mKeyboard->isKeyDown(KC_DOWN) || mKeyboard->isKeyDown(KC_S) )
309    mShip->setThrust(-1);
310  else
311    mShip->setThrust(0);
312
313  if( mKeyboard->isKeyDown(KC_ESCAPE) || mKeyboard->isKeyDown(KC_Q) )
314    return false;
315
316  if( mKeyboard->isKeyDown(KC_F) && mTimeUntilNextToggle <= 0 )
317  {
318    mStatsOn = !mStatsOn;
319    showDebugOverlay(mStatsOn);
320    mTimeUntilNextToggle = 1;
321  }
322
323  if( mKeyboard->isKeyDown(KC_T) && mTimeUntilNextToggle <= 0 )
324  {
325    switch(mFiltering)
326    {
327    case TFO_BILINEAR:
328      mFiltering = TFO_TRILINEAR;
329      mAniso = 1;
330      break;
331    case TFO_TRILINEAR:
332      mFiltering = TFO_ANISOTROPIC;
333      mAniso = 8;
334      break;
335    case TFO_ANISOTROPIC:
336      mFiltering = TFO_BILINEAR;
337      mAniso = 1;
338      break;
339    default: break;
340    }
341    MaterialManager::getSingleton().setDefaultTextureFiltering(mFiltering);
342    MaterialManager::getSingleton().setDefaultAnisotropy(mAniso);
343
344    showDebugOverlay(mStatsOn);
345    mTimeUntilNextToggle = 1;
346  }
347
348  if(mKeyboard->isKeyDown(KC_SYSRQ) && mTimeUntilNextToggle <= 0)
349  {
350    std::ostringstream ss;
351    ss << "screenshot_" << ++mNumScreenShots << ".png";
352    mWindow->writeContentsToFile(ss.str());
353    mTimeUntilNextToggle = 0.5;
354    mDebugText = "Saved: " + ss.str();
355  }
356
357  if(mKeyboard->isKeyDown(KC_R) && mTimeUntilNextToggle <=0)
358  {
359    mSceneDetailIndex = (mSceneDetailIndex+1)%3 ;
360    switch(mSceneDetailIndex) {
361        case 0 : mCamera->setPolygonMode(PM_SOLID); break;
362        case 1 : mCamera->setPolygonMode(PM_WIREFRAME); break;
363        case 2 : mCamera->setPolygonMode(PM_POINTS); break;
364    }
365    mTimeUntilNextToggle = 0.5;
366  }
367
368  static bool displayCameraDetails = false;
369  if(mKeyboard->isKeyDown(KC_P) && mTimeUntilNextToggle <= 0)
370  {
371    displayCameraDetails = !displayCameraDetails;
372    mTimeUntilNextToggle = 0.5;
373    if (!displayCameraDetails)
374      mDebugText = "";
375  }
376
377  // Print camera details
378  if(displayCameraDetails)
379    mDebugText = StringConverter::toString(mShip->getThrust())
380    + " | Speed = " + StringConverter::toString(mShip->speed);
381  // mDebugText = "P: " + StringConverter::toString(mCamera
382  //      ->getDerivedPosition()) + " " + "O: "
383  //      + StringConverter::toString(mCamera->getDerivedOrientation());
384
385  // Return true to continue rendering
386  return true;
387}
388
389
390/**
391* Processes the Mouse input.
392* TODO: Use listeners to improve performance.
393* A lookup table should be implemented to bind ANY button or movement
394* to a specific action.
395* @return Return true to keep rendering
396*/
397bool RunManager::processUnbufferedMouseInput()
398{
399  using namespace OIS;
400
401  const MouseState &ms = mMouse->getMouseState();
402
403  // This is a 'hack' to show some flying barrels..
404  // Usually, the Bullet created by the ship should be managed
405  // by the physics engine..
406  if (ms.buttonDown(MB_Left) && !leftButtonDown)
407  {
408    // Prevent continuous fire for the moment.
409    leftButtonDown = true;
410   
411    // let ship fire one shot with its only weapon (Barrels..)
412    Bullet *mTempBullet = mShip->fire();
413
414    // resize array if neccessary (double the size then)
415    if (mBulletsPosition >= mBulletsSize)
416    {
417      // redimension the array
418      Bullet **mTempArray = new Bullet*[2*mBulletsSize];
419      for (int i = 0; i < mBulletsSize; i++)
420        mTempArray[i] = mBullets[i];
421      mBulletsSize *= 2;
422      delete mBullets;
423      mBullets = mTempArray;
424    }
425
426    // add the bullet to the list
427    mBullets[mBulletsPosition++] = mTempBullet;
428
429  }
430  else if (!ms.buttons)
431    leftButtonDown = false;
432
433  // space ship steering. This should definitely be done in the steering object
434  // Simply give it the mouse movements.
435  mShip->mRootNode->pitch(Degree(-ms.Y.rel * 0.13), Ogre::Node::TransformSpace::TS_LOCAL);
436  mShip->mRootNode->yaw(Degree(-ms.X.rel * 0.13), Ogre::Node::TransformSpace::TS_PARENT);
437
438  // keep rendering
439  return true;
440}
441
442/**
443* Show the debug overlay of desired.
444* @param show Whether or not to show the debug overlay
445*/
446void RunManager::showDebugOverlay(bool show)
447{
448  if (mDebugOverlay)
449  {
450    if (show)
451      mDebugOverlay->show();
452    else
453      mDebugOverlay->hide();
454  }
455}
456
457
458/**
459* Show stats (e.g. FPS) in the left lower corner of the screen.
460* Copied from the ExampleFrameListener.h in the Ogre SDK
461*/
462void RunManager::updateStats(void)
463{
464  static String currFps = "Current FPS: ";
465  static String avgFps = "Average FPS: ";
466  static String bestFps = "Best FPS: ";
467  static String worstFps = "Worst FPS: ";
468  static String tris = "Triangle Count: ";
469  static String batches = "Batch Count: ";
470
471  // update stats when necessary
472  try {
473    OverlayElement* guiAvg = OverlayManager::getSingleton()
474      .getOverlayElement("Core/AverageFps");
475    OverlayElement* guiCurr = OverlayManager::getSingleton()
476      .getOverlayElement("Core/CurrFps");
477    OverlayElement* guiBest = OverlayManager::getSingleton()
478      .getOverlayElement("Core/BestFps");
479    OverlayElement* guiWorst = OverlayManager::getSingleton()
480      .getOverlayElement("Core/WorstFps");
481
482    const RenderTarget::FrameStats& stats = mWindow->getStatistics();
483    guiAvg->setCaption(avgFps + StringConverter::toString(stats.avgFPS));
484    guiCurr->setCaption(currFps + StringConverter::toString(stats.lastFPS));
485    guiBest->setCaption(bestFps + StringConverter::toString(stats.bestFPS)
486      +" "+StringConverter::toString(stats.bestFrameTime)+" ms");
487    guiWorst->setCaption(worstFps + StringConverter::toString(stats.worstFPS)
488      +" "+StringConverter::toString(stats.worstFrameTime)+" ms");
489
490    OverlayElement* guiTris = OverlayManager::getSingleton()
491      .getOverlayElement("Core/NumTris");
492    guiTris->setCaption(tris + StringConverter::toString(stats.triangleCount));
493
494    OverlayElement* guiBatches = OverlayManager::getSingleton()
495      .getOverlayElement("Core/NumBatches");
496    guiBatches->setCaption(batches
497      + StringConverter::toString(stats.batchCount));
498
499    OverlayElement* guiDbg = OverlayManager::getSingleton()
500      .getOverlayElement("Core/DebugText");
501    guiDbg->setCaption(mDebugText);
502  }
503  catch(...) { /* ignore */ }
504}
505
506
507
508/**
509* Simple camera creator.
510* mShipNode->attachObject(mCamera) should no be here! This is what the camera
511* manager is for. Right now, this method should do just fine, setting the
512* cam behind the ship.
513*/
514void RunManager::createCamera(void)
515{
516  mCamera = mSceneMgr->createCamera("PlayerCam");
517  mShipNode->attachObject(mCamera);
518  mCamera->setNearClipDistance(5);
519  mCamera->setPosition(Vector3(0,10,500));
520  mCamera->lookAt(Vector3(0,0,0));
521}
522
523/**
524* Simple viewport creator.
525* TODO: fully understand the concept of viewports concerning orxnox.
526* E.g. do we need splitscreen mode?
527* For now the viewport uses the entire render window and is based on the one
528* camera created so far.
529*/
530void RunManager::createViewports(void)
531{
532  // Create one viewport, entire window
533  Viewport* vp = mWindow->addViewport(mCamera);
534  vp->setBackgroundColour(ColourValue(0,0,0));
535
536  // Alter the camera aspect ratio to match the viewport
537  mCamera->setAspectRatio(
538    Real(vp->getActualWidth()) / Real(vp->getActualHeight()));
539}
540
541
Note: See TracBrowser for help on using the repository browser.