Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

Merged all remaining revisions from core4 back to the trunk.

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