Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 11504 was 11115, checked in by landauf, 9 years ago

fixed code to compile in 64bit mode

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