Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

Added convert test for gcc.

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