Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/gui/src/orxonox/Orxonox.cc @ 1665

Last change on this file since 1665 was 1663, checked in by rgrieder, 17 years ago

Added CommandLine class.
You can now call SetCommandLineArgument like SetConsoleCommand and hereby define a new command line argument. They are passed in main() and then they can be accessed by commandLine::getCommandLineArgument().

  • Property svn:eol-style set to native
File size: 14.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:
[1535]23 *      Reto Grieder
[1638]24 *      Benjamin Knecht <beni_at_orxonox.net>, (C) 2007
[1535]25 *   Co-authors:
[1638]26 *      ...
[1038]27 *
28 */
29
30/**
31 @file
32 @brief Orxonox Main Class
33 */
34
35// Precompiled Headers
36#include "OrxonoxStableHeaders.h"
[1039]37#include "Orxonox.h"
[1038]38
39//****** STD *******
40#include <deque>
[1638]41#include <cassert>
[1038]42
43//****** OGRE ******
44#include <OgreFrameListener.h>
45#include <OgreRoot.h>
46#include <OgreTimer.h>
47#include <OgreWindowEventUtilities.h>
48
49//***** ORXONOX ****
50// util
51//#include "util/Sleep.h"
52
[1293]53// core
[1054]54#include "core/ConfigFileManager.h"
[1638]55#include "core/ConfigValueIncludes.h"
[1293]56#include "core/ConsoleCommand.h"
[1038]57#include "core/Debug.h"
58#include "core/Loader.h"
[1638]59#include "core/Exception.h"
[1535]60#include "core/input/InputManager.h"
[1638]61#include "core/input/SimpleInputState.h"
62#include "core/input/KeyBinder.h"
[1293]63#include "core/TclBind.h"
[1535]64#include "core/Core.h"
[1038]65
66// audio
67#include "audio/AudioManager.h"
68
69// network
70#include "network/Server.h"
71#include "network/Client.h"
72
73// objects and tools
[1625]74#include "overlays/console/InGameConsole.h"
[1535]75#include "objects/Tickable.h"
[1608]76#include "objects/Backlight.h"
[1563]77#include "tools/ParticleInterface.h"
[1638]78#include "gui/GUIManager.h"
[1038]79
[1502]80#include "GraphicsEngine.h"
[1535]81#include "Settings.h"
[1625]82#include "Radar.h"
[1502]83
[1038]84// globals for the server or client
[1577]85static network::Client *client_g = 0;
86static network::Server *server_g = 0;
[1038]87
88namespace orxonox
[1293]89{
[1662]90  //SetConsoleCommand(Orxonox, exit, true).setKeybindMode(KeybindMode::OnPress);
91  //SetConsoleCommand(Orxonox, slomo, true).setAccessLevel(AccessLevel::Offline).setDefaultValue(0, 1.0).setAxisParamIndex(0).setIsAxisRelative(false);
92  //SetConsoleCommand(Orxonox, setTimeFactor, true).setAccessLevel(AccessLevel::Offline).setDefaultValue(0, 1.0);
93  //SetConsoleCommand(Orxonox, loadGame, true).setAccessLevel(AccessLevel::User).setDefaultValue(0, "standalone");
[1505]94
[1038]95  /**
96    @brief Reference to the only instance of the class.
97  */
98  Orxonox *Orxonox::singletonRef_s = 0;
99
100  /**
[1293]101   * Create a new instance of Orxonox. Avoid doing any actual work here.
[1038]102   */
[1625]103  Orxonox::Orxonox()
[1642]104    : startLevel_(0)
[1625]105    , hud_(0)
106    //, auMan_(0)
107    , timer_(0)
108    , bAbort_(false)
109    , timefactor_(1.0f)
[1638]110    , debugRefreshTime_(0.0f)
[1653]111    , graphicsEngine_(0)
[1642]112    , inputManager_(0)
113    , radar_(0)
[1646]114    , console_(0)
115    , guiManager_(0)
[1038]116  {
[1638]117    RegisterRootObject(Orxonox);
118
[1642]119    //assert(singletonRef_s == 0);
[1663]120    OrxAssert(singletonRef_s == 0, "Cannot instantiate Orxonox class twice");
[1638]121    singletonRef_s = this;
[1038]122  }
123
124  /**
[1293]125   * Destruct Orxonox.
[1038]126   */
127  Orxonox::~Orxonox()
128  {
129    // keep in mind: the order of deletion is very important!
[1642]130    if (this->timer_)
131      delete this->timer_;
132
133    Loader::unload(startLevel_);
[1625]134    if (this->startLevel_)
135      delete this->startLevel_;
136
137    Loader::unload(hud_);
138    if (this->hud_)
139      delete this->hud_;
140
141    if (this->radar_)
142      delete this->radar_;
143
[1646]144    if (this->guiManager_)
145      delete guiManager_;
146
[1293]147    //if (this->auMan_)
148    //  delete this->auMan_;
[1038]149
[1642]150    if (this->console_)
151      delete this->console_;
152
153    if (inputManager_)
154      delete inputManager_;
155
[1653]156    if (this->graphicsEngine_)
157      delete graphicsEngine_;
[1638]158
[1293]159    if (network::Client::getSingleton())
160      network::Client::destroySingleton();
[1038]161    if (server_g)
[1534]162      delete network::Server::getSingleton();
[1638]163
[1642]164    // this call will delete every BaseObject!
165    // But currently this will call methods of objects that exist no more
166    // The only 'memory leak' is the ParticleSpawer. They would be deleted here
167    // and call a sceneNode method that has already been destroy by the corresponding space ship.
168    //Loader::close();
169
[1638]170    singletonRef_s = 0;
[1038]171  }
172
[1638]173  void Orxonox::setConfigValues()
174  {
175    SetConfigValue(debugRefreshTime_, 0.2).description("Sets the time interval at which average fps, etc. get updated.");
176  }
[1038]177
[1638]178
[1038]179  /**
180    Asks the mainloop nicely to abort.
181  */
182  void Orxonox::abortRequest()
183  {
[1293]184    COUT(3) << "Orxonox: Abort requested." << std::endl;
[1038]185    bAbort_ = true;
186  }
187
188  /**
[1293]189   * @return singleton reference
[1038]190   */
[1654]191  Orxonox& Orxonox::getInstance()
[1038]192  {
[1638]193    assert(singletonRef_s);
194    return *singletonRef_s;
[1038]195  }
196
197  /**
[1563]198    @brief Changes the speed of Orxonox
199  */
200  void Orxonox::setTimeFactor(float factor)
201  {
[1654]202    float change = factor / Orxonox::getInstance().getTimeFactor();
203    Orxonox::getInstance().timefactor_ = factor;
[1563]204    for (Iterator<ParticleInterface> it = ObjectList<ParticleInterface>::begin(); it; ++it)
205        it->setSpeedFactor(it->getSpeedFactor() * change);
[1608]206
207    for (Iterator<Backlight> it = ObjectList<Backlight>::begin(); it; ++it)
[1654]208        it->setTimeFactor(Orxonox::getInstance().getTimeFactor());
[1563]209  }
210
[1653]211    /**
212    @brief
213        Starts the whole Game.
214    @param path
215        path to config (in home dir or something)
216    */
217    void Orxonox::start()
218    {
219#if ORXONOX_DEBUG_MODE == 1
220        ConfigFileManager::getSingleton()->setFile(CFT_Settings, "orxonox_d.ini");
[1535]221#else
[1653]222        ConfigFileManager::getSingleton()->setFile(CFT_Settings, "orxonox.ini");
[1535]223#endif
224
[1653]225        // creates the class hierarchy for all classes with factories
226        Factory::createClassHierarchy();
[1038]227
[1653]228        setConfigValues();
[1293]229
[1663]230        //const Settings::CommandLineArgument* dataPath = Settings::getCommandLineArgument("dataPath");
231        //assert(dataPath);
232        //if (!dataPath->bHasDefaultValue_)
233        //{
234        //    if (*dataPath->value_.getString().end() != '/' && *dataPath->value_.getString().end() != '\\')
235        //        Settings::tsetDataPath(dataPath->value_.getString() + "/");
236        //    else
237        //        Settings::tsetDataPath(dataPath->value_.getString());
238        //}
[1038]239
[1653]240        try
[1638]241        {
[1653]242            // initialise TCL
243            TclBind::getInstance().setDataPath(Settings::getDataPath());
[1052]244
[1653]245            graphicsEngine_ = new GraphicsEngine();
246            graphicsEngine_->setup();       // creates ogre root and other essentials
[1052]247
[1663]248            if (Settings::showsGraphics())
[1653]249            {
250                graphicsEngine_->loadRenderer();    // creates the render window
[1052]251
[1653]252                // TODO: Spread this so that this call only initialises things needed for the Console and GUI
253                graphicsEngine_->initialiseResources();
[1052]254
[1653]255                // Calls the InputManager which sets up the input devices.
256                // The render window width and height are used to set up the mouse movement.
257                inputManager_ = new InputManager();
258                inputManager_->initialise(graphicsEngine_->getWindowHandle(),
[1656]259                    graphicsEngine_->getWindowWidth(), graphicsEngine_->getWindowHeight(), true);
[1653]260                KeyBinder* keyBinder = new KeyBinder();
261                keyBinder->loadBindings();
262                inputManager_->createInputState<SimpleInputState>("game", 20)->setHandler(keyBinder);
[1638]263
[1653]264                // Load the InGameConsole
265                console_ = new InGameConsole();
266                console_->initialise();
[1638]267
[1653]268                // load the CEGUI interface
269                guiManager_ = new GUIManager();
270                guiManager_->initialise();
271            }
272            else
273            {
274                // TODO: Initialise a not yet written console that operates in the shell we
275                // started the game from.
276                // We probably want to use std::cin to catch input (OIS uses DirectX or X server)
277            }
[1638]278        }
[1653]279        catch (std::exception& ex)
[1638]280        {
[1653]281            COUT(1) << ex.what() << std::endl;
282            COUT(1) << "Loading sequence aborted." << std::endl;
283            return;
[1638]284        }
[1293]285
[1653]286        // here happens the game
287        startRenderLoop();
[1577]288
[1663]289        //if (mode_.mode == GameMode::Client)
290        //    network::Client::getSingleton()->closeConnection();
[1577]291
[1663]292        //if (mode_.hasServer)
293        //    server_g->close();
[1653]294    }
[1293]295
296  /**
297   * Loads everything in the scene except for the actual objects.
[1577]298   * This includes HUD, audio..
[1293]299   */
300  bool Orxonox::loadPlayground()
301  {
[1638]302    // Start the Radar
303    this->radar_ = new Radar();
[1052]304
[1293]305    // Load the HUD
[1625]306    COUT(3) << "Orxonox: Loading HUD" << std::endl;
307    hud_ = new Level(Settings::getDataPath() + "overlay/hud.oxo");
[1638]308    return Loader::load(hud_);
309  }
[1577]310
[1638]311  /**
312   * Helper method to load a level.
313   */
314  bool Orxonox::loadScene()
315  {
316    COUT(0) << "Loading level..." << std::endl;
317    startLevel_ = new Level(Settings::getDataPath() + "levels/sample.oxw");
318    return Loader::open(startLevel_);
[1293]319  }
[1052]320
[1293]321  /**
322   * Level loading method for server mode.
323   */
324  bool Orxonox::serverLoad()
325  {
[1577]326    COUT(0) << "Loading level in server mode" << std::endl;
[1052]327
[1663]328    //assert(Settings::getCommandLineArgument("port"));
329    //int serverPort = Settings::getCommandLineArgument("port")->value_;
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  {
[1577]346    COUT(0) << "Loading level in client mode" << std::endl;\
[1052]347
[1663]348    //assert(Settings::getCommandLineArgument("port"));
349    //assert(Settings::getCommandLineArgument("ip"));
350    //int serverPort = Settings::getCommandLineArgument("port")->value_;
351    //std::string serverIP = Settings::getCommandLineArgument("ip")->value_;
[1638]352
[1663]353    //if (serverIP.compare("") == 0)
354    //  client_g = network::Client::createSingleton();
355    //else
356    //  client_g = network::Client::createSingleton(serverIP, serverPort);
[1293]357
[1502]358    if(!client_g->establishConnection())
359      return false;
[1293]360    client_g->tick(0);
361
362    return true;
[1038]363  }
364
[1293]365  /**
366   * Level loading method for standalone mode.
367   */
368  bool Orxonox::standaloneLoad()
[1038]369  {
[1577]370    COUT(0) << "Loading level in standalone mode" << std::endl;
[1038]371
[1293]372    if (!loadScene())
373      return false;
[1038]374
[1293]375    return true;
[1038]376  }
377
378  /**
379    Main loop of the orxonox game.
[1638]380    We use the Ogre::Timer to measure time since it uses the most precise
381    method an a platform (however the windows timer lacks time when under
382    heavy kernel load!).
383    There is a simple mechanism to measure the average time spent in our
384    ticks as it may indicate performance issues.
385    A note about the Ogre::FrameListener: Even though we don't use them,
386    they still get called. However, the delta times are not correct (except
387    for timeSinceLastFrame, which is the most important). A little research
388    as shown that there is probably only one FrameListener that doesn't even
389    need the time. So we shouldn't run into problems.
[1038]390  */
[1638]391  void Orxonox::startRenderLoop()
[1038]392  {
[1120]393    Ogre::Root& ogreRoot = Ogre::Root::getSingleton();
[1038]394
395    // use the ogre timer class to measure time.
396    if (!timer_)
397      timer_ = new Ogre::Timer();
398
[1625]399    unsigned long frameCount = 0;
400   
[1645]401    const unsigned long refreshTime = (unsigned long)(debugRefreshTime_ * 1000000.0f);
[1625]402    unsigned long refreshStartTime = 0;
403    unsigned long tickTime = 0;
404    unsigned long oldFrameCount = 0;
[1502]405
[1625]406    unsigned long timeBeforeTick = 0;
407    unsigned long timeBeforeTickOld = 0;
408    unsigned long timeAfterTick = 0;
[1563]409
[1638]410    // TODO: Update time in seconds every 7 seconds to avoid any overflow (7 secs is very tight)
411
[1625]412    COUT(3) << "Orxonox: Starting the main loop." << std::endl;
[1502]413
[1638]414    try
415    {
[1625]416    timer_->reset();
[1502]417    while (!bAbort_)
418    {
[1638]419        // get current time
420        timeBeforeTickOld = timeBeforeTick;
421        timeBeforeTick    = timer_->getMicroseconds();
422        float dt = (timeBeforeTick - timeBeforeTickOld) / 1000000.0;
[1038]423
424
[1638]425        // tick the core (needs real time for input and tcl thread management)
426        Core::tick(dt);
[1625]427
[1638]428        // Call those objects that need the real time
429        for (Iterator<TickableReal> it = ObjectList<TickableReal>::start(); it; ++it)
430          it->tick(dt);
431        // Call the scene objects
432        for (Iterator<Tickable> it = ObjectList<Tickable>::start(); it; ++it)
433          it->tick(dt * this->timefactor_);
[1038]434
[1638]435        // call server/client with normal dt
436        if (client_g)
437          client_g->tick(dt * this->timefactor_);
438        if (server_g)
439          server_g->tick(dt * this->timefactor_);
[1625]440
441
[1638]442        // get current time once again
443        timeAfterTick = timer_->getMicroseconds();
[1625]444
[1638]445        tickTime += timeAfterTick - timeBeforeTick;
446        if (timeAfterTick > refreshStartTime + refreshTime)
447        {
[1653]448          GraphicsEngine::getInstance().setAverageTickTime(
[1638]449              (float)tickTime * 0.001 / (frameCount - oldFrameCount));
450          float avgFPS = (float)(frameCount - oldFrameCount) / (timeAfterTick - refreshStartTime) * 1000000.0;
[1653]451          GraphicsEngine::getInstance().setAverageFramesPerSecond(avgFPS);
[1625]452
[1638]453          oldFrameCount = frameCount;
454          tickTime = 0;
455          refreshStartTime = timeAfterTick;
456        }
[1502]457
[1638]458        // don't forget to call _fireFrameStarted in ogre to make sure
459        // everything goes smoothly
460        Ogre::FrameEvent evt;
461        evt.timeSinceLastFrame = dt;
462        evt.timeSinceLastEvent = dt; // note: same time, but shouldn't matter anyway
463        ogreRoot._fireFrameStarted(evt);
[1293]464
[1663]465        if (Settings::showsGraphics())
[1638]466        {
467          // Pump messages in all registered RenderWindows
468          // This calls the WindowEventListener objects.
469          Ogre::WindowEventUtilities::messagePump();
470          // make sure the window stays active even when not focused
471          // (probably only necessary on windows)
[1653]472          GraphicsEngine::getInstance().setWindowActivity(true);
[1502]473
[1638]474          // tick CEGUI
475          GUIManager::getInstance().tick(dt);
476
477          // render
478          ogreRoot._updateAllRenderTargets();
479        }
480
481        // again, just to be sure ogre works fine
482        ogreRoot._fireFrameEnded(evt); // note: uses the same time as _fireFrameStarted
483
484        ++frameCount;
485    }
486    }
487    catch (std::exception& ex)
488    {
489      // something went wrong.
490      COUT(1) << ex.what() << std::endl;
491      COUT(1) << "Main loop was stopped by an unhandled exception. Shutting down." << std::endl;
492    }
[1038]493  }
494}
Note: See TracBrowser for help on using the repository browser.