Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 1768 was 1768, checked in by rgrieder, 16 years ago

gcc test commit again

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