Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 1622 was 1621, checked in by rgrieder, 16 years ago
  • rewrote the time measure in the main loop frame smoothing is gone for good and the fps display updates faster
  • changed "render time ratio" to "tick time in ms" which represents the time necessary to tick() one frame that's saying a lot more to me
  • Property svn:eol-style set to native
File size: 13.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 *   Co-authors:
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"
36#include "Orxonox.h"
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
54// core
55#include "core/ConfigFileManager.h"
56#include "core/ConsoleCommand.h"
57#include "core/Debug.h"
58#include "core/Loader.h"
59#include "core/input/InputManager.h"
60#include "core/TclBind.h"
61#include "core/Core.h"
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
71#include "overlays/OverlayGroup.h"
72#include "overlays/console/InGameConsole.h"
73#include "objects/Tickable.h"
74#include "tools/ParticleInterface.h"
75
76#include "GraphicsEngine.h"
77#include "Settings.h"
78#include "Radar.h"
79
80// globals for the server or client
81static network::Client *client_g = 0;
82static network::Server *server_g = 0;
83
84namespace orxonox
85{
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);
89
90  /**
91    @brief Reference to the only instance of the class.
92  */
93  Orxonox *Orxonox::singletonRef_s = 0;
94
95  /**
96   * Create a new instance of Orxonox. Avoid doing any actual work here.
97   */
98  Orxonox::Orxonox()
99    : ogre_(0)
100    , startLevel_(0)
101    , hud_(0)
102    , radar_(0)
103    //, auMan_(0)
104    , timer_(0)
105    , bAbort_(false)
106    , timefactor_(1.0f)
107    , mode_(STANDALONE)
108    , serverIp_("")
109    , serverPort_(NETWORK_PORT)
110  {
111  }
112
113  /**
114   * Destruct Orxonox.
115   */
116  Orxonox::~Orxonox()
117  {
118    // keep in mind: the order of deletion is very important!
119    Loader::unload(startLevel_);
120    if (this->startLevel_)
121      delete this->startLevel_;
122
123    Loader::unload(hud_);
124    if (this->hud_)
125      delete this->hud_;
126
127    if (this->radar_)
128      delete this->radar_;
129
130    Loader::close();
131    //if (this->auMan_)
132    //  delete this->auMan_;
133    InGameConsole::getInstance().destroy();
134    if (this->timer_)
135      delete this->timer_;
136    InputManager::destroy();
137    GraphicsEngine::getSingleton().destroy();
138
139    if (network::Client::getSingleton())
140      network::Client::destroySingleton();
141    if (server_g)
142      delete network::Server::getSingleton();
143  }
144
145
146  /**
147    Asks the mainloop nicely to abort.
148  */
149  void Orxonox::abortRequest()
150  {
151    COUT(3) << "Orxonox: Abort requested." << std::endl;
152    bAbort_ = true;
153  }
154
155  /**
156   * @return singleton reference
157   */
158  Orxonox* Orxonox::getSingleton()
159  {
160    if (!singletonRef_s)
161      singletonRef_s = new Orxonox();
162    return singletonRef_s;
163  }
164
165  /**
166    @brief Destroys the Orxonox singleton.
167  */
168  void Orxonox::destroySingleton()
169  {
170    if (singletonRef_s)
171      delete singletonRef_s;
172    singletonRef_s = 0;
173  }
174
175  /**
176    @brief Changes the speed of Orxonox
177  */
178  void Orxonox::setTimeFactor(float factor)
179  {
180    float change = factor / Orxonox::getSingleton()->getTimeFactor();
181    Orxonox::getSingleton()->timefactor_ = factor;
182
183    for (Iterator<ParticleInterface> it = ObjectList<ParticleInterface>::begin(); it; ++it)
184        it->setSpeedFactor(it->getSpeedFactor() * change);
185  }
186
187  /**
188   * initialization of Orxonox object
189   * @param argc argument counter
190   * @param argv list of argumenst
191   * @param path path to config (in home dir or something)
192   */
193  bool Orxonox::init(int argc, char **argv)
194  {
195#ifdef _DEBUG
196    ConfigFileManager::getSingleton()->setFile(CFT_Settings, "orxonox_d.ini");
197#else
198    ConfigFileManager::getSingleton()->setFile(CFT_Settings, "orxonox.ini");
199#endif
200    Factory::createClassHierarchy();
201
202    std::string mode;
203    std::string tempDataPath;
204
205    ArgReader ar(argc, argv);
206    ar.checkArgument("mode", &mode, false);
207    ar.checkArgument("data", &tempDataPath, false);
208    ar.checkArgument("ip",   &serverIp_, false);
209    ar.checkArgument("port", &serverPort_, false);
210    if(ar.errorHandling())
211    {
212      COUT(1) << "Error while parsing command line arguments" << std::endl;
213      COUT(1) << ar.getErrorString();
214      COUT(0) << "Usage:" << std::endl << "orxonox [mode client|server|dedicated|standalone] "
215        << "[--data PATH] [--ip IP] [--port PORT]" << std::endl;
216      return false;
217    }
218
219    if (mode == "client")
220      mode_ = CLIENT;
221    else if (mode == "server")
222      mode_ = SERVER;
223    else if (mode == "dedicated")
224      mode_ = DEDICATED;
225    else
226    {
227      if (mode == "")
228        mode = "standalone";
229      if (mode != "standalone")
230      {
231        COUT(2) << "Warning: mode \"" << mode << "\" doesn't exist. "
232          << "Defaulting to standalone" << std::endl;
233        mode = "standalone";
234      }
235      mode_ = STANDALONE;
236    }
237    COUT(3) << "Orxonox: Mode is " << mode << "." << std::endl;
238
239    if (tempDataPath != "")
240    {
241      if (tempDataPath[tempDataPath.size() - 1] != '/')
242        tempDataPath += "/";
243      Settings::tsetDataPath(tempDataPath);
244    }
245
246    // initialise TCL
247    TclBind::getInstance().setDataPath(Settings::getDataPath());
248
249    //if (mode_ == DEDICATED)
250      // TODO: decide what to do here
251    //else
252
253    // for playable server, client and standalone, the startup
254    // procedure until the GUI is identical
255
256    ogre_ = &GraphicsEngine::getSingleton();
257    if (!ogre_->setup())       // creates ogre root and other essentials
258      return false;
259
260    return true;
261  }
262
263  /**
264   * start modules
265   */
266  bool Orxonox::start()
267  {
268    if (mode_ == DEDICATED)
269    {
270      // do something else
271    }
272    else
273    { // not dedicated server
274      if (!ogre_->loadRenderer())    // creates the render window
275        return false;
276
277      // TODO: Spread this so that this call only initialises things needed for the Console
278      if (!ogre_->initialiseResources())
279        return false;
280
281      // Load the InGameConsole
282      InGameConsole::getInstance().initialise();
283
284      // Calls the InputManager which sets up the input devices.
285      // The render window width and height are used to set up the mouse movement.
286      if (!InputManager::initialise(ogre_->getWindowHandle(),
287            ogre_->getWindowWidth(), ogre_->getWindowHeight(), true, true, true))
288        return false;
289
290      // TOOD: load the GUI here
291      // set InputManager to GUI mode
292      InputManager::setInputState(InputManager::IS_GUI);
293      // TODO: run GUI here
294
295      // The following lines depend very much on the GUI output, so they're probably misplaced here..
296
297      InputManager::setInputState(InputManager::IS_NONE);
298
299      // create Ogre SceneManager
300      ogre_->createNewScene();
301
302      if (!loadPlayground())
303        return false;
304    }
305
306    switch (mode_)
307    {
308    case SERVER:
309      if (!serverLoad())
310        return false;
311      break;
312    case CLIENT:
313      if (!clientLoad())
314        return false;
315      break;
316    case DEDICATED:
317      if (!serverLoad())
318        return false;
319      break;
320    default:
321      if (!standaloneLoad())
322        return false;
323    }
324
325    InputManager::setInputState(InputManager::IS_NORMAL);
326
327    return startRenderLoop();
328  }
329
330  /**
331   * Loads everything in the scene except for the actual objects.
332   * This includes HUD, audio..
333   */
334  bool Orxonox::loadPlayground()
335  {
336    // Init audio
337    //auMan_ = new audio::AudioManager();
338    //auMan_->ambientAdd("a1");
339    //auMan_->ambientAdd("a2");
340    //auMan_->ambientAdd("a3");
341    //auMan->ambientAdd("ambient1");
342    //auMan_->ambientStart();
343
344    // Load the HUD
345    COUT(3) << "Orxonox: Loading HUD" << std::endl;
346    hud_ = new Level(Settings::getDataPath() + "overlay/hud.oxo");
347    Loader::load(hud_);
348
349    // Start the Radar
350    this->radar_ = new Radar();
351
352    return true;
353  }
354
355  /**
356   * Level loading method for server mode.
357   */
358  bool Orxonox::serverLoad()
359  {
360    COUT(0) << "Loading level in server mode" << std::endl;
361
362    //server_g = new network::Server(serverPort_);
363    server_g = network::Server::createSingleton(serverPort_);
364
365    if (!loadScene())
366      return false;
367
368    server_g->open();
369
370    return true;
371  }
372
373  /**
374   * Level loading method for client mode.
375   */
376  bool Orxonox::clientLoad()
377  {
378    COUT(0) << "Loading level in client mode" << std::endl;\
379
380    if (serverIp_.compare("") == 0)
381      client_g = network::Client::createSingleton();
382    else
383
384      client_g = network::Client::createSingleton(serverIp_, serverPort_);
385
386    if(!client_g->establishConnection())
387      return false;
388    client_g->tick(0);
389
390    return true;
391  }
392
393  /**
394   * Level loading method for standalone mode.
395   */
396  bool Orxonox::standaloneLoad()
397  {
398    COUT(0) << "Loading level in standalone mode" << std::endl;
399
400    if (!loadScene())
401      return false;
402
403    return true;
404  }
405
406  /**
407   * Helper method to load a level.
408   */
409  bool Orxonox::loadScene()
410  {
411    startLevel_ = new Level("levels/sample.oxw");
412    Loader::open(startLevel_);
413
414    return true;
415  }
416
417
418  /**
419    Main loop of the orxonox game.
420    About the loop: The design is almost exactly like the one in ogre, so that
421    if any part of ogre registers a framelisteners, it will still behave
422    correctly. Furthermore the time smoothing feature from ogre has been
423    implemented too. If turned on (see orxonox constructor), it will calculate
424    the dt_n by means of the recent most dt_n-1, dt_n-2, etc.
425  */
426  bool Orxonox::startRenderLoop()
427  {
428    // first check whether ogre root object has been created
429    if (Ogre::Root::getSingletonPtr() == 0)
430    {
431      COUT(2) << "Orxonox Error: Could not start rendering. No Ogre root object found" << std::endl;
432      return false;
433    }
434    Ogre::Root& ogreRoot = Ogre::Root::getSingleton();
435
436
437    // use the ogre timer class to measure time.
438    if (!timer_)
439      timer_ = new Ogre::Timer();
440
441    unsigned long frameCount = 0;
442   
443    const unsigned long refreshTime = 3000000;
444    unsigned long refreshStartTime = 0;
445    unsigned long tickTime = 0;
446    unsigned long oldFrameCount = 0;
447
448    unsigned long timeBeforeTick = 0;
449    unsigned long timeBeforeTickOld = 0;
450    unsigned long timeAfterTick = 0;
451
452    COUT(3) << "Orxonox: Starting the main loop." << std::endl;
453
454    timer_->reset();
455    while (!bAbort_)
456    {
457      // get current time
458      timeBeforeTickOld = timeBeforeTick;
459      timeBeforeTick    = timer_->getMicroseconds();
460      float dt = (timeBeforeTick - timeBeforeTickOld) / 1000000.0;
461
462
463      // tick the core (needs real time for input and tcl thread management)
464      Core::tick(dt);
465
466      // Call those objects that need the real time
467      for (Iterator<TickableReal> it = ObjectList<TickableReal>::start(); it; ++it)
468        it->tick(dt);
469      // Call the scene objects
470      for (Iterator<Tickable> it = ObjectList<Tickable>::start(); it; ++it)
471        it->tick(dt * this->timefactor_);
472
473      // call server/client with normal dt
474      if (client_g)
475        client_g->tick(dt * this->timefactor_);
476      if (server_g)
477        server_g->tick(dt * this->timefactor_);
478
479
480      // get current time once again
481      timeAfterTick = timer_->getMicroseconds();
482
483      tickTime += timeAfterTick - timeBeforeTick;
484      if (timeAfterTick > refreshStartTime + refreshTime)
485      {
486        GraphicsEngine::getSingleton().setAverageTickTime(
487            (float)tickTime * 0.001 / (frameCount - oldFrameCount));
488        GraphicsEngine::getSingleton().setAverageFramesPerSecond(
489            (float)(frameCount - oldFrameCount) / (timeAfterTick - refreshStartTime) * 1000000.0);
490        oldFrameCount = frameCount;
491        tickTime = 0;
492        refreshStartTime = timeAfterTick;
493      }
494
495
496      // don't forget to call _fireFrameStarted in ogre to make sure
497      // everything goes smoothly
498      Ogre::FrameEvent evt;
499      evt.timeSinceLastFrame = dt;
500      evt.timeSinceLastEvent = dt; // note: same time, but shouldn't matter anyway
501      ogreRoot._fireFrameStarted(evt);
502
503      if (mode_ != DEDICATED)
504      {
505        // Pump messages in all registered RenderWindows
506        // This calls the WindowEventListener objects.
507        Ogre::WindowEventUtilities::messagePump();
508        // make sure the window stays active even when not focused
509        // (probably only necessary on windows)
510        GraphicsEngine::getSingleton().setWindowActivity(true);
511
512        // render
513        ogreRoot._updateAllRenderTargets();
514      }
515
516      // again, just to be sure ogre works fine
517      ogreRoot._fireFrameEnded(evt); // note: uses the same time as _fireFrameStarted
518
519      ++frameCount;
520    }
521
522    if (mode_ == CLIENT)
523      network::Client::getSingleton()->closeConnection();
524    else if (mode_ == SERVER)
525      server_g->close();
526
527    return true;
528  }
529}
Note: See TracBrowser for help on using the repository browser.