Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/libraries/core/GraphicsManager.cc @ 9983

Last change on this file since 9983 was 9675, checked in by landauf, 11 years ago

merged branch 'libs' back to trunk. orxonox now compiles and runs with ogre 1.8 and it compiles (but doesn't run) with cegui 0.8

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