Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/core7/src/libraries/core/GraphicsManager.cc @ 10479

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

moved config values and all related functions from Game and Core to GameConfig and CoreConfig respectively. this ensures that no framework features are used by Game and Core before Core itself initialized the framework.

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