Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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