Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/orxonox/GraphicsEngine.cc @ 1563

Last change on this file since 1563 was 1563, checked in by landauf, 16 years ago
  • added configurable detaillevel for particle effects to [GraphicsEngine]
  • thrusters work properly with changing gamespeed
  • Property svn:eol-style set to native
File size: 14.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 *      Reto Grieder
24 *   Co-authors:
25 *      Benjamin Knecht <beni_at_orxonox.net>, (C) 2007, Felix Schulthess
26 *
27 */
28
29 /**
30    @file orxonox.cc
31    @brief Orxonox class
32  */
33
34#include "OrxonoxStableHeaders.h"
35#include "GraphicsEngine.h"
36
37#include <fstream>
38
39#include <OgreConfigFile.h>
40#include <OgreException.h>
41#include <OgreLogManager.h>
42#include <OgreRoot.h>
43#include <OgreSceneManager.h>
44#include <OgreTextureManager.h>
45#include <OgreViewport.h>
46
47#include "core/CoreIncludes.h"
48#include "core/ConfigValueIncludes.h"
49#include "core/Debug.h"
50#include "core/CommandExecutor.h"
51#include "core/ConsoleCommand.h"
52#include "core/input/InputManager.h"
53
54#include "console/InGameConsole.h"
55#include "tools/ParticleInterface.h"
56#include "Settings.h"
57
58
59namespace orxonox {
60  /**
61    @brief Returns the singleton instance and creates it the first time.
62    @return The only instance of GraphicsEngine.
63  */
64  /*static*/ GraphicsEngine& GraphicsEngine::getSingleton()
65  {
66    static GraphicsEngine theOnlyInstance;
67    return theOnlyInstance;
68  }
69
70  /**
71    @brief Only use constructor to initialise variables and pointers!
72  */
73  GraphicsEngine::GraphicsEngine() :
74    root_(0),
75    scene_(0),
76    renderWindow_(0)
77  {
78    RegisterObject(GraphicsEngine);
79
80    this->detailLevelParticle_ = 0;
81
82    this->setConfigValues();
83    CCOUT(4) << "Constructed" << std::endl;
84  }
85
86  void GraphicsEngine::setConfigValues()
87  {
88    SetConfigValue(resourceFile_,    "resources.cfg").description("Location of the resources file in the data path.");
89    SetConfigValue(ogreConfigFile_,  "ogre.cfg").description("Location of the Ogre config file");
90    SetConfigValue(ogrePluginsFile_, "plugins.cfg").description("Location of the Ogre plugins file");
91    SetConfigValue(ogreLogFile_,     "ogre.log").description("Logfile for messages from Ogre. \
92                                                             Use \"\" to suppress log file creation.");
93    SetConfigValue(ogreLogLevelTrivial_ , 5).description("Corresponding orxonox debug level for ogre Trivial");
94    SetConfigValue(ogreLogLevelNormal_  , 4).description("Corresponding orxonox debug level for ogre Normal");
95    SetConfigValue(ogreLogLevelCritical_, 2).description("Corresponding orxonox debug level for ogre Critical");
96
97    unsigned int old = this->detailLevelParticle_;
98    SetConfigValue(detailLevelParticle_, 2).description("O: off, 1: low, 2: normal, 3: high");
99
100    if (this->detailLevelParticle_ != old)
101      for (Iterator<ParticleInterface> it = ObjectList<ParticleInterface>::begin(); it; ++it)
102        it->detailLevelChanged(this->detailLevelParticle_);
103  }
104
105  /**
106    @brief Called after main() --> call destroyObjects()!
107  */
108  GraphicsEngine::~GraphicsEngine()
109  {
110    this->destroy();
111  }
112
113  /**
114    @brief Destroys all the internal objects. Call this method when you
115           normally would call the destructor.
116  */
117  void GraphicsEngine::destroy()
118  {
119    CCOUT(4) << "Destroying objects..." << std::endl;
120    Ogre::WindowEventUtilities::removeWindowEventListener(this->renderWindow_, this);
121    if (this->root_)
122      delete this->root_;
123    this->root_ = 0;
124    this->scene_ = 0;
125    this->renderWindow_ = 0;
126    // delete the ogre log and the logManager (since we have created it).
127    if (Ogre::LogManager::getSingletonPtr() != 0)
128    {
129      Ogre::LogManager::getSingleton().getDefaultLog()->removeListener(this);
130      Ogre::LogManager::getSingleton().destroyLog(Ogre::LogManager::getSingleton().getDefaultLog());
131      delete Ogre::LogManager::getSingletonPtr();
132    }
133    CCOUT(4) << "Destroying objects done" << std::endl;
134  }
135
136  /**
137    @brief Creates the Ogre Root object and sets up the ogre log.
138  */
139  bool GraphicsEngine::setup()
140  {
141    CCOUT(3) << "Setting up..." << std::endl;
142    // temporary overwrite of dataPath, change ini file for permanent change
143
144// TODO: LogManager doesn't work on specific systems. The why is unknown yet.
145#if ORXONOX_PLATFORM == ORXONOX_PLATFORM_WIN32
146    // create a logManager
147    // note: If there's already a logManager, Ogre will complain by a failed assertation.
148    // but that shouldn't happen, since this is the first time to create a logManager..
149    Ogre::LogManager* logger = new Ogre::LogManager();
150    CCOUT(4) << "Ogre LogManager created" << std::endl;
151
152    // create our own log that we can listen to
153    Ogre::Log *myLog;
154    if (this->ogreLogFile_ == "")
155      myLog = logger->createLog("ogre.log", true, false, true);
156    else
157      myLog = logger->createLog(this->ogreLogFile_, true, false, false);
158    CCOUT(4) << "Ogre Log created" << std::endl;
159
160    myLog->setLogDetail(Ogre::LL_BOREME);
161    myLog->addListener(this);
162#endif
163
164    // Root will detect that we've already created a Log
165    CCOUT(4) << "Creating Ogre Root..." << std::endl;
166
167    if (ogrePluginsFile_ == "")
168    {
169      COUT(1) << "Error: Ogre plugins file set to \"\". Cannot load." << std::endl;
170      return false;
171    }
172    if (ogreConfigFile_ == "")
173    {
174      COUT(1) << "Error: Ogre config file set to \"\". Cannot load." << std::endl;
175      return false;
176    }
177    if (ogreLogFile_ == "")
178    {
179      COUT(1) << "Error: Ogre log file set to \"\". Cannot load." << std::endl;
180      return false;
181    }
182
183    try
184    {
185      root_ = new Ogre::Root(ogrePluginsFile_, ogreConfigFile_, ogreLogFile_);
186    }
187    catch (Ogre::Exception ex)
188    {
189      COUT(2) << "Error: There was an exception when creating Ogre Root." << std::endl;
190      return false;
191    }
192
193    if (!root_->getInstalledPlugins().size())
194    {
195      COUT(1) << "Error: No plugins declared. Cannot load Ogre." << std::endl;
196      COUT(0) << "Is the plugins file correctly declared?" << std::endl;
197      return false;
198    }
199
200#if 0
201    // tame the ogre ouput so we don't get all the mess in the console
202    Ogre::Log* defaultLog = Ogre::LogManager::getSingleton().getDefaultLog();
203    defaultLog->setDebugOutputEnabled(false);
204    defaultLog->setLogDetail(Ogre::LL_BOREME);
205    defaultLog->addListener(this);
206#endif
207
208    CCOUT(4) << "Creating Ogre Root done" << std::endl;
209
210    // specify where Ogre has to look for resources. This call doesn't parse anything yet!
211    if (!declareRessourceLocations())
212      return false;
213
214    CCOUT(3) << "Set up done." << std::endl;
215    return true;
216  }
217
218  bool GraphicsEngine::declareRessourceLocations()
219  {
220    CCOUT(4) << "Declaring Resources" << std::endl;
221    //TODO: Specify layout of data file and maybe use xml-loader
222    //TODO: Work with ressource groups (should be generated by a special loader)
223
224    if (resourceFile_ == "")
225    {
226      COUT(1) << "Error: Resource file set to \"\". Cannot load." << std::endl;
227      return false;
228    }
229
230    // Load resource paths from data file using configfile ressource type
231    Ogre::ConfigFile cf;
232    try
233    {
234      cf.load(Settings::getDataPath() + resourceFile_);
235    }
236    catch (Ogre::Exception ex)
237    {
238      COUT(1) << "Error: Could not load resources.cfg in path " << Settings::getDataPath() << std::endl;
239      COUT(0) << "Have you forgotten to set the data path in orxnox.ini?" << std::endl;
240      return false;
241    }
242
243    // Go through all sections & settings in the file
244    Ogre::ConfigFile::SectionIterator seci = cf.getSectionIterator();
245
246    std::string secName, typeName, archName;
247    while (seci.hasMoreElements())
248    {
249      try
250      {
251        secName = seci.peekNextKey();
252        Ogre::ConfigFile::SettingsMultiMap *settings = seci.getNext();
253        Ogre::ConfigFile::SettingsMultiMap::iterator i;
254        for (i = settings->begin(); i != settings->end(); ++i)
255        {
256          typeName = i->first; // for instance "FileSystem" or "Zip"
257          archName = i->second; // name (and location) of archive
258
259          Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
260              std::string(Settings::getDataPath() + archName), typeName, secName);
261        }
262      }
263      catch (Ogre::Exception ex)
264      {
265        COUT(2) << "Exception while reading resources.cfg. Proceeding.." << ex.getDescription() << std::endl;
266      }
267    }
268    return true;
269  }
270
271  bool GraphicsEngine::loadRenderer()
272  {
273    CCOUT(4) << "Configuring Renderer" << std::endl;
274
275    // check for file existence because Ogre displays exceptions if not
276    std::ifstream probe;
277    probe.open(ogreConfigFile_.c_str());
278    if (!probe)
279    {
280      // create a zero sized file
281      std::ofstream creator;
282      creator.open(ogreConfigFile_.c_str());
283      creator.close();
284    }
285    else
286      probe.close();
287
288    if (!root_->restoreConfig())
289      if (!root_->showConfigDialog())
290        return false;
291
292    CCOUT(4) << "Creating render window" << std::endl;
293    try
294    {
295      this->renderWindow_ = root_->initialise(true, "OrxonoxV2");
296    }
297    catch (Ogre::Exception ex)
298    {
299      COUT(2) << "Error: There was an exception when initialising Ogre Root." << std::endl;
300      return false;
301    }
302
303    if (!root_->isInitialised())
304    {
305      CCOUT(2) << "Error: Initialising Ogre root object failed." << std::endl;
306      return false;
307    }
308    Ogre::WindowEventUtilities::addWindowEventListener(this->renderWindow_, this);
309    Ogre::TextureManager::getSingleton().setDefaultNumMipmaps(5);
310    return true;
311  }
312
313  bool GraphicsEngine::initialiseResources()
314  {
315    CCOUT(4) << "Initialising resources" << std::endl;
316    //TODO: Do NOT load all the groups, why are we doing that? And do we really do that? initialise != load...
317    try
318    {
319      Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
320      /*Ogre::StringVector str = Ogre::ResourceGroupManager::getSingleton().getResourceGroups();
321      for (unsigned int i = 0; i < str.size(); i++)
322      {
323        Ogre::ResourceGroupManager::getSingleton().loadResourceGroup(str[i]);
324      }*/
325    }
326    catch (Ogre::Exception e)
327    {
328      CCOUT(2) << "Error: There was an Error when initialising the resources." << std::endl;
329      CCOUT(2) << "ErrorMessage: " << e.getFullDescription() << std::endl;
330      return false;
331    }
332    return true;
333  }
334
335  /**
336   * @brief Creates the SceneManager
337   */
338  bool GraphicsEngine::createNewScene()
339  {
340    CCOUT(4) << "Creating new SceneManager..." << std::endl;
341    if (scene_)
342    {
343      CCOUT(2) << "SceneManager already exists! Skipping." << std::endl;
344      return false;
345    }
346    scene_ = root_->createSceneManager(Ogre::ST_GENERIC, "Default SceneManager");
347    CCOUT(3) << "Created SceneManager: " << scene_ << std::endl;
348    return true;
349  }
350
351  /**
352    Returns the window handle of the render window.
353    At least the InputHandler uses this to create the OIS::InputManager
354    @return The window handle of the render window
355  */
356  size_t GraphicsEngine::getWindowHandle()
357  {
358    if (this->renderWindow_)
359    {
360      size_t windowHnd = 0;
361      this->renderWindow_->getCustomAttribute("WINDOW", &windowHnd);
362      return windowHnd;
363    }
364    else
365      return 0;
366  }
367
368  /**
369    Get the width of the current render window
370    @return The width of the current render window
371  */
372  int GraphicsEngine::getWindowWidth() const
373  {
374    if (this->renderWindow_)
375      return this->renderWindow_->getWidth();
376    else
377      return 0;
378  }
379
380  /**
381    Get the height of the current render window
382    @return The height of the current render window
383  */
384  int GraphicsEngine::getWindowHeight() const
385  {
386    if (this->renderWindow_)
387      return this->renderWindow_->getHeight();
388    else
389      return 0;
390  }
391
392  /**
393    @brief Returns the window aspect ratio height/width.
394    @return The ratio
395  */
396  float GraphicsEngine::getWindowAspectRatio() const
397  {
398    if (this->renderWindow_)
399        return (float)this->renderWindow_->getHeight() / (float)this->renderWindow_->getWidth();
400    else
401        return 1.0f;
402  }
403
404  /**
405    @brief Method called by the LogListener interface from Ogre.
406    We use it to capture Ogre log messages and handle it ourselves.
407    @param message The message to be logged
408    @param lml The message level the log is using
409    @param maskDebug If we are printing to the console or not
410    @param logName the name of this log (so you can have several listeners
411                   for different logs, and identify them)
412  */
413  void GraphicsEngine::messageLogged(const std::string& message,
414    Ogre::LogMessageLevel lml, bool maskDebug, const std::string &logName)
415  {
416    int orxonoxLevel;
417    switch (lml)
418    {
419      case Ogre::LML_TRIVIAL:
420        orxonoxLevel = this->ogreLogLevelTrivial_;
421        break;
422      case Ogre::LML_NORMAL:
423        orxonoxLevel = this->ogreLogLevelNormal_;
424        break;
425      case Ogre::LML_CRITICAL:
426        orxonoxLevel = this->ogreLogLevelCritical_;
427        break;
428      default:
429        orxonoxLevel = 0;
430    }
431    OutputHandler::getOutStream().setOutputLevel(orxonoxLevel)
432        << "Ogre: " << message << std::endl;
433  }
434
435  /**
436  * Window has resized.
437  * @param rw The render window it occured in
438  */
439  void GraphicsEngine::windowMoved(Ogre::RenderWindow *rw)
440  {
441    // note: this doesn't change the window extents
442  }
443
444  /**
445  * Window has resized.
446  * @param rw The render window it occured in
447  * @note GraphicsEngine has a render window stored itself. This is the same
448  *       as rw. But we have to be careful when using multiple render windows!
449  */
450  void GraphicsEngine::windowResized(Ogre::RenderWindow *rw)
451  {
452    // change the mouse clipping size for absolute mouse movements
453    int w = rw->getWidth();
454    int h = rw->getHeight();
455    InputManager::setWindowExtents(w, h);
456    InGameConsole::getInstance().resize();
457  }
458
459  /**
460  * Window has resized.
461  * @param rw The render window it occured in
462  */
463  void GraphicsEngine::windowFocusChanged(Ogre::RenderWindow *rw)
464  {
465    // note: this doesn't change the window extents
466  }
467
468  /**
469  * Window has resized.
470  * @param rw The render window it occured in
471  */
472  void GraphicsEngine::windowClosed(Ogre::RenderWindow *rw)
473  {
474    // using CommandExecutor in order to avoid depending on Orxonox class.
475    CommandExecutor::execute("exit", false);
476  }
477
478}
Note: See TracBrowser for help on using the repository browser.