Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 1610 was 1608, checked in by landauf, 16 years ago
  • added a backlight to all SpaceShips, leaving a trail behind (configurable)
  • changed some lines in SpaceShipAI, AI seems to be more fair now…
  • fixed a bug in Math.cc algorithms to calculate radar positions and AI movement (division by zero and acos of 1.000001)
  • added destroy() and destroydelay to ParticleSpawner, so the smoketrail of a destroyed enemy stays visible for some seconds
  • added 2 lines to Orxonox.cc to make the length of the backlight-trail independend of the gamespeed

########################
# !!! MEDIA UPDATE !!! #
########################

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