Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/buildsystem3/src/orxonox/gamestates/GSGraphics.cc @ 2731

Last change on this file since 2731 was 2702, checked in by rgrieder, 16 years ago

Completed work on installation:

  • The CMake switch INSTALL_COPYABLE tells whether you will be able to move the installed directory or not. If TRUE then all folders, including log and config directory, will be put into the CMAKE_INSTALL_PREFIX. Furthermore, relative paths are used, which get resolved at run time.
  • If INSTALL_COPYABLE is set to FALSE, the standard operating system directories will be used. That also means on Windows files get written to the Application Data/.orxonox folder instead of Program Files/Orxonox
  • Default configuration is INSTALL_COPYABLE=TRUE for Windows and FALSE for Unix
  • Split OrxonoxConfig.h.in in two to avoid complete recompiles when changing only a path or INSTALL_COPYABLE
  • Added a global constant character: CP_SLASH which stands for cross platform slash, meaning '/' on Unix and '
    ' on Windows
  • Core class now has methods getFooPath(), getFooPathString() and getFooPathPOSIXString() where Foo can be Media, Log or Config
  • getFooPathPOSIXString() will always return a directory formatted with slashes, even on Windows
  • getFooPath() returns a reference to the boost::filesystem::path
  • boost/filesystem.hpp does not get included to Core.h because it has a very large rat tail
  • The platform specific directory stuff gets done in Core::postMainInitialisation()
  • Adjusted all classes using the media path
  • Property svn:eol-style set to native
File size: 19.1 KB
RevLine 
[1661]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 *      ...
26 *
27 */
28
29#include "OrxonoxStableHeaders.h"
30#include "GSGraphics.h"
31
[1686]32#include <fstream>
[2664]33#include <boost/filesystem.hpp>
34
[2662]35#include <OgreCompositorManager.h>
[1686]36#include <OgreConfigFile.h>
[1661]37#include <OgreFrameListener.h>
38#include <OgreRoot.h>
[1891]39#include <OgreLogManager.h>
[1686]40#include <OgreException.h>
41#include <OgreRenderWindow.h>
[1828]42#include <OgreRenderSystem.h>
[1686]43#include <OgreTextureManager.h>
44#include <OgreViewport.h>
[1661]45#include <OgreWindowEventUtilities.h>
46
[2702]47#include "SpecialConfig.h"
[1755]48#include "util/Debug.h"
[1764]49#include "util/Exception.h"
[2664]50#include "util/String.h"
51#include "util/SubString.h"
[1662]52#include "core/ConsoleCommand.h"
53#include "core/ConfigValueIncludes.h"
[1686]54#include "core/CoreIncludes.h"
[2087]55#include "core/Core.h"
[1661]56#include "core/input/InputManager.h"
[1788]57#include "core/input/KeyBinder.h"
58#include "core/input/ExtendedInputState.h"
[2087]59#include "core/Loader.h"
60#include "core/XMLFile.h"
[1661]61#include "overlays/console/InGameConsole.h"
62#include "gui/GUIManager.h"
[1686]63#include "tools/WindowEventListener.h"
64
65// for compatibility
[1662]66#include "GraphicsEngine.h"
[1661]67
68namespace orxonox
69{
70    GSGraphics::GSGraphics()
[1689]71        : GameState<GSRoot>("graphics")
[1891]72        , renderWindow_(0)
73        , viewport_(0)
[2087]74        , bWindowEventListenerUpdateRequired_(false)
[1661]75        , inputManager_(0)
76        , console_(0)
77        , guiManager_(0)
[1891]78        , ogreRoot_(0)
79        , ogreLogger_(0)
80        , graphicsEngine_(0)
[1788]81        , masterKeyBinder_(0)
[2087]82        , debugOverlay_(0)
[1661]83    {
[1686]84        RegisterRootObject(GSGraphics);
[1891]85        setConfigValues();
[1661]86    }
87
88    GSGraphics::~GSGraphics()
89    {
90    }
91
92    void GSGraphics::setConfigValues()
93    {
[2103]94        SetConfigValue(resourceFile_,    "resources.cfg")
95            .description("Location of the resources file in the data path.");
96        SetConfigValue(ogreConfigFile_,  "ogre.cfg")
97            .description("Location of the Ogre config file");
[2664]98        SetConfigValue(ogrePluginsFolder_, ORXONOX_OGRE_PLUGINS_FOLDER)
99            .description("Folder where the Ogre plugins are located.");
100        SetConfigValue(ogrePlugins_, ORXONOX_OGRE_PLUGINS)
101            .description("Comma separated list of all plugins to load.");
[2103]102        SetConfigValue(ogreLogFile_,     "ogre.log")
103            .description("Logfile for messages from Ogre. Use \"\" to suppress log file creation.");
104        SetConfigValue(ogreLogLevelTrivial_ , 5)
105            .description("Corresponding orxonox debug level for ogre Trivial");
106        SetConfigValue(ogreLogLevelNormal_  , 4)
107            .description("Corresponding orxonox debug level for ogre Normal");
108        SetConfigValue(ogreLogLevelCritical_, 2)
109            .description("Corresponding orxonox debug level for ogre Critical");
[1661]110    }
111
112    void GSGraphics::enter()
113    {
[2087]114        Core::setShowsGraphics(true);
[1696]115
[1891]116        // initialise graphics engine. Doesn't load the render window yet!
117        graphicsEngine_ = new GraphicsEngine();
[1674]118
[1891]119        // Ogre setup procedure
120        setupOgre();
[2664]121        // load all the required plugins for Ogre
122        loadOgrePlugins();
123        // read resource declaration file
[1686]124        this->declareResources();
[2664]125        // Reads ogre config and creates the render window
126        this->loadRenderer();
127
[1661]128        // TODO: Spread this so that this call only initialises things needed for the Console and GUI
[1686]129        this->initialiseResources();
[1661]130
[2087]131        // We want to get informed whenever an object of type WindowEventListener is created
132        // in order to later update the window size.
133        bWindowEventListenerUpdateRequired_ = false;
134        RegisterConstructionCallback(GSGraphics, orxonox::WindowEventListener, requestWindowEventListenerUpdate);
[1686]135
[2087]136        // load debug overlay
137        COUT(3) << "Loading Debug Overlay..." << std::endl;
[2702]138        this->debugOverlay_ = new XMLFile((Core::getMediaPath() / "overlay" / "debug.oxo").file_string());
[2087]139        Loader::open(debugOverlay_);
[1686]140
[1661]141        // Calls the InputManager which sets up the input devices.
142        // The render window width and height are used to set up the mouse movement.
143        inputManager_ = new InputManager();
[1686]144        size_t windowHnd = 0;
145        this->renderWindow_->getCustomAttribute("WINDOW", &windowHnd);
146        inputManager_->initialise(windowHnd, renderWindow_->getWidth(), renderWindow_->getHeight(), true);
[1788]147        // Configure master input state with a KeyBinder
[2103]148        masterKeyBinder_ = new KeyBinder();
[2690]149        masterKeyBinder_->loadBindings("masterKeybindings.ini");
[2103]150        inputManager_->getMasterInputState()->addKeyHandler(masterKeyBinder_);
[1661]151
152        // Load the InGameConsole
153        console_ = new InGameConsole();
[2087]154        console_->initialise(this->renderWindow_->getWidth(), this->renderWindow_->getHeight());
[1661]155
156        // load the CEGUI interface
157        guiManager_ = new GUIManager();
[1686]158        guiManager_->initialise(this->renderWindow_);
[1674]159
[1686]160        // add console commands
161        FunctorMember<GSGraphics>* functor1 = createFunctor(&GSGraphics::printScreen);
162        functor1->setObject(this);
[2662]163        ccPrintScreen_ = createConsoleCommand(functor1, "printScreen");
164        CommandExecutor::addConsoleCommandShortcut(ccPrintScreen_);
[1661]165    }
166
167    void GSGraphics::leave()
168    {
[1824]169        using namespace Ogre;
170
[2662]171        delete this->ccPrintScreen_;
172
[1891]173        // remove our WindowEventListener first to avoid bad calls after the window has been destroyed
[1878]174        Ogre::WindowEventUtilities::removeWindowEventListener(this->renderWindow_, this);
175
[1662]176        delete this->guiManager_;
[1661]177
[1662]178        delete this->console_;
[1661]179
[1788]180        //inputManager_->getMasterInputState()->removeKeyHandler(this->masterKeyBinder_);
[2103]181        delete this->masterKeyBinder_;
[1662]182        delete this->inputManager_;
183
[2087]184        Loader::unload(this->debugOverlay_);
185        delete this->debugOverlay_;
186
[2662]187        // unload all compositors
188        Ogre::CompositorManager::getSingleton().removeAll();
189
[1824]190        // destroy render window
191        RenderSystem* renderer = this->ogreRoot_->getRenderSystem();
192        renderer->destroyRenderWindow("Orxonox");
[1696]193
[1891]194        /*** CODE SNIPPET, UNUSED ***/
[1824]195        // Does the opposite of initialise()
[1891]196        //ogreRoot_->shutdown();
[1824]197        // Remove all resources and resource groups
[1825]198        //StringVector groups = ResourceGroupManager::getSingleton().getResourceGroups();
199        //for (StringVector::iterator it = groups.begin(); it != groups.end(); ++it)
200        //{
201        //    ResourceGroupManager::getSingleton().destroyResourceGroup(*it);
202        //}
[1824]203
[1825]204        //ParticleSystemManager::getSingleton().removeAllTemplates();
[1824]205
206        // Shutdown the render system
[1825]207        //this->ogreRoot_->setRenderSystem(0);
[1824]208
[1891]209        delete this->ogreRoot_;
210
211        // delete the ogre log and the logManager (since we have created it).
212        this->ogreLogger_->getDefaultLog()->removeListener(this);
213        this->ogreLogger_->destroyLog(Ogre::LogManager::getSingleton().getDefaultLog());
214        delete this->ogreLogger_;
215
216        delete graphicsEngine_;
217
[2087]218        Core::setShowsGraphics(false);
[1661]219    }
220
[1662]221    /**
[2662]222    @note
[1662]223        A note about the Ogre::FrameListener: Even though we don't use them,
224        they still get called. However, the delta times are not correct (except
225        for timeSinceLastFrame, which is the most important). A little research
226        as shown that there is probably only one FrameListener that doesn't even
227        need the time. So we shouldn't run into problems.
228    */
[1674]229    void GSGraphics::ticked(const Clock& time)
[1661]230    {
[2662]231        uint64_t timeBeforeTick = time.getRealMicroseconds();
232
[1674]233        float dt = time.getDeltaTime();
234
[1672]235        this->inputManager_->tick(dt);
236        // tick console
237        this->console_->tick(dt);
[1674]238        this->tickChild(time);
[2087]239
240        if (this->bWindowEventListenerUpdateRequired_)
241        {
242            // Update all WindowEventListeners for the case a new one was created.
243            this->windowResized(this->renderWindow_);
244            this->bWindowEventListenerUpdateRequired_ = false;
245        }
246
[2662]247        uint64_t timeAfterTick = time.getRealMicroseconds();
[1661]248
[2662]249        // Also add our tick time to the list in GSRoot
250        this->getParent()->addTickTime(timeAfterTick - timeBeforeTick);
[1661]251
[2662]252        // Update statistics overlay. Note that the values only change periodically in GSRoot.
253        GraphicsEngine::getInstance().setAverageFramesPerSecond(this->getParent()->getAvgFPS());
254        GraphicsEngine::getInstance().setAverageTickTime(this->getParent()->getAvgTickTime());
[1661]255
[1672]256        // don't forget to call _fireFrameStarted in ogre to make sure
257        // everything goes smoothly
258        Ogre::FrameEvent evt;
259        evt.timeSinceLastFrame = dt;
260        evt.timeSinceLastEvent = dt; // note: same time, but shouldn't matter anyway
261        ogreRoot_->_fireFrameStarted(evt);
[1661]262
[1672]263        // Pump messages in all registered RenderWindows
264        // This calls the WindowEventListener objects.
265        Ogre::WindowEventUtilities::messagePump();
266        // make sure the window stays active even when not focused
267        // (probably only necessary on windows)
[1686]268        this->renderWindow_->setActive(true);
[1661]269
[1672]270        // render
271        ogreRoot_->_updateAllRenderTargets();
[1661]272
[1672]273        // again, just to be sure ogre works fine
274        ogreRoot_->_fireFrameEnded(evt); // note: uses the same time as _fireFrameStarted
[1661]275    }
[1686]276
[1891]277    /**
278    @brief
279        Creates the Ogre Root object and sets up the ogre log.
280    */
281    void GSGraphics::setupOgre()
282    {
283        COUT(3) << "Setting up Ogre..." << std::endl;
284
[2685]285        if (ogreConfigFile_ == "")
286        {
287            COUT(2) << "Warning: Ogre config file set to \"\". Defaulting to config.cfg" << std::endl;
288            ModifyConfigValue(ogreConfigFile_, tset, "config.cfg");
289        }
290        if (ogreLogFile_ == "")
291        {
292            COUT(2) << "Warning: Ogre log file set to \"\". Defaulting to ogre.log" << std::endl;
293            ModifyConfigValue(ogreLogFile_, tset, "ogre.log");
294        }
295
[2702]296        boost::filesystem::path ogreConfigFilepath(Core::getConfigPath() / this->ogreConfigFile_);
297        boost::filesystem::path ogreLogFilepath(Core::getLogPath() / this->ogreLogFile_);
[2685]298
[1891]299        // create a new logManager
[2685]300        // Ogre::Root will detect that we've already created a Log
[1891]301        ogreLogger_ = new Ogre::LogManager();
302        COUT(4) << "Ogre LogManager created" << std::endl;
303
304        // create our own log that we can listen to
305        Ogre::Log *myLog;
[2687]306        myLog = ogreLogger_->createLog(ogreLogFilepath.file_string(), true, false, false);
[1891]307        COUT(4) << "Ogre Log created" << std::endl;
308
309        myLog->setLogDetail(Ogre::LL_BOREME);
310        myLog->addListener(this);
311
312        COUT(4) << "Creating Ogre Root..." << std::endl;
313
314        // check for config file existence because Ogre displays (caught) exceptions if not
[2699]315        if (!boost::filesystem::exists(ogreConfigFilepath))
[1891]316        {
317            // create a zero sized file
318            std::ofstream creator;
[2687]319            creator.open(ogreConfigFilepath.file_string().c_str());
[1891]320            creator.close();
321        }
322
[2664]323        // Leave plugins file empty. We're going to do that part manually later
[2687]324        ogreRoot_ = new Ogre::Root("", ogreConfigFilepath.file_string(), ogreLogFilepath.file_string());
[1891]325
326        COUT(3) << "Ogre set up done." << std::endl;
327    }
328
[2664]329    void GSGraphics::loadOgrePlugins()
330    {
331        // just to make sure the next statement doesn't segfault
332        if (ogrePluginsFolder_ == "")
333            ogrePluginsFolder_ = ".";
334
335        boost::filesystem::path folder(ogrePluginsFolder_);
336        // Do some SubString magic to get the comma separated list of plugins
337        SubString plugins(ogrePlugins_, ",", " ", false, 92, false, 34, false, 40, 41, false, '\0');
338        for (unsigned int i = 0; i < plugins.size(); ++i)
[2687]339            ogreRoot_->loadPlugin((folder / plugins[i]).file_string());
[2664]340    }
341
[1686]342    void GSGraphics::declareResources()
343    {
344        CCOUT(4) << "Declaring Resources" << std::endl;
345        //TODO: Specify layout of data file and maybe use xml-loader
346        //TODO: Work with ressource groups (should be generated by a special loader)
347
348        if (resourceFile_ == "")
349        {
350            COUT(2) << "Warning: Ogre resource file set to \"\". Defaulting to resources.cfg" << std::endl;
351            ModifyConfigValue(resourceFile_, tset, "resources.cfg");
352        }
353
354        // Load resource paths from data file using configfile ressource type
355        Ogre::ConfigFile cf;
356        try
357        {
[2702]358            cf.load((Core::getMediaPath() / resourceFile_).file_string());
[1686]359        }
360        catch (...)
361        {
362            //COUT(1) << ex.getFullDescription() << std::endl;
363            COUT(0) << "Have you forgotten to set the data path in orxnox.ini?" << std::endl;
364            throw;
365        }
366
367        // Go through all sections & settings in the file
368        Ogre::ConfigFile::SectionIterator seci = cf.getSectionIterator();
369
370        std::string secName, typeName, archName;
371        while (seci.hasMoreElements())
372        {
373            try
374            {
375                secName = seci.peekNextKey();
376                Ogre::ConfigFile::SettingsMultiMap *settings = seci.getNext();
377                Ogre::ConfigFile::SettingsMultiMap::iterator i;
378                for (i = settings->begin(); i != settings->end(); ++i)
379                {
380                    typeName = i->first; // for instance "FileSystem" or "Zip"
381                    archName = i->second; // name (and location) of archive
382
383                    Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
[2702]384                        (Core::getMediaPath() / archName).directory_string(), typeName, secName);
[1686]385                }
386            }
387            catch (Ogre::Exception& ex)
388            {
389                COUT(1) << ex.getFullDescription() << std::endl;
390            }
391        }
392    }
393
394    void GSGraphics::loadRenderer()
395    {
396        CCOUT(4) << "Configuring Renderer" << std::endl;
397
398        if (!ogreRoot_->restoreConfig())
399            if (!ogreRoot_->showConfigDialog())
400                ThrowException(InitialisationFailed, "Could not show Ogre configuration dialogue.");
401
402        CCOUT(4) << "Creating render window" << std::endl;
403
[1824]404        this->renderWindow_ = ogreRoot_->initialise(true, "Orxonox");
[1686]405
406        Ogre::WindowEventUtilities::addWindowEventListener(this->renderWindow_, this);
407
[1820]408        Ogre::TextureManager::getSingleton().setDefaultNumMipmaps(0);
[1686]409
410        // create a full screen default viewport
411        this->viewport_ = this->renderWindow_->addViewport(0, 0);
[2662]412
413        if (this->graphicsEngine_)
414            this->graphicsEngine_->setViewport(this->viewport_);
[1686]415    }
416
417    void GSGraphics::initialiseResources()
418    {
419        CCOUT(4) << "Initialising resources" << std::endl;
420        //TODO: Do NOT load all the groups, why are we doing that? And do we really do that? initialise != load...
[1824]421        //try
422        //{
[1686]423            Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
424            /*Ogre::StringVector str = Ogre::ResourceGroupManager::getSingleton().getResourceGroups();
425            for (unsigned int i = 0; i < str.size(); i++)
426            {
427            Ogre::ResourceGroupManager::getSingleton().loadResourceGroup(str[i]);
428            }*/
[1824]429        //}
430        //catch (...)
431        //{
432        //    CCOUT(2) << "Error: There was a serious error when initialising the resources." << std::endl;
433        //    throw;
434        //}
[1686]435    }
436
[1891]437    /**
438    @brief
439        Method called by the LogListener interface from Ogre.
440        We use it to capture Ogre log messages and handle it ourselves.
441    @param message
442        The message to be logged
443    @param lml
444        The message level the log is using
445    @param maskDebug
446        If we are printing to the console or not
447    @param logName
448        The name of this log (so you can have several listeners
449        for different logs, and identify them)
450    */
451    void GSGraphics::messageLogged(const std::string& message,
452        Ogre::LogMessageLevel lml, bool maskDebug, const std::string& logName)
453    {
454        int orxonoxLevel;
455        switch (lml)
456        {
457        case Ogre::LML_TRIVIAL:
458            orxonoxLevel = this->ogreLogLevelTrivial_;
459            break;
460        case Ogre::LML_NORMAL:
461            orxonoxLevel = this->ogreLogLevelNormal_;
462            break;
463        case Ogre::LML_CRITICAL:
464            orxonoxLevel = this->ogreLogLevelCritical_;
465            break;
466        default:
467            orxonoxLevel = 0;
468        }
469        OutputHandler::getOutStream().setOutputLevel(orxonoxLevel)
470            << "Ogre: " << message << std::endl;
471    }
[1686]472
473    /**
474    @brief
475        Window has moved.
476    @param rw
477        The render window it occured in
478    */
479    void GSGraphics::windowMoved(Ogre::RenderWindow *rw)
480    {
[1755]481        for (ObjectList<orxonox::WindowEventListener>::iterator it = ObjectList<orxonox::WindowEventListener>::begin(); it; ++it)
[1686]482            it->windowMoved();
483    }
484
485    /**
486    @brief
487        Window has resized.
488    @param rw
489        The render window it occured in
490    @note
491        GraphicsEngine has a render window stored itself. This is the same
492        as rw. But we have to be careful when using multiple render windows!
493    */
494    void GSGraphics::windowResized(Ogre::RenderWindow *rw)
495    {
[1755]496        for (ObjectList<orxonox::WindowEventListener>::iterator it = ObjectList<orxonox::WindowEventListener>::begin(); it; ++it)
[1686]497            it->windowResized(this->renderWindow_->getWidth(), this->renderWindow_->getHeight());
[2087]498
499        // OIS needs this under linux even if we only use relative input measurement.
500        if (this->inputManager_)
501            this->inputManager_->setWindowExtents(renderWindow_->getWidth(), renderWindow_->getHeight());
[1686]502    }
503
504    /**
505    @brief
506        Window focus has changed.
507    @param rw
508        The render window it occured in
509    */
[1878]510    void GSGraphics::windowFocusChange(Ogre::RenderWindow *rw)
[1686]511    {
[1755]512        for (ObjectList<orxonox::WindowEventListener>::iterator it = ObjectList<orxonox::WindowEventListener>::begin(); it; ++it)
[1686]513            it->windowFocusChanged();
[1878]514
515        // instruct InputManager to clear the buffers (core library so we cannot use the interface)
[2087]516        if (this->inputManager_)
517            this->inputManager_->clearBuffers();
[1686]518    }
519
520    /**
521    @brief
522        Window was closed.
523    @param rw
524        The render window it occured in
525    */
526    void GSGraphics::windowClosed(Ogre::RenderWindow *rw)
527    {
528        this->requestState("root");
529    }
530
531    void GSGraphics::printScreen()
532    {
533        if (this->renderWindow_)
534        {
535            this->renderWindow_->writeContentsToTimestampedFile("shot_", ".jpg");
536        }
537    }
[1661]538}
Note: See TracBrowser for help on using the repository browser.