Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/pch/src/orxonox/GraphicsManager.cc @ 3172

Last change on this file since 3172 was 3172, checked in by rgrieder, 15 years ago

Hack-fixed exit crash: The Map stores a static MaterialPtr that gets destroyed after main and therefore after delete Ogre::Root.
This is no real solution because the Map destructor isn't even called at all, so I had to place a call in the GraphicsManager.

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