Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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