Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/hud/src/orxonox/Orxonox.cc @ 1618

Last change on this file since 1618 was 1618, checked in by rgrieder, 16 years ago
  • moved colours of the SpeedBar to XML file
  • enabled render window updating in inactive mode (under windows the render window didn't show anything if inactive)
  • added row/column to ticpp error description (there are a lot more to do this, may modify the macro..)
  • Property svn:eol-style set to native
File size: 14.8 KB
RevLine 
[1038]1/*
[1293]2 *   ORXONOX - the hottest 3D action shooter ever to exist
[1056]3 *                    > www.orxonox.net <
[1038]4 *
5 *
6 *   License notice:
7 *
8 *   This program is free software; you can redistribute it and/or
9 *   modify it under the terms of the GNU General Public License
10 *   as published by the Free Software Foundation; either version 2
11 *   of the License, or (at your option) any later version.
12 *
13 *   This program is distributed in the hope that it will be useful,
14 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *   GNU General Public License for more details.
17 *
18 *   You should have received a copy of the GNU General Public License
19 *   along with this program; if not, write to the Free Software
20 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 *
22 *   Author:
[1535]23 *      Reto Grieder
24 *   Co-authors:
[1038]25 *      Benjamin Knecht <beni_at_orxonox.net>, (C) 2007
26 *
27 */
28
29/**
30 @file
31 @brief Orxonox Main Class
32 */
33
34// Precompiled Headers
35#include "OrxonoxStableHeaders.h"
[1039]36#include "Orxonox.h"
[1038]37
38//****** STD *******
39#include <deque>
40
41//****** OGRE ******
42#include <OgreFrameListener.h>
43#include <OgreOverlay.h>
44#include <OgreOverlayManager.h>
45#include <OgreRoot.h>
46#include <OgreTimer.h>
47#include <OgreWindowEventUtilities.h>
48
49//***** ORXONOX ****
50// util
51//#include "util/Sleep.h"
52#include "util/ArgReader.h"
53
[1293]54// core
[1054]55#include "core/ConfigFileManager.h"
[1293]56#include "core/ConsoleCommand.h"
[1038]57#include "core/Debug.h"
58#include "core/Loader.h"
[1535]59#include "core/input/InputManager.h"
[1293]60#include "core/TclBind.h"
[1535]61#include "core/Core.h"
[1038]62
63// audio
64#include "audio/AudioManager.h"
65
66// network
67#include "network/Server.h"
68#include "network/Client.h"
69
70// objects and tools
[1601]71#include "overlays/OverlayGroup.h"
72#include "overlays/console/InGameConsole.h"
[1535]73#include "objects/Tickable.h"
[1563]74#include "tools/ParticleInterface.h"
[1038]75
[1502]76#include "GraphicsEngine.h"
[1535]77#include "Settings.h"
[1613]78#include "Radar.h"
[1502]79
[1038]80// globals for the server or client
[1577]81static network::Client *client_g = 0;
82static network::Server *server_g = 0;
[1038]83
84namespace orxonox
[1293]85{
[1502]86  SetConsoleCommandShortcut(Orxonox, exit).setKeybindMode(KeybindMode::OnPress);
87  SetConsoleCommandShortcut(Orxonox, slomo).setAccessLevel(AccessLevel::Offline).setDefaultValue(0, 1.0).setAxisParamIndex(0).setIsAxisRelative(false);
88  SetConsoleCommandShortcut(Orxonox, setTimeFactor).setAccessLevel(AccessLevel::Offline).setDefaultValue(0, 1.0);
[1505]89
[1038]90  /**
91    @brief Reference to the only instance of the class.
92  */
93  Orxonox *Orxonox::singletonRef_s = 0;
94
95  /**
[1293]96   * Create a new instance of Orxonox. Avoid doing any actual work here.
[1038]97   */
[1604]98  Orxonox::Orxonox()
99    : ogre_(0)
100    , startLevel_(0)
101    , hud_(0)
[1613]102    , radar_(0)
[1604]103    //, auMan_(0)
104    , timer_(0)
[1293]105    // turn on frame smoothing by setting a value different from 0
[1604]106    , frameSmoothingTime_(0.0f)
107    //orxonoxHUD_(0)
108    , bAbort_(false)
109    , timefactor_(1.0f)
110    , mode_(STANDALONE)
111    , serverIp_("")
112    , serverPort_(NETWORK_PORT)
[1038]113  {
114  }
115
116  /**
[1293]117   * Destruct Orxonox.
[1038]118   */
119  Orxonox::~Orxonox()
120  {
121    // keep in mind: the order of deletion is very important!
[1604]122    Loader::unload(startLevel_);
123    if (this->startLevel_)
124      delete this->startLevel_;
125
126    Loader::unload(hud_);
127    if (this->hud_)
128      delete this->hud_;
129
[1613]130    if (this->radar_)
131      delete this->radar_;
132
[1038]133    Loader::close();
[1293]134    //if (this->auMan_)
135    //  delete this->auMan_;
[1577]136    InGameConsole::getInstance().destroy();
[1038]137    if (this->timer_)
138      delete this->timer_;
[1604]139    InputManager::destroy();
[1038]140    GraphicsEngine::getSingleton().destroy();
141
[1293]142    if (network::Client::getSingleton())
143      network::Client::destroySingleton();
[1038]144    if (server_g)
[1534]145      delete network::Server::getSingleton();
[1038]146  }
147
148
149  /**
150    Asks the mainloop nicely to abort.
151  */
152  void Orxonox::abortRequest()
153  {
[1293]154    COUT(3) << "Orxonox: Abort requested." << std::endl;
[1038]155    bAbort_ = true;
156  }
157
158  /**
[1293]159   * @return singleton reference
[1038]160   */
161  Orxonox* Orxonox::getSingleton()
162  {
163    if (!singletonRef_s)
164      singletonRef_s = new Orxonox();
165    return singletonRef_s;
166  }
167
168  /**
169    @brief Destroys the Orxonox singleton.
170  */
171  void Orxonox::destroySingleton()
172  {
173    if (singletonRef_s)
174      delete singletonRef_s;
175    singletonRef_s = 0;
176  }
177
178  /**
[1563]179    @brief Changes the speed of Orxonox
180  */
181  void Orxonox::setTimeFactor(float factor)
182  {
183    float change = factor / Orxonox::getSingleton()->getTimeFactor();
184    Orxonox::getSingleton()->timefactor_ = factor;
185
186    for (Iterator<ParticleInterface> it = ObjectList<ParticleInterface>::begin(); it; ++it)
187        it->setSpeedFactor(it->getSpeedFactor() * change);
188  }
189
190  /**
[1038]191   * initialization of Orxonox object
192   * @param argc argument counter
193   * @param argv list of argumenst
194   * @param path path to config (in home dir or something)
195   */
[1535]196  bool Orxonox::init(int argc, char **argv)
[1038]197  {
[1535]198#ifdef _DEBUG
199    ConfigFileManager::getSingleton()->setFile(CFT_Settings, "orxonox_d.ini");
200#else
201    ConfigFileManager::getSingleton()->setFile(CFT_Settings, "orxonox.ini");
202#endif
203    Factory::createClassHierarchy();
204
[1038]205    std::string mode;
[1535]206    std::string tempDataPath;
[1038]207
208    ArgReader ar(argc, argv);
[1535]209    ar.checkArgument("mode", &mode, false);
210    ar.checkArgument("data", &tempDataPath, false);
211    ar.checkArgument("ip",   &serverIp_, false);
212    ar.checkArgument("port", &serverPort_, false);
[1293]213    if(ar.errorHandling())
[1535]214    {
215      COUT(1) << "Error while parsing command line arguments" << std::endl;
216      COUT(1) << ar.getErrorString();
217      COUT(0) << "Usage:" << std::endl << "orxonox [mode client|server|dedicated|standalone] "
218        << "[--data PATH] [--ip IP] [--port PORT]" << std::endl;
[1293]219      return false;
[1535]220    }
[1293]221
222    if (mode == "client")
[1038]223      mode_ = CLIENT;
[1293]224    else if (mode == "server")
[1038]225      mode_ = SERVER;
[1502]226    else if (mode == "dedicated")
227      mode_ = DEDICATED;
[1293]228    else
229    {
[1535]230      if (mode == "")
231        mode = "standalone";
232      if (mode != "standalone")
233      {
234        COUT(2) << "Warning: mode \"" << mode << "\" doesn't exist. "
235          << "Defaulting to standalone" << std::endl;
236        mode = "standalone";
237      }
[1038]238      mode_ = STANDALONE;
239    }
[1293]240    COUT(3) << "Orxonox: Mode is " << mode << "." << std::endl;
[1038]241
[1535]242    if (tempDataPath != "")
243    {
244      if (tempDataPath[tempDataPath.size() - 1] != '/')
245        tempDataPath += "/";
246      Settings::tsetDataPath(tempDataPath);
247    }
248
249    // initialise TCL
250    TclBind::getInstance().setDataPath(Settings::getDataPath());
251
[1293]252    //if (mode_ == DEDICATED)
253      // TODO: decide what to do here
254    //else
[1052]255
[1293]256    // for playable server, client and standalone, the startup
257    // procedure until the GUI is identical
[1052]258
[1293]259    ogre_ = &GraphicsEngine::getSingleton();
[1535]260    if (!ogre_->setup())       // creates ogre root and other essentials
[1293]261      return false;
[1052]262
[1293]263    return true;
[1038]264  }
[1052]265
[1038]266  /**
267   * start modules
268   */
[1293]269  bool Orxonox::start()
[1038]270  {
[1502]271    if (mode_ == DEDICATED)
272    {
273      // do something else
274    }
275    else
276    { // not dedicated server
277      if (!ogre_->loadRenderer())    // creates the render window
278        return false;
[1293]279
[1577]280      // TODO: Spread this so that this call only initialises things needed for the Console
281      if (!ogre_->initialiseResources())
282        return false;
283
284      // Load the InGameConsole
285      InGameConsole::getInstance().initialise();
286
[1502]287      // Calls the InputManager which sets up the input devices.
288      // The render window width and height are used to set up the mouse movement.
289      if (!InputManager::initialise(ogre_->getWindowHandle(),
290            ogre_->getWindowWidth(), ogre_->getWindowHeight(), true, true, true))
291        return false;
[1293]292
[1502]293      // TOOD: load the GUI here
294      // set InputManager to GUI mode
295      InputManager::setInputState(InputManager::IS_GUI);
296      // TODO: run GUI here
[1293]297
[1502]298      // The following lines depend very much on the GUI output, so they're probably misplaced here..
[1293]299
[1502]300      InputManager::setInputState(InputManager::IS_NONE);
[1293]301
[1502]302      // create Ogre SceneManager
303      ogre_->createNewScene();
[1293]304
[1502]305      if (!loadPlayground())
306        return false;
307    }
[1293]308
309    switch (mode_)
310    {
311    case SERVER:
312      if (!serverLoad())
313        return false;
314      break;
[1038]315    case CLIENT:
[1293]316      if (!clientLoad())
317        return false;
[1038]318      break;
[1502]319    case DEDICATED:
320      if (!serverLoad())
321        return false;
322      break;
[1038]323    default:
[1293]324      if (!standaloneLoad())
325        return false;
[1038]326    }
[1293]327
328    InputManager::setInputState(InputManager::IS_NORMAL);
329
330    return startRenderLoop();
[1038]331  }
[1052]332
[1293]333  /**
334   * Loads everything in the scene except for the actual objects.
[1577]335   * This includes HUD, audio..
[1293]336   */
337  bool Orxonox::loadPlayground()
338  {
[1502]339    // Init audio
[1293]340    //auMan_ = new audio::AudioManager();
341    //auMan_->ambientAdd("a1");
342    //auMan_->ambientAdd("a2");
343    //auMan_->ambientAdd("a3");
344    //auMan->ambientAdd("ambient1");
345    //auMan_->ambientStart();
[1052]346
[1293]347    // Load the HUD
[1588]348    COUT(3) << "Orxonox: Loading HUD" << std::endl;
[1604]349    hud_ = new Level(Settings::getDataPath() + "overlay/hud.oxo");
350    Loader::load(hud_);
[1588]351
[1613]352    // Start the Radar
353    this->radar_ = new Radar();
354
[1293]355    return true;
356  }
[1052]357
[1293]358  /**
359   * Level loading method for server mode.
360   */
361  bool Orxonox::serverLoad()
362  {
[1577]363    COUT(0) << "Loading level in server mode" << std::endl;
[1052]364
[1534]365    //server_g = new network::Server(serverPort_);
366    server_g = network::Server::createSingleton(serverPort_);
[1052]367
[1293]368    if (!loadScene())
369      return false;
[1052]370
[1038]371    server_g->open();
[1052]372
[1293]373    return true;
[1038]374  }
[1052]375
[1293]376  /**
377   * Level loading method for client mode.
378   */
379  bool Orxonox::clientLoad()
380  {
[1577]381    COUT(0) << "Loading level in client mode" << std::endl;\
[1052]382
[1293]383    if (serverIp_.compare("") == 0)
384      client_g = network::Client::createSingleton();
385    else
386
[1502]387      client_g = network::Client::createSingleton(serverIp_, serverPort_);
388
389    if(!client_g->establishConnection())
390      return false;
[1293]391    client_g->tick(0);
392
393    return true;
[1038]394  }
395
[1293]396  /**
397   * Level loading method for standalone mode.
398   */
399  bool Orxonox::standaloneLoad()
[1038]400  {
[1577]401    COUT(0) << "Loading level in standalone mode" << std::endl;
[1038]402
[1293]403    if (!loadScene())
404      return false;
[1038]405
[1293]406    return true;
[1038]407  }
408
409  /**
[1293]410   * Helper method to load a level.
411   */
412  bool Orxonox::loadScene()
[1038]413  {
[1604]414    startLevel_ = new Level("levels/sample.oxw");
415    Loader::open(startLevel_);
[1563]416
[1293]417    return true;
[1038]418  }
419
[1293]420
[1038]421  /**
422    Main loop of the orxonox game.
423    About the loop: The design is almost exactly like the one in ogre, so that
424    if any part of ogre registers a framelisteners, it will still behave
425    correctly. Furthermore the time smoothing feature from ogre has been
426    implemented too. If turned on (see orxonox constructor), it will calculate
427    the dt_n by means of the recent most dt_n-1, dt_n-2, etc.
428  */
[1293]429  bool Orxonox::startRenderLoop()
[1038]430  {
431    // first check whether ogre root object has been created
432    if (Ogre::Root::getSingletonPtr() == 0)
433    {
[1293]434      COUT(2) << "Orxonox Error: Could not start rendering. No Ogre root object found" << std::endl;
435      return false;
[1038]436    }
[1120]437    Ogre::Root& ogreRoot = Ogre::Root::getSingleton();
[1038]438
[1120]439
[1038]440    // Contains the times of recently fired events
441    // eventTimes[4] is the list for the times required for the fps counter
[1502]442    std::deque<unsigned long> eventTimes[3];
[1038]443    // Clear event times
[1502]444    for (int i = 0; i < 3; ++i)
[1038]445      eventTimes[i].clear();
446
447    // use the ogre timer class to measure time.
448    if (!timer_)
449      timer_ = new Ogre::Timer();
450    timer_->reset();
451
[1502]452    float renderTime = 0.0f;
453    float frameTime = 0.0f;
[1556]454//    clock_t time = 0;
[1502]455
[1293]456    COUT(3) << "Orxonox: Starting the main loop." << std::endl;
[1502]457    while (!bAbort_)
458    {
[1038]459      // get current time
460      unsigned long now = timer_->getMilliseconds();
461
462      // create an event to pass to the frameStarted method in ogre
463      Ogre::FrameEvent evt;
464      evt.timeSinceLastEvent = calculateEventTime(now, eventTimes[0]);
465      evt.timeSinceLastFrame = calculateEventTime(now, eventTimes[1]);
[1502]466      frameTime += evt.timeSinceLastFrame;
[1038]467
[1601]468      // OverlayGroup::getHUD().setTime(now);
[1502]469      if (mode_ != DEDICATED && frameTime > 0.4f)
470      {
[1588]471        GraphicsEngine::getSingleton().setAverageRTR(renderTime / frameTime);
[1502]472        frameTime = 0.0f;
473        renderTime = 0.0f;
474      }
[1038]475
[1535]476      // tick the core
477      Core::tick((float)evt.timeSinceLastFrame);
[1502]478      // Call those objects that need the real time
479      for (Iterator<TickableReal> it = ObjectList<TickableReal>::start(); it; ++it)
480        it->tick((float)evt.timeSinceLastFrame);
481      // Call the scene objects
[1092]482      for (Iterator<Tickable> it = ObjectList<Tickable>::start(); it; ++it)
483        it->tick((float)evt.timeSinceLastFrame * this->timefactor_);
[1535]484      //AudioManager::tick();
485      if (client_g)
486        client_g->tick((float)evt.timeSinceLastFrame);
487      if (server_g)
488        server_g->tick((float)evt.timeSinceLastFrame);
[1038]489
490      // don't forget to call _fireFrameStarted in ogre to make sure
491      // everything goes smoothly
[1120]492      ogreRoot._fireFrameStarted(evt);
[1038]493
[1502]494      // get current time
495      now = timer_->getMilliseconds();
496      calculateEventTime(now, eventTimes[2]);
[1038]497
[1502]498      if (mode_ != DEDICATED)
499      {
500        // Pump messages in all registered RenderWindows
501        // This calls the WindowEventListener objects.
502        Ogre::WindowEventUtilities::messagePump();
[1618]503        GraphicsEngine::getSingleton().setWindowActivity(true);
[1502]504
505        // render
506        ogreRoot._updateAllRenderTargets();
507      }
508
[1038]509      // get current time
510      now = timer_->getMilliseconds();
511
512      // create an event to pass to the frameEnded method in ogre
513      evt.timeSinceLastEvent = calculateEventTime(now, eventTimes[0]);
[1502]514      renderTime += calculateEventTime(now, eventTimes[2]);
[1038]515
516      // again, just to be sure ogre works fine
[1120]517      ogreRoot._fireFrameEnded(evt);
[1577]518      //msleep(500);
[1502]519    }
[1293]520
[1502]521    if (mode_ == CLIENT)
[1293]522      network::Client::getSingleton()->closeConnection();
[1502]523    else if (mode_ == SERVER)
[1293]524      server_g->close();
[1502]525
[1293]526    return true;
[1038]527  }
528
529  /**
530    Method for calculating the average time between recently fired events.
531    Code directly taken from OgreRoot.cc
532    @param now The current time in ms.
533    @param type The type of event to be considered.
534  */
535  float Orxonox::calculateEventTime(unsigned long now, std::deque<unsigned long> &times)
536  {
537    // Calculate the average time passed between events of the given type
538    // during the last frameSmoothingTime_ seconds.
539
540    times.push_back(now);
541
542    if(times.size() == 1)
543      return 0;
544
545    // Times up to frameSmoothingTime_ seconds old should be kept
546    unsigned long discardThreshold = (unsigned long)(frameSmoothingTime_ * 1000.0f);
547
548    // Find the oldest time to keep
549    std::deque<unsigned long>::iterator it  = times.begin();
550    // We need at least two times
551    std::deque<unsigned long>::iterator end = times.end() - 2;
552
553    while(it != end)
554    {
555      if (now - *it > discardThreshold)
556        ++it;
557      else
558        break;
559    }
560
561    // Remove old times
562    times.erase(times.begin(), it);
563
564    return (float)(times.back() - times.front()) / ((times.size() - 1) * 1000);
565  }
566}
Note: See TracBrowser for help on using the repository browser.