Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 1455 was 1454, checked in by landauf, 16 years ago

fixed tcl initialisation bug

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