Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 10519 was 10509, checked in by landauf, 10 years ago

moved static application paths (root, executable, modules) into new class named ApplicationPaths
moved configurable data paths (data, log, config) into new class named ConfigurablePaths
removed PathConfig

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