Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/console/src/orxonox/Orxonox.cc @ 1145

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