Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/output/src/libraries/core/GraphicsManager.cc @ 8803

Last change on this file since 8803 was 8798, checked in by landauf, 13 years ago

adjusted output levels for ogre and cegui messages
added config value for cegui logfile

  • Property svn:eol-style set to native
File size: 20.4 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 <cstdlib>
33#include <fstream>
34#include <sstream>
35#include <boost/filesystem.hpp>
36#include <boost/shared_array.hpp>
37
38#include <OgreFrameListener.h>
39#include <OgreRoot.h>
40#include <OgreLogManager.h>
41#include <OgreRenderWindow.h>
42#include <OgreRenderSystem.h>
43#include <OgreResourceGroupManager.h>
44#include <OgreTextureManager.h>
45#include <OgreViewport.h>
46#include <OgreWindowEventUtilities.h>
47
48#include "SpecialConfig.h"
49#include "util/Clock.h"
50#include "util/Convert.h"
51#include "util/Exception.h"
52#include "util/StringUtils.h"
53#include "util/SubString.h"
54#include "ConfigValueIncludes.h"
55#include "CoreIncludes.h"
56#include "Core.h"
57#include "Game.h"
58#include "GameMode.h"
59#include "GUIManager.h"
60#include "Loader.h"
61#include "PathConfig.h"
62#include "ViewportEventListener.h"
63#include "WindowEventListener.h"
64#include "XMLFile.h"
65#include "command/ConsoleCommand.h"
66#include "input/InputManager.h"
67
68namespace orxonox
69{
70    namespace context
71    {
72        namespace
73        {
74            REGISTER_OUTPUT_CONTEXT(ogre);
75        }
76    }
77
78    static const std::string __CC_GraphicsManager_group = "GraphicsManager";
79    static const std::string __CC_setScreenResolution_name = "setScreenResolution";
80    static const std::string __CC_setFSAA_name = "setFSAA";
81    static const std::string __CC_setVSync_name = "setVSync";
82    DeclareConsoleCommand(__CC_GraphicsManager_group, __CC_setScreenResolution_name, &prototype::string__uint_uint_bool);
83    DeclareConsoleCommand(__CC_GraphicsManager_group, __CC_setFSAA_name, &prototype::string__string);
84    DeclareConsoleCommand(__CC_GraphicsManager_group, __CC_setVSync_name, &prototype::string__bool);
85
86    static const std::string __CC_printScreen_name = "printScreen";
87    DeclareConsoleCommand(__CC_printScreen_name, &prototype::void__void);
88
89    class OgreWindowEventListener : public Ogre::WindowEventListener
90    {
91    public:
92        void windowResized     (Ogre::RenderWindow* rw)
93            { orxonox::WindowEventListener::resizeWindow(rw->getWidth(), rw->getHeight()); }
94        void windowFocusChange (Ogre::RenderWindow* rw)
95            { orxonox::WindowEventListener::changeWindowFocus(rw->isActive()); }
96        void windowClosed      (Ogre::RenderWindow* rw)
97            { orxonox::Game::getInstance().stop(); }
98        void windowMoved       (Ogre::RenderWindow* rw)
99            { orxonox::WindowEventListener::moveWindow(); }
100    };
101
102    GraphicsManager* GraphicsManager::singletonPtr_s = 0;
103
104    GraphicsManager::GraphicsManager(bool bLoadRenderer)
105        : ogreWindowEventListener_(new OgreWindowEventListener())
106        , renderWindow_(0)
107        , viewport_(0)
108        , lastFrameStartTime_(0.0f)
109        , lastFrameEndTime_(0.0f)
110        , destructionHelper_(this)
111    {
112        RegisterObject(GraphicsManager);
113
114        this->setConfigValues();
115
116        // Ogre setup procedure (creating Ogre::Root)
117        this->loadOgreRoot();
118
119        // At first, add the root paths of the data directories as resource locations
120        Ogre::ResourceGroupManager::getSingleton().addResourceLocation(PathConfig::getDataPathString(), "FileSystem");
121        // Load resources
122        resources_.reset(new XMLFile("DefaultResources.oxr"));
123        resources_->setLuaSupport(false);
124        Loader::open(resources_.get());
125
126        // Only for runs in the build directory (not installed)
127        if (PathConfig::buildDirectoryRun())
128            Ogre::ResourceGroupManager::getSingleton().addResourceLocation(PathConfig::getExternalDataPathString(), "FileSystem");
129
130        extResources_.reset(new XMLFile("resources.oxr"));
131        extResources_->setLuaSupport(false);
132        Loader::open(extResources_.get());
133
134        if (bLoadRenderer)
135        {
136            // Reads the ogre config and creates the render window
137            this->upgradeToGraphics();
138        }
139    }
140
141    void GraphicsManager::destroy()
142    {
143        Loader::unload(debugOverlay_.get());
144
145        Ogre::WindowEventUtilities::removeWindowEventListener(renderWindow_, ogreWindowEventListener_);
146        ModifyConsoleCommand(__CC_printScreen_name).resetFunction();
147        ModifyConsoleCommand(__CC_GraphicsManager_group, __CC_setScreenResolution_name).resetFunction();
148        ModifyConsoleCommand(__CC_GraphicsManager_group, __CC_setFSAA_name).resetFunction();
149        ModifyConsoleCommand(__CC_GraphicsManager_group, __CC_setVSync_name).resetFunction();
150
151        // Undeclare the resources
152        Loader::unload(resources_.get());
153        Loader::unload(extResources_.get());
154
155        safeObjectDelete(&ogreRoot_);
156        safeObjectDelete(&ogreLogger_);
157        safeObjectDelete(&ogreWindowEventListener_);
158    }
159
160    void GraphicsManager::setConfigValues()
161    {
162        SetConfigValue(ogreConfigFile_,  "ogre.cfg")
163            .description("Location of the Ogre config file");
164        SetConfigValue(ogrePlugins_, specialConfig::ogrePlugins)
165            .description("Comma separated list of all plugins to load.");
166        SetConfigValue(ogreLogFile_,     "ogre.log")
167            .description("Logfile for messages from Ogre. Use \"\" to suppress log file creation.");
168    }
169
170    /**
171    @brief
172        Loads the renderer and creates the render window if not yet done so.
173    @remarks
174        This operation is irreversible without recreating the GraphicsManager!
175        So if it throws you HAVE to recreate the GraphicsManager!!!
176        It therefore offers almost no exception safety.
177    */
178    void GraphicsManager::upgradeToGraphics()
179    {
180        if (renderWindow_ != NULL)
181            return;
182
183        // load all the required plugins for Ogre
184        this->loadOgrePlugins();
185
186        this->loadRenderer();
187
188        // Initialise all resources (do this AFTER the renderer has been loaded!)
189        // Note: You can only do this once! Ogre will check whether a resource group has
190        // already been initialised. If you need to load resources later, you will have to
191        // choose another resource group.
192        Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
193    }
194
195    /**
196    @brief
197        Creates the Ogre Root object and sets up the ogre log.
198    */
199    void GraphicsManager::loadOgreRoot()
200    {
201        COUT(3) << "Setting up Ogre..." << std::endl;
202
203        if (ogreConfigFile_.empty())
204        {
205            COUT(2) << "Warning: Ogre config file set to \"\". Defaulting to config.cfg" << std::endl;
206            ModifyConfigValue(ogreConfigFile_, tset, "config.cfg");
207        }
208        if (ogreLogFile_.empty())
209        {
210            COUT(2) << "Warning: Ogre log file set to \"\". Defaulting to ogre.log" << std::endl;
211            ModifyConfigValue(ogreLogFile_, tset, "ogre.log");
212        }
213
214        boost::filesystem::path ogreConfigFilepath(PathConfig::getConfigPath() / this->ogreConfigFile_);
215        boost::filesystem::path ogreLogFilepath(PathConfig::getLogPath() / this->ogreLogFile_);
216
217        // create a new logManager
218        // Ogre::Root will detect that we've already created a Log
219        ogreLogger_ = new Ogre::LogManager();
220        COUT(4) << "Ogre LogManager created" << std::endl;
221
222        // create our own log that we can listen to
223        Ogre::Log *myLog;
224        myLog = ogreLogger_->createLog(ogreLogFilepath.string(), true, false, false);
225        COUT(4) << "Ogre Log created" << std::endl;
226
227        myLog->setLogDetail(Ogre::LL_BOREME);
228        myLog->addListener(this);
229
230        COUT(4) << "Creating Ogre Root..." << std::endl;
231
232        // check for config file existence because Ogre displays (caught) exceptions if not
233        if (!boost::filesystem::exists(ogreConfigFilepath))
234        {
235            // create a zero sized file
236            std::ofstream creator;
237            creator.open(ogreConfigFilepath.string().c_str());
238            creator.close();
239        }
240
241        // Leave plugins file empty. We're going to do that part manually later
242        ogreRoot_ = new Ogre::Root("", ogreConfigFilepath.string(), ogreLogFilepath.string());
243
244        COUT(3) << "Ogre set up done." << std::endl;
245    }
246
247    void GraphicsManager::loadOgrePlugins()
248    {
249        // Plugin path can have many different locations...
250        std::string pluginPath = specialConfig::ogrePluginsDirectory;
251#ifdef DEPENDENCY_PACKAGE_ENABLE
252        if (!PathConfig::buildDirectoryRun())
253        {
254#  if defined(ORXONOX_PLATFORM_WINDOWS)
255            pluginPath = PathConfig::getExecutablePathString();
256#  elif defined(ORXONOX_PLATFORM_APPLE)
257            // TODO: Where are the plugins being installed to?
258            pluginPath = PathConfig::getExecutablePathString();
259#  endif
260        }
261#endif
262
263#ifdef ORXONOX_PLATFORM_WINDOWS
264        // Add OGRE plugin path to the environment. That way one plugin could
265        // also depend on another without problems on Windows
266        const char* currentPATH = getenv("PATH");
267        std::string newPATH = pluginPath;
268        if (currentPATH != NULL)
269            newPATH = std::string(currentPATH) + ';' + newPATH;
270        putenv(const_cast<char*>(("PATH=" + newPATH).c_str()));
271#endif
272
273        // Do some SubString magic to get the comma separated list of plugins
274        SubString plugins(ogrePlugins_, ",", " ", false, '\\', false, '"', false, '{', '}', false, '\0');
275        for (unsigned int i = 0; i < plugins.size(); ++i)
276            ogreRoot_->loadPlugin(pluginPath + '/' + plugins[i]);
277    }
278
279    void GraphicsManager::loadRenderer()
280    {
281        CCOUT(4) << "Configuring Renderer" << std::endl;
282
283        bool updatedConfig = Core::getInstance().getOgreConfigTimestamp() > Core::getInstance().getLastLevelTimestamp();
284        if (updatedConfig)
285            COUT(2) << "Ogre config file has changed, but no level was started since then. Displaying config dialogue again to verify the changes." << std::endl;
286
287        if (!ogreRoot_->restoreConfig() || updatedConfig)
288        {
289            if (!ogreRoot_->showConfigDialog())
290                ThrowException(InitialisationFailed, "OGRE graphics configuration dialogue canceled.");
291            else
292                Core::getInstance().updateOgreConfigTimestamp();
293        }
294
295        CCOUT(4) << "Creating render window" << std::endl;
296
297        this->renderWindow_ = ogreRoot_->initialise(true, "Orxonox");
298        // Propagate the size of the new winodw
299        this->ogreWindowEventListener_->windowResized(renderWindow_);
300
301        Ogre::WindowEventUtilities::addWindowEventListener(this->renderWindow_, ogreWindowEventListener_);
302
303        // create a full screen default viewport
304        // Note: This may throw when adding a viewport with an existing z-order!
305        //       But in our case we only have one viewport for now anyway, therefore
306        //       no ScopeGuards or anything to handle exceptions.
307        this->viewport_ = this->renderWindow_->addViewport(0, 0);
308
309        Ogre::TextureManager::getSingleton().setDefaultNumMipmaps(Ogre::MIP_UNLIMITED);
310
311        // add console commands
312        ModifyConsoleCommand(__CC_printScreen_name).setFunction(&GraphicsManager::printScreen, this);
313        ModifyConsoleCommand(__CC_GraphicsManager_group, __CC_setScreenResolution_name).setFunction(&GraphicsManager::setScreenResolution, this);
314        ModifyConsoleCommand(__CC_GraphicsManager_group, __CC_setFSAA_name).setFunction(&GraphicsManager::setFSAA, this);
315        ModifyConsoleCommand(__CC_GraphicsManager_group, __CC_setVSync_name).setFunction(&GraphicsManager::setVSync, this);
316    }
317
318    void GraphicsManager::loadDebugOverlay()
319    {
320        // Load debug overlay to show info about fps and tick time
321        COUT(4) << "Loading Debug Overlay..." << std::endl;
322        debugOverlay_.reset(new XMLFile("debug.oxo"));
323        Loader::open(debugOverlay_.get());
324    }
325
326    /**
327    @note
328        A note about the Ogre::FrameListener: Even though we don't use them,
329        they still get called.
330    */
331    void GraphicsManager::postUpdate(const Clock& time)
332    {
333        // Time before rendering
334        uint64_t timeBeforeTick = time.getRealMicroseconds();
335
336        // Ogre's time keeping object
337        Ogre::FrameEvent evt;
338
339        // Translate to Ogre float times before the update
340        float temp = lastFrameStartTime_;
341        lastFrameStartTime_ = (float)timeBeforeTick * 0.000001f;
342        evt.timeSinceLastFrame = lastFrameStartTime_ - temp;
343        evt.timeSinceLastEvent = lastFrameStartTime_ - lastFrameEndTime_;
344
345        // Ogre requires the time too
346        ogreRoot_->_fireFrameStarted(evt);
347
348        // Pump messages in all registered RenderWindows
349        // This calls the WindowEventListener objects.
350        Ogre::WindowEventUtilities::messagePump();
351        // Make sure the window stays active even when not focused
352        // (probably only necessary on windows)
353        this->renderWindow_->setActive(true);
354
355        // Render frame
356        ogreRoot_->_updateAllRenderTargets();
357
358        uint64_t timeAfterTick = time.getRealMicroseconds();
359        // Subtract the time used for rendering from the tick time counter
360        Game::getInstance().subtractTickTime((int32_t)(timeAfterTick - timeBeforeTick));
361
362        // Translate to Ogre float times after the update
363        temp = lastFrameEndTime_;
364        lastFrameEndTime_ = (float)timeBeforeTick * 0.000001f;
365        evt.timeSinceLastFrame = lastFrameEndTime_ - temp;
366        evt.timeSinceLastEvent = lastFrameEndTime_ - lastFrameStartTime_;
367
368        // Ogre also needs the time after the frame finished
369        ogreRoot_->_fireFrameEnded(evt);
370    }
371
372    void GraphicsManager::setCamera(Ogre::Camera* camera)
373    {
374        Ogre::Camera* oldCamera = this->viewport_->getCamera();
375
376        this->viewport_->setCamera(camera);
377        GUIManager::getInstance().setCamera(camera);
378
379        for (ObjectList<ViewportEventListener>::iterator it = ObjectList<ViewportEventListener>::begin(); it != ObjectList<ViewportEventListener>::end(); ++it)
380            it->cameraChanged(this->viewport_, oldCamera);
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        OutputLevel orxonoxLevel;
401        std::string introduction;
402        // Do not show caught OGRE exceptions in front
403        if (message.find("EXCEPTION") != std::string::npos)
404        {
405            orxonoxLevel = level::internal_error;
406            introduction = "Ogre, caught exception: ";
407        }
408        else
409        {
410            switch (lml)
411            {
412            case Ogre::LML_TRIVIAL:
413                orxonoxLevel = level::verbose_more;
414                break;
415            case Ogre::LML_NORMAL:
416                orxonoxLevel = level::verbose;
417                break;
418            case Ogre::LML_CRITICAL:
419                orxonoxLevel = level::internal_warning;
420                break;
421            default:
422                orxonoxLevel = level::debug_output;
423            }
424            introduction = "Ogre: ";
425        }
426
427        orxout(orxonoxLevel, context::ogre) << introduction << message << endl;
428    }
429
430    size_t GraphicsManager::getRenderWindowHandle()
431    {
432        size_t windowHnd = 0;
433        renderWindow_->getCustomAttribute("WINDOW", &windowHnd);
434        return windowHnd;
435    }
436
437    bool GraphicsManager::isFullScreen() const
438    {
439        return this->renderWindow_->isFullScreen();
440    }
441
442    unsigned int GraphicsManager::getWindowWidth() const
443    {
444        return this->renderWindow_->getWidth();
445    }
446
447    unsigned int GraphicsManager::getWindowHeight() const
448    {
449        return this->renderWindow_->getHeight();
450    }
451
452    bool GraphicsManager::hasVSyncEnabled() const
453    {
454        Ogre::ConfigOptionMap& options = ogreRoot_->getRenderSystem()->getConfigOptions();
455        Ogre::ConfigOptionMap::iterator it = options.find("VSync");
456        if (it != options.end())
457            return (it->second.currentValue == "Yes");
458        else
459            return false;
460    }
461
462    std::string GraphicsManager::getFSAAMode() const
463    {
464        Ogre::ConfigOptionMap& options = ogreRoot_->getRenderSystem()->getConfigOptions();
465        Ogre::ConfigOptionMap::iterator it = options.find("FSAA");
466        if (it != options.end())
467            return it->second.currentValue;
468        else
469            return "";
470    }
471
472    std::string GraphicsManager::setScreenResolution(unsigned int width, unsigned int height, bool fullscreen)
473    {
474        // workaround to detect if the colour depth should be written to the config file
475        bool bWriteColourDepth = false;
476        Ogre::ConfigOptionMap& options = ogreRoot_->getRenderSystem()->getConfigOptions();
477        Ogre::ConfigOptionMap::iterator it = options.find("Video Mode");
478        if (it != options.end())
479            bWriteColourDepth = (it->second.currentValue.find('@') != std::string::npos);
480
481        if (bWriteColourDepth)
482        {
483            this->ogreRoot_->getRenderSystem()->setConfigOption("Video Mode", multi_cast<std::string>(width)
484                                                                    + " x " + multi_cast<std::string>(height)
485                                                                    + " @ " + multi_cast<std::string>(this->getRenderWindow()->getColourDepth()) + "-bit colour");
486        }
487        else
488        {
489            this->ogreRoot_->getRenderSystem()->setConfigOption("Video Mode", multi_cast<std::string>(width)
490                                                                    + " x " + multi_cast<std::string>(height));
491        }
492
493        this->ogreRoot_->getRenderSystem()->setConfigOption("Full Screen", fullscreen ? "Yes" : "No");
494
495        std::string validate = this->ogreRoot_->getRenderSystem()->validateConfigOptions();
496
497        if (validate == "")
498        {
499            GraphicsManager::getInstance().getRenderWindow()->setFullscreen(fullscreen, width, height);
500            this->ogreRoot_->saveConfig();
501            Core::getInstance().updateOgreConfigTimestamp();
502            // Also reload the input devices
503            InputManager::getInstance().reload();
504        }
505
506        return validate;
507    }
508
509    std::string GraphicsManager::setFSAA(const std::string& mode)
510    {
511        this->ogreRoot_->getRenderSystem()->setConfigOption("FSAA", mode);
512
513        std::string validate = this->ogreRoot_->getRenderSystem()->validateConfigOptions();
514
515        if (validate == "")
516        {
517            //this->ogreRoot_->getRenderSystem()->reinitialise(); // can't use this that easily, because it recreates the render window, invalidating renderWindow_
518            this->ogreRoot_->saveConfig();
519            Core::getInstance().updateOgreConfigTimestamp();
520        }
521
522        return validate;
523    }
524
525    std::string GraphicsManager::setVSync(bool vsync)
526    {
527        this->ogreRoot_->getRenderSystem()->setConfigOption("VSync", vsync ? "Yes" : "No");
528
529        std::string validate = this->ogreRoot_->getRenderSystem()->validateConfigOptions();
530
531        if (validate == "")
532        {
533            //this->ogreRoot_->getRenderSystem()->reinitialise(); // can't use this that easily, because it recreates the render window, invalidating renderWindow_
534            this->ogreRoot_->saveConfig();
535            Core::getInstance().updateOgreConfigTimestamp();
536        }
537
538        return validate;
539    }
540
541    void GraphicsManager::printScreen()
542    {
543        assert(this->renderWindow_);
544        this->renderWindow_->writeContentsToTimestampedFile(PathConfig::getLogPathString() + "screenShot_", ".png");
545    }
546}
Note: See TracBrowser for help on using the repository browser.