Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 1112 was 1112, checked in by rgrieder, 16 years ago

added calculate command ;)
In the console, you can now type something like "calculate sin(cos(3+4) - 8)*7" or even "calculate 7>8" and you'll get the result as a double.
The parser was something I've written 3 years ago when I first used C++ (the parser is mainly C).

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