Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/orxonox/Orxonox.cc @ 1107

Last change on this file since 1107 was 1092, checked in by rgrieder, 17 years ago
  • little 'mess' cleanup
File size: 14.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 *      Benjamin Knecht <beni_at_orxonox.net>, (C) 2007
24 *   Co-authors:
25 *      ...
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 <iostream>
40//#include <exception>
41#include <deque>
42
43//****** OGRE ******
44//#include <OgreException.h>
45#include <OgreFrameListener.h>
46#include <OgreOverlay.h>
47#include <OgreOverlayManager.h>
48#include <OgreRoot.h>
49#include <OgreTimer.h>
50#include <OgreWindowEventUtilities.h>
51
52//***** ORXONOX ****
53// util
54//#include "util/Sleep.h"
55#include "util/ArgReader.h"
56
57// core
58#include "core/ConfigFileManager.h"
59#include "core/ConsoleCommand.h"
60#include "core/Debug.h"
61#include "core/Factory.h"
62#include "core/Loader.h"
63#include "core/Tickable.h"
64#include "core/InputBuffer.h"
65#include "core/InputManager.h"
66
67// audio
68#include "audio/AudioManager.h"
69
70// network
71#include "network/Server.h"
72#include "network/Client.h"
73
74// objects and tools
75#include "tools/Timer.h"
76#include "hud/HUD.h"
77
78// FIXME: is this really file scope?
79// globals for the server or client
80network::Client *client_g;
81network::Server *server_g;
82
83namespace orxonox
84{
85  ConsoleCommand(Orxonox, exit, AccessLevel::None, true);
86  ConsoleCommand(Orxonox, slomo, AccessLevel::Offline, true).setDefaultValue(0, 1.0);
87  ConsoleCommand(Orxonox, setTimeFactor, AccessLevel::Offline, false).setDefaultValue(0, 1.0);
88
89  class Testconsole : public InputBufferListener
90  {
91    public:
92      Testconsole(InputBuffer* ib) : ib_(ib) {}
93      void listen() const
94      {
95        std::cout << "> " << this->ib_->get() << std::endl;
96      }
97      void execute() const
98      {
99        std::cout << ">> " << this->ib_->get() << std::endl;
100        if (!CommandExecutor::execute(this->ib_->get()))
101          std::cout << "Error" << std::endl;
102        this->ib_->clear();
103      }
104      void hintandcomplete() const
105      {
106        std::cout << CommandExecutor::hint(this->ib_->get()) << std::endl;
107        this->ib_->set(CommandExecutor::complete(this->ib_->get()));
108      }
109      void clear() const
110      {
111        this->ib_->clear();
112      }
113      void removeLast() const
114      {
115        this->ib_->removeLast();
116      }
117      void exit() const
118      {
119        CommandExecutor::execute("setInputMode 2");
120      }
121
122    private:
123      InputBuffer* ib_;
124  };
125
126  /**
127    @brief Reference to the only instance of the class.
128  */
129  Orxonox *Orxonox::singletonRef_s = 0;
130
131  /**
132   * create a new instance of Orxonox
133   */
134  Orxonox::Orxonox()
135  {
136    this->ogre_ = &GraphicsEngine::getSingleton();
137    this->timer_ = 0;
138    this->dataPath_ = "";
139    this->auMan_ = 0;
140    this->inputHandler_ = 0;
141    // turn on frame smoothing by setting a value different from 0
142    this->frameSmoothingTime_ = 0.0f;
143    this->bAbort_ = false;
144    this->timefactor_ = 1.0f;
145  }
146
147  /**
148   * destruct Orxonox
149   */
150  Orxonox::~Orxonox()
151  {
152    // keep in mind: the order of deletion is very important!
153    if (this->orxonoxHUD_)
154      delete this->orxonoxHUD_;
155    Loader::close();
156    InputManager::getSingleton().destroy();
157    if (this->auMan_)
158      delete this->auMan_;
159    if (this->timer_)
160      delete this->timer_;
161    GraphicsEngine::getSingleton().destroy();
162
163    if (client_g)
164      delete client_g;
165    if (server_g)
166      delete server_g;
167  }
168
169  /**
170    @brief Immediately deletes the orxonox object.
171    Never use if you can help it while rendering!
172  */
173  void Orxonox::abortImmediateForce()
174  {
175    COUT(1) << "*** Orxonox Error: Orxonox object was unexpectedly destroyed." << std::endl;
176    delete this;
177  }
178
179  /**
180    Asks the mainloop nicely to abort.
181  */
182  void Orxonox::abortRequest()
183  {
184    COUT(3) << "*** Orxonox: Abort requested." << std::endl;
185    bAbort_ = true;
186  }
187
188  /**
189   * @return singleton object
190   */
191  Orxonox* Orxonox::getSingleton()
192  {
193    if (!singletonRef_s)
194      singletonRef_s = new Orxonox();
195    return singletonRef_s;
196  }
197
198  /**
199    @brief Destroys the Orxonox singleton.
200  */
201  void Orxonox::destroySingleton()
202  {
203    if (singletonRef_s)
204      delete singletonRef_s;
205    singletonRef_s = 0;
206  }
207
208  /**
209   * initialization of Orxonox object
210   * @param argc argument counter
211   * @param argv list of argumenst
212   * @param path path to config (in home dir or something)
213   */
214  void Orxonox::init(int argc, char **argv, std::string path)
215  {
216    //TODO: find config file (assuming executable directory)
217    //TODO: read config file
218    //TODO: give config file to Ogre
219    std::string mode;
220
221    ArgReader ar(argc, argv);
222    ar.checkArgument("mode", mode, false);
223    ar.checkArgument("data", this->dataPath_, false);
224    ar.checkArgument("ip", serverIp_, false);
225    if(ar.errorHandling()) abortImmediateForce();
226    if(mode == std::string("client"))
227    {
228      mode_ = CLIENT;
229      clientInit(path);
230    }
231    else if(mode== std::string("server")){
232      mode_ = SERVER;
233      serverInit(path);
234    }
235    else{
236      mode_ = STANDALONE;
237      standaloneInit(path);
238    }
239  }
240
241  void Orxonox::serverInit(std::string path)
242  {
243    COUT(2) << "initialising server" << std::endl;
244
245    ogre_->setConfigPath(path);
246    ogre_->setup();
247    //root_ = ogre_->getRoot();
248    if(!ogre_->load(this->dataPath_)) abortImmediateForce(/* unable to load */);
249
250    server_g = new network::Server();
251  }
252
253  void Orxonox::clientInit(std::string path)
254  {
255    COUT(2) << "initialising client" << std::endl;\
256
257    ogre_->setConfigPath(path);
258    ogre_->setup();
259    if(serverIp_.compare("")==0)
260      client_g = new network::Client();
261    else
262      client_g = new network::Client(serverIp_, NETWORK_PORT);
263    if(!ogre_->load(this->dataPath_)) abortImmediateForce(/* unable to load */);
264  }
265
266  void Orxonox::standaloneInit(std::string path)
267  {
268    COUT(2) << "initialising standalone mode" << std::endl;
269
270    ogre_->setConfigPath(path);
271    ogre_->setup();
272    if(!ogre_->load(this->dataPath_)) abortImmediateForce(/* unable to load */);
273  }
274
275  /**
276   * start modules
277   */
278  void Orxonox::start()
279  {
280    switch(mode_){
281    case CLIENT:
282      clientStart();
283      break;
284    case SERVER:
285      serverStart();
286      break;
287    default:
288      standaloneStart();
289    }
290  }
291
292  void Orxonox::clientStart(){
293    ogre_->initialise();
294    ConfigFileManager::getSingleton()->setFile(CFT_Settings, "orxonox.ini");
295    Factory::createClassHierarchy();
296
297
298    auMan_ = new audio::AudioManager();
299
300    Ogre::Overlay* hudOverlay = Ogre::OverlayManager::getSingleton().getByName("Orxonox/HUD1.2");
301    HUD* orxonoxHud;
302    orxonoxHud = new HUD();
303    orxonoxHud->setEnergyValue(20);
304    orxonoxHud->setEnergyDistr(20,20,60);
305    hudOverlay->show();
306
307    client_g->establishConnection();
308    client_g->tick(0);
309
310
311    //setupInputSystem();
312
313    startRenderLoop();
314  }
315
316  void Orxonox::serverStart(){
317    //TODO: start modules
318    ogre_->initialise();
319    //TODO: run engine
320    ConfigFileManager::getSingleton()->setFile(CFT_Settings, "orxonox.ini");
321    Factory::createClassHierarchy();
322    createScene();
323    setupInputSystem();
324
325    server_g->open();
326
327    startRenderLoop();
328  }
329
330  void Orxonox::standaloneStart(){
331    //TODO: start modules
332    ogre_->initialise();
333    //TODO: run engine
334    ConfigFileManager::getSingleton()->setFile(CFT_Settings, "orxonox.ini");
335    Factory::createClassHierarchy();
336    createScene();
337    setupInputSystem();
338
339    startRenderLoop();
340  }
341
342  void Orxonox::createScene(void)
343  {
344          // Init audio
345    auMan_ = new audio::AudioManager();
346
347    // load this file from config
348    Level* startlevel = new Level("levels/sample.oxw");
349    Loader::open(startlevel);
350
351    Ogre::Overlay* hudOverlay = Ogre::OverlayManager::getSingleton().getByName("Orxonox/HUD1.2");
352    orxonoxHUD_ = new HUD();
353    orxonoxHUD_->setEnergyValue(20);
354    orxonoxHUD_->setEnergyDistr(20,20,60);
355    hudOverlay->show();
356
357        /*
358    auMan_->ambientAdd("a1");
359    auMan_->ambientAdd("a2");
360    auMan_->ambientAdd("a3");
361    //auMan->ambientAdd("ambient1");
362    auMan_->ambientStart();
363  */
364  }
365
366  /**
367    @brief Calls the InputHandler which sets up the input devices.
368    The render window width and height are used to set up the mouse movement.
369  */
370  void Orxonox::setupInputSystem()
371  {
372    inputHandler_ = &InputManager::getSingleton();
373    if (!inputHandler_->initialise(ogre_->getWindowHandle(),
374          ogre_->getWindowWidth(), ogre_->getWindowHeight()))
375      abortImmediateForce();
376    inputHandler_->setInputMode(IM_INGAME);
377  }
378
379  /**
380    Main loop of the orxonox game.
381    This is a new solution, using the ogre engine instead of being used by it.
382    An alternative solution would be to simply use the timer of the Root object,
383    but that implies using Ogre in any case. There would be no way to test
384    our code without the help of the root object.
385    There's even a chance that we can dispose of the root object entirely
386    in server mode.
387    About the loop: The design is almost exactly like the one in ogre, so that
388    if any part of ogre registers a framelisteners, it will still behave
389    correctly. Furthermore the time smoothing feature from ogre has been
390    implemented too. If turned on (see orxonox constructor), it will calculate
391    the dt_n by means of the recent most dt_n-1, dt_n-2, etc.
392  */
393  void Orxonox::startRenderLoop()
394  {
395    InputBuffer* ib = new InputBuffer();
396    InputManager::getSingleton().feedInputBuffer(ib);
397    Testconsole* console = new Testconsole(ib);
398    ib->registerListener(console, &Testconsole::listen, true);
399    ib->registerListener(console, &Testconsole::execute, '\r', false);
400    ib->registerListener(console, &Testconsole::execute, '\n', false);
401    ib->registerListener(console, &Testconsole::hintandcomplete, '\t', true);
402    ib->registerListener(console, &Testconsole::clear, '§', true);
403    ib->registerListener(console, &Testconsole::removeLast, '\b', true);
404    ib->registerListener(console, &Testconsole::exit, (char)0x1B, true);
405
406    // first check whether ogre root object has been created
407    if (Ogre::Root::getSingletonPtr() == 0)
408    {
409      COUT(2) << "*** Orxonox Error: Could not start rendering. No Ogre root object found" << std::endl;
410      return;
411    }
412
413    // Contains the times of recently fired events
414    // eventTimes[4] is the list for the times required for the fps counter
415    std::deque<unsigned long> eventTimes[4];
416    // Clear event times
417    for (int i = 0; i < 4; ++i)
418      eventTimes[i].clear();
419    // fill the fps time list with zeros
420    for (int i = 0; i < 20; i++)
421      eventTimes[3].push_back(0);
422
423    // use the ogre timer class to measure time.
424    if (!timer_)
425      timer_ = new Ogre::Timer();
426    timer_->reset();
427
428          while (!bAbort_)
429          {
430                  // Pump messages in all registered RenderWindows
431      Ogre::WindowEventUtilities::messagePump();
432
433      // get current time
434      unsigned long now = timer_->getMilliseconds();
435      eventTimes[3].push_back(now);
436      eventTimes[3].erase(eventTimes[3].begin());
437
438      // create an event to pass to the frameStarted method in ogre
439      Ogre::FrameEvent evt;
440      evt.timeSinceLastEvent = calculateEventTime(now, eventTimes[0]);
441      evt.timeSinceLastFrame = calculateEventTime(now, eventTimes[1]);
442
443      // show the current time in the HUD
444      orxonoxHUD_->setTime((int)now, 0);
445      if (eventTimes[3].back() - eventTimes[3].front() != 0)
446        orxonoxHUD_->setRocket1((int)(20000.0f/(eventTimes[3].back() - eventTimes[3].front())));
447
448      // Iterate through all Tickables and call their tick(dt) function
449      for (Iterator<Tickable> it = ObjectList<Tickable>::start(); it; ++it)
450        it->tick((float)evt.timeSinceLastFrame * this->timefactor_);
451
452      // don't forget to call _fireFrameStarted in ogre to make sure
453      // everything goes smoothly
454      Ogre::Root::getSingleton()._fireFrameStarted(evt);
455
456      // server still renders at the moment
457      //if (mode_ != SERVER)
458      Ogre::Root::getSingleton()._updateAllRenderTargets(); // only render in non-server mode
459
460      // get current time
461      now = timer_->getMilliseconds();
462
463      // create an event to pass to the frameEnded method in ogre
464      evt.timeSinceLastEvent = calculateEventTime(now, eventTimes[0]);
465      evt.timeSinceLastFrame = calculateEventTime(now, eventTimes[2]);
466
467      // again, just to be sure ogre works fine
468      Ogre::Root::getSingleton()._fireFrameEnded(evt);
469          }
470  }
471
472  /**
473    Method for calculating the average time between recently fired events.
474    Code directly taken from OgreRoot.cc
475    @param now The current time in ms.
476    @param type The type of event to be considered.
477  */
478  float Orxonox::calculateEventTime(unsigned long now, std::deque<unsigned long> &times)
479  {
480    // Calculate the average time passed between events of the given type
481    // during the last frameSmoothingTime_ seconds.
482
483    times.push_back(now);
484
485    if(times.size() == 1)
486      return 0;
487
488    // Times up to frameSmoothingTime_ seconds old should be kept
489    unsigned long discardThreshold = (unsigned long)(frameSmoothingTime_ * 1000.0f);
490
491    // Find the oldest time to keep
492    std::deque<unsigned long>::iterator it  = times.begin();
493    // We need at least two times
494    std::deque<unsigned long>::iterator end = times.end() - 2;
495
496    while(it != end)
497    {
498      if (now - *it > discardThreshold)
499        ++it;
500      else
501        break;
502    }
503
504    // Remove old times
505    times.erase(times.begin(), it);
506
507    return (float)(times.back() - times.front()) / ((times.size() - 1) * 1000);
508  }
509
510  /**
511    @brief Test method for the InputHandler.
512    But: Is actually responsible for catching an exit event..
513  */
514  void Orxonox::eventOccured(InputEvent &evt)
515  {
516    if (evt.id == 1)
517      this->abortRequest();
518  }
519}
Note: See TracBrowser for help on using the repository browser.