Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/core4/src/orxonox/GraphicsManager.cc @ 3290

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

Moved WindowEventListener to the core to avoid some really ugly hacks in the Mouse.

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