Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/input/src/orxonox/Orxonox.cc @ 1340

Last change on this file since 1340 was 1340, checked in by rgrieder, 16 years ago
  • tried a more object oriented approach for the KeyBinder
  • things work as far as I can tell
  • tested slomo command on joy stick slider: I was able to steer the time factor with the slider.
  • more infos to come..
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 *      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 <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#include "util/ExprParser.h"
54
55// core
56#include "core/ConfigFileManager.h"
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#include "core/TclBind.h"
65
66// audio
67#include "audio/AudioManager.h"
68
69// network
70#include "network/Server.h"
71#include "network/Client.h"
72
73// objects and tools
74#include "tools/Timer.h"
75#include "hud/HUD.h"
76#include "console/InGameConsole.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  ConsoleCommandShortcut(Orxonox, exit, AccessLevel::None).setKeybindMode(KeybindMode::OnPress);
86  ConsoleCommandShortcut(Orxonox, slomo, AccessLevel::Offline).setDefaultValue(0, 1.0)
87    .setAxisParamIndex(0).setIsAxisRelative(false);
88  ConsoleCommandShortcut(Orxonox, setTimeFactor, AccessLevel::Offline).setDefaultValue(0, 1.0);
89  ConsoleCommandShortcut(Orxonox, activateConsole, AccessLevel::None);
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        InputManager::setInputState(InputManager::IS_NORMAL);
121      }
122
123    private:
124      InputBuffer* ib_;
125  };
126
127  class Calculator
128  {
129  public:
130    static float calculate(const std::string& calculation)
131    {
132      ExprParser expr(calculation);
133      if (expr.getSuccess())
134      {
135        if (expr.getResult() == 42.0)
136          std::cout << "Greetings from the restaurant at the end of the universe." << std::endl;
137        // FIXME: insert modifier to display in full precision
138        std::cout << "Result is: " << expr.getResult() << std::endl;
139        if (expr.getRemains() != "")
140          std::cout << "Warning: Expression could not be parsed to the end! Remains: '"
141              << expr.getRemains() << "'" << std::endl;
142        return expr.getResult();
143      }
144      else
145      {
146        std::cout << "Cannot calculate expression: Parse error" << std::endl;
147        return 0;
148      }
149    }
150  };
151  ConsoleCommandShortcut(Calculator, calculate, AccessLevel::None);
152
153  /**
154    @brief Reference to the only instance of the class.
155  */
156  Orxonox *Orxonox::singletonRef_s = 0;
157
158  /**
159   * Create a new instance of Orxonox. Avoid doing any actual work here.
160   */
161  Orxonox::Orxonox() :
162    ogre_(0),
163    //auMan_(0),
164    timer_(0),
165    // turn on frame smoothing by setting a value different from 0
166    frameSmoothingTime_(0.0f),
167    orxonoxConsole_(0),
168    orxonoxHUD_(0),
169    bAbort_(false),
170    timefactor_(1.0f),
171    mode_(STANDALONE),
172    serverIp_("")
173  {
174  }
175
176  /**
177   * Destruct Orxonox.
178   */
179  Orxonox::~Orxonox()
180  {
181    // keep in mind: the order of deletion is very important!
182    if (this->orxonoxHUD_)
183      delete this->orxonoxHUD_;
184    Loader::close();
185    InputManager::destroy();
186    //if (this->auMan_)
187    //  delete this->auMan_;
188    if (this->timer_)
189      delete this->timer_;
190    GraphicsEngine::getSingleton().destroy();
191
192    if (network::Client::getSingleton())
193      network::Client::destroySingleton();
194    if (server_g)
195      delete server_g;
196  }
197
198
199  /**
200    Asks the mainloop nicely to abort.
201  */
202  void Orxonox::abortRequest()
203  {
204    COUT(3) << "Orxonox: Abort requested." << std::endl;
205    bAbort_ = true;
206  }
207
208  /**
209   * @return singleton reference
210   */
211  Orxonox* Orxonox::getSingleton()
212  {
213    if (!singletonRef_s)
214      singletonRef_s = new Orxonox();
215    return singletonRef_s;
216  }
217
218  /**
219    @brief Destroys the Orxonox singleton.
220  */
221  void Orxonox::destroySingleton()
222  {
223    if (singletonRef_s)
224      delete singletonRef_s;
225    singletonRef_s = 0;
226  }
227
228  /**
229   * initialization of Orxonox object
230   * @param argc argument counter
231   * @param argv list of argumenst
232   * @param path path to config (in home dir or something)
233   */
234  bool Orxonox::init(int argc, char **argv, std::string path)
235  {
236    //TODO: find config file (assuming executable directory)
237    //TODO: read config file
238    //TODO: give config file to Ogre
239    std::string mode;
240    std::string dataPath;
241
242    ArgReader ar(argc, argv);
243    ar.checkArgument("mode", mode, false);
244    ar.checkArgument("data", dataPath, false);
245    ar.checkArgument("ip", serverIp_, false);
246    if(ar.errorHandling())
247      return false;
248
249    if (mode == "client")
250      mode_ = CLIENT;
251    else if (mode == "server")
252      mode_ = SERVER;
253    else
254    {
255      mode = "standalone";
256      mode_ = STANDALONE;
257    }
258    COUT(3) << "Orxonox: Mode is " << mode << "." << std::endl;
259
260    //if (mode_ == DEDICATED)
261      // TODO: decide what to do here
262    //else
263
264    // for playable server, client and standalone, the startup
265    // procedure until the GUI is identical
266
267    TclBind::getInstance().setDataPath(dataPath);
268    ConfigFileManager::getSingleton()->setFile(CFT_Settings, "orxonox.ini");
269    Factory::createClassHierarchy();
270
271    ogre_ = &GraphicsEngine::getSingleton();
272    if (!ogre_->setup(path))       // creates ogre root and other essentials
273      return false;
274
275    return true;
276  }
277
278  /**
279   * start modules
280   */
281  bool Orxonox::start()
282  {
283    //if (mode == DEDICATED)
284    // do something else
285    //else
286
287    if (!ogre_->loadRenderer())    // creates the render window
288      return false;
289
290    // Calls the InputManager which sets up the input devices.
291    // The render window width and height are used to set up the mouse movement.
292    if (!InputManager::initialise(ogre_->getWindowHandle(),
293          ogre_->getWindowWidth(), ogre_->getWindowHeight(), true, true, true))
294      return false;
295
296    // TODO: Spread this so that this call only initialises things needed for the GUI
297    if (!ogre_->initialiseResources())
298      return false;
299
300    // TOOD: load the GUI here
301    // set InputManager to GUI mode
302    InputManager::setInputState(InputManager::IS_GUI);
303    // TODO: run GUI here
304
305    // The following lines depend very much on the GUI output, so they're probably misplaced here..
306
307    InputManager::setInputState(InputManager::IS_NONE);
308
309    if (!loadPlayground())
310      return false;
311
312    switch (mode_)
313    {
314    case SERVER:
315      if (!serverLoad())
316        return false;
317      break;
318    case CLIENT:
319      if (!clientLoad())
320        return false;
321      break;
322    default:
323      if (!standaloneLoad())
324        return false;
325    }
326
327    InputManager::setInputState(InputManager::IS_NORMAL);
328
329    return startRenderLoop();
330  }
331
332  /**
333   * Loads everything in the scene except for the actual objects.
334   * This includes HUD, Console..
335   */
336  bool Orxonox::loadPlayground()
337  {
338    ogre_->createNewScene();
339
340          // Init audio
341    //auMan_ = new audio::AudioManager();
342    //auMan_->ambientAdd("a1");
343    //auMan_->ambientAdd("a2");
344    //auMan_->ambientAdd("a3");
345    //auMan->ambientAdd("ambient1");
346    //auMan_->ambientStart();
347
348    // Load the HUD
349    COUT(3) << "Orxonox: Loading HUD..." << std::endl;
350    Ogre::Overlay* hudOverlay = Ogre::OverlayManager::getSingleton().getByName("Orxonox/HUD1.2");
351    orxonoxHUD_ = new HUD();
352    orxonoxHUD_->setEnergyValue(20);
353    orxonoxHUD_->setEnergyDistr(20,20,60);
354    hudOverlay->show();
355
356    COUT(3) << "Orxonox: Loading Console..." << std::endl;
357    InputBuffer* ib = dynamic_cast<InputBuffer*>(InputManager::getKeyHandler("buffer"));
358    /*
359    Testconsole* console = new Testconsole(ib);
360    ib->registerListener(console, &Testconsole::listen, true);
361    ib->registerListener(console, &Testconsole::execute, '\r', false);
362    ib->registerListener(console, &Testconsole::hintandcomplete, '\t', true);
363    ib->registerListener(console, &Testconsole::clear, '§', true);
364    ib->registerListener(console, &Testconsole::removeLast, '\b', true);
365    ib->registerListener(console, &Testconsole::exit, (char)0x1B, true);
366    */
367    orxonoxConsole_ = new InGameConsole(ib);
368    ib->registerListener(orxonoxConsole_, &InGameConsole::listen, true);
369    ib->registerListener(orxonoxConsole_, &InGameConsole::execute, '\r', false);
370    ib->registerListener(orxonoxConsole_, &InGameConsole::hintandcomplete, '\t', true);
371    ib->registerListener(orxonoxConsole_, &InGameConsole::clear, '§', true);
372    ib->registerListener(orxonoxConsole_, &InGameConsole::removeLast, '\b', true);
373    ib->registerListener(orxonoxConsole_, &InGameConsole::exit, (char)0x1B, true);
374
375    return true;
376  }
377
378  /**
379   * Level loading method for server mode.
380   */
381  bool Orxonox::serverLoad()
382  {
383    COUT(2) << "Loading level in server mode" << std::endl;
384
385    server_g = new network::Server();
386
387    if (!loadScene())
388      return false;
389
390    server_g->open();
391
392    return true;
393  }
394
395  /**
396   * Level loading method for client mode.
397   */
398  bool Orxonox::clientLoad()
399  {
400    COUT(2) << "Loading level in client mode" << std::endl;\
401
402    if (serverIp_.compare("") == 0)
403      client_g = network::Client::createSingleton();
404    else
405      client_g = network::Client::createSingleton(serverIp_, NETWORK_PORT);
406
407    client_g->establishConnection();
408    client_g->tick(0);
409
410    return true;
411  }
412
413  /**
414   * Level loading method for standalone mode.
415   */
416  bool Orxonox::standaloneLoad()
417  {
418    COUT(2) << "Loading level in standalone mode" << std::endl;
419
420    if (!loadScene())
421      return false;
422
423    return true;
424  }
425
426  /**
427   * Helper method to load a level.
428   */
429  bool Orxonox::loadScene()
430  {
431    Level* startlevel = new Level("levels/sample.oxw");
432    Loader::open(startlevel);
433
434    return true;
435  }
436
437
438  /**
439    Main loop of the orxonox game.
440    About the loop: The design is almost exactly like the one in ogre, so that
441    if any part of ogre registers a framelisteners, it will still behave
442    correctly. Furthermore the time smoothing feature from ogre has been
443    implemented too. If turned on (see orxonox constructor), it will calculate
444    the dt_n by means of the recent most dt_n-1, dt_n-2, etc.
445  */
446  bool Orxonox::startRenderLoop()
447  {
448    // first check whether ogre root object has been created
449    if (Ogre::Root::getSingletonPtr() == 0)
450    {
451      COUT(2) << "Orxonox Error: Could not start rendering. No Ogre root object found" << std::endl;
452      return false;
453    }
454    Ogre::Root& ogreRoot = Ogre::Root::getSingleton();
455
456
457    // Contains the times of recently fired events
458    // eventTimes[4] is the list for the times required for the fps counter
459    std::deque<unsigned long> eventTimes[4];
460    // Clear event times
461    for (int i = 0; i < 4; ++i)
462      eventTimes[i].clear();
463    // fill the fps time list with zeros
464    for (int i = 0; i < 50; i++)
465      eventTimes[3].push_back(0);
466
467    // use the ogre timer class to measure time.
468    if (!timer_)
469      timer_ = new Ogre::Timer();
470    timer_->reset();
471
472    COUT(3) << "Orxonox: Starting the main loop." << std::endl;
473          while (!bAbort_)
474          {
475                  // Pump messages in all registered RenderWindows
476      // This calls the WindowEventListener objects.
477      Ogre::WindowEventUtilities::messagePump();
478
479      // get current time
480      unsigned long now = timer_->getMilliseconds();
481      eventTimes[3].push_back(now);
482      eventTimes[3].erase(eventTimes[3].begin());
483
484      // create an event to pass to the frameStarted method in ogre
485      Ogre::FrameEvent evt;
486      evt.timeSinceLastEvent = calculateEventTime(now, eventTimes[0]);
487      evt.timeSinceLastFrame = calculateEventTime(now, eventTimes[1]);
488
489      // show the current time in the HUD
490      orxonoxHUD_->setTime((int)now, 0);
491      orxonoxHUD_->setRocket2(ogreRoot.getCurrentFrameNumber());
492      if (eventTimes[3].back() - eventTimes[3].front() != 0)
493        orxonoxHUD_->setRocket1((int)(50000.0f/(eventTimes[3].back() - eventTimes[3].front())));
494
495      // Iterate through all Tickables and call their tick(dt) function
496      for (Iterator<Tickable> it = ObjectList<Tickable>::start(); it; ++it)
497        it->tick((float)evt.timeSinceLastFrame * this->timefactor_);
498      // Iterate through all TickableReals and call their tick(dt) function
499      for (Iterator<TickableReal> it = ObjectList<TickableReal>::start(); it; ++it)
500        it->tick((float)evt.timeSinceLastFrame);
501      orxonoxConsole_->tick((float)evt.timeSinceLastFrame);
502
503      // don't forget to call _fireFrameStarted in ogre to make sure
504      // everything goes smoothly
505      ogreRoot._fireFrameStarted(evt);
506
507      // server still renders at the moment
508      //if (mode_ != SERVER)
509      ogreRoot._updateAllRenderTargets(); // only render in non-server mode
510
511      // get current time
512      now = timer_->getMilliseconds();
513
514      // create an event to pass to the frameEnded method in ogre
515      evt.timeSinceLastEvent = calculateEventTime(now, eventTimes[0]);
516      evt.timeSinceLastFrame = calculateEventTime(now, eventTimes[2]);
517
518      // again, just to be sure ogre works fine
519      ogreRoot._fireFrameEnded(evt);
520          }
521
522    if(mode_==CLIENT)
523      network::Client::getSingleton()->closeConnection();
524    else if(mode_==SERVER)
525      server_g->close();
526    return true;
527  }
528
529  /**
530    Method for calculating the average time between recently fired events.
531    Code directly taken from OgreRoot.cc
532    @param now The current time in ms.
533    @param type The type of event to be considered.
534  */
535  float Orxonox::calculateEventTime(unsigned long now, std::deque<unsigned long> &times)
536  {
537    // Calculate the average time passed between events of the given type
538    // during the last frameSmoothingTime_ seconds.
539
540    times.push_back(now);
541
542    if(times.size() == 1)
543      return 0;
544
545    // Times up to frameSmoothingTime_ seconds old should be kept
546    unsigned long discardThreshold = (unsigned long)(frameSmoothingTime_ * 1000.0f);
547
548    // Find the oldest time to keep
549    std::deque<unsigned long>::iterator it  = times.begin();
550    // We need at least two times
551    std::deque<unsigned long>::iterator end = times.end() - 2;
552
553    while(it != end)
554    {
555      if (now - *it > discardThreshold)
556        ++it;
557      else
558        break;
559    }
560
561    // Remove old times
562    times.erase(times.begin(), it);
563
564    return (float)(times.back() - times.front()) / ((times.size() - 1) * 1000);
565  }
566
567  /**
568   * Static function that shows the console in game mode.
569   */
570  void Orxonox::activateConsole()
571  {
572    // currently, the console shows itself when feeded with input.
573    InputManager::setInputState(InputManager::IS_CONSOLE);
574  }
575}
Note: See TracBrowser for help on using the repository browser.