Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/gui/src/orxonox/GraphicsManager.cc @ 2816

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

Move graphic related content of GSGraphics to GraphicsManager which originally was GraphisEngine (but since we don't have an engine of our own, I renamed it).
Reduced OgreWindowEventUtilities.h dependency from GraphisManager.h (includes windows.h).

  • 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 "OrxonoxStableHeaders.h"
37#include "GraphicsManager.h"
38
39#include <fstream>
40#include <boost/filesystem.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/Debug.h"
56#include "util/Exception.h"
57#include "util/String.h"
58#include "util/SubString.h"
59#include "core/ConsoleCommand.h"
60#include "core/ConfigValueIncludes.h"
61#include "core/CoreIncludes.h"
62#include "core/Core.h"
63#include "tools/WindowEventListener.h"
64#include "tools/ParticleInterface.h"
65
66namespace orxonox
67{
68    class _OrxonoxExport OgreWindowEventListener : public Ogre::WindowEventListener
69    {
70        void windowResized     (Ogre::RenderWindow* rw);
71        void windowFocusChange (Ogre::RenderWindow* rw);
72        void windowClosed      (Ogre::RenderWindow* rw);
73        //void windowMoved       (Ogre::RenderWindow* rw);
74    };
75
76    GraphicsManager* GraphicsManager::singletonRef_s = 0;
77
78    /**
79    @brief
80        Non-initialising constructor.
81    */
82    GraphicsManager::GraphicsManager()
83        : ogreRoot_(0)
84        , ogreLogger_(0)
85        , renderWindow_(0)
86        , viewport_(0)
87        , ogreWindowEventListener_(0)
88        , avgTickTime_(0.0f)
89        , avgFramesPerSecond_(0.0f)
90    {
91        RegisterObject(GraphicsManager);
92
93        assert(singletonRef_s == 0);
94        singletonRef_s = this;
95
96        this->loaded_ = false;
97
98        this->setConfigValues();
99    }
100
101    void GraphicsManager::initialise()
102    {
103        Core::setShowsGraphics(true);
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            // unload all compositors
144            Ogre::CompositorManager::getSingleton().removeAll();
145
146            // destroy render window
147            Ogre::RenderSystem* renderer = this->ogreRoot_->getRenderSystem();
148            renderer->destroyRenderWindow("Orxonox");
149
150            // Delete OGRE main control organ
151            delete this->ogreRoot_;
152
153            // delete the ogre log and the logManager (since we have created it in the first place).
154            this->ogreLogger_->getDefaultLog()->removeListener(this);
155            this->ogreLogger_->destroyLog(Ogre::LogManager::getSingleton().getDefaultLog());
156            delete this->ogreLogger_;
157
158            // Don't showing graphics anymore
159            Core::setShowsGraphics(false);
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    /**
222    @brief
223        Creates the Ogre Root object and sets up the ogre log.
224    */
225    void GraphicsManager::setupOgre()
226    {
227        COUT(3) << "Setting up Ogre..." << std::endl;
228
229        if (ogreConfigFile_ == "")
230        {
231            COUT(2) << "Warning: Ogre config file set to \"\". Defaulting to config.cfg" << std::endl;
232            ModifyConfigValue(ogreConfigFile_, tset, "config.cfg");
233        }
234        if (ogreLogFile_ == "")
235        {
236            COUT(2) << "Warning: Ogre log file set to \"\". Defaulting to ogre.log" << std::endl;
237            ModifyConfigValue(ogreLogFile_, tset, "ogre.log");
238        }
239
240        boost::filesystem::path ogreConfigFilepath(Core::getConfigPath() / this->ogreConfigFile_);
241        boost::filesystem::path ogreLogFilepath(Core::getLogPath() / this->ogreLogFile_);
242
243        // create a new logManager
244        // Ogre::Root will detect that we've already created a Log
245        ogreLogger_ = new Ogre::LogManager();
246        COUT(4) << "Ogre LogManager created" << std::endl;
247
248        // create our own log that we can listen to
249        Ogre::Log *myLog;
250        myLog = ogreLogger_->createLog(ogreLogFilepath.string(), true, false, false);
251        COUT(4) << "Ogre Log created" << std::endl;
252
253        myLog->setLogDetail(Ogre::LL_BOREME);
254        myLog->addListener(this);
255
256        COUT(4) << "Creating Ogre Root..." << std::endl;
257
258        // check for config file existence because Ogre displays (caught) exceptions if not
259        if (!boost::filesystem::exists(ogreConfigFilepath))
260        {
261            // create a zero sized file
262            std::ofstream creator;
263            creator.open(ogreConfigFilepath.string().c_str());
264            creator.close();
265        }
266
267        // Leave plugins file empty. We're going to do that part manually later
268        ogreRoot_ = new Ogre::Root("", ogreConfigFilepath.string(), ogreLogFilepath.string());
269
270        COUT(3) << "Ogre set up done." << std::endl;
271    }
272
273    void GraphicsManager::loadOgrePlugins()
274    {
275        // just to make sure the next statement doesn't segfault
276        if (ogrePluginsFolder_ == "")
277            ogrePluginsFolder_ = ".";
278
279        boost::filesystem::path folder(ogrePluginsFolder_);
280        // Do some SubString magic to get the comma separated list of plugins
281        SubString plugins(ogrePlugins_, ",", " ", false, 92, false, 34, false, 40, 41, false, '\0');
282        // Use backslash paths on Windows! file_string() already does that though.
283        for (unsigned int i = 0; i < plugins.size(); ++i)
284            ogreRoot_->loadPlugin((folder / plugins[i]).file_string());
285    }
286
287    void GraphicsManager::declareResources()
288    {
289        CCOUT(4) << "Declaring Resources" << std::endl;
290        //TODO: Specify layout of data file and maybe use xml-loader
291        //TODO: Work with ressource groups (should be generated by a special loader)
292
293        if (resourceFile_ == "")
294        {
295            COUT(2) << "Warning: Ogre resource file set to \"\". Defaulting to resources.cfg" << std::endl;
296            ModifyConfigValue(resourceFile_, tset, "resources.cfg");
297        }
298
299        // Load resource paths from data file using configfile ressource type
300        Ogre::ConfigFile cf;
301        try
302        {
303            cf.load((Core::getMediaPath() / resourceFile_).string());
304        }
305        catch (...)
306        {
307            //COUT(1) << ex.getFullDescription() << std::endl;
308            COUT(0) << "Have you forgotten to set the data path in orxnox.ini?" << std::endl;
309            throw;
310        }
311
312        // Go through all sections & settings in the file
313        Ogre::ConfigFile::SectionIterator seci = cf.getSectionIterator();
314
315        std::string secName, typeName, archName;
316        while (seci.hasMoreElements())
317        {
318            try
319            {
320                secName = seci.peekNextKey();
321                Ogre::ConfigFile::SettingsMultiMap *settings = seci.getNext();
322                Ogre::ConfigFile::SettingsMultiMap::iterator i;
323                for (i = settings->begin(); i != settings->end(); ++i)
324                {
325                    typeName = i->first; // for instance "FileSystem" or "Zip"
326                    archName = i->second; // name (and location) of archive
327
328                    Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
329                        (Core::getMediaPath() / archName).string(), typeName, secName);
330                }
331            }
332            catch (Ogre::Exception& ex)
333            {
334                COUT(1) << ex.getFullDescription() << std::endl;
335            }
336        }
337    }
338
339    void GraphicsManager::loadRenderer()
340    {
341        CCOUT(4) << "Configuring Renderer" << std::endl;
342
343        if (!ogreRoot_->restoreConfig())
344            if (!ogreRoot_->showConfigDialog())
345                ThrowException(InitialisationFailed, "Could not show Ogre configuration dialogue.");
346
347        CCOUT(4) << "Creating render window" << std::endl;
348
349        this->renderWindow_ = ogreRoot_->initialise(true, "Orxonox");
350
351        this->ogreWindowEventListener_ = new OgreWindowEventListener();
352        Ogre::WindowEventUtilities::addWindowEventListener(this->renderWindow_, ogreWindowEventListener_);
353
354        Ogre::TextureManager::getSingleton().setDefaultNumMipmaps(0);
355
356        // create a full screen default viewport
357        this->viewport_ = this->renderWindow_->addViewport(0, 0);
358    }
359
360    void GraphicsManager::initialiseResources()
361    {
362        CCOUT(4) << "Initialising resources" << std::endl;
363        //TODO: Do NOT load all the groups, why are we doing that? And do we really do that? initialise != load...
364        //try
365        //{
366            Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
367            /*Ogre::StringVector str = Ogre::ResourceGroupManager::getSingleton().getResourceGroups();
368            for (unsigned int i = 0; i < str.size(); i++)
369            {
370            Ogre::ResourceGroupManager::getSingleton().loadResourceGroup(str[i]);
371            }*/
372        //}
373        //catch (...)
374        //{
375        //    CCOUT(2) << "Error: There was a serious error when initialising the resources." << std::endl;
376        //    throw;
377        //}
378    }
379
380    /**
381    @brief
382        Method called by the LogListener interface from Ogre.
383        We use it to capture Ogre log messages and handle it ourselves.
384    @param message
385        The message to be logged
386    @param lml
387        The message level the log is using
388    @param maskDebug
389        If we are printing to the console or not
390    @param logName
391        The name of this log (so you can have several listeners
392        for different logs, and identify them)
393    */
394    void GraphicsManager::messageLogged(const std::string& message,
395        Ogre::LogMessageLevel lml, bool maskDebug, const std::string& logName)
396    {
397        int orxonoxLevel;
398        switch (lml)
399        {
400        case Ogre::LML_TRIVIAL:
401            orxonoxLevel = this->ogreLogLevelTrivial_;
402            break;
403        case Ogre::LML_NORMAL:
404            orxonoxLevel = this->ogreLogLevelNormal_;
405            break;
406        case Ogre::LML_CRITICAL:
407            orxonoxLevel = this->ogreLogLevelCritical_;
408            break;
409        default:
410            orxonoxLevel = 0;
411        }
412        OutputHandler::getOutStream().setOutputLevel(orxonoxLevel)
413            << "Ogre: " << message << std::endl;
414    }
415
416    void GraphicsManager::printScreen()
417    {
418        assert(this->renderWindow_);
419       
420        this->renderWindow_->writeContentsToTimestampedFile(Core::getLogPathString() + "screenShot_", ".jpg");
421    }
422
423
424    /****** OgreWindowEventListener ******/
425
426    void OgreWindowEventListener::windowResized(Ogre::RenderWindow* rw)
427    {
428        for (ObjectList<orxonox::WindowEventListener>::iterator it
429            = ObjectList<orxonox::WindowEventListener>::begin(); it; ++it)
430            it->windowResized(rw->getWidth(), rw->getHeight());
431    }
432    void OgreWindowEventListener::windowFocusChange(Ogre::RenderWindow* rw)
433    {
434        for (ObjectList<orxonox::WindowEventListener>::iterator it
435            = ObjectList<orxonox::WindowEventListener>::begin(); it; ++it)
436            it->windowFocusChanged();
437    }
438    void OgreWindowEventListener::windowClosed(Ogre::RenderWindow* rw)
439    {
440        // TODO: Notify the right class to shut down the Game
441    }
442}
Note: See TracBrowser for help on using the repository browser.