Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/core/GraphicsManager.cc @ 4070

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

Merged resource branch back to the trunk. Changes:

  • Automated graphics loading by evaluating whether a GameState requires it
  • Using native Tcl library (x3n)

Windows users: Update your dependency package!

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