Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/cpp11_v2/src/libraries/core/GraphicsManager.cc @ 10916

Last change on this file since 10916 was 10845, checked in by landauf, 10 years ago

always use 'virtual' in the declaration of virtual functions even if they are inherited

  • Property svn:eol-style set to native
File size: 21.9 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
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/Convert.h"
50#include "util/Exception.h"
51#include "util/StringUtils.h"
52#include "util/SubString.h"
53#include "config/ConfigValueIncludes.h"
54#include "CoreIncludes.h"
55#include "Core.h"
56#include "Game.h"
57#include "GameMode.h"
58#include "GUIManager.h"
59#include "Loader.h"
60#include "ApplicationPaths.h"
61#include "ConfigurablePaths.h"
62#include "ViewportEventListener.h"
63#include "WindowEventListener.h"
64#include "XMLFile.h"
65#include "command/ConsoleCommandIncludes.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        virtual void windowResized     (Ogre::RenderWindow* rw) override
85            { orxonox::WindowEventListener::resizeWindow(rw->getWidth(), rw->getHeight()); }
86        virtual void windowFocusChange (Ogre::RenderWindow* rw) override
87            { orxonox::WindowEventListener::changeWindowFocus(rw->isActive()); }
88        virtual void windowClosed      (Ogre::RenderWindow* rw) override
89            { orxonox::Game::getInstance().stop(); }
90        virtual void windowMoved       (Ogre::RenderWindow* rw) override
91            { orxonox::WindowEventListener::moveWindow(); }
92    };
93
94    GraphicsManager* GraphicsManager::singletonPtr_s = nullptr;
95
96    RegisterAbstractClass(GraphicsManager).inheritsFrom<Configurable>();
97
98    GraphicsManager::GraphicsManager(bool bLoadRenderer)
99        : ogreWindowEventListener_(new OgreWindowEventListener())
100        , renderWindow_(nullptr)
101        , viewport_(nullptr)
102        , lastFrameStartTime_(0.0f)
103        , lastFrameEndTime_(0.0f)
104        , destructionHelper_(this)
105    {
106        RegisterObject(GraphicsManager);
107
108        orxout(internal_status) << "initializing GraphicsManager..." << endl;
109        this->setConfigValues();
110
111        // Ogre setup procedure (creating Ogre::Root)
112        this->loadOgreRoot();
113
114        // At first, add the root paths of the data directories as resource locations
115        Ogre::ResourceGroupManager::getSingleton().addResourceLocation(ConfigurablePaths::getDataPathString(), "FileSystem");
116        // Load resources
117        resources_.reset(new XMLFile("DefaultResources.oxr"));
118        resources_->setLuaSupport(false);
119        Loader::getInstance().load(resources_.get(), ClassTreeMask(), false);
120
121        // Only for runs in the build directory (not installed)
122        if (ApplicationPaths::buildDirectoryRun())
123            Ogre::ResourceGroupManager::getSingleton().addResourceLocation(ConfigurablePaths::getExternalDataPathString(), "FileSystem");
124
125        extResources_.reset(new XMLFile("resources.oxr"));
126        extResources_->setLuaSupport(false);
127        Loader::getInstance().load(extResources_.get(), ClassTreeMask(), false);
128
129        if (bLoadRenderer)
130        {
131            // Reads the ogre config and creates the render window
132            this->upgradeToGraphics();
133        }
134
135        orxout(internal_status) << "finished initializing GraphicsManager" << endl;
136    }
137
138    void GraphicsManager::destroy()
139    {
140        orxout(internal_status) << "destroying GraphicsManager..." << endl;
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::getInstance().unload(resources_.get());
150        Loader::getInstance().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_ != nullptr)
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(ConfigurablePaths::getConfigPath() / this->ogreConfigFile_);
221        boost::filesystem::path ogreLogFilepath(ConfigurablePaths::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 (!ApplicationPaths::buildDirectoryRun())
261        {
262#  if defined(ORXONOX_PLATFORM_WINDOWS)
263            pluginPath = ApplicationPaths::getExecutablePathString();
264#  elif defined(ORXONOX_PLATFORM_APPLE)
265            // TODO: Where are the plugins being installed to?
266            pluginPath = ApplicationPaths::getExecutablePathString();
267#  endif
268        }
269#endif
270
271        // Do some SubString magic to get the comma separated list of plugins
272        SubString plugins(ogrePlugins_, ",", " ", false, '\\', false, '"', false, '{', '}', false, '\0');
273        for (unsigned int i = 0; i < plugins.size(); ++i)
274            ogreRoot_->loadPlugin(pluginPath + '/' + plugins[i]);
275    }
276
277    void GraphicsManager::loadRenderer()
278    {
279        orxout(internal_info) << "GraphicsManager: Configuring Renderer" << endl;
280
281        bool updatedConfig = Core::getInstance().getConfig()->getOgreConfigTimestamp() > Core::getInstance().getConfig()->getLastLevelTimestamp();
282        if (updatedConfig)
283            orxout(user_info)<< "Ogre config file has changed, but no level was started since then. Displaying config dialogue again to verify the changes." << endl;
284
285        if (!ogreRoot_->restoreConfig() || updatedConfig)
286        {
287            if (!ogreRoot_->showConfigDialog())
288                ThrowException(InitialisationFailed, "OGRE graphics configuration dialogue canceled.");
289            else
290                Core::getInstance().getConfig()->updateOgreConfigTimestamp();
291        }
292
293        orxout(internal_info) << "Creating render window" << endl;
294
295        this->renderWindow_ = ogreRoot_->initialise(true, "Orxonox");
296        // Propagate the size of the new winodw
297        this->ogreWindowEventListener_->windowResized(renderWindow_);
298
299        Ogre::WindowEventUtilities::addWindowEventListener(this->renderWindow_, ogreWindowEventListener_);
300
301        // create a full screen default viewport
302        // Note: This may throw when adding a viewport with an existing z-order!
303        //       But in our case we only have one viewport for now anyway, therefore
304        //       no ScopeGuards or anything to handle exceptions.
305        this->viewport_ = this->renderWindow_->addViewport(nullptr, 0);
306
307        Ogre::TextureManager::getSingleton().setDefaultNumMipmaps(Ogre::MIP_UNLIMITED);
308
309        //Add program icon
310#if defined(ORXONOX_PLATFORM_WINDOWS)
311        HWND hwnd;
312        this->renderWindow_->getCustomAttribute("WINDOW", (void*)&hwnd);
313        LONG iconID = (LONG)LoadIcon(GetModuleHandle(nullptr), MAKEINTRESOURCE(101));
314        SetClassLong(hwnd, GCL_HICON, iconID);
315#endif
316
317
318        // add console commands
319        ModifyConsoleCommand(__CC_printScreen_name).setFunction(&GraphicsManager::printScreen, this);
320        ModifyConsoleCommand(__CC_GraphicsManager_group, __CC_setScreenResolution_name).setFunction(&GraphicsManager::setScreenResolution, this);
321        ModifyConsoleCommand(__CC_GraphicsManager_group, __CC_setFSAA_name).setFunction(&GraphicsManager::setFSAA, this);
322        ModifyConsoleCommand(__CC_GraphicsManager_group, __CC_setVSync_name).setFunction(&GraphicsManager::setVSync, this);
323    }
324
325    void GraphicsManager::loadDebugOverlay()
326    {
327        // Load debug overlay to show info about fps and tick time
328        orxout(internal_info) << "Loading Debug Overlay..." << endl;
329        debugOverlay_.reset(new XMLFile("debug.oxo"));
330        Loader::getInstance().load(debugOverlay_.get(), ClassTreeMask(), false);
331    }
332
333    void GraphicsManager::unloadDebugOverlay()
334    {
335        Loader::getInstance().unload(debugOverlay_.get());
336    }
337
338    /**
339    @note
340        A note about the Ogre::FrameListener: Even though we don't use them,
341        they still get called.
342    */
343    void GraphicsManager::postUpdate(const Clock& time)
344    {
345        // Time before rendering
346        uint64_t timeBeforeTick = time.getRealMicroseconds();
347
348        // Ogre's time keeping object
349        Ogre::FrameEvent evt;
350
351        // Translate to Ogre float times before the update
352        float temp = lastFrameStartTime_;
353        lastFrameStartTime_ = (float)timeBeforeTick * 0.000001f;
354        evt.timeSinceLastFrame = lastFrameStartTime_ - temp;
355        evt.timeSinceLastEvent = lastFrameStartTime_ - lastFrameEndTime_;
356
357        // Ogre requires the time too
358        ogreRoot_->_fireFrameStarted(evt);
359
360        // Pump messages in all registered RenderWindows
361        // This calls the WindowEventListener objects.
362        Ogre::WindowEventUtilities::messagePump();
363        // Make sure the window stays active even when not focused
364        // (probably only necessary on windows)
365        this->renderWindow_->setActive(true);
366
367        // Render frame
368        ogreRoot_->_updateAllRenderTargets();
369
370        uint64_t timeAfterTick = time.getRealMicroseconds();
371        // Subtract the time used for rendering from the tick time counter
372        Game::getInstance().subtractTickTime((int32_t)(timeAfterTick - timeBeforeTick));
373
374        // Translate to Ogre float times after the update
375        temp = lastFrameEndTime_;
376        lastFrameEndTime_ = (float)timeBeforeTick * 0.000001f;
377        evt.timeSinceLastFrame = lastFrameEndTime_ - temp;
378        evt.timeSinceLastEvent = lastFrameEndTime_ - lastFrameStartTime_;
379
380        // Ogre also needs the time after the frame finished
381        ogreRoot_->_fireFrameEnded(evt);
382    }
383
384    void GraphicsManager::setCamera(Ogre::Camera* camera)
385    {
386        Ogre::Camera* oldCamera = this->viewport_->getCamera();
387
388        this->viewport_->setCamera(camera);
389        GUIManager::getInstance().setCamera(camera);
390
391        for (ObjectList<ViewportEventListener>::iterator it = ObjectList<ViewportEventListener>::begin(); it != ObjectList<ViewportEventListener>::end(); ++it)
392            it->cameraChanged(this->viewport_, oldCamera);
393    }
394
395    /**
396    @brief
397        Method called by the LogListener interface from Ogre.
398        We use it to capture Ogre log messages and handle it ourselves.
399    @param message
400        The message to be logged
401    @param lml
402        The message level the log is using
403    @param maskDebug
404        If we are printing to the console or not
405    @param logName
406        The name of this log (so you can have several listeners
407        for different logs, and identify them)
408    @param skipThisMessage
409        If set to true by the messageLogged() implementation message will not be logged
410    */
411#if OGRE_VERSION >= 0x010800
412    void GraphicsManager::messageLogged(const std::string& message,
413        Ogre::LogMessageLevel lml, bool maskDebug, const std::string& logName, bool& skipThisMessage)
414        // TODO: do we have to ignore the message if skipThisMessage is set?
415#else
416    void GraphicsManager::messageLogged(const std::string& message,
417        Ogre::LogMessageLevel lml, bool maskDebug, const std::string& logName)
418#endif
419    {
420        OutputLevel orxonoxLevel;
421        std::string introduction;
422        // Do not show caught OGRE exceptions in front
423        if (message.find("EXCEPTION") != std::string::npos)
424        {
425            orxonoxLevel = level::internal_error;
426            introduction = "Ogre, caught exception: ";
427        }
428        else
429        {
430            switch (lml)
431            {
432            case Ogre::LML_TRIVIAL:
433                orxonoxLevel = level::verbose_more;
434                break;
435            case Ogre::LML_NORMAL:
436                orxonoxLevel = level::verbose;
437                break;
438            case Ogre::LML_CRITICAL:
439                orxonoxLevel = level::internal_warning;
440                break;
441            default:
442                orxonoxLevel = level::debug_output;
443            }
444            introduction = "Ogre: ";
445        }
446
447        orxout(orxonoxLevel, context::ogre) << introduction << message << endl;
448    }
449
450    size_t GraphicsManager::getRenderWindowHandle()
451    {
452        size_t windowHnd = 0;
453        renderWindow_->getCustomAttribute("WINDOW", &windowHnd);
454        return windowHnd;
455    }
456
457    bool GraphicsManager::isFullScreen() const
458    {
459        return this->renderWindow_->isFullScreen();
460    }
461
462    unsigned int GraphicsManager::getWindowWidth() const
463    {
464        return this->renderWindow_->getWidth();
465    }
466
467    unsigned int GraphicsManager::getWindowHeight() const
468    {
469        return this->renderWindow_->getHeight();
470    }
471
472    bool GraphicsManager::hasVSyncEnabled() const
473    {
474        Ogre::ConfigOptionMap& options = ogreRoot_->getRenderSystem()->getConfigOptions();
475        Ogre::ConfigOptionMap::iterator it = options.find("VSync");
476        if (it != options.end())
477            return (it->second.currentValue == "Yes");
478        else
479            return false;
480    }
481
482    std::string GraphicsManager::getFSAAMode() const
483    {
484        Ogre::ConfigOptionMap& options = ogreRoot_->getRenderSystem()->getConfigOptions();
485        Ogre::ConfigOptionMap::iterator it = options.find("FSAA");
486        if (it != options.end())
487            return it->second.currentValue;
488        else
489            return "";
490    }
491
492    std::string GraphicsManager::setScreenResolution(unsigned int width, unsigned int height, bool fullscreen)
493    {
494        // workaround to detect if the colour depth should be written to the config file
495        bool bWriteColourDepth = false;
496        Ogre::ConfigOptionMap& options = ogreRoot_->getRenderSystem()->getConfigOptions();
497        Ogre::ConfigOptionMap::iterator it = options.find("Video Mode");
498        if (it != options.end())
499            bWriteColourDepth = (it->second.currentValue.find('@') != std::string::npos);
500
501        if (bWriteColourDepth)
502        {
503            this->ogreRoot_->getRenderSystem()->setConfigOption("Video Mode", multi_cast<std::string>(width)
504                                                                    + " x " + multi_cast<std::string>(height)
505                                                                    + " @ " + multi_cast<std::string>(this->getRenderWindow()->getColourDepth()) + "-bit colour");
506        }
507        else
508        {
509            this->ogreRoot_->getRenderSystem()->setConfigOption("Video Mode", multi_cast<std::string>(width)
510                                                                    + " x " + multi_cast<std::string>(height));
511        }
512
513        this->ogreRoot_->getRenderSystem()->setConfigOption("Full Screen", fullscreen ? "Yes" : "No");
514
515        std::string validate = this->ogreRoot_->getRenderSystem()->validateConfigOptions();
516
517        if (validate == "")
518        {
519            GraphicsManager::getInstance().getRenderWindow()->setFullscreen(fullscreen, width, height);
520            this->ogreRoot_->saveConfig();
521            Core::getInstance().getConfig()->updateOgreConfigTimestamp();
522            // Also reload the input devices
523            InputManager::getInstance().reload();
524        }
525
526        return validate;
527    }
528
529    std::string GraphicsManager::setFSAA(const std::string& mode)
530    {
531        this->ogreRoot_->getRenderSystem()->setConfigOption("FSAA", mode);
532
533        std::string validate = this->ogreRoot_->getRenderSystem()->validateConfigOptions();
534
535        if (validate == "")
536        {
537            //this->ogreRoot_->getRenderSystem()->reinitialise(); // can't use this that easily, because it recreates the render window, invalidating renderWindow_
538            this->ogreRoot_->saveConfig();
539            Core::getInstance().getConfig()->updateOgreConfigTimestamp();
540        }
541
542        return validate;
543    }
544
545    std::string GraphicsManager::setVSync(bool vsync)
546    {
547        this->ogreRoot_->getRenderSystem()->setConfigOption("VSync", vsync ? "Yes" : "No");
548
549        std::string validate = this->ogreRoot_->getRenderSystem()->validateConfigOptions();
550
551        if (validate == "")
552        {
553            //this->ogreRoot_->getRenderSystem()->reinitialise(); // can't use this that easily, because it recreates the render window, invalidating renderWindow_
554            this->ogreRoot_->saveConfig();
555            Core::getInstance().getConfig()->updateOgreConfigTimestamp();
556        }
557
558        return validate;
559    }
560
561    void GraphicsManager::printScreen()
562    {
563        assert(this->renderWindow_);
564        this->renderWindow_->writeContentsToTimestampedFile(ConfigurablePaths::getLogPathString() + "screenShot_", ".png");
565    }
566}
Note: See TracBrowser for help on using the repository browser.