Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/core3/src/orxonox/Orxonox.cc @ 1658

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

ConfigFileManager::getSingleton —> getInstance()

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