Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/resource3/src/core/GraphicsManager.cc @ 5678

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

Merged most of revision 5614 and its fixes from revisions 5628, 5658, 5662 and 5670:

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