Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/network/src/orxonox/Orxonox.cc @ 1422

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