Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/kicklib/src/libraries/core/GraphicsManager.cc @ 8066

Last change on this file since 8066 was 8066, checked in by landauf, 14 years ago

added support for boost 1.46 and adjusted code to work with the 3rd version of boost::filesystem

  • Property svn:eol-style set to native
File size: 14.8 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#include "GraphicsManager.h"
31
32#include <fstream>
33#include <sstream>
34#include <boost/filesystem.hpp>
35#include <boost/shared_array.hpp>
36
37#include <OgreFrameListener.h>
38#include <OgreRoot.h>
39#include <OgreLogManager.h>
40#include <OgreRenderWindow.h>
41#include <OgreRenderSystem.h>
42#include <OgreResourceGroupManager.h>
43#include <OgreTextureManager.h>
44#include <OgreViewport.h>
45#include <OgreWindowEventUtilities.h>
46
47#include "SpecialConfig.h"
48#include "util/Clock.h"
49#include "util/Exception.h"
50#include "util/StringUtils.h"
51#include "util/SubString.h"
52#include "ConfigValueIncludes.h"
53#include "CoreIncludes.h"
54#include "Core.h"
55#include "Game.h"
56#include "GameMode.h"
57#include "Loader.h"
58#include "PathConfig.h"
59#include "WindowEventListener.h"
60#include "XMLFile.h"
61#include "command/ConsoleCommand.h"
62
63namespace orxonox
64{
65    static const std::string __CC_printScreen_name = "printScreen";
66    DeclareConsoleCommand(__CC_printScreen_name, &prototype::void__void);
67
68    class OgreWindowEventListener : public Ogre::WindowEventListener
69    {
70    public:
71        void windowResized     (Ogre::RenderWindow* rw)
72            { orxonox::WindowEventListener::resizeWindow(rw->getWidth(), rw->getHeight()); }
73        void windowFocusChange (Ogre::RenderWindow* rw)
74            { orxonox::WindowEventListener::changeWindowFocus(rw->isActive()); }
75        void windowClosed      (Ogre::RenderWindow* rw)
76            { orxonox::Game::getInstance().stop(); }
77        void windowMoved       (Ogre::RenderWindow* rw)
78            { orxonox::WindowEventListener::moveWindow(); }
79    };
80
81    GraphicsManager* GraphicsManager::singletonPtr_s = 0;
82
83    /**
84    @brief
85        Non-initialising constructor.
86    */
87    GraphicsManager::GraphicsManager(bool bLoadRenderer)
88        : ogreWindowEventListener_(new OgreWindowEventListener())
89        , renderWindow_(0)
90        , viewport_(0)
91    {
92        RegisterObject(GraphicsManager);
93
94        this->setConfigValues();
95
96        // Ogre setup procedure (creating Ogre::Root)
97        this->loadOgreRoot();
98
99        // At first, add the root paths of the data directories as resource locations
100        Ogre::ResourceGroupManager::getSingleton().addResourceLocation(PathConfig::getDataPathString(), "FileSystem");
101        // Load resources
102        resources_.reset(new XMLFile("DefaultResources.oxr"));
103        resources_->setLuaSupport(false);
104        Loader::open(resources_.get());
105
106        // Only for development runs
107        if (PathConfig::isDevelopmentRun())
108        {
109            Ogre::ResourceGroupManager::getSingleton().addResourceLocation(PathConfig::getExternalDataPathString(), "FileSystem");
110            extResources_.reset(new XMLFile("resources.oxr"));
111            extResources_->setLuaSupport(false);
112            Loader::open(extResources_.get());
113        }
114
115        if (bLoadRenderer)
116        {
117            // Reads the ogre config and creates the render window
118            this->upgradeToGraphics();
119        }
120    }
121
122    /**
123    @brief
124        Destruction is done by the member scoped_ptrs.
125    */
126    GraphicsManager::~GraphicsManager()
127    {
128        Loader::unload(debugOverlay_.get());
129
130        Ogre::WindowEventUtilities::removeWindowEventListener(renderWindow_, ogreWindowEventListener_.get());
131        ModifyConsoleCommand(__CC_printScreen_name).resetFunction();
132
133        // Undeclare the resources
134        Loader::unload(resources_.get());
135        if (PathConfig::isDevelopmentRun())
136            Loader::unload(extResources_.get());
137    }
138
139    void GraphicsManager::setConfigValues()
140    {
141        SetConfigValue(ogreConfigFile_,  "ogre.cfg")
142            .description("Location of the Ogre config file");
143        SetConfigValue(ogrePluginsDirectory_, specialConfig::ogrePluginsDirectory)
144            .description("Folder where the Ogre plugins are located.");
145        SetConfigValue(ogrePlugins_, specialConfig::ogrePlugins)
146            .description("Comma separated list of all plugins to load.");
147        SetConfigValue(ogreLogFile_,     "ogre.log")
148            .description("Logfile for messages from Ogre. Use \"\" to suppress log file creation.");
149        SetConfigValue(ogreLogLevelTrivial_ , 5)
150            .description("Corresponding orxonox debug level for ogre Trivial");
151        SetConfigValue(ogreLogLevelNormal_  , 4)
152            .description("Corresponding orxonox debug level for ogre Normal");
153        SetConfigValue(ogreLogLevelCritical_, 2)
154            .description("Corresponding orxonox debug level for ogre Critical");
155    }
156
157    /**
158    @brief
159        Loads the renderer and creates the render window if not yet done so.
160    @remarks
161        This operation is irreversible without recreating the GraphicsManager!
162        So if it throws you HAVE to recreate the GraphicsManager!!!
163        It therefore offers almost no exception safety.
164    */
165    void GraphicsManager::upgradeToGraphics()
166    {
167        if (renderWindow_ != NULL)
168            return;
169
170        // load all the required plugins for Ogre
171        this->loadOgrePlugins();
172
173        this->loadRenderer();
174
175        // Initialise all resources (do this AFTER the renderer has been loaded!)
176        // Note: You can only do this once! Ogre will check whether a resource group has
177        // already been initialised. If you need to load resources later, you will have to
178        // choose another resource group.
179        Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
180    }
181
182    /**
183    @brief
184        Creates the Ogre Root object and sets up the ogre log.
185    */
186    void GraphicsManager::loadOgreRoot()
187    {
188        COUT(3) << "Setting up Ogre..." << std::endl;
189
190        if (ogreConfigFile_.empty())
191        {
192            COUT(2) << "Warning: Ogre config file set to \"\". Defaulting to config.cfg" << std::endl;
193            ModifyConfigValue(ogreConfigFile_, tset, "config.cfg");
194        }
195        if (ogreLogFile_.empty())
196        {
197            COUT(2) << "Warning: Ogre log file set to \"\". Defaulting to ogre.log" << std::endl;
198            ModifyConfigValue(ogreLogFile_, tset, "ogre.log");
199        }
200
201        boost::filesystem::path ogreConfigFilepath(PathConfig::getConfigPath() / this->ogreConfigFile_);
202        boost::filesystem::path ogreLogFilepath(PathConfig::getLogPath() / this->ogreLogFile_);
203
204        // create a new logManager
205        // Ogre::Root will detect that we've already created a Log
206        ogreLogger_.reset(new Ogre::LogManager());
207        COUT(4) << "Ogre LogManager created" << std::endl;
208
209        // create our own log that we can listen to
210        Ogre::Log *myLog;
211        myLog = ogreLogger_->createLog(ogreLogFilepath.string(), true, false, false);
212        COUT(4) << "Ogre Log created" << std::endl;
213
214        myLog->setLogDetail(Ogre::LL_BOREME);
215        myLog->addListener(this);
216
217        COUT(4) << "Creating Ogre Root..." << std::endl;
218
219        // check for config file existence because Ogre displays (caught) exceptions if not
220        if (!boost::filesystem::exists(ogreConfigFilepath))
221        {
222            // create a zero sized file
223            std::ofstream creator;
224            creator.open(ogreConfigFilepath.string().c_str());
225            creator.close();
226        }
227
228        // Leave plugins file empty. We're going to do that part manually later
229        ogreRoot_.reset(new Ogre::Root("", ogreConfigFilepath.string(), ogreLogFilepath.string()));
230
231        COUT(3) << "Ogre set up done." << std::endl;
232    }
233
234    void GraphicsManager::loadOgrePlugins()
235    {
236        // just to make sure the next statement doesn't segfault
237        if (ogrePluginsDirectory_.empty())
238            ogrePluginsDirectory_ = '.';
239
240        boost::filesystem::path folder(ogrePluginsDirectory_);
241        // Do some SubString magic to get the comma separated list of plugins
242        SubString plugins(ogrePlugins_, ",", " ", false, '\\', false, '"', false, '{', '}', false, '\0');
243        // Use backslash paths on Windows! file_string() already does that though.
244        for (unsigned int i = 0; i < plugins.size(); ++i)
245#if BOOST_FILESYSTEM_VERSION < 3
246            ogreRoot_->loadPlugin((folder / plugins[i]).file_string());
247#else
248            ogreRoot_->loadPlugin((folder / plugins[i]).string());
249#endif
250    }
251
252    void GraphicsManager::loadRenderer()
253    {
254        CCOUT(4) << "Configuring Renderer" << std::endl;
255
256        if (!ogreRoot_->restoreConfig() || Core::getInstance().getOgreConfigTimestamp() > Core::getInstance().getLastLevelTimestamp())
257        {
258            if (!ogreRoot_->showConfigDialog())
259                ThrowException(InitialisationFailed, "OGRE graphics configuration dialogue canceled.");
260            else
261                Core::getInstance().updateOgreConfigTimestamp();
262        }
263
264        CCOUT(4) << "Creating render window" << std::endl;
265
266        this->renderWindow_ = ogreRoot_->initialise(true, "Orxonox");
267        // Propagate the size of the new winodw
268        this->ogreWindowEventListener_->windowResized(renderWindow_);
269
270        Ogre::WindowEventUtilities::addWindowEventListener(this->renderWindow_, ogreWindowEventListener_.get());
271
272        // create a full screen default viewport
273        // Note: This may throw when adding a viewport with an existing z-order!
274        //       But in our case we only have one viewport for now anyway, therefore
275        //       no ScopeGuards or anything to handle exceptions.
276        this->viewport_ = this->renderWindow_->addViewport(0, 0);
277
278        Ogre::TextureManager::getSingleton().setDefaultNumMipmaps(Ogre::MIP_UNLIMITED);
279
280        // add console commands
281        ModifyConsoleCommand(__CC_printScreen_name).setFunction(&GraphicsManager::printScreen, this);
282    }
283
284    void GraphicsManager::loadDebugOverlay()
285    {
286        // Load debug overlay to show info about fps and tick time
287        COUT(4) << "Loading Debug Overlay..." << std::endl;
288        debugOverlay_.reset(new XMLFile("debug.oxo"));
289        Loader::open(debugOverlay_.get());
290    }
291
292    /**
293    @note
294        A note about the Ogre::FrameListener: Even though we don't use them,
295        they still get called. However, the delta times are not correct (except
296        for timeSinceLastFrame, which is the most important). A little research
297        as shown that there is probably only one FrameListener that doesn't even
298        need the time. So we shouldn't run into problems.
299    */
300    void GraphicsManager::postUpdate(const Clock& time)
301    {
302        Ogre::FrameEvent evt;
303        evt.timeSinceLastFrame = time.getDeltaTime();
304        evt.timeSinceLastEvent = time.getDeltaTime(); // note: same time, but shouldn't matter anyway
305
306        // don't forget to call _fireFrameStarted to OGRE to make sure
307        // everything goes smoothly
308        ogreRoot_->_fireFrameStarted(evt);
309
310        // Pump messages in all registered RenderWindows
311        // This calls the WindowEventListener objects.
312        Ogre::WindowEventUtilities::messagePump();
313        // make sure the window stays active even when not focused
314        // (probably only necessary on windows)
315        this->renderWindow_->setActive(true);
316
317        // Time before rendering
318        uint64_t timeBeforeTick = time.getRealMicroseconds();
319
320        // Render frame
321        ogreRoot_->_updateAllRenderTargets();
322
323        uint64_t timeAfterTick = time.getRealMicroseconds();
324        // Subtract the time used for rendering from the tick time counter
325        Game::getInstance().subtractTickTime((int32_t)(timeAfterTick - timeBeforeTick));
326
327        // again, just to be sure OGRE works fine
328        ogreRoot_->_fireFrameEnded(evt); // note: uses the same time as _fireFrameStarted
329    }
330
331    void GraphicsManager::setCamera(Ogre::Camera* camera)
332    {
333        this->viewport_->setCamera(camera);
334    }
335
336    /**
337    @brief
338        Method called by the LogListener interface from Ogre.
339        We use it to capture Ogre log messages and handle it ourselves.
340    @param message
341        The message to be logged
342    @param lml
343        The message level the log is using
344    @param maskDebug
345        If we are printing to the console or not
346    @param logName
347        The name of this log (so you can have several listeners
348        for different logs, and identify them)
349    */
350    void GraphicsManager::messageLogged(const std::string& message,
351        Ogre::LogMessageLevel lml, bool maskDebug, const std::string& logName)
352    {
353        int orxonoxLevel;
354        std::string introduction;
355        // Do not show caught OGRE exceptions in front
356        if (message.find("EXCEPTION") != std::string::npos)
357        {
358            orxonoxLevel = OutputLevel::Debug;
359            introduction = "Ogre, caught exception: ";
360        }
361        else
362        {
363            switch (lml)
364            {
365            case Ogre::LML_TRIVIAL:
366                orxonoxLevel = this->ogreLogLevelTrivial_;
367                break;
368            case Ogre::LML_NORMAL:
369                orxonoxLevel = this->ogreLogLevelNormal_;
370                break;
371            case Ogre::LML_CRITICAL:
372                orxonoxLevel = this->ogreLogLevelCritical_;
373                break;
374            default:
375                orxonoxLevel = 0;
376            }
377            introduction = "Ogre: ";
378        }
379        OutputHandler::getOutStream(orxonoxLevel)
380            << introduction << message << std::endl;
381    }
382
383    size_t GraphicsManager::getRenderWindowHandle()
384    {
385        size_t windowHnd = 0;
386        renderWindow_->getCustomAttribute("WINDOW", &windowHnd);
387        return windowHnd;
388    }
389
390    bool GraphicsManager::isFullScreen() const
391    {
392        Ogre::ConfigOptionMap& options = ogreRoot_->getRenderSystem()->getConfigOptions();
393        if (options.find("Full Screen") != options.end())
394        {
395            if (options["Full Screen"].currentValue == "Yes")
396                return true;
397            else
398                return false;
399        }
400        else
401        {
402            COUT(0) << "Could not find 'Full Screen' render system option. Fix This!!!" << std::endl;
403            return false;
404        }
405    }
406
407    void GraphicsManager::printScreen()
408    {
409        assert(this->renderWindow_);
410        this->renderWindow_->writeContentsToTimestampedFile(PathConfig::getLogPathString() + "screenShot_", ".png");
411    }
412}
Note: See TracBrowser for help on using the repository browser.