Index: code/trunk/src/orxonox/Orxonox.cc
===================================================================
--- code/trunk/src/orxonox/Orxonox.cc	(revision 871)
+++ code/trunk/src/orxonox/Orxonox.cc	(revision 1021)
@@ -35,27 +35,21 @@
 
 //****** OGRE ******
-#include <OgreException.h>
-#include <OgreRoot.h>
+//#include <OgreException.h>
 #include <OgreFrameListener.h>
-#include <OgreRenderWindow.h>
-#include <OgreTextureManager.h>
-#include <OgreResourceGroupManager.h>
-#include <OgreConfigFile.h>
 #include <OgreOverlay.h>
 #include <OgreOverlayManager.h>
-
-//****** OIS *******
-#include <OIS/OIS.h>
+#include <OgreTimer.h>
+#include <OgreWindowEventUtilities.h>
 
 //****** STD *******
-#include <iostream>
-#include <exception>
+//#include <iostream>
+//#include <exception>
+#include <deque>
 
 //***** ORXONOX ****
 //misc
-#include "util/Sleep.h"
-
-// loader and audio
-//#include "loader/LevelLoader.h"
+//#include "util/Sleep.h"
+
+// audio
 #include "audio/AudioManager.h"
 
@@ -63,17 +57,18 @@
 #include "network/Server.h"
 #include "network/Client.h"
-#include "network/NetworkFrameListener.h"
+network::Client *client_g;
+network::Server *server_g;
 
 // objects
-#include "objects/Tickable.h"
+#include "core/ArgReader.h"
+#include "core/Debug.h"
+#include "core/Factory.h"
+#include "core/Loader.h"
+#include "core/Tickable.h"
+#include "hud/HUD.h"
 #include "tools/Timer.h"
-#include "objects/NPC.h"
-#include "core/ArgReader.h"
-#include "core/Factory.h"
-#include "core/Debug.h"
-#include "core/Loader.h"
-#include "hud/HUD.h"
 #include "objects/weapon/BulletManager.h"
-#include "GraphicsEngine.h"
+
+#include "InputHandler.h"
 
 #include "Orxonox.h"
@@ -81,47 +76,8 @@
 namespace orxonox
 {
-   // put this in a seperate Class or solve the problem in another fashion
-  class OrxListener : public Ogre::FrameListener
-  {
-    public:
-      OrxListener(OIS::Keyboard *keyboard, audio::AudioManager*  auMan, gameMode mode)
-      {
-        mKeyboard = keyboard;
-        mode_=mode;
-        auMan_ = auMan;
-      }
-
-      bool frameStarted(const Ogre::FrameEvent& evt)
-      {
-        auMan_->update();
-        updateAI();
-
-        if(mode_ == PRESENTATION)
-          server_g->tick(evt.timeSinceLastFrame);
-        else if(mode_ == CLIENT)
-          client_g->tick(evt.timeSinceLastFrame);
-
-        usleep(10);
-
-        mKeyboard->capture();
-        return !mKeyboard->isKeyDown(OIS::KC_ESCAPE);
-      }
-
-      void updateAI()
-      {
-        for(Iterator<NPC> it = ObjectList<NPC>::start(); it; ++it)
-        {
-          it->update();
-        }
-      }
-
-    private:
-      gameMode mode_;
-      OIS::Keyboard *mKeyboard;
-      audio::AudioManager*  auMan_;
-  };
-
-  // init static singleton reference of Orxonox
-  Orxonox* Orxonox::singletonRef_ = NULL;
+  /**
+    @brief Reference to the only instance of the class.
+  */
+  Orxonox *Orxonox::singletonRef_s = 0;
 
   /**
@@ -131,13 +87,12 @@
   {
     this->ogre_ = new GraphicsEngine();
+    this->timer_ = 0;
     this->dataPath_ = "";
-//    this->loader_ = 0;
     this->auMan_ = 0;
-    this->singletonRef_ = 0;
-    this->keyboard_ = 0;
-    this->mouse_ = 0;
-    this->inputManager_ = 0;
-    this->frameListener_ = 0;
-    this->root_ = 0;
+    this->inputHandler_ = 0;
+    //this->root_ = 0;
+    // turn on frame smoothing by setting a value different from 0
+    this->frameSmoothingTime_ = 0.0f;
+    this->bAbort_ = false;
   }
 
@@ -147,5 +102,61 @@
   Orxonox::~Orxonox()
   {
-    // nothing to delete as for now
+    // keep in mind: the order of deletion is very important!
+    if (this->bulletMgr_)
+      delete this->bulletMgr_;
+    if (this->orxonoxHUD_)
+      delete this->orxonoxHUD_;
+    Loader::close();
+    InputHandler::destroy();
+    if (this->auMan_)
+      delete this->auMan_;
+    if (this->timer_)
+      delete this->timer_;
+    if (this->ogre_)
+      delete this->ogre_;
+
+    if (client_g)
+      delete client_g;
+    if (server_g)
+      delete server_g;
+  }
+
+  /**
+   * error kills orxonox
+   */
+  void Orxonox::abortImmediate(/* some error code */)
+  {
+    //TODO: destroy and destruct everything and print nice error msg
+    delete this;
+  }
+
+  /**
+    Asks the mainloop nicely to abort.
+  */
+  void Orxonox::abortRequest()
+  {
+    bAbort_ = true;
+  }
+
+  /**
+   * @return singleton object
+   */
+  Orxonox* Orxonox::getSingleton()
+  {
+    if (!singletonRef_s)
+      singletonRef_s = new Orxonox();
+    return singletonRef_s;
+    //static Orxonox theOnlyInstance;
+    //return &theOnlyInstance;
+  }
+
+  /**
+    @brief Destroys the Orxonox singleton.
+  */
+  void Orxonox::destroy()
+  {
+    if (singletonRef_s)
+      delete singletonRef_s;
+    singletonRef_s = 0;
   }
 
@@ -153,5 +164,5 @@
    * initialization of Orxonox object
    * @param argc argument counter
-   * @param argv list of arguments
+   * @param argv list of argumenst
    * @param path path to config (in home dir or something)
    */
@@ -162,154 +173,41 @@
     //TODO: give config file to Ogre
     std::string mode;
-//     if(argc>=2)
-//       mode = std::string(argv[1]);
-//     else
-//       mode = "";
+
     ArgReader ar = ArgReader(argc, argv);
     ar.checkArgument("mode", mode, false);
     ar.checkArgument("data", this->dataPath_, false);
     ar.checkArgument("ip", serverIp_, false);
-    //mode = "presentation";
-    if(ar.errorHandling()) die();
-    if(mode == std::string("server"))
+    if(ar.errorHandling()) abortImmediate();
+    if(mode == std::string("client"))
     {
+      mode_ = CLIENT;
+      clientInit(path);
+    }
+    else if(mode== std::string("server")){
+      mode_ = SERVER;
       serverInit(path);
-      mode_ = SERVER;
-    }
-    else if(mode == std::string("client"))
-    {
-      clientInit(path);
-      mode_ = CLIENT;
-    }
-    else if(mode == std::string("presentation"))
-    {
-      serverInit(path);
-      mode_ = PRESENTATION;
     }
     else{
+      mode_ = STANDALONE;
       standaloneInit(path);
-      mode_ = STANDALONE;
     }
   }
 
-  /**
-   * start modules
-   */
-  void Orxonox::start()
-  {
-    //TODO: start modules
-    ogre_->startRender();
-    //TODO: run engine
-    Factory::createClassHierarchy();
-    createScene();
-    setupScene();
-    setupInputSystem();
-    if(mode_!=CLIENT){ // remove this in future ---- presentation hack
-    }
-    else
-      std::cout << "client here" << std::endl;
-    createFrameListener();
-    switch(mode_){
-    case PRESENTATION:
-      //ogre_->getRoot()->addFrameListener(new network::ServerFrameListener());
-      //std::cout << "could not add framelistener" << std::endl;
-      server_g->open();
-      break;
-    case CLIENT:
-      client_g->establishConnection();
-      break;
-    case SERVER:
-    case STANDALONE:
-    default:
-      break;
-    }
-    startRenderLoop();
-  }
-
-  /**
-   * @return singleton object
-   */
-  Orxonox* Orxonox::getSingleton()
-  {
-    if (!singletonRef_)
-      singletonRef_ = new Orxonox();
-    return singletonRef_;
-  }
-
-  /**
-   * error kills orxonox
-   */
-  void Orxonox::die(/* some error code */)
-  {
-    //TODO: destroy and destruct everything and print nice error msg
-    delete this;
-  }
-
-  void Orxonox::standaloneInit(std::string path)
-  {
+  void Orxonox::serverInit(std::string path)
+  {
+    COUT(2) << "initialising server" << std::endl;
+    
     ogre_->setConfigPath(path);
     ogre_->setup();
-    root_ = ogre_->getRoot();
-    if(!ogre_->load()) die(/* unable to load */);
-
-    //defineResources();
-    //setupRenderSystem();
-    //createRenderWindow();
-    //initializeResourceGroups();
-    /*createScene();
-    setupScene();
-    setupInputSystem();
-    createFrameListener();
-    Factory::createClassHierarchy();
-    startRenderLoop();*/
-  }
-
-  void Orxonox::playableServer(std::string path)
-  {
-    ogre_->setConfigPath(path);
-    ogre_->setup();
-    root_ = ogre_->getRoot();
-    defineResources();
-    setupRenderSystem();
-    createRenderWindow();
-    initializeResourceGroups();
-    setupInputSystem();
-    Factory::createClassHierarchy();
-    createScene();
-    setupScene();
-    createFrameListener();
-    try{
-      server_g = new network::Server(); //!< add port and bindadress
-      server_g->open(); //!< open server and create listener thread
-      if(ogre_ && ogre_->getRoot())
-        ogre_->getRoot()->addFrameListener(new network::ServerFrameListener()); // adds a framelistener for the server
-      COUT(3) << "Info: network framelistener added" << std::endl;
-    }
-    catch(...)
-    {
-      COUT(1) << "Error: There was a problem initialising the server :(" << std::endl;
-    }
-    startRenderLoop();
-  }
-
-  void Orxonox::standalone(){
-
-
-
-  }
-
-  void Orxonox::serverInit(std::string path)
-  {
-    COUT(2) << "initialising server" << std::endl;
-    ogre_->setConfigPath(path);
-    ogre_->setup();
-    server_g = new network::Server(); // FIXME add some settings if wanted
-    if(!ogre_->load()) die(/* unable to load */);
-    // FIXME add network framelistener
+    //root_ = ogre_->getRoot();
+    if(!ogre_->load(this->dataPath_)) abortImmediate(/* unable to load */);
+    
+    server_g = new network::Server();
   }
 
   void Orxonox::clientInit(std::string path)
   {
-    COUT(2) << "initialising client" << std::endl;
+    COUT(2) << "initialising client" << std::endl;\
+    
     ogre_->setConfigPath(path);
     ogre_->setup();
@@ -317,72 +215,44 @@
       client_g = new network::Client();
     else
-      client_g = new network::Client(serverIp_, 55556);
-    if(!ogre_->load()) die(/* unable to load */);
-    ogre_->getRoot()->addFrameListener(new network::ClientFrameListener());
-  }
-
-  void Orxonox::defineResources()
-  {
-    std::string secName, typeName, archName;
-    Ogre::ConfigFile cf;
-#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
-    cf.load(macBundlePath() + "/Contents/Resources/resources.cfg");
-#else
-    cf.load(dataPath_ + "resources.cfg");
-#endif
-
-    Ogre::ConfigFile::SectionIterator seci = cf.getSectionIterator();
-    while (seci.hasMoreElements())
-    {
-      secName = seci.peekNextKey();
-      Ogre::ConfigFile::SettingsMultiMap *settings = seci.getNext();
-      Ogre::ConfigFile::SettingsMultiMap::iterator i;
-      for (i = settings->begin(); i != settings->end(); ++i)
-      {
-        typeName = i->first;
-        archName = i->second;
-#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
-        Ogre::ResourceGroupManager::getSingleton().addResourceLocation( std::string(macBundlePath() + "/" + archName), typeName, secName);
-#else
-        Ogre::ResourceGroupManager::getSingleton().addResourceLocation( archName, typeName, secName);
-#endif
-      }
+      client_g = new network::Client(serverIp_, NETWORK_PORT);
+    if(!ogre_->load(this->dataPath_)) abortImmediate(/* unable to load */);
+  }
+  
+  void Orxonox::standaloneInit(std::string path)
+  {
+    COUT(2) << "initialising standalone mode" << std::endl;
+    
+    ogre_->setConfigPath(path);
+    ogre_->setup();
+    //root_ = ogre_->getRoot();
+    if(!ogre_->load(this->dataPath_)) abortImmediate(/* unable to load */);
+  }
+  
+  /**
+   * start modules
+   */
+  void Orxonox::start()
+  {
+    switch(mode_){
+    case CLIENT:
+      clientStart();
+      break;
+    case SERVER:
+      serverStart();
+      break;
+    default:
+      standaloneStart();
     }
   }
-
-  void Orxonox::setupRenderSystem()
-  {
-    if (!root_->restoreConfig() && !root_->showConfigDialog())
-      throw Ogre::Exception(52, "User canceled the config dialog!", "OrxApplication::setupRenderSystem()");
-  }
-
-  void Orxonox::createRenderWindow()
-  {
-    root_->initialise(true, "OrxonoxV2");
-  }
-
-  void Orxonox::initializeResourceGroups()
-  {
-    Ogre::TextureManager::getSingleton().setDefaultNumMipmaps(5);
-    Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
-  }
-
-  /**
-   *
-   * @param
-   */
-  void Orxonox::createScene(void)
-  {
-	// Init audio
+  
+  void Orxonox::clientStart(){
+    ogre_->initialise();
+    Factory::createClassHierarchy();
+    
+    
     auMan_ = new audio::AudioManager();
 
     bulletMgr_ = new BulletManager();
-
-    // load this file from config
-//    loader_ = new loader::LevelLoader("sample.oxw");
-//    loader_->loadLevel();
-    Level* startlevel = new Level("levels/sample.oxw");
-    Loader::open(startlevel);
-
+    
     Ogre::Overlay* hudOverlay = Ogre::OverlayManager::getSingleton().getByName("Orxonox/HUD1.2");
     HUD* orxonoxHud;
@@ -391,4 +261,54 @@
     orxonoxHud->setEnergyDistr(20,20,60);
     hudOverlay->show();
+    
+    client_g->establishConnection();
+    client_g->tick(0);
+    
+    
+    //setupInputSystem();
+    
+    startRenderLoop();
+  }
+  
+  void Orxonox::serverStart(){
+    //TODO: start modules
+    ogre_->initialise();
+    //TODO: run engine
+    Factory::createClassHierarchy();
+    createScene();
+    setupInputSystem();
+    
+    server_g->open();
+    
+    startRenderLoop();
+  }
+  
+  void Orxonox::standaloneStart(){
+    //TODO: start modules
+    ogre_->initialise();
+    //TODO: run engine
+    Factory::createClassHierarchy();
+    createScene();
+    setupInputSystem();
+    
+    startRenderLoop();
+  }
+
+  void Orxonox::createScene(void)
+  {
+	  // Init audio
+    auMan_ = new audio::AudioManager();
+
+    bulletMgr_ = new BulletManager();
+
+    // load this file from config
+    Level* startlevel = new Level("levels/sample.oxw");
+    Loader::open(startlevel);
+
+    Ogre::Overlay* hudOverlay = Ogre::OverlayManager::getSingleton().getByName("Orxonox/HUD1.2");
+    orxonoxHUD_ = new HUD();
+    orxonoxHUD_->setEnergyValue(20);
+    orxonoxHUD_->setEnergyDistr(20,20,60);
+    hudOverlay->show();
 
 	/*
@@ -396,89 +316,132 @@
     auMan_->ambientAdd("a2");
     auMan_->ambientAdd("a3");
-                                //auMan->ambientAdd("ambient1");
-    auMan_->ambientStart();*/
-  }
-
-
-  /**
-   *
-   */
-  void Orxonox::setupScene()
-  {
-//    SceneManager *mgr = ogre_->getSceneManager();
-
-
-//    SceneNode* node = (SceneNode*)mgr->getRootSceneNode()->getChild("OgreHeadNode");
-//     SceneNode *node = mgr->getRootSceneNode()->createChildSceneNode("OgreHeadNode", Vector3(0,0,0));
-
-
-/*
-    particle::ParticleInterface *e = new particle::ParticleInterface(mgr,"engine","Orxonox/strahl");
-    e->particleSystem_->setParameter("local_space","true");
-    e->setPositionOfEmitter(0, Vector3(0,-10,0));
-    e->setDirection(Vector3(0,0,-1));
-    e->addToSceneNode(node);
-*/
-  }
-
-
+    //auMan->ambientAdd("ambient1");
+    auMan_->ambientStart();
+  */
+  }
+
+  /**
+    @brief Calls the InputHandler which sets up the input devices.
+    The render window width and height are used to set up the mouse movement.
+  */
   void Orxonox::setupInputSystem()
   {
-    size_t windowHnd = 0;
-    std::ostringstream windowHndStr;
-    OIS::ParamList pl;
-
-    // fixes auto repeat problem
-    #if defined OIS_LINUX_PLATFORM
-      pl.insert(std::make_pair(std::string("XAutoRepeatOn"), std::string("true")));
-    #endif
-
-      Ogre::RenderWindow *win = ogre_->getRoot()->getAutoCreatedWindow();
-    win->getCustomAttribute("WINDOW", &windowHnd);
-    windowHndStr << windowHnd;
-    pl.insert(std::make_pair(std::string("WINDOW"), windowHndStr.str()));
-    inputManager_ = OIS::InputManager::createInputSystem(pl);
-
-    try
+    inputHandler_ = InputHandler::getSingleton();
+    if (!inputHandler_->initialise(ogre_->getWindowHandle(),
+          ogre_->getWindowWidth(), ogre_->getWindowHeight()))
+      abortImmediate();
+  }
+
+  /**
+    Main loop of the orxonox game.
+    This is a new solution, using the ogre engine instead of being used by it.
+    An alternative solution would be to simply use the timer of the Root object,
+    but that implies using Ogre in any case. There would be no way to test
+    our code without the help of the root object.
+    There's even a chance that we can dispose of the root object entirely
+    in server mode.
+    About the loop: The design is almost exactly like the one in ogre, so that
+    if any part of ogre registers a framelisteners, it will still behave
+    correctly. Furthermore I have taken over the time smoothing feature from
+    ogre. If turned on (see orxonox constructor), it will calculate the dt_n by
+    means of the recent most dt_n-1, dt_n-2, etc.
+  */
+  void Orxonox::startRenderLoop()
+  {
+    // use the ogre timer class to measure time.
+    if (!timer_)
+      timer_ = new Ogre::Timer();
+    timer_->reset();
+
+    // Contains the times of recently fired events
+    std::deque<unsigned long> eventTimes[3];
+    // Clear event times
+    for (int i = 0; i < 3; ++i)
+      eventTimes[i].clear();
+
+	  while (!bAbort_)
+	  {
+		  // Pump messages in all registered RenderWindows
+      Ogre::WindowEventUtilities::messagePump();
+
+      // get current time
+      unsigned long now = timer_->getMilliseconds();
+
+      // create an event to pass to the frameStarted method in ogre
+      Ogre::FrameEvent evt;
+      evt.timeSinceLastEvent = calculateEventTime(now, eventTimes[0]);
+      evt.timeSinceLastFrame = calculateEventTime(now, eventTimes[1]);
+
+      // show the current time in the HUD
+      orxonoxHUD_->setTime((int)now, 0);
+
+      // Iterate through all Tickables and call their tick(dt) function
+      for (Iterator<Tickable> it = ObjectList<Tickable>::start(); it; )
+        (it++)->tick((float)evt.timeSinceLastFrame);
+
+      // don't forget to call _fireFrameStarted in ogre to make sure
+      // everything goes smoothly
+      ogre_->frameStarted(evt);
+
+      if (mode_ != SERVER)
+        ogre_->renderOneFrame(); // only render in non-server mode
+
+      // get current time
+      now = timer_->getMilliseconds();
+
+      // create an event to pass to the frameEnded method in ogre
+      evt.timeSinceLastEvent = calculateEventTime(now, eventTimes[0]);
+      evt.timeSinceLastFrame = calculateEventTime(now, eventTimes[2]);
+
+      // again, just to be sure ogre works fine
+      ogre_->frameEnded(evt);
+	  }
+  }
+
+  /**
+    Method for calculating the average time between recently fired events.
+    Code directly taken from OgreRoot.cc
+    @param now The current time in ms.
+    @param type The type of event to be considered.
+  */
+  float Orxonox::calculateEventTime(unsigned long now, std::deque<unsigned long> &times)
+  {
+    // Calculate the average time passed between events of the given type
+    // during the last mFrameSmoothingTime seconds.
+
+    times.push_back(now);
+
+    if(times.size() == 1)
+      return 0;
+
+    // Times up to mFrameSmoothingTime seconds old should be kept
+    unsigned long discardThreshold =
+      static_cast<unsigned long>(frameSmoothingTime_ * 1000.0f);
+
+    // Find the oldest time to keep
+    std::deque<unsigned long>::iterator it = times.begin(),
+      end = times.end()-2; // We need at least two times
+    while(it != end)
     {
-      keyboard_ = static_cast<OIS::Keyboard*>(inputManager_->createInputObject(OIS::OISKeyboard, false));
-      mouse_ = static_cast<OIS::Mouse*>(inputManager_->createInputObject(OIS::OISMouse, true));
+      if (now - *it > discardThreshold)
+        ++it;
+      else
+        break;
     }
-    catch (const OIS::Exception &e)
-    {
-      throw new Ogre::Exception(42, e.eText, "OrxApplication::setupInputSystem");
-    }
-  }
-
-  // FIXME we actually want to do this differently...
-  void Orxonox::createFrameListener()
-  {
-    TickFrameListener* TickFL = new TickFrameListener();
-    ogre_->getRoot()->addFrameListener(TickFL);
-
-    TimerFrameListener* TimerFL = new TimerFrameListener();
-    ogre_->getRoot()->addFrameListener(TimerFL);
-
-    //if(mode_!=CLIENT) // FIXME just a hack ------- remove this in future
-      frameListener_ = new OrxListener(keyboard_, auMan_, mode_);
-    ogre_->getRoot()->addFrameListener(frameListener_);
-  }
-
-  void Orxonox::startRenderLoop()
-  {
-    // FIXME
-    // this is a hack!!!
-    // the call to reset the mouse clipping size should probably be somewhere
-    // else, however this works for the moment.
-    unsigned int width, height, depth;
-    int left, top;
-    ogre_->getRoot()->getAutoCreatedWindow()->getMetrics(width, height, depth, left, top);
-
-    if(mode_!=CLIENT){
-      const OIS::MouseState &ms = mouse_->getMouseState();
-      ms.width = width;
-      ms.height = height;
-    }
-    ogre_->getRoot()->startRendering();
+
+    // Remove old times
+    times.erase(times.begin(), it);
+
+    return (float)(times.back() - times.front()) / ((times.size()-1) * 1000);
+  }
+
+  /**
+    @brief Test method for the InputHandler.
+    But: Is actually responsible for catching an exit event..
+  */
+  void Orxonox::eventOccured(InputEvent &evt)
+  {
+    if (evt.id == 1)
+      this->abortRequest();
   }
 }
