Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/network/src/orxonox/Orxonox.cc @ 1494

Last change on this file since 1494 was 1494, checked in by rgrieder, 16 years ago
  • set the svn:eol-style property to all files so, that where ever you check out, you'll get the right line endings (had to change every file with mixed endings to windows in order to set the property)
  • Property svn:eol-style set to native
File size: 13.5 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
54// core
55#include "core/ConfigFileManager.h"
56#include "core/ConsoleCommand.h"
57#include "core/Debug.h"
58#include "core/Loader.h"
59#include "core/Tickable.h"
60#include "core/InputManager.h"
61#include "core/TclBind.h"
62
63// audio
64#include "audio/AudioManager.h"
65
66// network
67#include "network/Server.h"
68#include "network/Client.h"
69
70// objects and tools
71#include "hud/HUD.h"
72#include <Ogre.h>
73
74#include "GraphicsEngine.h"
75
76// FIXME: is this really file scope?
77// globals for the server or client
78network::Client *client_g;
79network::Server *server_g;
80
81namespace orxonox
82{
83  SetConsoleCommandShortcut(Orxonox, exit).setKeybindMode(KeybindMode::OnPress);
84  SetConsoleCommandShortcut(Orxonox, slomo).setAccessLevel(AccessLevel::Offline).setDefaultValue(0, 1.0).setAxisParamIndex(0).setIsAxisRelative(false);
85  SetConsoleCommandShortcut(Orxonox, setTimeFactor).setAccessLevel(AccessLevel::Offline).setDefaultValue(0, 1.0);
86  /**
87    @brief Reference to the only instance of the class.
88  */
89  Orxonox *Orxonox::singletonRef_s = 0;
90
91  /**
92   * Create a new instance of Orxonox. Avoid doing any actual work here.
93   */
94  Orxonox::Orxonox() :
95    ogre_(0),
96    //auMan_(0),
97    timer_(0),
98    // turn on frame smoothing by setting a value different from 0
99    frameSmoothingTime_(0.0f),
100    orxonoxHUD_(0),
101    bAbort_(false),
102    timefactor_(1.0f),
103    mode_(STANDALONE),
104    serverIp_(""),
105    serverPort_(NETWORK_PORT)
106  {
107  }
108
109  /**
110   * Destruct Orxonox.
111   */
112  Orxonox::~Orxonox()
113  {
114    // keep in mind: the order of deletion is very important!
115//    if (this->orxonoxHUD_)
116//      delete this->orxonoxHUD_;
117    Loader::close();
118    InputManager::destroy();
119    //if (this->auMan_)
120    //  delete this->auMan_;
121    if (this->timer_)
122      delete this->timer_;
123    GraphicsEngine::getSingleton().destroy();
124
125    if (network::Client::getSingleton())
126      network::Client::destroySingleton();
127    if (server_g)
128      delete server_g;
129  }
130
131
132  /**
133    Asks the mainloop nicely to abort.
134  */
135  void Orxonox::abortRequest()
136  {
137    COUT(3) << "Orxonox: Abort requested." << std::endl;
138    bAbort_ = true;
139  }
140
141  /**
142   * @return singleton reference
143   */
144  Orxonox* Orxonox::getSingleton()
145  {
146    if (!singletonRef_s)
147      singletonRef_s = new Orxonox();
148    return singletonRef_s;
149  }
150
151  /**
152    @brief Destroys the Orxonox singleton.
153  */
154  void Orxonox::destroySingleton()
155  {
156    if (singletonRef_s)
157      delete singletonRef_s;
158    singletonRef_s = 0;
159  }
160
161  /**
162   * initialization of Orxonox object
163   * @param argc argument counter
164   * @param argv list of argumenst
165   * @param path path to config (in home dir or something)
166   */
167  bool Orxonox::init(int argc, char **argv, std::string path)
168  {
169    //TODO: find config file (assuming executable directory)
170    //TODO: read config file
171    //TODO: give config file to Ogre
172    std::string mode;
173    std::string dataPath;
174
175    ArgReader ar(argc, argv);
176    ar.checkArgument("mode", mode, false);
177    ar.checkArgument("data", dataPath, false);
178    ar.checkArgument("ip", serverIp_, false);
179    ar.checkArgument("port", serverPort_, false);
180    if(ar.errorHandling())
181      return false;
182
183    if (mode == "client")
184      mode_ = CLIENT;
185    else if (mode == "server")
186      mode_ = SERVER;
187    else if (mode == "dedicated")
188      mode_ = DEDICATED;
189    else
190    {
191      mode = "standalone";
192      mode_ = STANDALONE;
193    }
194    COUT(3) << "Orxonox: Mode is " << mode << "." << std::endl;
195
196    //if (mode_ == DEDICATED)
197      // TODO: decide what to do here
198    //else
199
200    // for playable server, client and standalone, the startup
201    // procedure until the GUI is identical
202
203    ConfigFileManager::getSingleton()->setFile(CFT_Settings, "orxonox.ini");
204    Factory::createClassHierarchy();
205
206    ogre_ = &GraphicsEngine::getSingleton();
207    if (!ogre_->setup(path))       // creates ogre root and other essentials
208      return false;
209
210    return true;
211  }
212
213  /**
214   * start modules
215   */
216  bool Orxonox::start()
217  {
218    if (mode_ == DEDICATED)
219    {
220      // do something else
221    }
222    else
223    { // not dedicated server
224      if (!ogre_->loadRenderer())    // creates the render window
225        return false;
226
227      // Calls the InputManager which sets up the input devices.
228      // The render window width and height are used to set up the mouse movement.
229      if (!InputManager::initialise(ogre_->getWindowHandle(),
230            ogre_->getWindowWidth(), ogre_->getWindowHeight(), true, true, true))
231        return false;
232
233      // TODO: Spread this so that this call only initialises things needed for the GUI
234      if (!ogre_->initialiseResources())
235        return false;
236
237      // TOOD: load the GUI here
238      // set InputManager to GUI mode
239      InputManager::setInputState(InputManager::IS_GUI);
240      // TODO: run GUI here
241
242      // The following lines depend very much on the GUI output, so they're probably misplaced here..
243
244      InputManager::setInputState(InputManager::IS_NONE);
245
246      // create Ogre SceneManager
247      ogre_->createNewScene();
248
249      if (!loadPlayground())
250        return false;
251    }
252
253    switch (mode_)
254    {
255    case SERVER:
256      if (!serverLoad())
257        return false;
258      break;
259    case CLIENT:
260      if (!clientLoad())
261        return false;
262      break;
263    case DEDICATED:
264      if (!serverLoad())
265        return false;
266      break;
267    default:
268      if (!standaloneLoad())
269        return false;
270    }
271
272    InputManager::setInputState(InputManager::IS_NORMAL);
273
274    return startRenderLoop();
275  }
276
277  /**
278   * Loads everything in the scene except for the actual objects.
279   * This includes HUD, Console..
280   */
281  bool Orxonox::loadPlayground()
282  {
283    // Init audio
284    //auMan_ = new audio::AudioManager();
285    //auMan_->ambientAdd("a1");
286    //auMan_->ambientAdd("a2");
287    //auMan_->ambientAdd("a3");
288    //auMan->ambientAdd("ambient1");
289    //auMan_->ambientStart();
290
291    // Load the HUD
292    COUT(3) << "Orxonox: Loading HUD..." << std::endl;
293    orxonoxHUD_ = &HUD::getSingleton();
294    return true;
295  }
296
297  /**
298   * Level loading method for server mode.
299   */
300  bool Orxonox::serverLoad()
301  {
302    COUT(2) << "Loading level in server mode" << std::endl;
303
304    server_g = new network::Server(serverPort_);
305
306    if (!loadScene())
307      return false;
308
309    server_g->open();
310
311    return true;
312  }
313
314  /**
315   * Level loading method for client mode.
316   */
317  bool Orxonox::clientLoad()
318  {
319    COUT(2) << "Loading level in client mode" << std::endl;\
320
321    if (serverIp_.compare("") == 0)
322      client_g = network::Client::createSingleton();
323    else
324
325      client_g = network::Client::createSingleton(serverIp_, serverPort_);
326
327    if(!client_g->establishConnection())
328      return false;
329    client_g->tick(0);
330
331    return true;
332  }
333
334  /**
335   * Level loading method for standalone mode.
336   */
337  bool Orxonox::standaloneLoad()
338  {
339    COUT(2) << "Loading level in standalone mode" << std::endl;
340
341    if (!loadScene())
342      return false;
343
344    return true;
345  }
346
347  /**
348   * Helper method to load a level.
349   */
350  bool Orxonox::loadScene()
351  {
352    Level* startlevel = new Level("levels/sample.oxw");
353    Loader::open(startlevel);
354   
355    // HACK: shader stuff for presentation
356    /*Ogre::SceneManager* mSceneMgr = GraphicsEngine::getSingleton().getSceneManager();
357    mSceneMgr->setAmbientLight(ColourValue(0.4,0.4,0.4));
358    Ogre::Light* dirlight = mSceneMgr->createLight("Light1");
359
360    dirlight->setType(Ogre::Light::LT_DIRECTIONAL);
361    dirlight->setDirection(Vector3( 0, 1, 5 ));
362    dirlight->setDiffuseColour(ColourValue(0.6, 0.6, 0.4));
363    dirlight->setSpecularColour(ColourValue(1.0, 1.0, 1.0));*/
364   
365    return true;
366  }
367
368
369  /**
370    Main loop of the orxonox game.
371    About the loop: The design is almost exactly like the one in ogre, so that
372    if any part of ogre registers a framelisteners, it will still behave
373    correctly. Furthermore the time smoothing feature from ogre has been
374    implemented too. If turned on (see orxonox constructor), it will calculate
375    the dt_n by means of the recent most dt_n-1, dt_n-2, etc.
376  */
377  bool Orxonox::startRenderLoop()
378  {
379    // first check whether ogre root object has been created
380    if (Ogre::Root::getSingletonPtr() == 0)
381    {
382      COUT(2) << "Orxonox Error: Could not start rendering. No Ogre root object found" << std::endl;
383      return false;
384    }
385    Ogre::Root& ogreRoot = Ogre::Root::getSingleton();
386
387
388    // Contains the times of recently fired events
389    // eventTimes[4] is the list for the times required for the fps counter
390    std::deque<unsigned long> eventTimes[3];
391    // Clear event times
392    for (int i = 0; i < 3; ++i)
393      eventTimes[i].clear();
394
395    // use the ogre timer class to measure time.
396    if (!timer_)
397      timer_ = new Ogre::Timer();
398    timer_->reset();
399
400    float renderTime = 0.0f;
401    float frameTime = 0.0f;
402    clock_t time = 0;
403
404    //Ogre::SceneManager* mSceneMgr = GraphicsEngine::getSingleton().getSceneManager();
405    //Ogre::Viewport* mViewport = mSceneMgr->getCurrentViewport();
406   
407    //Ogre::CompositorManager::getSingleton().addCompositor(mViewport, "Bloom");
408    //Ogre::CompositorManager::getSingleton().addCompositor(mViewport, "MotionBlur");
409
410    COUT(3) << "Orxonox: Starting the main loop." << std::endl;
411    while (!bAbort_)
412    {
413      // get current time
414      unsigned long now = timer_->getMilliseconds();
415
416      // create an event to pass to the frameStarted method in ogre
417      Ogre::FrameEvent evt;
418      evt.timeSinceLastEvent = calculateEventTime(now, eventTimes[0]);
419      evt.timeSinceLastFrame = calculateEventTime(now, eventTimes[1]);
420      frameTime += evt.timeSinceLastFrame;
421
422      // show the current time in the HUD
423      // HUD::getSingleton().setTime(now);
424      if (mode_ != DEDICATED && frameTime > 0.4f)
425      {
426        HUD::getSingleton().setRenderTimeRatio(renderTime / frameTime);
427        frameTime = 0.0f;
428        renderTime = 0.0f;
429      }
430
431      // Call those objects that need the real time
432      for (Iterator<TickableReal> it = ObjectList<TickableReal>::start(); it; ++it)
433        it->tick((float)evt.timeSinceLastFrame);
434      // Call the scene objects
435      for (Iterator<Tickable> it = ObjectList<Tickable>::start(); it; ++it)
436        it->tick((float)evt.timeSinceLastFrame * this->timefactor_);
437
438      // don't forget to call _fireFrameStarted in ogre to make sure
439      // everything goes smoothly
440      ogreRoot._fireFrameStarted(evt);
441
442      // get current time
443      now = timer_->getMilliseconds();
444      calculateEventTime(now, eventTimes[2]);
445
446      if (mode_ != DEDICATED)
447      {
448        // Pump messages in all registered RenderWindows
449        // This calls the WindowEventListener objects.
450        Ogre::WindowEventUtilities::messagePump();
451
452        // render
453        ogreRoot._updateAllRenderTargets();
454      }
455
456      // get current time
457      now = timer_->getMilliseconds();
458
459      // create an event to pass to the frameEnded method in ogre
460      evt.timeSinceLastEvent = calculateEventTime(now, eventTimes[0]);
461      renderTime += calculateEventTime(now, eventTimes[2]);
462
463      // again, just to be sure ogre works fine
464      ogreRoot._fireFrameEnded(evt);
465      //msleep(200);
466    }
467
468    if (mode_ == CLIENT)
469      network::Client::getSingleton()->closeConnection();
470    else if (mode_ == SERVER)
471      server_g->close();
472
473    return true;
474  }
475
476  /**
477    Method for calculating the average time between recently fired events.
478    Code directly taken from OgreRoot.cc
479    @param now The current time in ms.
480    @param type The type of event to be considered.
481  */
482  float Orxonox::calculateEventTime(unsigned long now, std::deque<unsigned long> &times)
483  {
484    // Calculate the average time passed between events of the given type
485    // during the last frameSmoothingTime_ seconds.
486
487    times.push_back(now);
488
489    if(times.size() == 1)
490      return 0;
491
492    // Times up to frameSmoothingTime_ seconds old should be kept
493    unsigned long discardThreshold = (unsigned long)(frameSmoothingTime_ * 1000.0f);
494
495    // Find the oldest time to keep
496    std::deque<unsigned long>::iterator it  = times.begin();
497    // We need at least two times
498    std::deque<unsigned long>::iterator end = times.end() - 2;
499
500    while(it != end)
501    {
502      if (now - *it > discardThreshold)
503        ++it;
504      else
505        break;
506    }
507
508    // Remove old times
509    times.erase(times.begin(), it);
510
511    return (float)(times.back() - times.front()) / ((times.size() - 1) * 1000);
512  }
513}
Note: See TracBrowser for help on using the repository browser.