Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/resource/src/orxonox/GraphicsManager.cc @ 3343

Last change on this file since 3343 was 3338, checked in by rgrieder, 16 years ago

Improved exception-safety in the GUIManager.

  • Property svn:eol-style set to native
File size: 14.3 KB
RevLine 
[612]1/*
2 *   ORXONOX - the hottest 3D action shooter ever to exist
[1293]3 *                    > www.orxonox.net <
[612]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
[1349]11 *   of the License, or (at your option) any later version.
12 *
[612]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:
[1535]23 *      Reto Grieder
[1755]24 *      Benjamin Knecht <beni_at_orxonox.net>, (C) 2007
[612]25 *   Co-authors:
[1755]26 *      Felix Schulthess
[612]27 *
28 */
[1035]29
[1755]30/**
31@file
32@brief
33    Implementation of an partial interface to Ogre.
34*/
[612]35
[2801]36#include "GraphicsManager.h"
[612]37
[2801]38#include <fstream>
[3338]39#include <memory>
[2801]40#include <boost/filesystem.hpp>
[3280]41#include <boost/shared_ptr.hpp>
[2801]42
43#include <OgreCompositorManager.h>
44#include <OgreConfigFile.h>
45#include <OgreFrameListener.h>
46#include <OgreRoot.h>
47#include <OgreLogManager.h>
48#include <OgreException.h>
[1755]49#include <OgreRenderWindow.h>
[2801]50#include <OgreRenderSystem.h>
51#include <OgreTextureManager.h>
52#include <OgreViewport.h>
53#include <OgreWindowEventUtilities.h>
[1538]54
[2801]55#include "SpecialConfig.h"
56#include "util/Exception.h"
[3280]57#include "util/StringUtils.h"
[2801]58#include "util/SubString.h"
[2842]59#include "core/Clock.h"
[2801]60#include "core/ConsoleCommand.h"
61#include "core/ConfigValueIncludes.h"
[1293]62#include "core/CoreIncludes.h"
[2801]63#include "core/Core.h"
[3091]64#include "core/Game.h"
[2848]65#include "core/GameMode.h"
[3327]66#include "core/WindowEventListener.h"
[1563]67#include "tools/ParticleInterface.h"
[1032]68
[3196]69// HACK!
70#include "overlays/map/Map.h"
71
[1625]72namespace orxonox
73{
[3280]74    using boost::shared_ptr;
75
[3327]76    class OgreWindowEventListener : public Ogre::WindowEventListener
[2801]77    {
[3327]78    public:
79        void windowResized     (Ogre::RenderWindow* rw)
80            { orxonox::WindowEventListener::resizeWindow(rw->getWidth(), rw->getHeight()); }
81        void windowFocusChange (Ogre::RenderWindow* rw)
82            { orxonox::WindowEventListener::changeWindowFocus(); }
83        void windowClosed      (Ogre::RenderWindow* rw)
84            { orxonox::Game::getInstance().stop(); }
85        void windowMoved       (Ogre::RenderWindow* rw)
86            { orxonox::WindowEventListener::moveWindow(); }
[2801]87    };
[1032]88
[2801]89    GraphicsManager* GraphicsManager::singletonRef_s = 0;
[1293]90
[1755]91    /**
92    @brief
[2801]93        Non-initialising constructor.
[1755]94    */
[2801]95    GraphicsManager::GraphicsManager()
96        : ogreRoot_(0)
97        , ogreLogger_(0)
98        , renderWindow_(0)
99        , viewport_(0)
[3280]100        , ogreWindowEventListener_(new OgreWindowEventListener())
[1024]101    {
[2801]102        RegisterObject(GraphicsManager);
103
104        assert(singletonRef_s == 0);
105        singletonRef_s = this;
106
107        this->setConfigValues();
[612]108
[2801]109        // Ogre setup procedure
110        setupOgre();
111
[3280]112        try
113        {
114            // load all the required plugins for Ogre
115            loadOgrePlugins();
116            // read resource declaration file
117            this->declareResources();
118            // Reads ogre config and creates the render window
119            this->loadRenderer();
[2801]120
[3280]121            // TODO: Spread this
122            this->initialiseResources();
[2801]123
[3280]124            // add console commands
125            FunctorMember<GraphicsManager>* functor1 = createFunctor(&GraphicsManager::printScreen);
126            functor1->setObject(this);
127            ccPrintScreen_ = createConsoleCommand(functor1, "printScreen");
128            CommandExecutor::addConsoleCommandShortcut(ccPrintScreen_);
129        }
130        catch (...)
131        {
132            // clean up
133            delete this->ogreRoot_;
134            delete this->ogreLogger_;
135            delete this->ogreWindowEventListener_;
136            throw;
137        }
[2801]138    }
139
[1755]140    /**
141    @brief
[2801]142        Destroys all the Ogre related objects
[1755]143    */
[2801]144    GraphicsManager::~GraphicsManager()
[1535]145    {
[3280]146/*
147        delete this->ccPrintScreen_;
148*/
[1502]149
[3280]150        // HACK! This fixes an exit crash
151        Map::hackDestroyMap();
152        // unload all compositors (this is only necessary because we don't yet destroy all resources!)
153        Ogre::CompositorManager::getSingleton().removeAll();
[1535]154
[3280]155        // Delete OGRE main control organ
156        delete this->ogreRoot_;
[3103]157
[3280]158        // delete the logManager (since we have created it in the first place).
159        delete this->ogreLogger_;
[3196]160
[3280]161        delete this->ogreWindowEventListener_;
[2662]162
[2801]163        assert(singletonRef_s);
164        singletonRef_s = 0;
[1535]165    }
166
[2801]167    void GraphicsManager::setConfigValues()
[1535]168    {
[2801]169        SetConfigValue(resourceFile_,    "resources.cfg")
170            .description("Location of the resources file in the data path.");
171        SetConfigValue(ogreConfigFile_,  "ogre.cfg")
172            .description("Location of the Ogre config file");
173        SetConfigValue(ogrePluginsFolder_, ORXONOX_OGRE_PLUGINS_FOLDER)
174            .description("Folder where the Ogre plugins are located.");
175        SetConfigValue(ogrePlugins_, ORXONOX_OGRE_PLUGINS)
176            .description("Comma separated list of all plugins to load.");
177        SetConfigValue(ogreLogFile_,     "ogre.log")
178            .description("Logfile for messages from Ogre. Use \"\" to suppress log file creation.");
179        SetConfigValue(ogreLogLevelTrivial_ , 5)
180            .description("Corresponding orxonox debug level for ogre Trivial");
181        SetConfigValue(ogreLogLevelNormal_  , 4)
182            .description("Corresponding orxonox debug level for ogre Normal");
183        SetConfigValue(ogreLogLevelCritical_, 2)
184            .description("Corresponding orxonox debug level for ogre Critical");
[1535]185    }
[612]186
[2801]187    void GraphicsManager::update(const Clock& time)
188    {
[3280]189        Ogre::FrameEvent evt;
190        evt.timeSinceLastFrame = time.getDeltaTime();
191        evt.timeSinceLastEvent = time.getDeltaTime(); // note: same time, but shouldn't matter anyway
[2801]192
[3280]193        // don't forget to call _fireFrameStarted to OGRE to make sure
194        // everything goes smoothly
195        ogreRoot_->_fireFrameStarted(evt);
[2801]196
[3280]197        // Pump messages in all registered RenderWindows
198        // This calls the WindowEventListener objects.
199        Ogre::WindowEventUtilities::messagePump();
200        // make sure the window stays active even when not focused
201        // (probably only necessary on windows)
202        this->renderWindow_->setActive(true);
[2801]203
[3280]204        // render
205        ogreRoot_->_updateAllRenderTargets();
[2801]206
[3280]207        // again, just to be sure OGRE works fine
208        ogreRoot_->_fireFrameEnded(evt); // note: uses the same time as _fireFrameStarted
[2801]209    }
210
[2850]211    void GraphicsManager::setCamera(Ogre::Camera* camera)
212    {
213        this->viewport_->setCamera(camera);
214    }
215
[1755]216    /**
217    @brief
[2801]218        Creates the Ogre Root object and sets up the ogre log.
[1755]219    */
[2801]220    void GraphicsManager::setupOgre()
[1538]221    {
[2801]222        COUT(3) << "Setting up Ogre..." << std::endl;
223
224        if (ogreConfigFile_ == "")
225        {
226            COUT(2) << "Warning: Ogre config file set to \"\". Defaulting to config.cfg" << std::endl;
227            ModifyConfigValue(ogreConfigFile_, tset, "config.cfg");
228        }
229        if (ogreLogFile_ == "")
230        {
231            COUT(2) << "Warning: Ogre log file set to \"\". Defaulting to ogre.log" << std::endl;
232            ModifyConfigValue(ogreLogFile_, tset, "ogre.log");
233        }
234
235        boost::filesystem::path ogreConfigFilepath(Core::getConfigPath() / this->ogreConfigFile_);
236        boost::filesystem::path ogreLogFilepath(Core::getLogPath() / this->ogreLogFile_);
237
238        // create a new logManager
239        // Ogre::Root will detect that we've already created a Log
[3280]240        std::auto_ptr<Ogre::LogManager> logger(new Ogre::LogManager());
[2801]241        COUT(4) << "Ogre LogManager created" << std::endl;
242
243        // create our own log that we can listen to
244        Ogre::Log *myLog;
[3280]245        myLog = logger->createLog(ogreLogFilepath.string(), true, false, false);
[2801]246        COUT(4) << "Ogre Log created" << std::endl;
247
248        myLog->setLogDetail(Ogre::LL_BOREME);
249        myLog->addListener(this);
250
251        COUT(4) << "Creating Ogre Root..." << std::endl;
252
253        // check for config file existence because Ogre displays (caught) exceptions if not
254        if (!boost::filesystem::exists(ogreConfigFilepath))
255        {
256            // create a zero sized file
257            std::ofstream creator;
258            creator.open(ogreConfigFilepath.string().c_str());
259            creator.close();
260        }
261
262        // Leave plugins file empty. We're going to do that part manually later
263        ogreRoot_ = new Ogre::Root("", ogreConfigFilepath.string(), ogreLogFilepath.string());
[3280]264        // In case that new Root failed the logger gets destroyed because of the std::auto_ptr
265        ogreLogger_ = logger.release();
[2801]266
267        COUT(3) << "Ogre set up done." << std::endl;
[1538]268    }
[2801]269
270    void GraphicsManager::loadOgrePlugins()
271    {
272        // just to make sure the next statement doesn't segfault
273        if (ogrePluginsFolder_ == "")
274            ogrePluginsFolder_ = ".";
275
276        boost::filesystem::path folder(ogrePluginsFolder_);
277        // Do some SubString magic to get the comma separated list of plugins
[3323]278        SubString plugins(ogrePlugins_, ",", " ", false, '\\', false, '"', false, '(', ')', false, '\0');
[2801]279        // Use backslash paths on Windows! file_string() already does that though.
280        for (unsigned int i = 0; i < plugins.size(); ++i)
281            ogreRoot_->loadPlugin((folder / plugins[i]).file_string());
282    }
283
284    void GraphicsManager::declareResources()
285    {
286        CCOUT(4) << "Declaring Resources" << std::endl;
287        //TODO: Specify layout of data file and maybe use xml-loader
288        //TODO: Work with ressource groups (should be generated by a special loader)
289
290        if (resourceFile_ == "")
291        {
292            COUT(2) << "Warning: Ogre resource file set to \"\". Defaulting to resources.cfg" << std::endl;
293            ModifyConfigValue(resourceFile_, tset, "resources.cfg");
294        }
295
296        // Load resource paths from data file using configfile ressource type
297        Ogre::ConfigFile cf;
298        try
299        {
300            cf.load((Core::getMediaPath() / resourceFile_).string());
301        }
302        catch (...)
303        {
304            //COUT(1) << ex.getFullDescription() << std::endl;
305            COUT(0) << "Have you forgotten to set the data path in orxnox.ini?" << std::endl;
306            throw;
307        }
308
309        // Go through all sections & settings in the file
310        Ogre::ConfigFile::SectionIterator seci = cf.getSectionIterator();
311
312        std::string secName, typeName, archName;
313        while (seci.hasMoreElements())
314        {
315            try
316            {
317                secName = seci.peekNextKey();
318                Ogre::ConfigFile::SettingsMultiMap *settings = seci.getNext();
319                Ogre::ConfigFile::SettingsMultiMap::iterator i;
320                for (i = settings->begin(); i != settings->end(); ++i)
321                {
322                    typeName = i->first; // for instance "FileSystem" or "Zip"
323                    archName = i->second; // name (and location) of archive
324
325                    Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
326                        (Core::getMediaPath() / archName).string(), typeName, secName);
327                }
328            }
329            catch (Ogre::Exception& ex)
330            {
331                COUT(1) << ex.getFullDescription() << std::endl;
332            }
333        }
334    }
335
336    void GraphicsManager::loadRenderer()
337    {
338        CCOUT(4) << "Configuring Renderer" << std::endl;
339
340        if (!ogreRoot_->restoreConfig())
341            if (!ogreRoot_->showConfigDialog())
[3280]342                ThrowException(InitialisationFailed, "OGRE graphics configuration dialogue failed.");
[2801]343
344        CCOUT(4) << "Creating render window" << std::endl;
345
346        this->renderWindow_ = ogreRoot_->initialise(true, "Orxonox");
[3327]347        this->ogreWindowEventListener_->windowResized(renderWindow_);
[2801]348
349        Ogre::WindowEventUtilities::addWindowEventListener(this->renderWindow_, ogreWindowEventListener_);
350
351        Ogre::TextureManager::getSingleton().setDefaultNumMipmaps(0);
352
353        // create a full screen default viewport
354        this->viewport_ = this->renderWindow_->addViewport(0, 0);
355    }
356
357    void GraphicsManager::initialiseResources()
358    {
359        CCOUT(4) << "Initialising resources" << std::endl;
360        //TODO: Do NOT load all the groups, why are we doing that? And do we really do that? initialise != load...
361        //try
362        //{
363            Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
364            /*Ogre::StringVector str = Ogre::ResourceGroupManager::getSingleton().getResourceGroups();
365            for (unsigned int i = 0; i < str.size(); i++)
366            {
367            Ogre::ResourceGroupManager::getSingleton().loadResourceGroup(str[i]);
368            }*/
369        //}
370        //catch (...)
371        //{
372        //    CCOUT(2) << "Error: There was a serious error when initialising the resources." << std::endl;
373        //    throw;
374        //}
375    }
376
377    /**
378    @brief
379        Method called by the LogListener interface from Ogre.
380        We use it to capture Ogre log messages and handle it ourselves.
381    @param message
382        The message to be logged
383    @param lml
384        The message level the log is using
385    @param maskDebug
386        If we are printing to the console or not
387    @param logName
388        The name of this log (so you can have several listeners
389        for different logs, and identify them)
390    */
391    void GraphicsManager::messageLogged(const std::string& message,
392        Ogre::LogMessageLevel lml, bool maskDebug, const std::string& logName)
393    {
394        int orxonoxLevel;
395        switch (lml)
396        {
397        case Ogre::LML_TRIVIAL:
398            orxonoxLevel = this->ogreLogLevelTrivial_;
399            break;
400        case Ogre::LML_NORMAL:
401            orxonoxLevel = this->ogreLogLevelNormal_;
402            break;
403        case Ogre::LML_CRITICAL:
404            orxonoxLevel = this->ogreLogLevelCritical_;
405            break;
406        default:
407            orxonoxLevel = 0;
408        }
409        OutputHandler::getOutStream().setOutputLevel(orxonoxLevel)
410            << "Ogre: " << message << std::endl;
411    }
412
413    void GraphicsManager::printScreen()
414    {
415        assert(this->renderWindow_);
416       
417        this->renderWindow_->writeContentsToTimestampedFile(Core::getLogPathString() + "screenShot_", ".jpg");
418    }
[612]419}
Note: See TracBrowser for help on using the repository browser.