Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 1601 was 1601, checked in by rgrieder, 16 years ago
  • new folder structure for overlays:

orxonox/


overlays/

console/
hud/

  • Navigation —> HUDNavigation
  • HUD —> OverlayGroup (not yet begun with that except renaming and getHUD() instead of getSingleton())
  • moved HUD.oxo (file ending should stand for orxonox overlay) to media repository in overlay folder
  • fixed a bug with console noiseSize_ config value (was inverted..)
  • Property svn:eol-style set to native
File size: 14.4 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    //auMan_(0),
100    timer_(0),
101    // turn on frame smoothing by setting a value different from 0
102    frameSmoothingTime_(0.0f),
103    //orxonoxHUD_(0),
104    bAbort_(false),
105    timefactor_(1.0f),
106    mode_(STANDALONE),
107    serverIp_(""),
108    serverPort_(NETWORK_PORT)
109  {
110  }
111
112  /**
113   * Destruct Orxonox.
114   */
115  Orxonox::~Orxonox()
116  {
117    // keep in mind: the order of deletion is very important!
118    /*if (this->orxonoxHUD_)
119      delete this->orxonoxHUD_;*/
120    Loader::close();
121    InputManager::destroy();
122    //if (this->auMan_)
123    //  delete this->auMan_;
124    InGameConsole::getInstance().destroy();
125    if (this->timer_)
126      delete this->timer_;
127    GraphicsEngine::getSingleton().destroy();
128
129    if (network::Client::getSingleton())
130      network::Client::destroySingleton();
131    if (server_g)
132      delete network::Server::getSingleton();
133  }
134
135
136  /**
137    Asks the mainloop nicely to abort.
138  */
139  void Orxonox::abortRequest()
140  {
141    COUT(3) << "Orxonox: Abort requested." << std::endl;
142    bAbort_ = true;
143  }
144
145  /**
146   * @return singleton reference
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  /**
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
173    for (Iterator<ParticleInterface> it = ObjectList<ParticleInterface>::begin(); it; ++it)
174        it->setSpeedFactor(it->getSpeedFactor() * change);
175  }
176
177  /**
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   */
183  bool Orxonox::init(int argc, char **argv)
184  {
185#ifdef _DEBUG
186    ConfigFileManager::getSingleton()->setFile(CFT_Settings, "orxonox_d.ini");
187#else
188    ConfigFileManager::getSingleton()->setFile(CFT_Settings, "orxonox.ini");
189#endif
190    Factory::createClassHierarchy();
191
192    std::string mode;
193    std::string tempDataPath;
194
195    ArgReader ar(argc, argv);
196    ar.checkArgument("mode", &mode, false);
197    ar.checkArgument("data", &tempDataPath, false);
198    ar.checkArgument("ip",   &serverIp_, false);
199    ar.checkArgument("port", &serverPort_, false);
200    if(ar.errorHandling())
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;
206      return false;
207    }
208
209    if (mode == "client")
210      mode_ = CLIENT;
211    else if (mode == "server")
212      mode_ = SERVER;
213    else if (mode == "dedicated")
214      mode_ = DEDICATED;
215    else
216    {
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      }
225      mode_ = STANDALONE;
226    }
227    COUT(3) << "Orxonox: Mode is " << mode << "." << std::endl;
228
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
239    //if (mode_ == DEDICATED)
240      // TODO: decide what to do here
241    //else
242
243    // for playable server, client and standalone, the startup
244    // procedure until the GUI is identical
245
246    ogre_ = &GraphicsEngine::getSingleton();
247    if (!ogre_->setup())       // creates ogre root and other essentials
248      return false;
249
250    return true;
251  }
252
253  /**
254   * start modules
255   */
256  bool Orxonox::start()
257  {
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;
266
267      // TODO: Spread this so that this call only initialises things needed for the Console
268      if (!ogre_->initialiseResources())
269        return false;
270
271      // Load the InGameConsole
272      InGameConsole::getInstance().initialise();
273
274      // Calls the InputManager which sets up the input devices.
275      // The render window width and height are used to set up the mouse movement.
276      if (!InputManager::initialise(ogre_->getWindowHandle(),
277            ogre_->getWindowWidth(), ogre_->getWindowHeight(), true, true, true))
278        return false;
279
280      // TOOD: load the GUI here
281      // set InputManager to GUI mode
282      InputManager::setInputState(InputManager::IS_GUI);
283      // TODO: run GUI here
284
285      // The following lines depend very much on the GUI output, so they're probably misplaced here..
286
287      InputManager::setInputState(InputManager::IS_NONE);
288
289      // create Ogre SceneManager
290      ogre_->createNewScene();
291
292      if (!loadPlayground())
293        return false;
294    }
295
296    switch (mode_)
297    {
298    case SERVER:
299      if (!serverLoad())
300        return false;
301      break;
302    case CLIENT:
303      if (!clientLoad())
304        return false;
305      break;
306    case DEDICATED:
307      if (!serverLoad())
308        return false;
309      break;
310    default:
311      if (!standaloneLoad())
312        return false;
313    }
314
315    InputManager::setInputState(InputManager::IS_NORMAL);
316
317    return startRenderLoop();
318  }
319
320  /**
321   * Loads everything in the scene except for the actual objects.
322   * This includes HUD, audio..
323   */
324  bool Orxonox::loadPlayground()
325  {
326    // Init audio
327    //auMan_ = new audio::AudioManager();
328    //auMan_->ambientAdd("a1");
329    //auMan_->ambientAdd("a2");
330    //auMan_->ambientAdd("a3");
331    //auMan->ambientAdd("ambient1");
332    //auMan_->ambientStart();
333
334    // Load the HUD
335    COUT(3) << "Orxonox: Loading HUD" << std::endl;
336
337    Level* hud = new Level(Settings::getDataPath() + "overlay/hud.oxo");
338    Loader::load(hud);
339
340    return true;
341  }
342
343  /**
344   * Level loading method for server mode.
345   */
346  bool Orxonox::serverLoad()
347  {
348    COUT(0) << "Loading level in server mode" << std::endl;
349
350    //server_g = new network::Server(serverPort_);
351    server_g = network::Server::createSingleton(serverPort_);
352
353    if (!loadScene())
354      return false;
355
356    server_g->open();
357
358    return true;
359  }
360
361  /**
362   * Level loading method for client mode.
363   */
364  bool Orxonox::clientLoad()
365  {
366    COUT(0) << "Loading level in client mode" << std::endl;\
367
368    if (serverIp_.compare("") == 0)
369      client_g = network::Client::createSingleton();
370    else
371
372      client_g = network::Client::createSingleton(serverIp_, serverPort_);
373
374    if(!client_g->establishConnection())
375      return false;
376    client_g->tick(0);
377
378    return true;
379  }
380
381  /**
382   * Level loading method for standalone mode.
383   */
384  bool Orxonox::standaloneLoad()
385  {
386    COUT(0) << "Loading level in standalone mode" << std::endl;
387
388    if (!loadScene())
389      return false;
390
391    return true;
392  }
393
394  /**
395   * Helper method to load a level.
396   */
397  bool Orxonox::loadScene()
398  {
399    Level* startlevel = new Level("levels/sample.oxw");
400    Loader::open(startlevel);
401
402    return true;
403  }
404
405
406  /**
407    Main loop of the orxonox game.
408    About the loop: The design is almost exactly like the one in ogre, so that
409    if any part of ogre registers a framelisteners, it will still behave
410    correctly. Furthermore the time smoothing feature from ogre has been
411    implemented too. If turned on (see orxonox constructor), it will calculate
412    the dt_n by means of the recent most dt_n-1, dt_n-2, etc.
413  */
414  bool Orxonox::startRenderLoop()
415  {
416    // first check whether ogre root object has been created
417    if (Ogre::Root::getSingletonPtr() == 0)
418    {
419      COUT(2) << "Orxonox Error: Could not start rendering. No Ogre root object found" << std::endl;
420      return false;
421    }
422    Ogre::Root& ogreRoot = Ogre::Root::getSingleton();
423
424
425    // Contains the times of recently fired events
426    // eventTimes[4] is the list for the times required for the fps counter
427    std::deque<unsigned long> eventTimes[3];
428    // Clear event times
429    for (int i = 0; i < 3; ++i)
430      eventTimes[i].clear();
431
432    // use the ogre timer class to measure time.
433    if (!timer_)
434      timer_ = new Ogre::Timer();
435    timer_->reset();
436
437    float renderTime = 0.0f;
438    float frameTime = 0.0f;
439//    clock_t time = 0;
440
441    COUT(3) << "Orxonox: Starting the main loop." << std::endl;
442    while (!bAbort_)
443    {
444      // get current time
445      unsigned long now = timer_->getMilliseconds();
446
447      // create an event to pass to the frameStarted method in ogre
448      Ogre::FrameEvent evt;
449      evt.timeSinceLastEvent = calculateEventTime(now, eventTimes[0]);
450      evt.timeSinceLastFrame = calculateEventTime(now, eventTimes[1]);
451      frameTime += evt.timeSinceLastFrame;
452
453      // OverlayGroup::getHUD().setTime(now);
454      if (mode_ != DEDICATED && frameTime > 0.4f)
455      {
456        GraphicsEngine::getSingleton().setAverageRTR(renderTime / frameTime);
457        frameTime = 0.0f;
458        renderTime = 0.0f;
459      }
460
461      // tick the core
462      Core::tick((float)evt.timeSinceLastFrame);
463      // Call those objects that need the real time
464      for (Iterator<TickableReal> it = ObjectList<TickableReal>::start(); it; ++it)
465        it->tick((float)evt.timeSinceLastFrame);
466      // Call the scene objects
467      for (Iterator<Tickable> it = ObjectList<Tickable>::start(); it; ++it)
468        it->tick((float)evt.timeSinceLastFrame * this->timefactor_);
469      //AudioManager::tick();
470      if (client_g)
471        client_g->tick((float)evt.timeSinceLastFrame);
472      if (server_g)
473        server_g->tick((float)evt.timeSinceLastFrame);
474
475      // don't forget to call _fireFrameStarted in ogre to make sure
476      // everything goes smoothly
477      ogreRoot._fireFrameStarted(evt);
478
479      // get current time
480      now = timer_->getMilliseconds();
481      calculateEventTime(now, eventTimes[2]);
482
483      if (mode_ != DEDICATED)
484      {
485        // Pump messages in all registered RenderWindows
486        // This calls the WindowEventListener objects.
487        Ogre::WindowEventUtilities::messagePump();
488
489        // render
490        ogreRoot._updateAllRenderTargets();
491      }
492
493      // get current time
494      now = timer_->getMilliseconds();
495
496      // create an event to pass to the frameEnded method in ogre
497      evt.timeSinceLastEvent = calculateEventTime(now, eventTimes[0]);
498      renderTime += calculateEventTime(now, eventTimes[2]);
499
500      // again, just to be sure ogre works fine
501      ogreRoot._fireFrameEnded(evt);
502      //msleep(500);
503    }
504
505    if (mode_ == CLIENT)
506      network::Client::getSingleton()->closeConnection();
507    else if (mode_ == SERVER)
508      server_g->close();
509
510    return true;
511  }
512
513  /**
514    Method for calculating the average time between recently fired events.
515    Code directly taken from OgreRoot.cc
516    @param now The current time in ms.
517    @param type The type of event to be considered.
518  */
519  float Orxonox::calculateEventTime(unsigned long now, std::deque<unsigned long> &times)
520  {
521    // Calculate the average time passed between events of the given type
522    // during the last frameSmoothingTime_ seconds.
523
524    times.push_back(now);
525
526    if(times.size() == 1)
527      return 0;
528
529    // Times up to frameSmoothingTime_ seconds old should be kept
530    unsigned long discardThreshold = (unsigned long)(frameSmoothingTime_ * 1000.0f);
531
532    // Find the oldest time to keep
533    std::deque<unsigned long>::iterator it  = times.begin();
534    // We need at least two times
535    std::deque<unsigned long>::iterator end = times.end() - 2;
536
537    while(it != end)
538    {
539      if (now - *it > discardThreshold)
540        ++it;
541      else
542        break;
543    }
544
545    // Remove old times
546    times.erase(times.begin(), it);
547
548    return (float)(times.back() - times.front()) / ((times.size() - 1) * 1000);
549  }
550}
Note: See TracBrowser for help on using the repository browser.