| [612] | 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 | |
|---|
| [784] | 33 | // Precompiled Headers |
|---|
| [786] | 34 | #include "OrxonoxStableHeaders.h" |
|---|
| [784] | 35 | |
|---|
| [612] | 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> |
|---|
| [900] | 46 | #include <OgreTimer.h> |
|---|
| 47 | #include <OgreWindowEventUtilities.h> |
|---|
| [612] | 48 | |
|---|
| 49 | //****** OIS ******* |
|---|
| 50 | #include <OIS/OIS.h> |
|---|
| 51 | |
|---|
| 52 | //****** STD ******* |
|---|
| 53 | #include <iostream> |
|---|
| [673] | 54 | #include <exception> |
|---|
| [612] | 55 | |
|---|
| 56 | //***** ORXONOX **** |
|---|
| [871] | 57 | //misc |
|---|
| [742] | 58 | #include "util/Sleep.h" |
|---|
| [708] | 59 | |
|---|
| [612] | 60 | // loader and audio |
|---|
| [871] | 61 | //#include "loader/LevelLoader.h" |
|---|
| [612] | 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" |
|---|
| [748] | 71 | #include "tools/Timer.h" |
|---|
| [627] | 72 | #include "objects/NPC.h" |
|---|
| [612] | 73 | #include "core/ArgReader.h" |
|---|
| 74 | #include "core/Factory.h" |
|---|
| 75 | #include "core/Debug.h" |
|---|
| [871] | 76 | #include "core/Loader.h" |
|---|
| [612] | 77 | #include "hud/HUD.h" |
|---|
| [768] | 78 | #include "objects/weapon/BulletManager.h" |
|---|
| [729] | 79 | #include "GraphicsEngine.h" |
|---|
| [612] | 80 | |
|---|
| [667] | 81 | #include "Orxonox.h" |
|---|
| [612] | 82 | |
|---|
| 83 | namespace orxonox |
|---|
| 84 | { |
|---|
| 85 | // put this in a seperate Class or solve the problem in another fashion |
|---|
| [708] | 86 | class OrxListener : public Ogre::FrameListener |
|---|
| [612] | 87 | { |
|---|
| 88 | public: |
|---|
| 89 | OrxListener(OIS::Keyboard *keyboard, audio::AudioManager* auMan, gameMode mode) |
|---|
| [616] | 90 | { |
|---|
| [612] | 91 | mKeyboard = keyboard; |
|---|
| 92 | mode_=mode; |
|---|
| 93 | auMan_ = auMan; |
|---|
| [616] | 94 | } |
|---|
| [612] | 95 | |
|---|
| [708] | 96 | bool frameStarted(const Ogre::FrameEvent& evt) |
|---|
| [612] | 97 | { |
|---|
| 98 | auMan_->update(); |
|---|
| [627] | 99 | updateAI(); |
|---|
| [612] | 100 | |
|---|
| [665] | 101 | if(mode_ == PRESENTATION) |
|---|
| [612] | 102 | server_g->tick(evt.timeSinceLastFrame); |
|---|
| [665] | 103 | else if(mode_ == CLIENT) |
|---|
| [616] | 104 | client_g->tick(evt.timeSinceLastFrame); |
|---|
| [612] | 105 | |
|---|
| [616] | 106 | usleep(10); |
|---|
| [612] | 107 | |
|---|
| [616] | 108 | mKeyboard->capture(); |
|---|
| [612] | 109 | return !mKeyboard->isKeyDown(OIS::KC_ESCAPE); |
|---|
| 110 | } |
|---|
| 111 | |
|---|
| [627] | 112 | void updateAI() |
|---|
| 113 | { |
|---|
| 114 | for(Iterator<NPC> it = ObjectList<NPC>::start(); it; ++it) |
|---|
| 115 | { |
|---|
| 116 | it->update(); |
|---|
| 117 | } |
|---|
| 118 | } |
|---|
| 119 | |
|---|
| [612] | 120 | private: |
|---|
| 121 | gameMode mode_; |
|---|
| 122 | OIS::Keyboard *mKeyboard; |
|---|
| 123 | audio::AudioManager* auMan_; |
|---|
| [616] | 124 | }; |
|---|
| [612] | 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_ = ""; |
|---|
| [871] | 136 | // this->loader_ = 0; |
|---|
| [616] | 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; |
|---|
| [900] | 144 | // turn frame smoothing on by setting a value different from 0 |
|---|
| 145 | this->frameSmoothingTime_ = 0.1f; |
|---|
| [612] | 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 | */ |
|---|
| [715] | 162 | void Orxonox::init(int argc, char **argv, std::string path) |
|---|
| [612] | 163 | { |
|---|
| 164 | //TODO: find config file (assuming executable directory) |
|---|
| 165 | //TODO: read config file |
|---|
| 166 | //TODO: give config file to Ogre |
|---|
| [715] | 167 | std::string mode; |
|---|
| [612] | 168 | // if(argc>=2) |
|---|
| [715] | 169 | // mode = std::string(argv[1]); |
|---|
| [612] | 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); |
|---|
| [637] | 176 | //mode = "presentation"; |
|---|
| [612] | 177 | if(ar.errorHandling()) die(); |
|---|
| [715] | 178 | if(mode == std::string("server")) |
|---|
| [612] | 179 | { |
|---|
| 180 | serverInit(path); |
|---|
| 181 | mode_ = SERVER; |
|---|
| 182 | } |
|---|
| [715] | 183 | else if(mode == std::string("client")) |
|---|
| [612] | 184 | { |
|---|
| 185 | clientInit(path); |
|---|
| 186 | mode_ = CLIENT; |
|---|
| 187 | } |
|---|
| [715] | 188 | else if(mode == std::string("presentation")) |
|---|
| [612] | 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 |
|---|
| [667] | 207 | Factory::createClassHierarchy(); |
|---|
| [612] | 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 | |
|---|
| [715] | 252 | void Orxonox::standaloneInit(std::string path) |
|---|
| [612] | 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 | |
|---|
| [715] | 271 | void Orxonox::playableServer(std::string path) |
|---|
| [612] | 272 | { |
|---|
| 273 | ogre_->setConfigPath(path); |
|---|
| 274 | ogre_->setup(); |
|---|
| 275 | root_ = ogre_->getRoot(); |
|---|
| 276 | defineResources(); |
|---|
| 277 | setupRenderSystem(); |
|---|
| 278 | createRenderWindow(); |
|---|
| 279 | initializeResourceGroups(); |
|---|
| [616] | 280 | setupInputSystem(); |
|---|
| 281 | Factory::createClassHierarchy(); |
|---|
| [612] | 282 | createScene(); |
|---|
| 283 | setupScene(); |
|---|
| 284 | createFrameListener(); |
|---|
| 285 | try{ |
|---|
| [665] | 286 | server_g = new network::Server(); //!< add port and bindadress |
|---|
| 287 | server_g->open(); //!< open server and create listener thread |
|---|
| [612] | 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 | } |
|---|
| [782] | 292 | catch(...) |
|---|
| [612] | 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 | |
|---|
| [715] | 305 | void Orxonox::serverInit(std::string path) |
|---|
| [612] | 306 | { |
|---|
| 307 | COUT(2) << "initialising server" << std::endl; |
|---|
| 308 | ogre_->setConfigPath(path); |
|---|
| 309 | ogre_->setup(); |
|---|
| [665] | 310 | server_g = new network::Server(); // FIXME add some settings if wanted |
|---|
| [612] | 311 | if(!ogre_->load()) die(/* unable to load */); |
|---|
| [665] | 312 | // FIXME add network framelistener |
|---|
| [612] | 313 | } |
|---|
| 314 | |
|---|
| [715] | 315 | void Orxonox::clientInit(std::string path) |
|---|
| [612] | 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 | { |
|---|
| [715] | 330 | std::string secName, typeName, archName; |
|---|
| [612] | 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 |
|---|
| [715] | 349 | Ogre::ResourceGroupManager::getSingleton().addResourceLocation( std::string(macBundlePath() + "/" + archName), typeName, secName); |
|---|
| [612] | 350 | #else |
|---|
| [708] | 351 | Ogre::ResourceGroupManager::getSingleton().addResourceLocation( archName, typeName, secName); |
|---|
| [612] | 352 | #endif |
|---|
| 353 | } |
|---|
| 354 | } |
|---|
| 355 | } |
|---|
| 356 | |
|---|
| 357 | void Orxonox::setupRenderSystem() |
|---|
| 358 | { |
|---|
| 359 | if (!root_->restoreConfig() && !root_->showConfigDialog()) |
|---|
| [708] | 360 | throw Ogre::Exception(52, "User canceled the config dialog!", "OrxApplication::setupRenderSystem()"); |
|---|
| [612] | 361 | } |
|---|
| 362 | |
|---|
| 363 | void Orxonox::createRenderWindow() |
|---|
| 364 | { |
|---|
| 365 | root_->initialise(true, "OrxonoxV2"); |
|---|
| 366 | } |
|---|
| 367 | |
|---|
| 368 | void Orxonox::initializeResourceGroups() |
|---|
| 369 | { |
|---|
| [708] | 370 | Ogre::TextureManager::getSingleton().setDefaultNumMipmaps(5); |
|---|
| 371 | Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups(); |
|---|
| [612] | 372 | } |
|---|
| 373 | |
|---|
| 374 | void Orxonox::createScene(void) |
|---|
| 375 | { |
|---|
| 376 | // Init audio |
|---|
| 377 | auMan_ = new audio::AudioManager(); |
|---|
| 378 | |
|---|
| [637] | 379 | bulletMgr_ = new BulletManager(); |
|---|
| 380 | |
|---|
| [612] | 381 | // load this file from config |
|---|
| [871] | 382 | // loader_ = new loader::LevelLoader("sample.oxw"); |
|---|
| 383 | // loader_->loadLevel(); |
|---|
| 384 | Level* startlevel = new Level("levels/sample.oxw"); |
|---|
| 385 | Loader::open(startlevel); |
|---|
| [612] | 386 | |
|---|
| [708] | 387 | Ogre::Overlay* hudOverlay = Ogre::OverlayManager::getSingleton().getByName("Orxonox/HUD1.2"); |
|---|
| [900] | 388 | //HUD* orxonoxHud; |
|---|
| 389 | orxonoxHUD_ = new HUD(); |
|---|
| 390 | orxonoxHUD_->setEnergyValue(20); |
|---|
| 391 | orxonoxHUD_->setEnergyDistr(20,20,60); |
|---|
| [612] | 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; |
|---|
| [663] | 427 | |
|---|
| 428 | // fixes auto repeat problem |
|---|
| 429 | #if defined OIS_LINUX_PLATFORM |
|---|
| [715] | 430 | pl.insert(std::make_pair(std::string("XAutoRepeatOn"), std::string("true"))); |
|---|
| [663] | 431 | #endif |
|---|
| 432 | |
|---|
| [708] | 433 | Ogre::RenderWindow *win = ogre_->getRoot()->getAutoCreatedWindow(); |
|---|
| [612] | 434 | win->getCustomAttribute("WINDOW", &windowHnd); |
|---|
| 435 | windowHndStr << windowHnd; |
|---|
| [715] | 436 | pl.insert(std::make_pair(std::string("WINDOW"), windowHndStr.str())); |
|---|
| [612] | 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 | |
|---|
| [665] | 450 | // FIXME we actually want to do this differently... |
|---|
| [612] | 451 | void Orxonox::createFrameListener() |
|---|
| 452 | { |
|---|
| [900] | 453 | //TickFrameListener* TickFL = new TickFrameListener(); |
|---|
| 454 | //ogre_->getRoot()->addFrameListener(TickFL); |
|---|
| [612] | 455 | |
|---|
| [900] | 456 | //TimerFrameListener* TimerFL = new TimerFrameListener(); |
|---|
| 457 | //ogre_->getRoot()->addFrameListener(TimerFL); |
|---|
| [612] | 458 | |
|---|
| [665] | 459 | //if(mode_!=CLIENT) // FIXME just a hack ------- remove this in future |
|---|
| [612] | 460 | frameListener_ = new OrxListener(keyboard_, auMan_, mode_); |
|---|
| 461 | ogre_->getRoot()->addFrameListener(frameListener_); |
|---|
| 462 | } |
|---|
| 463 | |
|---|
| 464 | void Orxonox::startRenderLoop() |
|---|
| 465 | { |
|---|
| [665] | 466 | // FIXME |
|---|
| [612] | 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 | } |
|---|
| [900] | 479 | //ogre_->getRoot()->startRendering(); |
|---|
| 480 | mainLoop(); |
|---|
| [612] | 481 | } |
|---|
| [900] | 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> ×) |
|---|
| 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 | } |
|---|
| [612] | 626 | } |
|---|