Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/core3/src/orxonox/Orxonox.cc @ 1591

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

Again some heavy changes in ObjectList and Iterator:
there are now two types of iterators:

Iterator<ClassName> can iterate through any objectlist, either given by ObjectList<AnyClassName>::begin() or anyidentifier→getObjects()→begin(). Important note Iterator<ClassName> uses dynamic_cast.
And yes, it's possible to do this: Iterator<WorldEntity> it = ObjectList<SpaceShip>::begin()

ObjectList<ClassName>::iterator is the second iterator - it uses the ObjectList in a templated manner and therefore doesn't need dynamic_cast. But the only thing you can do is iterating through exactly the right ObjectList: ObjectList<ClassName>::iterator it = ObjectList<ClassName>::begin(). Anything else fails.

Those changes bring, at my system, something around +12% FPS compared with trunk and +25% FPS compared with the last revision of core3. Although I have to admit the FPS gain is only that high because iterating through objects is the main thing we're doing ingame right now. It would look totally different with physics, sound, AI, scripts, triggers and so on.

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