Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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