Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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