Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 3100 was 3100, checked in by scheusso, 15 years ago

reapplied retos patch

  • Property svn:eol-style set to native
File size: 15.3 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/Clock.h"
60#include "core/ConsoleCommand.h"
61#include "core/ConfigValueIncludes.h"
62#include "core/CoreIncludes.h"
63#include "core/Core.h"
64#include "core/Game.h"
65#include "core/GameMode.h"
66#include "tools/WindowEventListener.h"
67#include "tools/ParticleInterface.h"
68
69namespace orxonox
70{
71    class _OrxonoxExport OgreWindowEventListener : public Ogre::WindowEventListener
72    {
73        void windowResized     (Ogre::RenderWindow* rw);
74        void windowFocusChange (Ogre::RenderWindow* rw);
75        void windowClosed      (Ogre::RenderWindow* rw);
76        //void windowMoved       (Ogre::RenderWindow* rw);
77    };
78
79    GraphicsManager* GraphicsManager::singletonRef_s = 0;
80
81    /**
82    @brief
83        Non-initialising constructor.
84    */
85    GraphicsManager::GraphicsManager()
86        : ogreRoot_(0)
87        , ogreLogger_(0)
88        , renderWindow_(0)
89        , viewport_(0)
90        , ogreWindowEventListener_(0)
91        , avgTickTime_(0.0f)
92        , avgFramesPerSecond_(0.0f)
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            // unload all compositors
145            Ogre::CompositorManager::getSingleton().removeAll();
146
147            // Delete OGRE main control organ
148            delete this->ogreRoot_;
149
150            // delete the ogre log and the logManager (since we have created it in the first place).
151            this->ogreLogger_->getDefaultLog()->removeListener(this);
152            this->ogreLogger_->destroyLog(Ogre::LogManager::getSingleton().getDefaultLog());
153            delete this->ogreLogger_;
154        }
155
156        assert(singletonRef_s);
157        singletonRef_s = 0;
158    }
159
160    void GraphicsManager::setConfigValues()
161    {
162        SetConfigValue(resourceFile_,    "resources.cfg")
163            .description("Location of the resources file in the data path.");
164        SetConfigValue(ogreConfigFile_,  "ogre.cfg")
165            .description("Location of the Ogre config file");
166        SetConfigValue(ogrePluginsFolder_, ORXONOX_OGRE_PLUGINS_FOLDER)
167            .description("Folder where the Ogre plugins are located.");
168        SetConfigValue(ogrePlugins_, ORXONOX_OGRE_PLUGINS)
169            .description("Comma separated list of all plugins to load.");
170        SetConfigValue(ogreLogFile_,     "ogre.log")
171            .description("Logfile for messages from Ogre. Use \"\" to suppress log file creation.");
172        SetConfigValue(ogreLogLevelTrivial_ , 5)
173            .description("Corresponding orxonox debug level for ogre Trivial");
174        SetConfigValue(ogreLogLevelNormal_  , 4)
175            .description("Corresponding orxonox debug level for ogre Normal");
176        SetConfigValue(ogreLogLevelCritical_, 2)
177            .description("Corresponding orxonox debug level for ogre Critical");
178        SetConfigValue(detailLevelParticle_, 2)
179            .description("O: off, 1: low, 2: normal, 3: high").callback(this, &GraphicsManager::detailLevelParticleChanged);
180    }
181
182    void GraphicsManager::detailLevelParticleChanged()
183    {
184        for (ObjectList<ParticleInterface>::iterator it = ObjectList<ParticleInterface>::begin(); it; ++it)
185            it->detailLevelChanged(this->detailLevelParticle_);
186    }
187
188    void GraphicsManager::update(const Clock& time)
189    {
190        if (this->loaded_)
191        {
192            Ogre::FrameEvent evt;
193            evt.timeSinceLastFrame = time.getDeltaTime();
194            evt.timeSinceLastEvent = time.getDeltaTime(); // note: same time, but shouldn't matter anyway
195
196            // don't forget to call _fireFrameStarted to OGRE to make sure
197            // everything goes smoothly
198            ogreRoot_->_fireFrameStarted(evt);
199
200            // Pump messages in all registered RenderWindows
201            // This calls the WindowEventListener objects.
202            Ogre::WindowEventUtilities::messagePump();
203            // make sure the window stays active even when not focused
204            // (probably only necessary on windows)
205            this->renderWindow_->setActive(true);
206
207            // render
208            ogreRoot_->_updateAllRenderTargets();
209
210            // again, just to be sure OGRE works fine
211            ogreRoot_->_fireFrameEnded(evt); // note: uses the same time as _fireFrameStarted
212        }
213    }
214
215    void GraphicsManager::setCamera(Ogre::Camera* camera)
216    {
217        this->viewport_->setCamera(camera);
218    }
219
220    /**
221    @brief
222        Creates the Ogre Root object and sets up the ogre log.
223    */
224    void GraphicsManager::setupOgre()
225    {
226        COUT(3) << "Setting up Ogre..." << std::endl;
227
228        if (ogreConfigFile_ == "")
229        {
230            COUT(2) << "Warning: Ogre config file set to \"\". Defaulting to config.cfg" << std::endl;
231            ModifyConfigValue(ogreConfigFile_, tset, "config.cfg");
232        }
233        if (ogreLogFile_ == "")
234        {
235            COUT(2) << "Warning: Ogre log file set to \"\". Defaulting to ogre.log" << std::endl;
236            ModifyConfigValue(ogreLogFile_, tset, "ogre.log");
237        }
238
239        boost::filesystem::path ogreConfigFilepath(Core::getConfigPath() / this->ogreConfigFile_);
240        boost::filesystem::path ogreLogFilepath(Core::getLogPath() / this->ogreLogFile_);
241
242        // create a new logManager
243        // Ogre::Root will detect that we've already created a Log
244        ogreLogger_ = new Ogre::LogManager();
245        COUT(4) << "Ogre LogManager created" << std::endl;
246
247        // create our own log that we can listen to
248        Ogre::Log *myLog;
249        myLog = ogreLogger_->createLog(ogreLogFilepath.string(), true, false, false);
250        COUT(4) << "Ogre Log created" << std::endl;
251
252        myLog->setLogDetail(Ogre::LL_BOREME);
253        myLog->addListener(this);
254
255        COUT(4) << "Creating Ogre Root..." << std::endl;
256
257        // check for config file existence because Ogre displays (caught) exceptions if not
258        if (!boost::filesystem::exists(ogreConfigFilepath))
259        {
260            // create a zero sized file
261            std::ofstream creator;
262            creator.open(ogreConfigFilepath.string().c_str());
263            creator.close();
264        }
265
266        // Leave plugins file empty. We're going to do that part manually later
267        ogreRoot_ = new Ogre::Root("", ogreConfigFilepath.string(), ogreLogFilepath.string());
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, "Could not show Ogre configuration dialogue.");
345
346        CCOUT(4) << "Creating render window" << std::endl;
347
348        this->renderWindow_ = ogreRoot_->initialise(true, "Orxonox");
349
350        this->ogreWindowEventListener_ = new OgreWindowEventListener();
351        Ogre::WindowEventUtilities::addWindowEventListener(this->renderWindow_, ogreWindowEventListener_);
352
353        Ogre::TextureManager::getSingleton().setDefaultNumMipmaps(0);
354
355        // create a full screen default viewport
356        this->viewport_ = this->renderWindow_->addViewport(0, 0);
357    }
358
359    void GraphicsManager::initialiseResources()
360    {
361        CCOUT(4) << "Initialising resources" << std::endl;
362        //TODO: Do NOT load all the groups, why are we doing that? And do we really do that? initialise != load...
363        //try
364        //{
365            Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
366            /*Ogre::StringVector str = Ogre::ResourceGroupManager::getSingleton().getResourceGroups();
367            for (unsigned int i = 0; i < str.size(); i++)
368            {
369            Ogre::ResourceGroupManager::getSingleton().loadResourceGroup(str[i]);
370            }*/
371        //}
372        //catch (...)
373        //{
374        //    CCOUT(2) << "Error: There was a serious error when initialising the resources." << std::endl;
375        //    throw;
376        //}
377    }
378
379    /**
380    @brief
381        Method called by the LogListener interface from Ogre.
382        We use it to capture Ogre log messages and handle it ourselves.
383    @param message
384        The message to be logged
385    @param lml
386        The message level the log is using
387    @param maskDebug
388        If we are printing to the console or not
389    @param logName
390        The name of this log (so you can have several listeners
391        for different logs, and identify them)
392    */
393    void GraphicsManager::messageLogged(const std::string& message,
394        Ogre::LogMessageLevel lml, bool maskDebug, const std::string& logName)
395    {
396        int orxonoxLevel;
397        switch (lml)
398        {
399        case Ogre::LML_TRIVIAL:
400            orxonoxLevel = this->ogreLogLevelTrivial_;
401            break;
402        case Ogre::LML_NORMAL:
403            orxonoxLevel = this->ogreLogLevelNormal_;
404            break;
405        case Ogre::LML_CRITICAL:
406            orxonoxLevel = this->ogreLogLevelCritical_;
407            break;
408        default:
409            orxonoxLevel = 0;
410        }
411        OutputHandler::getOutStream().setOutputLevel(orxonoxLevel)
412            << "Ogre: " << message << std::endl;
413    }
414
415    void GraphicsManager::printScreen()
416    {
417        assert(this->renderWindow_);
418       
419        this->renderWindow_->writeContentsToTimestampedFile(Core::getLogPathString() + "screenShot_", ".jpg");
420    }
421
422
423    /****** OgreWindowEventListener ******/
424
425    void OgreWindowEventListener::windowResized(Ogre::RenderWindow* rw)
426    {
427        for (ObjectList<orxonox::WindowEventListener>::iterator it
428            = ObjectList<orxonox::WindowEventListener>::begin(); it; ++it)
429            it->windowResized(rw->getWidth(), rw->getHeight());
430    }
431    void OgreWindowEventListener::windowFocusChange(Ogre::RenderWindow* rw)
432    {
433        for (ObjectList<orxonox::WindowEventListener>::iterator it
434            = ObjectList<orxonox::WindowEventListener>::begin(); it; ++it)
435            it->windowFocusChanged();
436    }
437    void OgreWindowEventListener::windowClosed(Ogre::RenderWindow* rw)
438    {
439        Game::getInstance().stop();
440    }
441}
Note: See TracBrowser for help on using the repository browser.