Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/ogre/src/orxonox/Orxonox.cc @ 1243

Last change on this file since 1243 was 1243, checked in by rgrieder, 16 years ago
  • rearranged function calls in Orxonox.cc has yet to be looked at clearly, commented and COUTed
  • works so far, but using 3 little hacks… in hacky classes
File size: 14.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#include "core/TclBind.h"
68
69// audio
70#include "audio/AudioManager.h"
71
72// network
73#include "network/Server.h"
74#include "network/Client.h"
75
76// objects and tools
77#include "tools/Timer.h"
78#include "hud/HUD.h"
79#include "console/InGameConsole.h"
80
81// FIXME: is this really file scope?
82// globals for the server or client
83network::Client *client_g;
84network::Server *server_g;
85
86namespace orxonox
87{
88  ConsoleCommandShortcut(Orxonox, exit, AccessLevel::None);
89  ConsoleCommandShortcut(Orxonox, slomo, AccessLevel::Offline).setDefaultValue(0, 1.0);
90  ConsoleCommandShortcut(Orxonox, setTimeFactor, AccessLevel::Offline).setDefaultValue(0, 1.0);
91  ConsoleCommandShortcut(Orxonox, activateConsole, AccessLevel::None);
92  class Testconsole : public InputBufferListener
93  {
94    public:
95      Testconsole(InputBuffer* ib) : ib_(ib) {}
96      void listen() const
97      {
98        std::cout << "> " << this->ib_->get() << std::endl;
99      }
100      void execute() const
101      {
102        std::cout << ">> " << this->ib_->get() << std::endl;
103        if (!CommandExecutor::execute(this->ib_->get()))
104          std::cout << "Error" << std::endl;
105        this->ib_->clear();
106      }
107      void hintandcomplete() const
108      {
109        std::cout << CommandExecutor::hint(this->ib_->get()) << std::endl;
110        this->ib_->set(CommandExecutor::complete(this->ib_->get()));
111      }
112      void clear() const
113      {
114        this->ib_->clear();
115      }
116      void removeLast() const
117      {
118        this->ib_->removeLast();
119      }
120      void exit() const
121      {
122        InputManager::setInputState(InputManager::IS_NORMAL);
123      }
124
125    private:
126      InputBuffer* ib_;
127  };
128
129  class Calculator
130  {
131  public:
132    static float calculate(const std::string& calculation)
133    {
134      ExprParser expr(calculation);
135      if (expr.getSuccess())
136      {
137        if (expr.getResult() == 42.0)
138          std::cout << "Greetings from the restaurant at the end of the universe." << std::endl;
139        // FIXME: insert modifier to display in full precision
140        std::cout << "Result is: " << expr.getResult() << std::endl;
141        if (expr.getRemains() != "")
142          std::cout << "Warning: Expression could not be parsed to the end! Remains: '"
143              << expr.getRemains() << "'" << std::endl;
144        return expr.getResult();
145      }
146      else
147      {
148        std::cout << "Cannot calculate expression: Parse error" << std::endl;
149        return 0;
150      }
151    }
152  };
153  ConsoleCommandShortcut(Calculator, calculate, AccessLevel::None);
154
155  /**
156    @brief Reference to the only instance of the class.
157  */
158  Orxonox *Orxonox::singletonRef_s = 0;
159
160  /**
161   * create a new instance of Orxonox
162   */
163  Orxonox::Orxonox()
164  {
165    this->mode_ = STANDALONE;
166    this->serverIp_ = "";
167    this->ogre_ = &GraphicsEngine::getSingleton();
168    this->timer_ = 0;
169    //this->auMan_ = 0;
170    this->orxonoxHUD_ = 0;
171    this->inputHandler_ = 0;
172    // turn on frame smoothing by setting a value different from 0
173    this->frameSmoothingTime_ = 0.0f;
174    this->bAbort_ = false;
175    this->timefactor_ = 1.0f;
176  }
177
178  /**
179   * destruct Orxonox
180   */
181  Orxonox::~Orxonox()
182  {
183    // keep in mind: the order of deletion is very important!
184    if (this->orxonoxHUD_)
185      delete this->orxonoxHUD_;
186    Loader::close();
187    InputManager::destroy();
188    //if (this->auMan_)
189    //  delete this->auMan_;
190    if (this->timer_)
191      delete this->timer_;
192    GraphicsEngine::getSingleton().destroy();
193
194    if (client_g)
195      delete client_g;
196    if (server_g)
197      delete server_g;
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  bool 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    std::string dataPath;
242
243    ArgReader ar(argc, argv);
244    ar.checkArgument("mode", mode, false);
245    ar.checkArgument("data", dataPath, false);
246    ar.checkArgument("ip", serverIp_, false);
247    if(ar.errorHandling())
248      return false;
249
250    if (mode == "client")
251      mode_ = CLIENT;
252    else if (mode == "server")
253      mode_ = SERVER;
254    else
255      mode_ = STANDALONE;
256
257    //if (mode_ == DEDICATED)
258      // TODO: decide what to do here
259    //else
260
261    // for playable server, client and standalone, the startup
262    // procedure until the GUI is the identical
263
264    TclBind::getInstance().setDataPath(dataPath);
265    ConfigFileManager::getSingleton()->setFile(CFT_Settings, "orxonox.ini");
266    Factory::createClassHierarchy();
267
268    if (!ogre_->setup(path))       // creates ogre root and other essentials
269      return false;
270
271    return true;
272  }
273
274  /**
275   * start modules
276   */
277  bool Orxonox::start()
278  {
279    //if (mode == DEDICATED)
280    // do something else
281    //else
282
283    if (!ogre_->loadRenderer())    // creates the render window
284      return false;
285
286    // Calls the InputHandler which sets up the input devices.
287    // The render window width and height are used to set up the mouse movement.
288    if (!InputManager::initialise(ogre_->getWindowHandle(),
289          ogre_->getWindowWidth(), ogre_->getWindowHeight()))
290      return false;
291
292    // TODO: Spread this so that this call onyl initialises things needed for the GUI
293    ogre_->initialiseResources();
294
295    // TOOD: load the GUI here
296    // set InputManager to GUI mode
297    InputManager::setInputState(InputManager::IS_GUI);
298    // TODO: run GUI here
299
300    // The following lines depend very much on the GUI, so they're probably misplaced here..
301
302    InputManager::setInputState(InputManager::IS_NONE);
303
304    if (!loadPlayground())
305      return false;
306
307    switch (mode_)
308    {
309    case SERVER:
310      if (!serverLoad())
311        return false;
312      break;
313    case CLIENT:
314      if (!clientLoad())
315        return false;
316      break;
317    default:
318      if (!standaloneLoad())
319        return false;
320    }
321
322    InputManager::setInputState(InputManager::IS_NORMAL);
323
324    return startRenderLoop();
325  }
326
327  bool Orxonox::loadPlayground()
328  {
329    ogre_->createNewScene();
330
331          // Init audio
332    //auMan_ = new audio::AudioManager();
333    //auMan_->ambientAdd("a1");
334    //auMan_->ambientAdd("a2");
335    //auMan_->ambientAdd("a3");
336    //auMan->ambientAdd("ambient1");
337    //auMan_->ambientStart();
338
339    // Load the HUD
340    Ogre::Overlay* hudOverlay = Ogre::OverlayManager::getSingleton().getByName("Orxonox/HUD1.2");
341    orxonoxHUD_ = new HUD();
342    orxonoxHUD_->setEnergyValue(20);
343    orxonoxHUD_->setEnergyDistr(20,20,60);
344    hudOverlay->show();
345
346    InputBuffer* ib = dynamic_cast<InputBuffer*>(InputManager::getKeyHandler("buffer"));
347    /*
348    Testconsole* console = new Testconsole(ib);
349    ib->registerListener(console, &Testconsole::listen, true);
350    ib->registerListener(console, &Testconsole::execute, '\r', false);
351    ib->registerListener(console, &Testconsole::hintandcomplete, '\t', true);
352    ib->registerListener(console, &Testconsole::clear, '§', true);
353    ib->registerListener(console, &Testconsole::removeLast, '\b', true);
354    ib->registerListener(console, &Testconsole::exit, (char)0x1B, true);
355    */
356    orxonoxConsole_ = new InGameConsole(ib);
357    ib->registerListener(orxonoxConsole_, &InGameConsole::listen, true);
358    ib->registerListener(orxonoxConsole_, &InGameConsole::execute, '\r', false);
359    ib->registerListener(orxonoxConsole_, &InGameConsole::hintandcomplete, '\t', true);
360    ib->registerListener(orxonoxConsole_, &InGameConsole::clear, '§', true);
361    ib->registerListener(orxonoxConsole_, &InGameConsole::removeLast, '\b', true);
362    ib->registerListener(orxonoxConsole_, &InGameConsole::exit, (char)0x1B, true);
363
364    return true;
365  }
366
367  bool Orxonox::serverLoad()
368  {
369    COUT(2) << "initialising server" << std::endl;
370
371    server_g = new network::Server();
372
373    if (!loadScene())
374      return false;
375
376    server_g->open();
377
378    return true;
379  }
380
381  bool Orxonox::clientLoad()
382  {
383    COUT(2) << "initialising client" << std::endl;\
384
385    if (serverIp_.compare("") == 0)
386      client_g = new network::Client();
387    else
388      client_g = new network::Client(serverIp_, NETWORK_PORT);
389
390    client_g->establishConnection();
391    client_g->tick(0);
392
393    return true;
394  }
395
396  bool Orxonox::standaloneLoad()
397  {
398    COUT(2) << "initialising standalone mode" << std::endl;
399
400    if (!loadScene())
401      return false;
402
403    return true;
404  }
405
406  bool Orxonox::loadScene()
407  {
408    Level* startlevel = new Level("levels/sample.oxw");
409    Loader::open(startlevel);
410
411    return true;
412  }
413
414
415  /**
416    Main loop of the orxonox game.
417    About the loop: The design is almost exactly like the one in ogre, so that
418    if any part of ogre registers a framelisteners, it will still behave
419    correctly. Furthermore the time smoothing feature from ogre has been
420    implemented too. If turned on (see orxonox constructor), it will calculate
421    the dt_n by means of the recent most dt_n-1, dt_n-2, etc.
422  */
423  bool Orxonox::startRenderLoop()
424  {
425    // first check whether ogre root object has been created
426    if (Ogre::Root::getSingletonPtr() == 0)
427    {
428      COUT(2) << "*** Orxonox Error: Could not start rendering. No Ogre root object found" << std::endl;
429      return false;
430    }
431    Ogre::Root& ogreRoot = Ogre::Root::getSingleton();
432
433
434    // Contains the times of recently fired events
435    // eventTimes[4] is the list for the times required for the fps counter
436    std::deque<unsigned long> eventTimes[4];
437    // Clear event times
438    for (int i = 0; i < 4; ++i)
439      eventTimes[i].clear();
440    // fill the fps time list with zeros
441    for (int i = 0; i < 50; i++)
442      eventTimes[3].push_back(0);
443
444    // use the ogre timer class to measure time.
445    if (!timer_)
446      timer_ = new Ogre::Timer();
447    timer_->reset();
448
449          while (!bAbort_)
450          {
451                  // Pump messages in all registered RenderWindows
452      // This calls the WindowEventListener objects.
453      Ogre::WindowEventUtilities::messagePump();
454
455      // get current time
456      unsigned long now = timer_->getMilliseconds();
457      eventTimes[3].push_back(now);
458      eventTimes[3].erase(eventTimes[3].begin());
459
460      // create an event to pass to the frameStarted method in ogre
461      Ogre::FrameEvent evt;
462      evt.timeSinceLastEvent = calculateEventTime(now, eventTimes[0]);
463      evt.timeSinceLastFrame = calculateEventTime(now, eventTimes[1]);
464
465      // show the current time in the HUD
466      orxonoxHUD_->setTime((int)now, 0);
467      orxonoxHUD_->setRocket2(ogreRoot.getCurrentFrameNumber());
468      if (eventTimes[3].back() - eventTimes[3].front() != 0)
469        orxonoxHUD_->setRocket1((int)(50000.0f/(eventTimes[3].back() - eventTimes[3].front())));
470
471      // Iterate through all Tickables and call their tick(dt) function
472      for (Iterator<Tickable> it = ObjectList<Tickable>::start(); it; ++it)
473        it->tick((float)evt.timeSinceLastFrame * this->timefactor_);
474      orxonoxConsole_->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    return true;
496  }
497
498  /**
499    Method for calculating the average time between recently fired events.
500    Code directly taken from OgreRoot.cc
501    @param now The current time in ms.
502    @param type The type of event to be considered.
503  */
504  float Orxonox::calculateEventTime(unsigned long now, std::deque<unsigned long> &times)
505  {
506    // Calculate the average time passed between events of the given type
507    // during the last frameSmoothingTime_ seconds.
508
509    times.push_back(now);
510
511    if(times.size() == 1)
512      return 0;
513
514    // Times up to frameSmoothingTime_ seconds old should be kept
515    unsigned long discardThreshold = (unsigned long)(frameSmoothingTime_ * 1000.0f);
516
517    // Find the oldest time to keep
518    std::deque<unsigned long>::iterator it  = times.begin();
519    // We need at least two times
520    std::deque<unsigned long>::iterator end = times.end() - 2;
521
522    while(it != end)
523    {
524      if (now - *it > discardThreshold)
525        ++it;
526      else
527        break;
528    }
529
530    // Remove old times
531    times.erase(times.begin(), it);
532
533    return (float)(times.back() - times.front()) / ((times.size() - 1) * 1000);
534  }
535
536  void Orxonox::activateConsole()
537  {
538    InputManager::setInputState(InputManager::IS_CONSOLE);
539  }
540}
Note: See TracBrowser for help on using the repository browser.