Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/main_reto/src/run_manager.cc @ 146

Last change on this file since 146 was 146, checked in by rgrieder, 16 years ago
File size: 16.5 KB
RevLine 
[146]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 "run_manager.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}
Note: See TracBrowser for help on using the repository browser.