Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 1535 was 1535, checked in by rgrieder, 16 years ago

merged input branch back to trunk

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