Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/orxonox/GraphicsManager.cc @ 3196

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

Merged pch branch back to trunk.

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