Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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