Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

svn save

  • 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
88template <>
89struct 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.