Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/hud3/src/orxonox/Orxonox.cc @ 1332

Last change on this file since 1332 was 1281, checked in by chaiy, 17 years ago

irgendwas sinnvolles

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