Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

merged core3 back to trunk

  • Property svn:eol-style set to native
File size: 13.8 KB
Line 
1/*
2 *   ORXONOX - the hottest 3D action shooter ever to exist
3 *                    > www.orxonox.net <
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 *      Reto Grieder
24 *   Co-authors:
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"
36#include "Orxonox.h"
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
54// core
55#include "core/ConfigFileManager.h"
56#include "core/Iterator.h"
57#include "core/ConsoleCommand.h"
58#include "core/Loader.h"
59#include "core/input/InputManager.h"
60#include "core/TclBind.h"
61#include "core/Core.h"
62#include "util/Debug.h"
63
64// audio
65#include "audio/AudioManager.h"
66
67// network
68#include "network/Server.h"
69#include "network/Client.h"
70
71// objects and tools
72#include "overlays/OverlayGroup.h"
73#include "overlays/console/InGameConsole.h"
74#include "objects/Tickable.h"
75#include "objects/Backlight.h"
76#include "tools/ParticleInterface.h"
77
78#include "GraphicsEngine.h"
79#include "Settings.h"
80#include "Radar.h"
81
82// globals for the server or client
83static network::Client *client_g = 0;
84static network::Server *server_g = 0;
85
86namespace orxonox
87{
88  SetConsoleCommandShortcut(Orxonox, exit).keybindMode(KeybindMode::OnPress);
89  SetConsoleCommandShortcut(Orxonox, slomo).accessLevel(AccessLevel::Offline).defaultValue(0, 1.0).axisParamIndex(0).isAxisRelative(false);
90  SetConsoleCommandShortcut(Orxonox, setTimeFactor).accessLevel(AccessLevel::Offline).defaultValue(0, 1.0);
91
92  /**
93    @brief Reference to the only instance of the class.
94  */
95  Orxonox *Orxonox::singletonRef_s = 0;
96
97  /**
98   * Create a new instance of Orxonox. Avoid doing any actual work here.
99   */
100  Orxonox::Orxonox()
101    : ogre_(0)
102    , startLevel_(0)
103    , hud_(0)
104    , radar_(0)
105    //, auMan_(0)
106    , timer_(0)
107    , bAbort_(false)
108    , timefactor_(1.0f)
109    , mode_(STANDALONE)
110    , serverIp_("")
111    , serverPort_(NETWORK_PORT)
112  {
113  }
114
115  /**
116   * Destruct Orxonox.
117   */
118  Orxonox::~Orxonox()
119  {
120    // keep in mind: the order of deletion is very important!
121    Loader::unload(startLevel_);
122    if (this->startLevel_)
123      delete this->startLevel_;
124
125    Loader::unload(hud_);
126    if (this->hud_)
127      delete this->hud_;
128
129    if (this->radar_)
130      delete this->radar_;
131
132    Loader::close();
133    //if (this->auMan_)
134    //  delete this->auMan_;
135    InGameConsole::getInstance().destroy();
136    if (this->timer_)
137      delete this->timer_;
138    InputManager::destroy();
139    GraphicsEngine::getSingleton().destroy();
140
141    if (client_g)
142      delete client_g;
143    if (server_g)
144      delete server_g;
145  }
146
147
148  /**
149    Asks the mainloop nicely to abort.
150  */
151  void Orxonox::abortRequest()
152  {
153    COUT(3) << "Orxonox: Abort requested." << std::endl;
154    bAbort_ = true;
155  }
156
157  /**
158   * @return singleton reference
159   */
160  Orxonox* Orxonox::getSingleton()
161  {
162    if (!singletonRef_s)
163      singletonRef_s = new Orxonox();
164    return singletonRef_s;
165  }
166
167  /**
168    @brief Destroys the Orxonox singleton.
169  */
170  void Orxonox::destroySingleton()
171  {
172    if (singletonRef_s)
173      delete singletonRef_s;
174    singletonRef_s = 0;
175  }
176
177  /**
178    @brief Changes the speed of Orxonox
179  */
180  void Orxonox::setTimeFactor(float factor)
181  {
182    float change = factor / Orxonox::getSingleton()->getTimeFactor();
183    Orxonox::getSingleton()->timefactor_ = factor;
184    for (ObjectList<ParticleInterface>::iterator it = ObjectList<ParticleInterface>::begin(); it; ++it)
185        it->setSpeedFactor(it->getSpeedFactor() * change);
186
187    for (Iterator<Backlight> it = ObjectList<Backlight>::begin(); it; ++it)
188        it->setTimeFactor(Orxonox::getSingleton()->getTimeFactor());
189  }
190
191  /**
192   * initialization of Orxonox object
193   * @param argc argument counter
194   * @param argv list of argumenst
195   * @param path path to config (in home dir or something)
196   */
197  bool Orxonox::init(int argc, char **argv)
198  {
199#ifdef _DEBUG
200    ConfigFileManager::getInstance()->setFile(CFT_Settings, "orxonox_d.ini");
201#else
202    ConfigFileManager::getInstance()->setFile(CFT_Settings, "orxonox.ini");
203#endif
204    Factory::createClassHierarchy();
205
206    std::string mode;
207    std::string tempDataPath;
208
209    ArgReader ar(argc, argv);
210    ar.checkArgument("mode", &mode, false);
211    ar.checkArgument("data", &tempDataPath, false);
212    ar.checkArgument("ip",   &serverIp_, false);
213    ar.checkArgument("port", &serverPort_, false);
214    if(ar.errorHandling())
215    {
216      COUT(1) << "Error while parsing command line arguments" << std::endl;
217      COUT(1) << ar.getErrorString();
218      COUT(0) << "Usage:" << std::endl << "orxonox [mode client|server|dedicated|standalone] "
219        << "[--data PATH] [--ip IP] [--port PORT]" << std::endl;
220      return false;
221    }
222
223    if (mode == "client")
224      mode_ = CLIENT;
225    else if (mode == "server")
226      mode_ = SERVER;
227    else if (mode == "dedicated")
228      mode_ = DEDICATED;
229    else
230    {
231      if (mode == "")
232        mode = "standalone";
233      if (mode != "standalone")
234      {
235        COUT(2) << "Warning: mode \"" << mode << "\" doesn't exist. "
236          << "Defaulting to standalone" << std::endl;
237        mode = "standalone";
238      }
239      mode_ = STANDALONE;
240    }
241    COUT(3) << "Orxonox: Mode is " << mode << "." << std::endl;
242
243    if (tempDataPath != "")
244    {
245      if (tempDataPath[tempDataPath.size() - 1] != '/')
246        tempDataPath += "/";
247      Settings::tsetDataPath(tempDataPath);
248    }
249
250    // initialise TCL
251    TclBind::getInstance().setDataPath(Settings::getDataPath());
252
253    //if (mode_ == DEDICATED)
254      // TODO: decide what to do here
255    //else
256
257    // for playable server, client and standalone, the startup
258    // procedure until the GUI is identical
259
260    ogre_ = &GraphicsEngine::getSingleton();
261    if (!ogre_->setup())       // creates ogre root and other essentials
262      return false;
263
264    return true;
265  }
266
267  /**
268   * start modules
269   */
270  bool Orxonox::start()
271  {
272    if (mode_ == DEDICATED)
273    {
274      // do something else
275    }
276    else
277    { // not dedicated server
278      if (!ogre_->loadRenderer())    // creates the render window
279        return false;
280
281      // TODO: Spread this so that this call only initialises things needed for the Console
282      if (!ogre_->initialiseResources())
283        return false;
284
285      // Load the InGameConsole
286      InGameConsole::getInstance().initialise();
287
288      // Calls the InputManager which sets up the input devices.
289      // The render window width and height are used to set up the mouse movement.
290      if (!InputManager::initialise(ogre_->getWindowHandle(),
291            ogre_->getWindowWidth(), ogre_->getWindowHeight(), true, true, true))
292        return false;
293
294      // TOOD: load the GUI here
295      // set InputManager to GUI mode
296      InputManager::setInputState(InputManager::IS_GUI);
297      // TODO: run GUI here
298
299      // The following lines depend very much on the GUI output, so they're probably misplaced here..
300
301      InputManager::setInputState(InputManager::IS_NONE);
302
303      // create Ogre SceneManager
304      ogre_->createNewScene();
305
306      if (!loadPlayground())
307        return false;
308    }
309
310    switch (mode_)
311    {
312    case SERVER:
313      if (!serverLoad())
314        return false;
315      break;
316    case CLIENT:
317      if (!clientLoad())
318        return false;
319      break;
320    case DEDICATED:
321      if (!serverLoad())
322        return false;
323      break;
324    default:
325      if (!standaloneLoad())
326        return false;
327    }
328
329    InputManager::setInputState(InputManager::IS_NORMAL);
330
331    return startRenderLoop();
332  }
333
334  /**
335   * Loads everything in the scene except for the actual objects.
336   * This includes HUD, audio..
337   */
338  bool Orxonox::loadPlayground()
339  {
340    // Init audio
341    //auMan_ = new audio::AudioManager();
342    //auMan_->ambientAdd("a1");
343    //auMan_->ambientAdd("a2");
344    //auMan_->ambientAdd("a3");
345    //auMan->ambientAdd("ambient1");
346    //auMan_->ambientStart();
347
348    // Load the HUD
349    COUT(3) << "Orxonox: Loading HUD" << std::endl;
350    hud_ = new Level(Settings::getDataPath() + "overlay/hud.oxo");
351    Loader::load(hud_);
352
353    // Start the Radar
354    this->radar_ = new Radar();
355
356    return true;
357  }
358
359  /**
360   * Level loading method for server mode.
361   */
362  bool Orxonox::serverLoad()
363  {
364    COUT(0) << "Loading level in server mode" << std::endl;
365
366    server_g = new network::Server(serverPort_);
367    COUT(0) << "Loading scene in server mode" << std::endl;
368    if (!loadScene())
369      return false;
370
371    server_g->open();
372
373    return true;
374  }
375
376  /**
377   * Level loading method for client mode.
378   */
379  bool Orxonox::clientLoad()
380  {
381    COUT(0) << "Loading level in client mode" << std::endl;\
382
383    if (serverIp_.compare("") == 0)
384      client_g = new network::Client();
385    else
386
387      client_g = new network::Client(serverIp_, serverPort_);
388
389    if(!client_g->establishConnection())
390      return false;
391    client_g->tick(0);
392
393    return true;
394  }
395
396  /**
397   * Level loading method for standalone mode.
398   */
399  bool Orxonox::standaloneLoad()
400  {
401    COUT(0) << "Loading level in standalone mode" << std::endl;
402
403    if (!loadScene())
404      return false;
405
406    return true;
407  }
408
409  /**
410   * Helper method to load a level.
411   */
412  bool Orxonox::loadScene()
413  {
414    startLevel_ = new Level("levels/sample.oxw");
415    Loader::open(startLevel_);
416
417    return true;
418  }
419
420
421  /**
422    Main loop of the orxonox game.
423    About the loop: The design is almost exactly like the one in ogre, so that
424    if any part of ogre registers a framelisteners, it will still behave
425    correctly. Furthermore the time smoothing feature from ogre has been
426    implemented too. If turned on (see orxonox constructor), it will calculate
427    the dt_n by means of the recent most dt_n-1, dt_n-2, etc.
428  */
429  bool Orxonox::startRenderLoop()
430  {
431    // first check whether ogre root object has been created
432    if (Ogre::Root::getSingletonPtr() == 0)
433    {
434      COUT(2) << "Orxonox Error: Could not start rendering. No Ogre root object found" << std::endl;
435      return false;
436    }
437    Ogre::Root& ogreRoot = Ogre::Root::getSingleton();
438
439
440    // use the ogre timer class to measure time.
441    if (!timer_)
442      timer_ = new Ogre::Timer();
443
444    unsigned long frameCount = 0;
445
446    // TODO: this would very well fit into a configValue
447    const unsigned long refreshTime = 200000;
448    unsigned long refreshStartTime = 0;
449    unsigned long tickTime = 0;
450    unsigned long oldFrameCount = 0;
451
452    unsigned long timeBeforeTick = 0;
453    unsigned long timeBeforeTickOld = 0;
454    unsigned long timeAfterTick = 0;
455
456    COUT(3) << "Orxonox: Starting the main loop." << std::endl;
457
458    timer_->reset();
459    while (!bAbort_)
460    {
461      // get current time
462      timeBeforeTickOld = timeBeforeTick;
463      timeBeforeTick    = timer_->getMicroseconds();
464      float dt = (timeBeforeTick - timeBeforeTickOld) / 1000000.0;
465
466
467      // tick the core (needs real time for input and tcl thread management)
468      Core::tick(dt);
469
470      // Call those objects that need the real time
471      for (ObjectList<TickableReal>::iterator it = ObjectList<TickableReal>::begin(); it; ++it)
472        it->tick(dt);
473      // Call the scene objects
474      for (ObjectList<Tickable>::iterator it = ObjectList<Tickable>::begin(); it; ++it)
475        it->tick(dt * this->timefactor_);
476
477      // call server/client with normal dt
478      if (client_g)
479        client_g->tick(dt * this->timefactor_);
480      if (server_g)
481        server_g->tick(dt * this->timefactor_);
482
483
484      // get current time once again
485      timeAfterTick = timer_->getMicroseconds();
486
487      tickTime += timeAfterTick - timeBeforeTick;
488      if (timeAfterTick > refreshStartTime + refreshTime)
489      {
490        GraphicsEngine::getSingleton().setAverageTickTime(
491            (float)tickTime * 0.001 / (frameCount - oldFrameCount));
492        GraphicsEngine::getSingleton().setAverageFramesPerSecond(
493            (float)(frameCount - oldFrameCount) / (timeAfterTick - refreshStartTime) * 1000000.0);
494        oldFrameCount = frameCount;
495        tickTime = 0;
496        refreshStartTime = timeAfterTick;
497      }
498
499
500      // don't forget to call _fireFrameStarted in ogre to make sure
501      // everything goes smoothly
502      Ogre::FrameEvent evt;
503      evt.timeSinceLastFrame = dt;
504      evt.timeSinceLastEvent = dt; // note: same time, but shouldn't matter anyway
505      ogreRoot._fireFrameStarted(evt);
506
507      if (mode_ != DEDICATED)
508      {
509        // Pump messages in all registered RenderWindows
510        // This calls the WindowEventListener objects.
511        Ogre::WindowEventUtilities::messagePump();
512        // make sure the window stays active even when not focused
513        // (probably only necessary on windows)
514        GraphicsEngine::getSingleton().setWindowActivity(true);
515
516        // render
517        ogreRoot._updateAllRenderTargets();
518      }
519
520      // again, just to be sure ogre works fine
521      ogreRoot._fireFrameEnded(evt); // note: uses the same time as _fireFrameStarted
522
523      ++frameCount;
524    }
525
526    if (mode_ == CLIENT)
527      client_g->closeConnection();
528    else if (mode_ == SERVER)
529      server_g->close();
530
531    return true;
532  }
533}
Note: See TracBrowser for help on using the repository browser.