Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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