Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

test files, no real content.

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