Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

Convert.h done. Has yet to be tested with gcc. And the comments have to be adapted.

  • Property svn:eol-style set to native
File size: 16.7 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    std::string nmbr;
234    char res('a');
235    //const char* nmbr;
236    //const char* str;
237    convertValue(&nmbr, res);
238    //const unsigned int blah = 4;
239    //convertValue(nmbr, blah);
240    //convertValue(&str, 4.0f);
241
242    //using ::operator<<;
243    using std::string;
244    int a = 3;
245    FooBar asdf;
246    std::string asdf2;
247    std::ostringstream oss;
248    char blah(' ');
249    oss << blah;
250    std::cout << asdf2;
251    ConvertValue(&asdf2, asdf);
252    //if (conversionTests::OStreamOperator<FooBar>::exists)
253    //    int asdfasdfasdf = 7;
254    //COUT(3) << asdf;
255
256    TestConv(1, int, (3), float, 3.0);
257    TestConv(2, int, (3), string, "3");
258    TestConv(3, string, ("3.0"), float, 3.0f);
259    TestConv(4, char, ('a'), string, "a");
260    TestConv(5, string, ("df"), char, 'd');
261    TestConv(6, Vector2, (3,4), string, "3,4");
262    TestConv(7, const char*, ("4.3"), float, 4.3f);
263    TestConv(8, const char*, ("4,3"), Vector2, Vector2(4,3));
264    //TestConv(9, const char*, ("4.4"), Radian, Radian(4.4));
265    TestConv(100, int, (3), const char*, "3");
266    TestConv(101, Vector3, (1, 2, 3), float, 3.0);
267
268    std::ostringstream out;
269
270    std::string mode;
271    std::string tempDataPath;
272
273    ArgReader ar(argc, argv);
274    ar.checkArgument("mode", &mode, false);
275    ar.checkArgument("data", &tempDataPath, false);
276    ar.checkArgument("ip",   &serverIp_, false);
277    ar.checkArgument("port", &serverPort_, false);
278    if(ar.errorHandling())
279    {
280      COUT(1) << "Error while parsing command line arguments" << std::endl;
281      COUT(1) << ar.getErrorString();
282      COUT(0) << "Usage:" << std::endl << "orxonox [mode client|server|dedicated|standalone] "
283        << "[--data PATH] [--ip IP] [--port PORT]" << std::endl;
284      return false;
285    }
286
287    if (mode == "client")
288      mode_ = CLIENT;
289    else if (mode == "server")
290      mode_ = SERVER;
291    else if (mode == "dedicated")
292      mode_ = DEDICATED;
293    else
294    {
295      if (mode == "")
296        mode = "standalone";
297      if (mode != "standalone")
298      {
299        COUT(2) << "Warning: mode \"" << mode << "\" doesn't exist. "
300          << "Defaulting to standalone" << std::endl;
301        mode = "standalone";
302      }
303      mode_ = STANDALONE;
304    }
305    COUT(3) << "Orxonox: Mode is " << mode << "." << std::endl;
306
307    if (tempDataPath != "")
308    {
309      if (tempDataPath[tempDataPath.size() - 1] != '/')
310        tempDataPath += "/";
311      Settings::tsetDataPath(tempDataPath);
312    }
313
314    // initialise TCL
315    TclBind::getInstance().setDataPath(Settings::getDataPath());
316
317    //if (mode_ == DEDICATED)
318      // TODO: decide what to do here
319    //else
320
321    // for playable server, client and standalone, the startup
322    // procedure until the GUI is identical
323
324    ogre_ = &GraphicsEngine::getSingleton();
325    if (!ogre_->setup())       // creates ogre root and other essentials
326      return false;
327
328    return true;
329  }
330
331  /**
332   * start modules
333   */
334  bool Orxonox::start()
335  {
336    if (mode_ == DEDICATED)
337    {
338      // do something else
339    }
340    else
341    { // not dedicated server
342      if (!ogre_->loadRenderer())    // creates the render window
343        return false;
344
345      // Calls the InputManager which sets up the input devices.
346      // The render window width and height are used to set up the mouse movement.
347      if (!InputManager::initialise(ogre_->getWindowHandle(),
348            ogre_->getWindowWidth(), ogre_->getWindowHeight(), true, true, true))
349        return false;
350
351      // TODO: Spread this so that this call only initialises things needed for the GUI
352      if (!ogre_->initialiseResources())
353        return false;
354
355      // TOOD: load the GUI here
356      // set InputManager to GUI mode
357      InputManager::setInputState(InputManager::IS_GUI);
358      // TODO: run GUI here
359
360      // The following lines depend very much on the GUI output, so they're probably misplaced here..
361
362      InputManager::setInputState(InputManager::IS_NONE);
363
364      // create Ogre SceneManager
365      ogre_->createNewScene();
366
367      if (!loadPlayground())
368        return false;
369    }
370
371    switch (mode_)
372    {
373    case SERVER:
374      if (!serverLoad())
375        return false;
376      break;
377    case CLIENT:
378      if (!clientLoad())
379        return false;
380      break;
381    case DEDICATED:
382      if (!serverLoad())
383        return false;
384      break;
385    default:
386      if (!standaloneLoad())
387        return false;
388    }
389
390    InputManager::setInputState(InputManager::IS_NORMAL);
391
392    return startRenderLoop();
393  }
394
395  /**
396   * Loads everything in the scene except for the actual objects.
397   * This includes HUD, Console..
398   */
399  bool Orxonox::loadPlayground()
400  {
401    // Init audio
402    //auMan_ = new audio::AudioManager();
403    //auMan_->ambientAdd("a1");
404    //auMan_->ambientAdd("a2");
405    //auMan_->ambientAdd("a3");
406    //auMan->ambientAdd("ambient1");
407    //auMan_->ambientStart();
408
409    // Load the HUD
410    COUT(3) << "Orxonox: Loading HUD..." << std::endl;
411    orxonoxHUD_ = &HUD::getSingleton();
412    orxonoxHUD_->initialise();
413    return true;
414  }
415
416  /**
417   * Level loading method for server mode.
418   */
419  bool Orxonox::serverLoad()
420  {
421    COUT(2) << "Loading level in server mode" << std::endl;
422
423    //server_g = new network::Server(serverPort_);
424    server_g = network::Server::createSingleton(serverPort_);
425
426    if (!loadScene())
427      return false;
428
429    server_g->open();
430
431    return true;
432  }
433
434  /**
435   * Level loading method for client mode.
436   */
437  bool Orxonox::clientLoad()
438  {
439    COUT(2) << "Loading level in client mode" << std::endl;\
440
441    if (serverIp_.compare("") == 0)
442      client_g = network::Client::createSingleton();
443    else
444
445      client_g = network::Client::createSingleton(serverIp_, serverPort_);
446
447    if(!client_g->establishConnection())
448      return false;
449    client_g->tick(0);
450
451    return true;
452  }
453
454  /**
455   * Level loading method for standalone mode.
456   */
457  bool Orxonox::standaloneLoad()
458  {
459    COUT(2) << "Loading level in standalone mode" << std::endl;
460
461    if (!loadScene())
462      return false;
463
464    return true;
465  }
466
467  /**
468   * Helper method to load a level.
469   */
470  bool Orxonox::loadScene()
471  {
472    Level* startlevel = new Level("levels/sample.oxw");
473    Loader::open(startlevel);
474
475    return true;
476  }
477
478
479  /**
480    Main loop of the orxonox game.
481    About the loop: The design is almost exactly like the one in ogre, so that
482    if any part of ogre registers a framelisteners, it will still behave
483    correctly. Furthermore the time smoothing feature from ogre has been
484    implemented too. If turned on (see orxonox constructor), it will calculate
485    the dt_n by means of the recent most dt_n-1, dt_n-2, etc.
486  */
487  bool Orxonox::startRenderLoop()
488  {
489    // first check whether ogre root object has been created
490    if (Ogre::Root::getSingletonPtr() == 0)
491    {
492      COUT(2) << "Orxonox Error: Could not start rendering. No Ogre root object found" << std::endl;
493      return false;
494    }
495    Ogre::Root& ogreRoot = Ogre::Root::getSingleton();
496
497
498    // Contains the times of recently fired events
499    // eventTimes[4] is the list for the times required for the fps counter
500    std::deque<unsigned long> eventTimes[3];
501    // Clear event times
502    for (int i = 0; i < 3; ++i)
503      eventTimes[i].clear();
504
505    // use the ogre timer class to measure time.
506    if (!timer_)
507      timer_ = new Ogre::Timer();
508    timer_->reset();
509
510    float renderTime = 0.0f;
511    float frameTime = 0.0f;
512//    clock_t time = 0;
513
514    //Ogre::SceneManager* mSceneMgr = GraphicsEngine::getSingleton().getSceneManager();
515    //Ogre::Viewport* mViewport = mSceneMgr->getCurrentViewport();
516
517    //Ogre::CompositorManager::getSingleton().addCompositor(mViewport, "Bloom");
518    //Ogre::CompositorManager::getSingleton().addCompositor(mViewport, "MotionBlur");
519
520    COUT(3) << "Orxonox: Starting the main loop." << std::endl;
521    while (!bAbort_)
522    {
523      // get current time
524      unsigned long now = timer_->getMilliseconds();
525
526      // create an event to pass to the frameStarted method in ogre
527      Ogre::FrameEvent evt;
528      evt.timeSinceLastEvent = calculateEventTime(now, eventTimes[0]);
529      evt.timeSinceLastFrame = calculateEventTime(now, eventTimes[1]);
530      frameTime += evt.timeSinceLastFrame;
531
532      // show the current time in the HUD
533      // HUD::getSingleton().setTime(now);
534      if (mode_ != DEDICATED && frameTime > 0.4f)
535      {
536        HUD::getSingleton().setRenderTimeRatio(renderTime / frameTime);
537        frameTime = 0.0f;
538        renderTime = 0.0f;
539      }
540
541      // tick the core
542      Core::tick((float)evt.timeSinceLastFrame);
543      // Call those objects that need the real time
544      for (ObjectList<TickableReal>::iterator it = ObjectList<TickableReal>::begin(); it; ++it)
545        it->tick((float)evt.timeSinceLastFrame);
546      // Call the scene objects
547      for (ObjectList<Tickable>::iterator it = ObjectList<Tickable>::begin(); it; ++it)
548        it->tick((float)evt.timeSinceLastFrame * this->timefactor_);
549      //AudioManager::tick();
550      if (client_g)
551        client_g->tick((float)evt.timeSinceLastFrame);
552      if (server_g)
553        server_g->tick((float)evt.timeSinceLastFrame);
554
555      // don't forget to call _fireFrameStarted in ogre to make sure
556      // everything goes smoothly
557      ogreRoot._fireFrameStarted(evt);
558
559      // get current time
560      now = timer_->getMilliseconds();
561      calculateEventTime(now, eventTimes[2]);
562
563      if (mode_ != DEDICATED)
564      {
565        // Pump messages in all registered RenderWindows
566        // This calls the WindowEventListener objects.
567        Ogre::WindowEventUtilities::messagePump();
568
569        // render
570        ogreRoot._updateAllRenderTargets();
571      }
572
573      // get current time
574      now = timer_->getMilliseconds();
575
576      // create an event to pass to the frameEnded method in ogre
577      evt.timeSinceLastEvent = calculateEventTime(now, eventTimes[0]);
578      renderTime += calculateEventTime(now, eventTimes[2]);
579
580      // again, just to be sure ogre works fine
581      ogreRoot._fireFrameEnded(evt);
582      //msleep(200);
583    }
584
585    if (mode_ == CLIENT)
586      network::Client::getSingleton()->closeConnection();
587    else if (mode_ == SERVER)
588      server_g->close();
589
590    return true;
591  }
592
593  /**
594    Method for calculating the average time between recently fired events.
595    Code directly taken from OgreRoot.cc
596    @param now The current time in ms.
597    @param type The type of event to be considered.
598  */
599  float Orxonox::calculateEventTime(unsigned long now, std::deque<unsigned long> &times)
600  {
601    // Calculate the average time passed between events of the given type
602    // during the last frameSmoothingTime_ seconds.
603
604    times.push_back(now);
605
606    if(times.size() == 1)
607      return 0;
608
609    // Times up to frameSmoothingTime_ seconds old should be kept
610    unsigned long discardThreshold = (unsigned long)(frameSmoothingTime_ * 1000.0f);
611
612    // Find the oldest time to keep
613    std::deque<unsigned long>::iterator it  = times.begin();
614    // We need at least two times
615    std::deque<unsigned long>::iterator end = times.end() - 2;
616
617    while(it != end)
618    {
619      if (now - *it > discardThreshold)
620        ++it;
621      else
622        break;
623    }
624
625    // Remove old times
626    times.erase(times.begin(), it);
627
628    return (float)(times.back() - times.front()) / ((times.size() - 1) * 1000);
629  }
630}
Note: See TracBrowser for help on using the repository browser.