Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 1651 was 1637, checked in by rgrieder, 17 years ago

Finally! The InputManager is now working like I imagined it to. And it's even easier to use it as well.
A little explanation: Every time you change something about the input distribution, it is a change of 'state' represented by the class 'InputState'.
That can be for instance: "console", "game", "gui", etc. Every state has a name and a priority which describes who comes first. Now if one state doesn't handle mouse input or instance, then the one with the next lower priority gets it. To prevent that, you can add the 'EmptyHandler' to the state with setMouseHandler.
InputState is just an abstract base class. There are two classes implementing it: SimpleInputState and ExtendedInputState. The latter allows for multiple input handlers for one single device.

Basically, what you need to know is what you see in Orxonox.cc, InGameConsole.cc and Shell.cc.

  • Property svn:eol-style set to native
File size: 14.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 *      Reto Grieder
24 *   Co-authors:
25 *      Benjamin Knecht <beni_at_orxonox.net>, (C) 2007
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/input/InputManager.h"
60#include "core/input/SimpleInputState.h"
61#include "core/input/KeyBinder.h"
62#include "core/TclBind.h"
63#include "core/Core.h"
64
65// audio
66#include "audio/AudioManager.h"
67
68// network
69#include "network/Server.h"
70#include "network/Client.h"
71
72// objects and tools
73#include "overlays/OverlayGroup.h"
74#include "overlays/console/InGameConsole.h"
75#include "objects/Tickable.h"
76#include "objects/Backlight.h"
77#include "tools/ParticleInterface.h"
78
79#include "GraphicsEngine.h"
80#include "Settings.h"
81#include "Radar.h"
82
83// globals for the server or client
84static network::Client *client_g = 0;
85static network::Server *server_g = 0;
86
87namespace orxonox
88{
89  SetConsoleCommandShortcut(Orxonox, exit).setKeybindMode(KeybindMode::OnPress);
90  SetConsoleCommandShortcut(Orxonox, slomo).setAccessLevel(AccessLevel::Offline).setDefaultValue(0, 1.0).setAxisParamIndex(0).setIsAxisRelative(false);
91  SetConsoleCommandShortcut(Orxonox, setTimeFactor).setAccessLevel(AccessLevel::Offline).setDefaultValue(0, 1.0);
92
93  /**
94    @brief Reference to the only instance of the class.
95  */
96  Orxonox *Orxonox::singletonRef_s = 0;
97
98  /**
99   * Create a new instance of Orxonox. Avoid doing any actual work here.
100   */
101  Orxonox::Orxonox()
102    : ogre_(0)
103    , startLevel_(0)
104    , hud_(0)
105    , radar_(0)
106    //, auMan_(0)
107    , timer_(0)
108    , bAbort_(false)
109    , timefactor_(1.0f)
110    , mode_(STANDALONE)
111    , serverIp_("")
112    , serverPort_(NETWORK_PORT)
113  {
114  }
115
116  /**
117   * Destruct Orxonox.
118   */
119  Orxonox::~Orxonox()
120  {
121    // keep in mind: the order of deletion is very important!
122    Loader::unload(startLevel_);
123    if (this->startLevel_)
124      delete this->startLevel_;
125
126    Loader::unload(hud_);
127    if (this->hud_)
128      delete this->hud_;
129
130    if (this->radar_)
131      delete this->radar_;
132
133    Loader::close();
134    //if (this->auMan_)
135    //  delete this->auMan_;
136    InGameConsole::getInstance().destroy();
137    if (this->timer_)
138      delete this->timer_;
139    dynamic_cast<SimpleInputState*>(InputManager::getState("game"))->removeAndDestroyAllHandlers();
140    InputManager::destroy();
141    GraphicsEngine::getSingleton().destroy();
142
143    if (network::Client::getSingleton())
144      network::Client::destroySingleton();
145    if (server_g)
146      delete network::Server::getSingleton();
147  }
148
149
150  /**
151    Asks the mainloop nicely to abort.
152  */
153  void Orxonox::abortRequest()
154  {
155    COUT(3) << "Orxonox: Abort requested." << std::endl;
156    bAbort_ = true;
157  }
158
159  /**
160   * @return singleton reference
161   */
162  Orxonox* Orxonox::getSingleton()
163  {
164    if (!singletonRef_s)
165      singletonRef_s = new Orxonox();
166    return singletonRef_s;
167  }
168
169  /**
170    @brief Destroys the Orxonox singleton.
171  */
172  void Orxonox::destroySingleton()
173  {
174    if (singletonRef_s)
175      delete singletonRef_s;
176    singletonRef_s = 0;
177  }
178
179  /**
180    @brief Changes the speed of Orxonox
181  */
182  void Orxonox::setTimeFactor(float factor)
183  {
184    float change = factor / Orxonox::getSingleton()->getTimeFactor();
185    Orxonox::getSingleton()->timefactor_ = factor;
186    for (Iterator<ParticleInterface> it = ObjectList<ParticleInterface>::begin(); it; ++it)
187        it->setSpeedFactor(it->getSpeedFactor() * change);
188
189    for (Iterator<Backlight> it = ObjectList<Backlight>::begin(); it; ++it)
190        it->setTimeFactor(Orxonox::getSingleton()->getTimeFactor());
191  }
192
193  /**
194   * initialization of Orxonox object
195   * @param argc argument counter
196   * @param argv list of argumenst
197   * @param path path to config (in home dir or something)
198   */
199  bool Orxonox::init(int argc, char **argv)
200  {
201#ifdef _DEBUG
202    ConfigFileManager::getSingleton()->setFile(CFT_Settings, "orxonox_d.ini");
203#else
204    ConfigFileManager::getSingleton()->setFile(CFT_Settings, "orxonox.ini");
205#endif
206    Factory::createClassHierarchy();
207
208    std::string mode;
209    std::string tempDataPath;
210
211    ArgReader ar(argc, argv);
212    ar.checkArgument("mode", &mode, false);
213    ar.checkArgument("data", &tempDataPath, false);
214    ar.checkArgument("ip",   &serverIp_, false);
215    ar.checkArgument("port", &serverPort_, false);
216    if(ar.errorHandling())
217    {
218      COUT(1) << "Error while parsing command line arguments" << std::endl;
219      COUT(1) << ar.getErrorString();
220      COUT(0) << "Usage:" << std::endl << "orxonox [mode client|server|dedicated|standalone] "
221        << "[--data PATH] [--ip IP] [--port PORT]" << std::endl;
222      return false;
223    }
224
225    if (mode == "client")
226      mode_ = CLIENT;
227    else if (mode == "server")
228      mode_ = SERVER;
229    else if (mode == "dedicated")
230      mode_ = DEDICATED;
231    else
232    {
233      if (mode == "")
234        mode = "standalone";
235      if (mode != "standalone")
236      {
237        COUT(2) << "Warning: mode \"" << mode << "\" doesn't exist. "
238          << "Defaulting to standalone" << std::endl;
239        mode = "standalone";
240      }
241      mode_ = STANDALONE;
242    }
243    COUT(3) << "Orxonox: Mode is " << mode << "." << std::endl;
244
245    if (tempDataPath != "")
246    {
247      if (tempDataPath[tempDataPath.size() - 1] != '/')
248        tempDataPath += "/";
249      Settings::tsetDataPath(tempDataPath);
250    }
251
252    // initialise TCL
253    TclBind::getInstance().setDataPath(Settings::getDataPath());
254
255    //if (mode_ == DEDICATED)
256      // TODO: decide what to do here
257    //else
258
259    // for playable server, client and standalone, the startup
260    // procedure until the GUI is identical
261
262    ogre_ = &GraphicsEngine::getSingleton();
263    if (!ogre_->setup())       // creates ogre root and other essentials
264      return false;
265
266    return true;
267  }
268
269  /**
270   * start modules
271   */
272  bool Orxonox::start()
273  {
274    if (mode_ == DEDICATED)
275    {
276      // do something else
277    }
278    else
279    { // not dedicated server
280      if (!ogre_->loadRenderer())    // creates the render window
281        return false;
282
283      // TODO: Spread this so that this call only initialises things needed for the Console
284      if (!ogre_->initialiseResources())
285        return false;
286
287      // Load the InGameConsole
288      InGameConsole::getInstance().initialise();
289
290      // Calls the InputManager which sets up the input devices.
291      // The render window width and height are used to set up the mouse movement.
292      if (!InputManager::initialise(ogre_->getWindowHandle(),
293            ogre_->getWindowWidth(), ogre_->getWindowHeight(), true, true, true))
294        return false;
295      KeyBinder* keyBinder = new KeyBinder();
296      InputManager::createSimpleInputState("game", 20)->setHandler(keyBinder);
297
298      // TOOD: load the GUI here
299      // set InputManager to GUI mode
300      // TODO: run GUI here
301
302      // The following lines depend very much on the GUI output, so they're probably misplaced here..
303
304      keyBinder->loadBindings();
305      InputManager::requestEnterState("game");
306
307      // create Ogre SceneManager
308      ogre_->createNewScene();
309
310      if (!loadPlayground())
311        return false;
312    }
313
314    switch (mode_)
315    {
316    case SERVER:
317      if (!serverLoad())
318        return false;
319      break;
320    case CLIENT:
321      if (!clientLoad())
322        return false;
323      break;
324    case DEDICATED:
325      if (!serverLoad())
326        return false;
327      break;
328    default:
329      if (!standaloneLoad())
330        return false;
331    }
332
333    return startRenderLoop();
334  }
335
336  /**
337   * Loads everything in the scene except for the actual objects.
338   * This includes HUD, audio..
339   */
340  bool Orxonox::loadPlayground()
341  {
342    // Init audio
343    //auMan_ = new audio::AudioManager();
344    //auMan_->ambientAdd("a1");
345    //auMan_->ambientAdd("a2");
346    //auMan_->ambientAdd("a3");
347    //auMan->ambientAdd("ambient1");
348    //auMan_->ambientStart();
349
350    // Load the HUD
351    COUT(3) << "Orxonox: Loading HUD" << std::endl;
352    hud_ = new Level(Settings::getDataPath() + "overlay/hud.oxo");
353    Loader::load(hud_);
354
355    // Start the Radar
356    this->radar_ = new Radar();
357
358    return true;
359  }
360
361  /**
362   * Level loading method for server mode.
363   */
364  bool Orxonox::serverLoad()
365  {
366    COUT(0) << "Loading level in server mode" << std::endl;
367
368    //server_g = new network::Server(serverPort_);
369    server_g = network::Server::createSingleton(serverPort_);
370
371    if (!loadScene())
372      return false;
373
374    server_g->open();
375
376    return true;
377  }
378
379  /**
380   * Level loading method for client mode.
381   */
382  bool Orxonox::clientLoad()
383  {
384    COUT(0) << "Loading level in client mode" << std::endl;\
385
386    if (serverIp_.compare("") == 0)
387      client_g = network::Client::createSingleton();
388    else
389
390      client_g = network::Client::createSingleton(serverIp_, serverPort_);
391
392    if(!client_g->establishConnection())
393      return false;
394    client_g->tick(0);
395
396    return true;
397  }
398
399  /**
400   * Level loading method for standalone mode.
401   */
402  bool Orxonox::standaloneLoad()
403  {
404    COUT(0) << "Loading level in standalone mode" << std::endl;
405
406    if (!loadScene())
407      return false;
408
409    return true;
410  }
411
412  /**
413   * Helper method to load a level.
414   */
415  bool Orxonox::loadScene()
416  {
417    startLevel_ = new Level("levels/sample.oxw");
418    Loader::open(startLevel_);
419
420    return true;
421  }
422
423
424  /**
425    Main loop of the orxonox game.
426    About the loop: The design is almost exactly like the one in ogre, so that
427    if any part of ogre registers a framelisteners, it will still behave
428    correctly. Furthermore the time smoothing feature from ogre has been
429    implemented too. If turned on (see orxonox constructor), it will calculate
430    the dt_n by means of the recent most dt_n-1, dt_n-2, etc.
431  */
432  bool Orxonox::startRenderLoop()
433  {
434    // first check whether ogre root object has been created
435    if (Ogre::Root::getSingletonPtr() == 0)
436    {
437      COUT(2) << "Orxonox Error: Could not start rendering. No Ogre root object found" << std::endl;
438      return false;
439    }
440    Ogre::Root& ogreRoot = Ogre::Root::getSingleton();
441
442
443    // use the ogre timer class to measure time.
444    if (!timer_)
445      timer_ = new Ogre::Timer();
446
447    unsigned long frameCount = 0;
448   
449    // TODO: this would very well fit into a configValue
450    const unsigned long refreshTime = 200000;
451    unsigned long refreshStartTime = 0;
452    unsigned long tickTime = 0;
453    unsigned long oldFrameCount = 0;
454
455    unsigned long timeBeforeTick = 0;
456    unsigned long timeBeforeTickOld = 0;
457    unsigned long timeAfterTick = 0;
458
459    COUT(3) << "Orxonox: Starting the main loop." << std::endl;
460
461    timer_->reset();
462    while (!bAbort_)
463    {
464      // get current time
465      timeBeforeTickOld = timeBeforeTick;
466      timeBeforeTick    = timer_->getMicroseconds();
467      float dt = (timeBeforeTick - timeBeforeTickOld) / 1000000.0;
468
469
470      // tick the core (needs real time for input and tcl thread management)
471      Core::tick(dt);
472
473      // Call those objects that need the real time
474      for (Iterator<TickableReal> it = ObjectList<TickableReal>::start(); it; ++it)
475        it->tick(dt);
476      // Call the scene objects
477      for (Iterator<Tickable> it = ObjectList<Tickable>::start(); it; ++it)
478        it->tick(dt * this->timefactor_);
479
480      // call server/client with normal dt
481      if (client_g)
482        client_g->tick(dt * this->timefactor_);
483      if (server_g)
484        server_g->tick(dt * this->timefactor_);
485
486
487      // get current time once again
488      timeAfterTick = timer_->getMicroseconds();
489
490      tickTime += timeAfterTick - timeBeforeTick;
491      if (timeAfterTick > refreshStartTime + refreshTime)
492      {
493        GraphicsEngine::getSingleton().setAverageTickTime(
494            (float)tickTime * 0.001 / (frameCount - oldFrameCount));
495        GraphicsEngine::getSingleton().setAverageFramesPerSecond(
496            (float)(frameCount - oldFrameCount) / (timeAfterTick - refreshStartTime) * 1000000.0);
497        oldFrameCount = frameCount;
498        tickTime = 0;
499        refreshStartTime = timeAfterTick;
500      }
501
502
503      // don't forget to call _fireFrameStarted in ogre to make sure
504      // everything goes smoothly
505      Ogre::FrameEvent evt;
506      evt.timeSinceLastFrame = dt;
507      evt.timeSinceLastEvent = dt; // note: same time, but shouldn't matter anyway
508      ogreRoot._fireFrameStarted(evt);
509
510      if (mode_ != DEDICATED)
511      {
512        // Pump messages in all registered RenderWindows
513        // This calls the WindowEventListener objects.
514        Ogre::WindowEventUtilities::messagePump();
515        // make sure the window stays active even when not focused
516        // (probably only necessary on windows)
517        GraphicsEngine::getSingleton().setWindowActivity(true);
518
519        // render
520        ogreRoot._updateAllRenderTargets();
521      }
522
523      // again, just to be sure ogre works fine
524      ogreRoot._fireFrameEnded(evt); // note: uses the same time as _fireFrameStarted
525
526      ++frameCount;
527    }
528
529    if (mode_ == CLIENT)
530      network::Client::getSingleton()->closeConnection();
531    else if (mode_ == SERVER)
532      server_g->close();
533
534    return true;
535  }
536}
Note: See TracBrowser for help on using the repository browser.