Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

merged input branch into gui test branch (was about time)
svn save (it's still a mess and CMLs haven't been updated)
I'll have to create a special project to create the tolua_bind files for tolua itself anyway..

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