Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 1053 was 1052, checked in by landauf, 17 years ago

merged core2 back to trunk
there might be some errors, wasn't able to test it yet due to some strange g++ and linker behaviour.

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