Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 900 was 900, checked in by rgrieder, 16 years ago
  • implemented a new main loop, calling the ticks and the timers. —> removed the (Timer/Tick)FrameListener and displaced the code to Orxonox.cc (don't like it yet)
File size: 17.2 KB
Line 
1/*
2 *   ORXONOX - the hottest 3D action shooter ever to exist
3 *
4 *
5 *   License notice:
6 *
7 *   This program is free software; you can redistribute it and/or
8 *   modify it under the terms of the GNU General Public License
9 *   as published by the Free Software Foundation; either version 2
10 *   of the License, or (at your option) any later version.
11 *
12 *   This program is distributed in the hope that it will be useful,
13 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
14 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 *   GNU General Public License for more details.
16 *
17 *   You should have received a copy of the GNU General Public License
18 *   along with this program; if not, write to the Free Software
19 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20 *
21 *   Author:
22 *      Benjamin Knecht <beni_at_orxonox.net>, (C) 2007
23 *   Co-authors:
24 *      ...
25 *
26 */
27
28/**
29 @file  Orxonox.cc
30 @brief Orxonox Main Class
31 */
32
33// Precompiled Headers
34#include "OrxonoxStableHeaders.h"
35
36//****** OGRE ******
37#include <OgreException.h>
38#include <OgreRoot.h>
39#include <OgreFrameListener.h>
40#include <OgreRenderWindow.h>
41#include <OgreTextureManager.h>
42#include <OgreResourceGroupManager.h>
43#include <OgreConfigFile.h>
44#include <OgreOverlay.h>
45#include <OgreOverlayManager.h>
46#include <OgreTimer.h>
47#include <OgreWindowEventUtilities.h>
48
49//****** OIS *******
50#include <OIS/OIS.h>
51
52//****** STD *******
53#include <iostream>
54#include <exception>
55
56//***** ORXONOX ****
57//misc
58#include "util/Sleep.h"
59
60// loader and audio
61//#include "loader/LevelLoader.h"
62#include "audio/AudioManager.h"
63
64// network
65#include "network/Server.h"
66#include "network/Client.h"
67#include "network/NetworkFrameListener.h"
68
69// objects
70#include "objects/Tickable.h"
71#include "tools/Timer.h"
72#include "objects/NPC.h"
73#include "core/ArgReader.h"
74#include "core/Factory.h"
75#include "core/Debug.h"
76#include "core/Loader.h"
77#include "hud/HUD.h"
78#include "objects/weapon/BulletManager.h"
79#include "GraphicsEngine.h"
80
81#include "Orxonox.h"
82
83namespace orxonox
84{
85   // put this in a seperate Class or solve the problem in another fashion
86  class OrxListener : public Ogre::FrameListener
87  {
88    public:
89      OrxListener(OIS::Keyboard *keyboard, audio::AudioManager*  auMan, gameMode mode)
90      {
91        mKeyboard = keyboard;
92        mode_=mode;
93        auMan_ = auMan;
94      }
95
96      bool frameStarted(const Ogre::FrameEvent& evt)
97      {
98        auMan_->update();
99        updateAI();
100
101        if(mode_ == PRESENTATION)
102          server_g->tick(evt.timeSinceLastFrame);
103        else if(mode_ == CLIENT)
104          client_g->tick(evt.timeSinceLastFrame);
105
106        usleep(10);
107
108        mKeyboard->capture();
109        return !mKeyboard->isKeyDown(OIS::KC_ESCAPE);
110      }
111
112      void updateAI()
113      {
114        for(Iterator<NPC> it = ObjectList<NPC>::start(); it; ++it)
115        {
116          it->update();
117        }
118      }
119
120    private:
121      gameMode mode_;
122      OIS::Keyboard *mKeyboard;
123      audio::AudioManager*  auMan_;
124  };
125
126  // init static singleton reference of Orxonox
127  Orxonox* Orxonox::singletonRef_ = NULL;
128
129  /**
130   * create a new instance of Orxonox
131   */
132  Orxonox::Orxonox()
133  {
134    this->ogre_ = new GraphicsEngine();
135    this->dataPath_ = "";
136//    this->loader_ = 0;
137    this->auMan_ = 0;
138    this->singletonRef_ = 0;
139    this->keyboard_ = 0;
140    this->mouse_ = 0;
141    this->inputManager_ = 0;
142    this->frameListener_ = 0;
143    this->root_ = 0;
144    // turn frame smoothing on by setting a value different from 0
145    this->frameSmoothingTime_ = 0.1f;
146  }
147
148  /**
149   * destruct Orxonox
150   */
151  Orxonox::~Orxonox()
152  {
153    // nothing to delete as for now
154  }
155
156  /**
157   * initialization of Orxonox object
158   * @param argc argument counter
159   * @param argv list of arguments
160   * @param path path to config (in home dir or something)
161   */
162  void Orxonox::init(int argc, char **argv, std::string path)
163  {
164    //TODO: find config file (assuming executable directory)
165    //TODO: read config file
166    //TODO: give config file to Ogre
167    std::string mode;
168//     if(argc>=2)
169//       mode = std::string(argv[1]);
170//     else
171//       mode = "";
172    ArgReader ar = ArgReader(argc, argv);
173    ar.checkArgument("mode", mode, false);
174    ar.checkArgument("data", this->dataPath_, false);
175    ar.checkArgument("ip", serverIp_, false);
176    //mode = "presentation";
177    if(ar.errorHandling()) die();
178    if(mode == std::string("server"))
179    {
180      serverInit(path);
181      mode_ = SERVER;
182    }
183    else if(mode == std::string("client"))
184    {
185      clientInit(path);
186      mode_ = CLIENT;
187    }
188    else if(mode == std::string("presentation"))
189    {
190      serverInit(path);
191      mode_ = PRESENTATION;
192    }
193    else{
194      standaloneInit(path);
195      mode_ = STANDALONE;
196    }
197  }
198
199  /**
200   * start modules
201   */
202  void Orxonox::start()
203  {
204    //TODO: start modules
205    ogre_->startRender();
206    //TODO: run engine
207    Factory::createClassHierarchy();
208    createScene();
209    setupScene();
210    setupInputSystem();
211    if(mode_!=CLIENT){ // remove this in future ---- presentation hack
212    }
213    else
214      std::cout << "client here" << std::endl;
215    createFrameListener();
216    switch(mode_){
217    case PRESENTATION:
218      //ogre_->getRoot()->addFrameListener(new network::ServerFrameListener());
219      //std::cout << "could not add framelistener" << std::endl;
220      server_g->open();
221      break;
222    case CLIENT:
223      client_g->establishConnection();
224      break;
225    case SERVER:
226    case STANDALONE:
227    default:
228      break;
229    }
230    startRenderLoop();
231  }
232
233  /**
234   * @return singleton object
235   */
236  Orxonox* Orxonox::getSingleton()
237  {
238    if (!singletonRef_)
239      singletonRef_ = new Orxonox();
240    return singletonRef_;
241  }
242
243  /**
244   * error kills orxonox
245   */
246  void Orxonox::die(/* some error code */)
247  {
248    //TODO: destroy and destruct everything and print nice error msg
249    delete this;
250  }
251
252  void Orxonox::standaloneInit(std::string path)
253  {
254    ogre_->setConfigPath(path);
255    ogre_->setup();
256    root_ = ogre_->getRoot();
257    if(!ogre_->load()) die(/* unable to load */);
258
259    //defineResources();
260    //setupRenderSystem();
261    //createRenderWindow();
262    //initializeResourceGroups();
263    /*createScene();
264    setupScene();
265    setupInputSystem();
266    createFrameListener();
267    Factory::createClassHierarchy();
268    startRenderLoop();*/
269  }
270
271  void Orxonox::playableServer(std::string path)
272  {
273    ogre_->setConfigPath(path);
274    ogre_->setup();
275    root_ = ogre_->getRoot();
276    defineResources();
277    setupRenderSystem();
278    createRenderWindow();
279    initializeResourceGroups();
280    setupInputSystem();
281    Factory::createClassHierarchy();
282    createScene();
283    setupScene();
284    createFrameListener();
285    try{
286      server_g = new network::Server(); //!< add port and bindadress
287      server_g->open(); //!< open server and create listener thread
288      if(ogre_ && ogre_->getRoot())
289        ogre_->getRoot()->addFrameListener(new network::ServerFrameListener()); // adds a framelistener for the server
290      COUT(3) << "Info: network framelistener added" << std::endl;
291    }
292    catch(...)
293    {
294      COUT(1) << "Error: There was a problem initialising the server :(" << std::endl;
295    }
296    startRenderLoop();
297  }
298
299  void Orxonox::standalone(){
300
301
302
303  }
304
305  void Orxonox::serverInit(std::string path)
306  {
307    COUT(2) << "initialising server" << std::endl;
308    ogre_->setConfigPath(path);
309    ogre_->setup();
310    server_g = new network::Server(); // FIXME add some settings if wanted
311    if(!ogre_->load()) die(/* unable to load */);
312    // FIXME add network framelistener
313  }
314
315  void Orxonox::clientInit(std::string path)
316  {
317    COUT(2) << "initialising client" << std::endl;
318    ogre_->setConfigPath(path);
319    ogre_->setup();
320    if(serverIp_.compare("")==0)
321      client_g = new network::Client();
322    else
323      client_g = new network::Client(serverIp_, 55556);
324    if(!ogre_->load()) die(/* unable to load */);
325    ogre_->getRoot()->addFrameListener(new network::ClientFrameListener());
326  }
327
328  void Orxonox::defineResources()
329  {
330    std::string secName, typeName, archName;
331    Ogre::ConfigFile cf;
332#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
333    cf.load(macBundlePath() + "/Contents/Resources/resources.cfg");
334#else
335    cf.load(dataPath_ + "resources.cfg");
336#endif
337
338    Ogre::ConfigFile::SectionIterator seci = cf.getSectionIterator();
339    while (seci.hasMoreElements())
340    {
341      secName = seci.peekNextKey();
342      Ogre::ConfigFile::SettingsMultiMap *settings = seci.getNext();
343      Ogre::ConfigFile::SettingsMultiMap::iterator i;
344      for (i = settings->begin(); i != settings->end(); ++i)
345      {
346        typeName = i->first;
347        archName = i->second;
348#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
349        Ogre::ResourceGroupManager::getSingleton().addResourceLocation( std::string(macBundlePath() + "/" + archName), typeName, secName);
350#else
351        Ogre::ResourceGroupManager::getSingleton().addResourceLocation( archName, typeName, secName);
352#endif
353      }
354    }
355  }
356
357  void Orxonox::setupRenderSystem()
358  {
359    if (!root_->restoreConfig() && !root_->showConfigDialog())
360      throw Ogre::Exception(52, "User canceled the config dialog!", "OrxApplication::setupRenderSystem()");
361  }
362
363  void Orxonox::createRenderWindow()
364  {
365    root_->initialise(true, "OrxonoxV2");
366  }
367
368  void Orxonox::initializeResourceGroups()
369  {
370    Ogre::TextureManager::getSingleton().setDefaultNumMipmaps(5);
371    Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
372  }
373
374  void Orxonox::createScene(void)
375  {
376        // Init audio
377    auMan_ = new audio::AudioManager();
378
379    bulletMgr_ = new BulletManager();
380
381    // load this file from config
382//    loader_ = new loader::LevelLoader("sample.oxw");
383//    loader_->loadLevel();
384    Level* startlevel = new Level("levels/sample.oxw");
385    Loader::open(startlevel);
386
387    Ogre::Overlay* hudOverlay = Ogre::OverlayManager::getSingleton().getByName("Orxonox/HUD1.2");
388    //HUD* orxonoxHud;
389    orxonoxHUD_ = new HUD();
390    orxonoxHUD_->setEnergyValue(20);
391    orxonoxHUD_->setEnergyDistr(20,20,60);
392    hudOverlay->show();
393
394        /*
395    auMan_->ambientAdd("a1");
396    auMan_->ambientAdd("a2");
397    auMan_->ambientAdd("a3");
398                                //auMan->ambientAdd("ambient1");
399    auMan_->ambientStart();*/
400  }
401
402
403  void Orxonox::setupScene()
404  {
405//    SceneManager *mgr = ogre_->getSceneManager();
406
407
408//    SceneNode* node = (SceneNode*)mgr->getRootSceneNode()->getChild("OgreHeadNode");
409//     SceneNode *node = mgr->getRootSceneNode()->createChildSceneNode("OgreHeadNode", Vector3(0,0,0));
410
411
412/*
413    particle::ParticleInterface *e = new particle::ParticleInterface(mgr,"engine","Orxonox/strahl");
414    e->particleSystem_->setParameter("local_space","true");
415    e->setPositionOfEmitter(0, Vector3(0,-10,0));
416    e->setDirection(Vector3(0,0,-1));
417    e->addToSceneNode(node);
418*/
419  }
420
421
422  void Orxonox::setupInputSystem()
423  {
424    size_t windowHnd = 0;
425    std::ostringstream windowHndStr;
426    OIS::ParamList pl;
427
428    // fixes auto repeat problem
429    #if defined OIS_LINUX_PLATFORM
430      pl.insert(std::make_pair(std::string("XAutoRepeatOn"), std::string("true")));
431    #endif
432
433      Ogre::RenderWindow *win = ogre_->getRoot()->getAutoCreatedWindow();
434    win->getCustomAttribute("WINDOW", &windowHnd);
435    windowHndStr << windowHnd;
436    pl.insert(std::make_pair(std::string("WINDOW"), windowHndStr.str()));
437    inputManager_ = OIS::InputManager::createInputSystem(pl);
438
439    try
440    {
441      keyboard_ = static_cast<OIS::Keyboard*>(inputManager_->createInputObject(OIS::OISKeyboard, false));
442      mouse_ = static_cast<OIS::Mouse*>(inputManager_->createInputObject(OIS::OISMouse, true));
443    }
444    catch (const OIS::Exception &e)
445    {
446      throw new Ogre::Exception(42, e.eText, "OrxApplication::setupInputSystem");
447    }
448  }
449
450  // FIXME we actually want to do this differently...
451  void Orxonox::createFrameListener()
452  {
453    //TickFrameListener* TickFL = new TickFrameListener();
454    //ogre_->getRoot()->addFrameListener(TickFL);
455
456    //TimerFrameListener* TimerFL = new TimerFrameListener();
457    //ogre_->getRoot()->addFrameListener(TimerFL);
458
459    //if(mode_!=CLIENT) // FIXME just a hack ------- remove this in future
460      frameListener_ = new OrxListener(keyboard_, auMan_, mode_);
461    ogre_->getRoot()->addFrameListener(frameListener_);
462  }
463
464  void Orxonox::startRenderLoop()
465  {
466    // FIXME
467    // this is a hack!!!
468    // the call to reset the mouse clipping size should probably be somewhere
469    // else, however this works for the moment.
470    unsigned int width, height, depth;
471    int left, top;
472    ogre_->getRoot()->getAutoCreatedWindow()->getMetrics(width, height, depth, left, top);
473
474    if(mode_!=CLIENT){
475      const OIS::MouseState &ms = mouse_->getMouseState();
476      ms.width = width;
477      ms.height = height;
478    }
479    //ogre_->getRoot()->startRendering();
480    mainLoop();
481  }
482
483  /**
484    Main loop of the orxonox game.
485    This is a new solution, using the ogre engine instead of beeing used by it.
486    An alternative solution would be to simply use the timer of the Root object,
487    but that implies using Ogre in any case. There would be no way to test
488    our code without the help of the root object.
489    There's even a chance that we can dispose of the root object entirely
490    in server mode.
491    About the loop: The design is almost exactly like the one in ogre, so that
492    if any part of ogre registers a framelisteners, it will still behave
493    correctly. Furthermore I have taken over the time smoothing feature from
494    ogre. If turned on (see orxonox constructor), it will calculate the dt_n by
495    means of the recent most dt_n-1, dt_n-2, etc.
496  */
497  void Orxonox::mainLoop()
498  {
499    // use the ogre timer class to measure time.
500    Ogre::Timer *timer = new Ogre::Timer();
501    timer->reset();
502
503    // Contains the times of recently fired events
504    std::deque<unsigned long> eventTimes[3];
505    // Clear event times
506    for (int i = 0; i < 3; ++i)
507      eventTimes[i].clear();
508
509          while (true)
510          {
511                  //Pump messages in all registered RenderWindows
512      Ogre::WindowEventUtilities::messagePump();
513
514      // get current time
515      unsigned long now = timer->getMilliseconds();
516
517      // create an event to pass to the frameStarted method in ogre
518      Ogre::FrameEvent evt;
519      evt.timeSinceLastEvent = calculateEventTime(now, eventTimes[0]);
520      evt.timeSinceLastFrame = calculateEventTime(now, eventTimes[1]);
521
522      // show the current time in the HUD
523      orxonoxHUD_->setTime((int)now, 0);
524
525      // don't forget to call _fireFrameStarted in ogre to make sure
526      // everything goes smoothly
527      if (!ogre_->getRoot()->_fireFrameStarted(evt))
528        break;
529
530      // Iterate through all Tickables and call their tick(dt) function
531      for (Iterator<Tickable> it = ObjectList<Tickable>::start(); it; )
532        (it++)->tick((float)evt.timeSinceLastFrame);
533
534      // Update the timers
535      updateTimers((float)evt.timeSinceLastFrame);
536
537      if (mode_ != SERVER)
538      {
539        // only render in non-server mode
540        ogre_->getRoot()->_updateAllRenderTargets();
541      }
542
543      // get current time
544      now = timer->getMilliseconds();
545
546      // create an event to pass to the frameEnded method in ogre
547      evt.timeSinceLastEvent = calculateEventTime(now, eventTimes[0]);
548      evt.timeSinceLastFrame = calculateEventTime(now, eventTimes[2]);
549
550      // again, just to be sure ogre works fine
551      if (!ogre_->getRoot()->_fireFrameEnded(evt))
552        break;
553          }
554  }
555
556  /**
557    Timer updater function.
558    Updates all timers with the current dt.
559    Timers have been tested since their displacement.
560    @param dt The delta time
561  */
562  void Orxonox::updateTimers(float dt)
563  {
564    // Iterate through all Timers
565    for (Iterator<TimerBase> it = ObjectList<TimerBase>::start(); it; )
566    {
567      if (it->isActive())
568      {
569        // If active: Decrease the timer by the duration of the last frame
570        it->time_ -= dt;
571
572        if (it->time_ <= 0)
573        {
574          // It's time to call the function
575          if (it->bLoop_)
576            it->time_ += it->interval_; // Q: Why '+=' and not '='? A: Think about it. It's more accurate like that. Seriously.
577          else
578            it->stopTimer(); // Stop the timer if we don't want to loop
579
580          (it++)->run();
581        }
582        else
583        ++it;
584      }
585      else
586      ++it;
587    }
588
589  }
590  /**
591    Method for calculating the average time between recently fired events.
592    Code directly taken from OgreRoot.cc
593    @param now The current time in ms.
594    @param type The type of event to be considered.
595  */
596  float Orxonox::calculateEventTime(unsigned long now, std::deque<unsigned long> &times)
597  {
598    // Calculate the average time passed between events of the given type
599    // during the last mFrameSmoothingTime seconds.
600
601    times.push_back(now);
602
603    if(times.size() == 1)
604      return 0;
605
606    // Times up to mFrameSmoothingTime seconds old should be kept
607    unsigned long discardThreshold =
608      static_cast<unsigned long>(frameSmoothingTime_ * 1000.0f);
609
610    // Find the oldest time to keep
611    std::deque<unsigned long>::iterator it = times.begin(),
612      end = times.end()-2; // We need at least two times
613    while(it != end)
614    {
615      if (now - *it > discardThreshold)
616        ++it;
617      else
618        break;
619    }
620
621    // Remove old times
622    times.erase(times.begin(), it);
623
624    return (float)(times.back() - times.front()) / ((times.size()-1) * 1000);
625  }
626}
Note: See TracBrowser for help on using the repository browser.