Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/orxonox/Orxonox.cc @ 1602

Last change on this file since 1602 was 1577, checked in by rgrieder, 16 years ago
  • cleaned up InGameConsole a little bit
  • adjusted noise (has a config value noiseSize_)
  • replaced panel cursor with text area cursor
  • initialise()/destroy() concept like in other Singletons
  • therefore console is initialised and destroyed via Orxonox class (earlier message colouring)
  • replace linear console scroll with exponential one (no, it doesn't take infinite time to reach 0 ;))

UPDATE YOUR MEDIA REPOSITORY!

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