Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

Either my box or Oli's gets screwed, but since it only didn't work on my box, we're going back to the old solution.

  • 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/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            // destroy render window
145            Ogre::RenderSystem* renderer = this->ogreRoot_->getRenderSystem();
146            renderer->destroyRenderWindow("Orxonox");
147
148            // unload all compositors
149            Ogre::CompositorManager::getSingleton().removeAll();
150
151            // Delete OGRE main control organ
152            delete this->ogreRoot_;
153
154            // delete the ogre log and the logManager (since we have created it in the first place).
155            this->ogreLogger_->getDefaultLog()->removeListener(this);
156            this->ogreLogger_->destroyLog(Ogre::LogManager::getSingleton().getDefaultLog());
157            delete this->ogreLogger_;
158        }
159
160        assert(singletonRef_s);
161        singletonRef_s = 0;
162    }
163
164    void GraphicsManager::setConfigValues()
165    {
166        SetConfigValue(resourceFile_,    "resources.cfg")
167            .description("Location of the resources file in the data path.");
168        SetConfigValue(ogreConfigFile_,  "ogre.cfg")
169            .description("Location of the Ogre config file");
170        SetConfigValue(ogrePluginsFolder_, ORXONOX_OGRE_PLUGINS_FOLDER)
171            .description("Folder where the Ogre plugins are located.");
172        SetConfigValue(ogrePlugins_, ORXONOX_OGRE_PLUGINS)
173            .description("Comma separated list of all plugins to load.");
174        SetConfigValue(ogreLogFile_,     "ogre.log")
175            .description("Logfile for messages from Ogre. Use \"\" to suppress log file creation.");
176        SetConfigValue(ogreLogLevelTrivial_ , 5)
177            .description("Corresponding orxonox debug level for ogre Trivial");
178        SetConfigValue(ogreLogLevelNormal_  , 4)
179            .description("Corresponding orxonox debug level for ogre Normal");
180        SetConfigValue(ogreLogLevelCritical_, 2)
181            .description("Corresponding orxonox debug level for ogre Critical");
182        SetConfigValue(detailLevelParticle_, 2)
183            .description("O: off, 1: low, 2: normal, 3: high").callback(this, &GraphicsManager::detailLevelParticleChanged);
184    }
185
186    void GraphicsManager::detailLevelParticleChanged()
187    {
188        for (ObjectList<ParticleInterface>::iterator it = ObjectList<ParticleInterface>::begin(); it; ++it)
189            it->detailLevelChanged(this->detailLevelParticle_);
190    }
191
192    void GraphicsManager::update(const Clock& time)
193    {
194        if (this->loaded_)
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
219    void GraphicsManager::setCamera(Ogre::Camera* camera)
220    {
221        this->viewport_->setCamera(camera);
222    }
223
224    /**
225    @brief
226        Creates the Ogre Root object and sets up the ogre log.
227    */
228    void GraphicsManager::setupOgre()
229    {
230        COUT(3) << "Setting up Ogre..." << std::endl;
231
232        if (ogreConfigFile_ == "")
233        {
234            COUT(2) << "Warning: Ogre config file set to \"\". Defaulting to config.cfg" << std::endl;
235            ModifyConfigValue(ogreConfigFile_, tset, "config.cfg");
236        }
237        if (ogreLogFile_ == "")
238        {
239            COUT(2) << "Warning: Ogre log file set to \"\". Defaulting to ogre.log" << std::endl;
240            ModifyConfigValue(ogreLogFile_, tset, "ogre.log");
241        }
242
243        boost::filesystem::path ogreConfigFilepath(Core::getConfigPath() / this->ogreConfigFile_);
244        boost::filesystem::path ogreLogFilepath(Core::getLogPath() / this->ogreLogFile_);
245
246        // create a new logManager
247        // Ogre::Root will detect that we've already created a Log
248        ogreLogger_ = new Ogre::LogManager();
249        COUT(4) << "Ogre LogManager created" << std::endl;
250
251        // create our own log that we can listen to
252        Ogre::Log *myLog;
253        myLog = ogreLogger_->createLog(ogreLogFilepath.string(), true, false, false);
254        COUT(4) << "Ogre Log created" << std::endl;
255
256        myLog->setLogDetail(Ogre::LL_BOREME);
257        myLog->addListener(this);
258
259        COUT(4) << "Creating Ogre Root..." << std::endl;
260
261        // check for config file existence because Ogre displays (caught) exceptions if not
262        if (!boost::filesystem::exists(ogreConfigFilepath))
263        {
264            // create a zero sized file
265            std::ofstream creator;
266            creator.open(ogreConfigFilepath.string().c_str());
267            creator.close();
268        }
269
270        // Leave plugins file empty. We're going to do that part manually later
271        ogreRoot_ = new Ogre::Root("", ogreConfigFilepath.string(), ogreLogFilepath.string());
272
273        COUT(3) << "Ogre set up done." << std::endl;
274    }
275
276    void GraphicsManager::loadOgrePlugins()
277    {
278        // just to make sure the next statement doesn't segfault
279        if (ogrePluginsFolder_ == "")
280            ogrePluginsFolder_ = ".";
281
282        boost::filesystem::path folder(ogrePluginsFolder_);
283        // Do some SubString magic to get the comma separated list of plugins
284        SubString plugins(ogrePlugins_, ",", " ", false, 92, false, 34, false, 40, 41, false, '\0');
285        // Use backslash paths on Windows! file_string() already does that though.
286        for (unsigned int i = 0; i < plugins.size(); ++i)
287            ogreRoot_->loadPlugin((folder / plugins[i]).file_string());
288    }
289
290    void GraphicsManager::declareResources()
291    {
292        CCOUT(4) << "Declaring Resources" << std::endl;
293        //TODO: Specify layout of data file and maybe use xml-loader
294        //TODO: Work with ressource groups (should be generated by a special loader)
295
296        if (resourceFile_ == "")
297        {
298            COUT(2) << "Warning: Ogre resource file set to \"\". Defaulting to resources.cfg" << std::endl;
299            ModifyConfigValue(resourceFile_, tset, "resources.cfg");
300        }
301
302        // Load resource paths from data file using configfile ressource type
303        Ogre::ConfigFile cf;
304        try
305        {
306            cf.load((Core::getMediaPath() / resourceFile_).string());
307        }
308        catch (...)
309        {
310            //COUT(1) << ex.getFullDescription() << std::endl;
311            COUT(0) << "Have you forgotten to set the data path in orxnox.ini?" << std::endl;
312            throw;
313        }
314
315        // Go through all sections & settings in the file
316        Ogre::ConfigFile::SectionIterator seci = cf.getSectionIterator();
317
318        std::string secName, typeName, archName;
319        while (seci.hasMoreElements())
320        {
321            try
322            {
323                secName = seci.peekNextKey();
324                Ogre::ConfigFile::SettingsMultiMap *settings = seci.getNext();
325                Ogre::ConfigFile::SettingsMultiMap::iterator i;
326                for (i = settings->begin(); i != settings->end(); ++i)
327                {
328                    typeName = i->first; // for instance "FileSystem" or "Zip"
329                    archName = i->second; // name (and location) of archive
330
331                    Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
332                        (Core::getMediaPath() / archName).string(), typeName, secName);
333                }
334            }
335            catch (Ogre::Exception& ex)
336            {
337                COUT(1) << ex.getFullDescription() << std::endl;
338            }
339        }
340    }
341
342    void GraphicsManager::loadRenderer()
343    {
344        CCOUT(4) << "Configuring Renderer" << std::endl;
345
346        if (!ogreRoot_->restoreConfig())
347            if (!ogreRoot_->showConfigDialog())
348                ThrowException(InitialisationFailed, "Could not show Ogre configuration dialogue.");
349
350        CCOUT(4) << "Creating render window" << std::endl;
351
352        this->renderWindow_ = ogreRoot_->initialise(true, "Orxonox");
353
354        this->ogreWindowEventListener_ = new OgreWindowEventListener();
355        Ogre::WindowEventUtilities::addWindowEventListener(this->renderWindow_, ogreWindowEventListener_);
356
357        Ogre::TextureManager::getSingleton().setDefaultNumMipmaps(0);
358
359        // create a full screen default viewport
360        this->viewport_ = this->renderWindow_->addViewport(0, 0);
361    }
362
363    void GraphicsManager::initialiseResources()
364    {
365        CCOUT(4) << "Initialising resources" << std::endl;
366        //TODO: Do NOT load all the groups, why are we doing that? And do we really do that? initialise != load...
367        //try
368        //{
369            Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
370            /*Ogre::StringVector str = Ogre::ResourceGroupManager::getSingleton().getResourceGroups();
371            for (unsigned int i = 0; i < str.size(); i++)
372            {
373            Ogre::ResourceGroupManager::getSingleton().loadResourceGroup(str[i]);
374            }*/
375        //}
376        //catch (...)
377        //{
378        //    CCOUT(2) << "Error: There was a serious error when initialising the resources." << std::endl;
379        //    throw;
380        //}
381    }
382
383    /**
384    @brief
385        Method called by the LogListener interface from Ogre.
386        We use it to capture Ogre log messages and handle it ourselves.
387    @param message
388        The message to be logged
389    @param lml
390        The message level the log is using
391    @param maskDebug
392        If we are printing to the console or not
393    @param logName
394        The name of this log (so you can have several listeners
395        for different logs, and identify them)
396    */
397    void GraphicsManager::messageLogged(const std::string& message,
398        Ogre::LogMessageLevel lml, bool maskDebug, const std::string& logName)
399    {
400        int orxonoxLevel;
401        switch (lml)
402        {
403        case Ogre::LML_TRIVIAL:
404            orxonoxLevel = this->ogreLogLevelTrivial_;
405            break;
406        case Ogre::LML_NORMAL:
407            orxonoxLevel = this->ogreLogLevelNormal_;
408            break;
409        case Ogre::LML_CRITICAL:
410            orxonoxLevel = this->ogreLogLevelCritical_;
411            break;
412        default:
413            orxonoxLevel = 0;
414        }
415        OutputHandler::getOutStream().setOutputLevel(orxonoxLevel)
416            << "Ogre: " << message << std::endl;
417    }
418
419    void GraphicsManager::printScreen()
420    {
421        assert(this->renderWindow_);
422       
423        this->renderWindow_->writeContentsToTimestampedFile(Core::getLogPathString() + "screenShot_", ".jpg");
424    }
425
426
427    /****** OgreWindowEventListener ******/
428
429    void OgreWindowEventListener::windowResized(Ogre::RenderWindow* rw)
430    {
431        for (ObjectList<orxonox::WindowEventListener>::iterator it
432            = ObjectList<orxonox::WindowEventListener>::begin(); it; ++it)
433            it->windowResized(rw->getWidth(), rw->getHeight());
434    }
435    void OgreWindowEventListener::windowFocusChange(Ogre::RenderWindow* rw)
436    {
437        for (ObjectList<orxonox::WindowEventListener>::iterator it
438            = ObjectList<orxonox::WindowEventListener>::begin(); it; ++it)
439            it->windowFocusChanged();
440    }
441    void OgreWindowEventListener::windowClosed(Ogre::RenderWindow* rw)
442    {
443        Game::getInstance().stop();
444    }
445}
Note: See TracBrowser for help on using the repository browser.