Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/gui/src/orxonox/Orxonox.cc @ 1640

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

When starting with no arguments, the GUI is loaded, which consists merely of 2 buttons: start and exit (quite self explanatory). But it does work on my box (still no cmake support).

  • Property svn:eol-style set to native
File size: 15.5 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
[1638]24 *      Benjamin Knecht <beni_at_orxonox.net>, (C) 2007
[1535]25 *   Co-authors:
[1638]26 *      ...
[1038]27 *
28 */
29
30/**
31 @file
32 @brief Orxonox Main Class
33 */
34
35// Precompiled Headers
36#include "OrxonoxStableHeaders.h"
[1039]37#include "Orxonox.h"
[1038]38
39//****** STD *******
40#include <deque>
[1638]41#include <cassert>
[1038]42
43//****** OGRE ******
44#include <OgreFrameListener.h>
45#include <OgreOverlay.h>
46#include <OgreOverlayManager.h>
47#include <OgreRoot.h>
48#include <OgreTimer.h>
49#include <OgreWindowEventUtilities.h>
50
51//***** ORXONOX ****
52// util
53//#include "util/Sleep.h"
54
[1293]55// core
[1054]56#include "core/ConfigFileManager.h"
[1638]57#include "core/ConfigValueIncludes.h"
[1293]58#include "core/ConsoleCommand.h"
[1038]59#include "core/Debug.h"
60#include "core/Loader.h"
[1638]61#include "core/Exception.h"
[1535]62#include "core/input/InputManager.h"
[1638]63#include "core/input/SimpleInputState.h"
64#include "core/input/KeyBinder.h"
[1293]65#include "core/TclBind.h"
[1535]66#include "core/Core.h"
[1038]67
68// audio
69#include "audio/AudioManager.h"
70
71// network
72#include "network/Server.h"
73#include "network/Client.h"
74
75// objects and tools
[1625]76#include "overlays/console/InGameConsole.h"
[1535]77#include "objects/Tickable.h"
[1608]78#include "objects/Backlight.h"
[1563]79#include "tools/ParticleInterface.h"
[1638]80#include "gui/GUIManager.h"
[1038]81
[1502]82#include "GraphicsEngine.h"
[1535]83#include "Settings.h"
[1625]84#include "Radar.h"
[1502]85
[1038]86// globals for the server or client
[1577]87static network::Client *client_g = 0;
88static network::Server *server_g = 0;
[1038]89
90namespace orxonox
[1293]91{
[1638]92  SetConsoleCommand(Orxonox, exit, true).setKeybindMode(KeybindMode::OnPress);
93  SetConsoleCommand(Orxonox, slomo, true).setAccessLevel(AccessLevel::Offline).setDefaultValue(0, 1.0).setAxisParamIndex(0).setIsAxisRelative(false);
94  SetConsoleCommand(Orxonox, setTimeFactor, true).setAccessLevel(AccessLevel::Offline).setDefaultValue(0, 1.0);
95  SetConsoleCommand(Orxonox, loadGame, true).setAccessLevel(AccessLevel::User).setDefaultValue(0, "standalone");
[1505]96
[1038]97  /**
98    @brief Reference to the only instance of the class.
99  */
100  Orxonox *Orxonox::singletonRef_s = 0;
101
102  /**
[1293]103   * Create a new instance of Orxonox. Avoid doing any actual work here.
[1038]104   */
[1625]105  Orxonox::Orxonox()
106    : ogre_(0)
107    , startLevel_(0)
108    , hud_(0)
109    , radar_(0)
110    //, auMan_(0)
111    , timer_(0)
112    , bAbort_(false)
113    , timefactor_(1.0f)
[1638]114    , mode_(GameMode::GM_Unspecified)
115    , debugRefreshTime_(0.0f)
[1038]116  {
[1638]117    RegisterRootObject(Orxonox);
118
119    assert(singletonRef_s == 0);
120    singletonRef_s = this;
[1038]121  }
122
123  /**
[1293]124   * Destruct Orxonox.
[1038]125   */
126  Orxonox::~Orxonox()
127  {
128    // keep in mind: the order of deletion is very important!
[1638]129    Loader::unload();
[1625]130    if (this->startLevel_)
131      delete this->startLevel_;
132
133    Loader::unload(hud_);
134    if (this->hud_)
135      delete this->hud_;
136
137    if (this->radar_)
138      delete this->radar_;
139
[1038]140    Loader::close();
[1293]141    //if (this->auMan_)
142    //  delete this->auMan_;
[1577]143    InGameConsole::getInstance().destroy();
[1038]144    if (this->timer_)
145      delete this->timer_;
[1625]146    InputManager::destroy();
[1038]147
[1638]148    if (this->ogre_)
149      delete ogre_;
150
[1293]151    if (network::Client::getSingleton())
152      network::Client::destroySingleton();
[1038]153    if (server_g)
[1534]154      delete network::Server::getSingleton();
[1638]155
156    singletonRef_s = 0;
[1038]157  }
158
[1638]159  void Orxonox::setConfigValues()
160  {
161    SetConfigValue(debugRefreshTime_, 0.2).description("Sets the time interval at which average fps, etc. get updated.");
162  }
[1038]163
[1638]164
[1038]165  /**
166    Asks the mainloop nicely to abort.
167  */
168  void Orxonox::abortRequest()
169  {
[1293]170    COUT(3) << "Orxonox: Abort requested." << std::endl;
[1038]171    bAbort_ = true;
172  }
173
174  /**
[1293]175   * @return singleton reference
[1038]176   */
[1638]177  Orxonox& Orxonox::getSingleton()
[1038]178  {
[1638]179    assert(singletonRef_s);
180    return *singletonRef_s;
[1038]181  }
182
183  /**
[1563]184    @brief Changes the speed of Orxonox
185  */
186  void Orxonox::setTimeFactor(float factor)
187  {
[1638]188    float change = factor / Orxonox::getSingleton().getTimeFactor();
189    Orxonox::getSingleton().timefactor_ = factor;
[1563]190    for (Iterator<ParticleInterface> it = ObjectList<ParticleInterface>::begin(); it; ++it)
191        it->setSpeedFactor(it->getSpeedFactor() * change);
[1608]192
193    for (Iterator<Backlight> it = ObjectList<Backlight>::begin(); it; ++it)
[1638]194        it->setTimeFactor(Orxonox::getSingleton().getTimeFactor());
[1563]195  }
196
[1638]197
[1563]198  /**
[1638]199   * Starts the whole Game.
[1038]200   * @param path path to config (in home dir or something)
201   */
[1638]202  void Orxonox::start()
[1038]203  {
[1535]204#ifdef _DEBUG
205    ConfigFileManager::getSingleton()->setFile(CFT_Settings, "orxonox_d.ini");
206#else
207    ConfigFileManager::getSingleton()->setFile(CFT_Settings, "orxonox.ini");
208#endif
209    Factory::createClassHierarchy();
210
[1638]211    setConfigValues();
[1038]212
[1638]213    const Settings::CommandLineArgument* mode = Settings::getCommandLineArgument("mode");
214    assert(mode);
215    if (!mode->bHasDefaultValue_)
[1535]216    {
[1638]217      Settings::setGameMode(mode->value_);
218      this->mode_ = Settings::getGameMode();
[1535]219    }
[1638]220    COUT(3) << "Orxonox: Game mode is " << mode_.name << "." << std::endl;
[1293]221
[1638]222    const Settings::CommandLineArgument* dataPath = Settings::getCommandLineArgument("dataPath");
223    assert(dataPath);
224    if (!dataPath->bHasDefaultValue_)
[1293]225    {
[1638]226      if (*dataPath->value_.getString().end() != '/' && *dataPath->value_.getString().end() != '\\')
227        Settings::tsetDataPath(dataPath->value_.getString() + "/");
228      else
229        Settings::tsetDataPath(dataPath->value_.getString());
[1038]230    }
231
[1638]232    try
[1535]233    {
[1638]234        // initialise TCL
235        TclBind::getInstance().setDataPath(Settings::getDataPath());
[1535]236
[1638]237        ogre_ = new GraphicsEngine();
238        ogre_->setup();       // creates ogre root and other essentials
[1535]239
[1638]240        if (mode_.showsGraphics)
241        {
242          ogre_->loadRenderer();    // creates the render window
[1052]243
[1638]244          // TODO: Spread this so that this call only initialises things needed for the Console and GUI
245          ogre_->initialiseResources();
[1052]246
[1638]247          // Calls the InputManager which sets up the input devices.
248          // The render window width and height are used to set up the mouse movement.
249          InputManager::initialise(ogre_->getWindowHandle(),
250                ogre_->getWindowWidth(), ogre_->getWindowHeight(), true, true, true);
251          KeyBinder* keyBinder = new KeyBinder();
[1640]252          keyBinder->loadBindings();
[1638]253          InputManager::createSimpleInputState("game", 20)->setHandler(keyBinder);
[1052]254
[1638]255          // Load the InGameConsole
256          InGameConsole::getInstance().initialise();
[1052]257
[1638]258          // load the CEGUI interface
259          GUIManager::getInstance().initialise();
260        }
261
262        bool showGUI = true;
263        if (mode_.mode != GameMode::Unspecified)
264        {
265          showGUI = false;
266          // a game mode was specified with the command line
267          // we therefore load the game and level directly
268
269          if (!loadLevel(this->mode_))
270          {
271            COUT(1) << "Loading with predefined mode failed. Showing main menu." << std::endl;
272            showGUI = true;
273            mode_ = GameMode::GM_Unspecified;
274          }
275        }
276       
277        if (showGUI)
278        {
279          // show main menu
[1640]280          GUIManager::getInstance().showGUI("MainMenu", 0);
281          GraphicsEngine::getSingleton().getViewport()->setCamera(GUIManager::getInstance().getCamera());
[1638]282        }
283    }
284    catch (std::exception& ex)
[1502]285    {
[1638]286      COUT(1) << ex.what() << std::endl;
287      COUT(1) << "Loading sequence aborted." << std::endl;
288      return;
[1502]289    }
[1293]290
[1638]291    modeRequest_ = mode_;
292    // here happens the game
293    startRenderLoop();
[1577]294
[1638]295    if (mode_.mode == GameMode::Client)
296      network::Client::getSingleton()->closeConnection();
[1577]297
[1638]298    if (mode_.hasServer)
299      server_g->close();
300  }
[1293]301
[1638]302  void Orxonox::loadGame(const std::string& name)
303  {
304      const GameMode& mode = Settings::getGameMode(name);
305      if (mode.mode == GameMode::None)
306          return;
[1293]307
[1638]308      getSingleton().modeRequest_ = mode;
309  }
[1293]310
[1638]311  bool Orxonox::loadLevel(const GameMode& mode)
312  {
313      bool success = true;
[1293]314
[1638]315      if (mode.showsGraphics)
316      {
317        // create Ogre SceneManager for the level
318        ogre_->createNewScene();
[1293]319
[1638]320        if (!loadPlayground())
321            return false;
322      }
[1293]323
[1638]324      switch (mode.mode)
325      {
326      case GameMode::Server:
327        success &= serverLoad();
328        break;
329      case GameMode::Client:
330        success &= clientLoad();
331        break;
332      case GameMode::Dedicated:
333        success &= serverLoad();
334        break;
335      case GameMode::Standalone:
336        success &= standaloneLoad();
337        break;
338      default: // never happens
339          assert(false);
340      }
341     
342      if (success)
[1640]343      {
[1638]344        InputManager::requestEnterState("game");
[1640]345        this->mode_ = mode;
346      }
[1293]347
[1638]348      return success;
[1038]349  }
[1052]350
[1293]351  /**
352   * Loads everything in the scene except for the actual objects.
[1577]353   * This includes HUD, audio..
[1293]354   */
355  bool Orxonox::loadPlayground()
356  {
[1638]357    // Start the Radar
358    this->radar_ = new Radar();
[1052]359
[1293]360    // Load the HUD
[1625]361    COUT(3) << "Orxonox: Loading HUD" << std::endl;
362    hud_ = new Level(Settings::getDataPath() + "overlay/hud.oxo");
[1638]363    return Loader::load(hud_);
364  }
[1577]365
[1638]366  /**
367   * Helper method to load a level.
368   */
369  bool Orxonox::loadScene()
370  {
371    COUT(0) << "Loading level..." << std::endl;
372    startLevel_ = new Level(Settings::getDataPath() + "levels/sample.oxw");
373    return Loader::open(startLevel_);
[1293]374  }
[1052]375
[1293]376  /**
377   * Level loading method for server mode.
378   */
379  bool Orxonox::serverLoad()
380  {
[1577]381    COUT(0) << "Loading level in server mode" << std::endl;
[1052]382
[1638]383    assert(Settings::getCommandLineArgument("port"));
384    int serverPort = Settings::getCommandLineArgument("port")->value_;
[1534]385    //server_g = new network::Server(serverPort_);
[1638]386    server_g = network::Server::createSingleton(serverPort);
[1052]387
[1293]388    if (!loadScene())
389      return false;
[1052]390
[1038]391    server_g->open();
[1052]392
[1293]393    return true;
[1038]394  }
[1052]395
[1293]396  /**
397   * Level loading method for client mode.
398   */
399  bool Orxonox::clientLoad()
400  {
[1577]401    COUT(0) << "Loading level in client mode" << std::endl;\
[1052]402
[1638]403    assert(Settings::getCommandLineArgument("port"));
404    assert(Settings::getCommandLineArgument("ip"));
405    int serverPort = Settings::getCommandLineArgument("port")->value_;
406    std::string serverIP = Settings::getCommandLineArgument("ip")->value_;
407
408    if (serverIP.compare("") == 0)
[1293]409      client_g = network::Client::createSingleton();
410    else
[1638]411      client_g = network::Client::createSingleton(serverIP, serverPort);
[1293]412
[1502]413    if(!client_g->establishConnection())
414      return false;
[1293]415    client_g->tick(0);
416
417    return true;
[1038]418  }
419
[1293]420  /**
421   * Level loading method for standalone mode.
422   */
423  bool Orxonox::standaloneLoad()
[1038]424  {
[1577]425    COUT(0) << "Loading level in standalone mode" << std::endl;
[1038]426
[1293]427    if (!loadScene())
428      return false;
[1038]429
[1293]430    return true;
[1038]431  }
432
433  /**
434    Main loop of the orxonox game.
[1638]435    We use the Ogre::Timer to measure time since it uses the most precise
436    method an a platform (however the windows timer lacks time when under
437    heavy kernel load!).
438    There is a simple mechanism to measure the average time spent in our
439    ticks as it may indicate performance issues.
440    A note about the Ogre::FrameListener: Even though we don't use them,
441    they still get called. However, the delta times are not correct (except
442    for timeSinceLastFrame, which is the most important). A little research
443    as shown that there is probably only one FrameListener that doesn't even
444    need the time. So we shouldn't run into problems.
[1038]445  */
[1638]446  void Orxonox::startRenderLoop()
[1038]447  {
[1120]448    Ogre::Root& ogreRoot = Ogre::Root::getSingleton();
[1038]449
450    // use the ogre timer class to measure time.
451    if (!timer_)
452      timer_ = new Ogre::Timer();
453
[1625]454    unsigned long frameCount = 0;
455   
[1640]456    const unsigned long refreshTime = 50000.0f;
457    //const unsigned long refreshTime = debugRefreshTime_ * 1000000.0f;
[1625]458    unsigned long refreshStartTime = 0;
459    unsigned long tickTime = 0;
460    unsigned long oldFrameCount = 0;
[1502]461
[1625]462    unsigned long timeBeforeTick = 0;
463    unsigned long timeBeforeTickOld = 0;
464    unsigned long timeAfterTick = 0;
[1563]465
[1638]466    // TODO: Update time in seconds every 7 seconds to avoid any overflow (7 secs is very tight)
467
[1625]468    COUT(3) << "Orxonox: Starting the main loop." << std::endl;
[1502]469
[1638]470    try
471    {
[1625]472    timer_->reset();
[1502]473    while (!bAbort_)
474    {
[1638]475        // get current time
476        timeBeforeTickOld = timeBeforeTick;
477        timeBeforeTick    = timer_->getMicroseconds();
478        float dt = (timeBeforeTick - timeBeforeTickOld) / 1000000.0;
[1038]479
[1638]480        // check whether we have to load a game
481        if (mode_.mode != modeRequest_.mode && mode_.mode == GameMode::Unspecified)
482        {
483            this->loadLevel(modeRequest_);
484            this->modeRequest_ = GameMode::GM_None;
485        }
[1038]486
487
[1638]488        // tick the core (needs real time for input and tcl thread management)
489        Core::tick(dt);
[1625]490
[1638]491        // Call those objects that need the real time
492        for (Iterator<TickableReal> it = ObjectList<TickableReal>::start(); it; ++it)
493          it->tick(dt);
494        // Call the scene objects
495        for (Iterator<Tickable> it = ObjectList<Tickable>::start(); it; ++it)
496          it->tick(dt * this->timefactor_);
[1038]497
[1638]498        // call server/client with normal dt
499        if (client_g)
500          client_g->tick(dt * this->timefactor_);
501        if (server_g)
502          server_g->tick(dt * this->timefactor_);
[1625]503
504
[1638]505        // get current time once again
506        timeAfterTick = timer_->getMicroseconds();
[1625]507
[1638]508        tickTime += timeAfterTick - timeBeforeTick;
509        if (timeAfterTick > refreshStartTime + refreshTime)
510        {
511          GraphicsEngine::getSingleton().setAverageTickTime(
512              (float)tickTime * 0.001 / (frameCount - oldFrameCount));
513          float avgFPS = (float)(frameCount - oldFrameCount) / (timeAfterTick - refreshStartTime) * 1000000.0;
514          GraphicsEngine::getSingleton().setAverageFramesPerSecond(avgFPS);
[1625]515
[1638]516          oldFrameCount = frameCount;
517          tickTime = 0;
518          refreshStartTime = timeAfterTick;
519        }
[1502]520
[1638]521        // don't forget to call _fireFrameStarted in ogre to make sure
522        // everything goes smoothly
523        Ogre::FrameEvent evt;
524        evt.timeSinceLastFrame = dt;
525        evt.timeSinceLastEvent = dt; // note: same time, but shouldn't matter anyway
526        ogreRoot._fireFrameStarted(evt);
[1293]527
[1638]528        if (mode_.showsGraphics)
529        {
530          // Pump messages in all registered RenderWindows
531          // This calls the WindowEventListener objects.
532          Ogre::WindowEventUtilities::messagePump();
533          // make sure the window stays active even when not focused
534          // (probably only necessary on windows)
535          GraphicsEngine::getSingleton().setWindowActivity(true);
[1502]536
[1638]537          // tick CEGUI
538          GUIManager::getInstance().tick(dt);
539
540          // render
541          ogreRoot._updateAllRenderTargets();
542        }
543
544        // again, just to be sure ogre works fine
545        ogreRoot._fireFrameEnded(evt); // note: uses the same time as _fireFrameStarted
546
547        ++frameCount;
548    }
549    }
550    catch (std::exception& ex)
551    {
552      // something went wrong.
553      COUT(1) << ex.what() << std::endl;
554      COUT(1) << "Main loop was stopped by an unhandled exception. Shutting down." << std::endl;
555    }
[1038]556  }
557}
Note: See TracBrowser for help on using the repository browser.