Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 10518 was 10295, checked in by muemart, 10 years ago

Avoid using the system path variables on windows (also gets rid of two MSVC warnings)
The altered search path already includes the dll's directory, but it didn't work with forward slashes

  • Property svn:eol-style set to native
File size: 21.5 KB
Line 
1/*
2 *   ORXONOX - the hottest 3D action shooter ever to exist
3 *                    > www.orxonox.net <
4 *
5 *
6 *   License notice:
7 *
8 *   This program is free software; you can redistribute it and/or
9 *   modify it under the terms of the GNU General Public License
10 *   as published by the Free Software Foundation; either version 2
11 *   of the License, or (at your option) any later version.
12 *
13 *   This program is distributed in the hope that it will be useful,
14 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *   GNU General Public License for more details.
17 *
18 *   You should have received a copy of the GNU General Public License
19 *   along with this program; if not, write to the Free Software
20 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 *
22 *   Author:
23 *      Reto Grieder
24 *      Benjamin Knecht <beni_at_orxonox.net>, (C) 2007
25 *   Co-authors:
26 *      Felix Schulthess
27 *
28 */
29
30#include "GraphicsManager.h"
31
32#include <cstdlib>
33#include <fstream>
34#include <sstream>
35#include <boost/filesystem.hpp>
36#include <boost/shared_array.hpp>
37
38#include <OgreFrameListener.h>
39#include <OgreRoot.h>
40#include <OgreLogManager.h>
41#include <OgreRenderWindow.h>
42#include <OgreRenderSystem.h>
43#include <OgreResourceGroupManager.h>
44#include <OgreTextureManager.h>
45#include <OgreViewport.h>
46#include <OgreWindowEventUtilities.h>
47
48#include "SpecialConfig.h"
49#include "util/Clock.h"
50#include "util/Convert.h"
51#include "util/Exception.h"
52#include "util/StringUtils.h"
53#include "util/SubString.h"
54#include "config/ConfigValueIncludes.h"
55#include "CoreIncludes.h"
56#include "Core.h"
57#include "Game.h"
58#include "GameMode.h"
59#include "GUIManager.h"
60#include "Loader.h"
61#include "PathConfig.h"
62#include "ViewportEventListener.h"
63#include "WindowEventListener.h"
64#include "XMLFile.h"
65#include "command/ConsoleCommand.h"
66#include "input/InputManager.h"
67
68namespace orxonox
69{
70    static const std::string __CC_GraphicsManager_group = "GraphicsManager";
71    static const std::string __CC_setScreenResolution_name = "setScreenResolution";
72    static const std::string __CC_setFSAA_name = "setFSAA";
73    static const std::string __CC_setVSync_name = "setVSync";
74    DeclareConsoleCommand(__CC_GraphicsManager_group, __CC_setScreenResolution_name, &prototype::string__uint_uint_bool);
75    DeclareConsoleCommand(__CC_GraphicsManager_group, __CC_setFSAA_name, &prototype::string__string);
76    DeclareConsoleCommand(__CC_GraphicsManager_group, __CC_setVSync_name, &prototype::string__bool);
77
78    static const std::string __CC_printScreen_name = "printScreen";
79    DeclareConsoleCommand(__CC_printScreen_name, &prototype::void__void);
80
81    class OgreWindowEventListener : public Ogre::WindowEventListener
82    {
83    public:
84        void windowResized     (Ogre::RenderWindow* rw)
85            { orxonox::WindowEventListener::resizeWindow(rw->getWidth(), rw->getHeight()); }
86        void windowFocusChange (Ogre::RenderWindow* rw)
87            { orxonox::WindowEventListener::changeWindowFocus(rw->isActive()); }
88        void windowClosed      (Ogre::RenderWindow* rw)
89            { orxonox::Game::getInstance().stop(); }
90        void windowMoved       (Ogre::RenderWindow* rw)
91            { orxonox::WindowEventListener::moveWindow(); }
92    };
93
94    GraphicsManager* GraphicsManager::singletonPtr_s = 0;
95
96    GraphicsManager::GraphicsManager(bool bLoadRenderer)
97        : ogreWindowEventListener_(new OgreWindowEventListener())
98        , renderWindow_(0)
99        , viewport_(0)
100        , lastFrameStartTime_(0.0f)
101        , lastFrameEndTime_(0.0f)
102        , destructionHelper_(this)
103    {
104        RegisterObject(GraphicsManager);
105
106        orxout(internal_status) << "initializing GraphicsManager..." << endl;
107        this->setConfigValues();
108
109        // Ogre setup procedure (creating Ogre::Root)
110        this->loadOgreRoot();
111
112        // At first, add the root paths of the data directories as resource locations
113        Ogre::ResourceGroupManager::getSingleton().addResourceLocation(PathConfig::getDataPathString(), "FileSystem");
114        // Load resources
115        resources_.reset(new XMLFile("DefaultResources.oxr"));
116        resources_->setLuaSupport(false);
117        Loader::open(resources_.get(), ClassTreeMask(), false);
118
119        // Only for runs in the build directory (not installed)
120        if (PathConfig::buildDirectoryRun())
121            Ogre::ResourceGroupManager::getSingleton().addResourceLocation(PathConfig::getExternalDataPathString(), "FileSystem");
122
123        extResources_.reset(new XMLFile("resources.oxr"));
124        extResources_->setLuaSupport(false);
125        Loader::open(extResources_.get(), ClassTreeMask(), false);
126
127        if (bLoadRenderer)
128        {
129            // Reads the ogre config and creates the render window
130            this->upgradeToGraphics();
131        }
132
133        orxout(internal_status) << "finished initializing GraphicsManager" << endl;
134    }
135
136    void GraphicsManager::destroy()
137    {
138        orxout(internal_status) << "destroying GraphicsManager..." << endl;
139
140        Loader::unload(debugOverlay_.get());
141
142        Ogre::WindowEventUtilities::removeWindowEventListener(renderWindow_, ogreWindowEventListener_);
143        ModifyConsoleCommand(__CC_printScreen_name).resetFunction();
144        ModifyConsoleCommand(__CC_GraphicsManager_group, __CC_setScreenResolution_name).resetFunction();
145        ModifyConsoleCommand(__CC_GraphicsManager_group, __CC_setFSAA_name).resetFunction();
146        ModifyConsoleCommand(__CC_GraphicsManager_group, __CC_setVSync_name).resetFunction();
147
148        // Undeclare the resources
149        Loader::unload(resources_.get());
150        Loader::unload(extResources_.get());
151
152        safeObjectDelete(&ogreRoot_);
153        safeObjectDelete(&ogreLogger_);
154        safeObjectDelete(&ogreWindowEventListener_);
155
156        orxout(internal_status) << "finished destroying GraphicsManager" << endl;
157    }
158
159    void GraphicsManager::setConfigValues()
160    {
161        SetConfigValue(ogreConfigFile_,  "ogre.cfg")
162            .description("Location of the Ogre config file");
163        SetConfigValue(ogrePlugins_, specialConfig::ogrePlugins)
164            .description("Comma separated list of all plugins to load.");
165        SetConfigValue(ogreLogFile_,     "ogre.log")
166            .description("Logfile for messages from Ogre. Use \"\" to suppress log file creation.");
167    }
168
169    /**
170    @brief
171        Loads the renderer and creates the render window if not yet done so.
172    @remarks
173        This operation is irreversible without recreating the GraphicsManager!
174        So if it throws you HAVE to recreate the GraphicsManager!!!
175        It therefore offers almost no exception safety.
176    */
177    void GraphicsManager::upgradeToGraphics()
178    {
179        if (renderWindow_ != NULL)
180            return;
181
182        orxout(internal_info) << "GraphicsManager upgrade to graphics" << endl;
183
184        // load all the required plugins for Ogre
185        orxout(user_info) << "Loading Ogre plugins..." << endl;
186        this->loadOgrePlugins();
187
188        orxout(user_info) << "Creating render window..." << endl;
189        this->loadRenderer();
190
191        // Initialise all resources (do this AFTER the renderer has been loaded!)
192        // Note: You can only do this once! Ogre will check whether a resource group has
193        // already been initialised. If you need to load resources later, you will have to
194        // choose another resource group.
195        orxout(user_info) << "Initializing all resource groups..." << endl;
196        Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
197
198        orxout(internal_info) << "GraphicsManager finished upgrade to graphics" << endl;
199    }
200
201    /**
202    @brief
203        Creates the Ogre Root object and sets up the ogre log.
204    */
205    void GraphicsManager::loadOgreRoot()
206    {
207        orxout(internal_info) << "Setting up Ogre..." << endl;
208
209        if (ogreConfigFile_.empty())
210        {
211            orxout(internal_warning) << "Ogre config file set to \"\". Defaulting to config.cfg" << endl;
212            ModifyConfigValue(ogreConfigFile_, tset, "config.cfg");
213        }
214        if (ogreLogFile_.empty())
215        {
216            orxout(internal_warning) << "Ogre log file set to \"\". Defaulting to ogre.log" << endl;
217            ModifyConfigValue(ogreLogFile_, tset, "ogre.log");
218        }
219
220        boost::filesystem::path ogreConfigFilepath(PathConfig::getConfigPath() / this->ogreConfigFile_);
221        boost::filesystem::path ogreLogFilepath(PathConfig::getLogPath() / this->ogreLogFile_);
222
223        // create a new logManager
224        // Ogre::Root will detect that we've already created a Log
225        ogreLogger_ = new Ogre::LogManager();
226        orxout(internal_info) << "Ogre LogManager created" << endl;
227
228        // create our own log that we can listen to
229        Ogre::Log *myLog;
230        myLog = ogreLogger_->createLog(ogreLogFilepath.string(), true, false, false);
231        orxout(internal_info) << "Ogre Log created" << endl;
232
233        myLog->setLogDetail(Ogre::LL_BOREME);
234        myLog->addListener(this);
235
236        orxout(internal_info) << "Creating Ogre Root..." << endl;
237
238        // check for config file existence because Ogre displays (caught) exceptions if not
239        if (!boost::filesystem::exists(ogreConfigFilepath))
240        {
241            // create a zero sized file
242            std::ofstream creator;
243            creator.open(ogreConfigFilepath.string().c_str());
244            creator.close();
245        }
246
247        // Leave plugins file empty. We're going to do that part manually later
248        ogreRoot_ = new Ogre::Root("", ogreConfigFilepath.string(), ogreLogFilepath.string());
249
250        orxout(internal_info) << "Ogre set up done." << endl;
251    }
252
253    void GraphicsManager::loadOgrePlugins()
254    {
255        orxout(internal_info) << "loading ogre plugins" << endl;
256
257        // Plugin path can have many different locations...
258        std::string pluginPath = specialConfig::ogrePluginsDirectory;
259#ifdef DEPENDENCY_PACKAGE_ENABLE
260        if (!PathConfig::buildDirectoryRun())
261        {
262#  if defined(ORXONOX_PLATFORM_WINDOWS)
263            pluginPath = PathConfig::getExecutablePathString();
264#  elif defined(ORXONOX_PLATFORM_APPLE)
265            // TODO: Where are the plugins being installed to?
266            pluginPath = PathConfig::getExecutablePathString();
267#  endif
268        }
269#endif
270
271        // 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().getOgreConfigTimestamp() > Core::getInstance().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().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(0, 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(0), 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::open(debugOverlay_.get(), ClassTreeMask(), false);
331    }
332
333    /**
334    @note
335        A note about the Ogre::FrameListener: Even though we don't use them,
336        they still get called.
337    */
338    void GraphicsManager::postUpdate(const Clock& time)
339    {
340        // Time before rendering
341        uint64_t timeBeforeTick = time.getRealMicroseconds();
342
343        // Ogre's time keeping object
344        Ogre::FrameEvent evt;
345
346        // Translate to Ogre float times before the update
347        float temp = lastFrameStartTime_;
348        lastFrameStartTime_ = (float)timeBeforeTick * 0.000001f;
349        evt.timeSinceLastFrame = lastFrameStartTime_ - temp;
350        evt.timeSinceLastEvent = lastFrameStartTime_ - lastFrameEndTime_;
351
352        // Ogre requires the time too
353        ogreRoot_->_fireFrameStarted(evt);
354
355        // Pump messages in all registered RenderWindows
356        // This calls the WindowEventListener objects.
357        Ogre::WindowEventUtilities::messagePump();
358        // Make sure the window stays active even when not focused
359        // (probably only necessary on windows)
360        this->renderWindow_->setActive(true);
361
362        // Render frame
363        ogreRoot_->_updateAllRenderTargets();
364
365        uint64_t timeAfterTick = time.getRealMicroseconds();
366        // Subtract the time used for rendering from the tick time counter
367        Game::getInstance().subtractTickTime((int32_t)(timeAfterTick - timeBeforeTick));
368
369        // Translate to Ogre float times after the update
370        temp = lastFrameEndTime_;
371        lastFrameEndTime_ = (float)timeBeforeTick * 0.000001f;
372        evt.timeSinceLastFrame = lastFrameEndTime_ - temp;
373        evt.timeSinceLastEvent = lastFrameEndTime_ - lastFrameStartTime_;
374
375        // Ogre also needs the time after the frame finished
376        ogreRoot_->_fireFrameEnded(evt);
377    }
378
379    void GraphicsManager::setCamera(Ogre::Camera* camera)
380    {
381        Ogre::Camera* oldCamera = this->viewport_->getCamera();
382
383        this->viewport_->setCamera(camera);
384        GUIManager::getInstance().setCamera(camera);
385
386        for (ObjectList<ViewportEventListener>::iterator it = ObjectList<ViewportEventListener>::begin(); it != ObjectList<ViewportEventListener>::end(); ++it)
387            it->cameraChanged(this->viewport_, oldCamera);
388    }
389
390    /**
391    @brief
392        Method called by the LogListener interface from Ogre.
393        We use it to capture Ogre log messages and handle it ourselves.
394    @param message
395        The message to be logged
396    @param lml
397        The message level the log is using
398    @param maskDebug
399        If we are printing to the console or not
400    @param logName
401        The name of this log (so you can have several listeners
402        for different logs, and identify them)
403    @param skipThisMessage
404        If set to true by the messageLogged() implementation message will not be logged
405    */
406#if OGRE_VERSION >= 0x010800
407    void GraphicsManager::messageLogged(const std::string& message,
408        Ogre::LogMessageLevel lml, bool maskDebug, const std::string& logName, bool& skipThisMessage)
409        // TODO: do we have to ignore the message if skipThisMessage is set?
410#else
411    void GraphicsManager::messageLogged(const std::string& message,
412        Ogre::LogMessageLevel lml, bool maskDebug, const std::string& logName)
413#endif
414    {
415        OutputLevel orxonoxLevel;
416        std::string introduction;
417        // Do not show caught OGRE exceptions in front
418        if (message.find("EXCEPTION") != std::string::npos)
419        {
420            orxonoxLevel = level::internal_error;
421            introduction = "Ogre, caught exception: ";
422        }
423        else
424        {
425            switch (lml)
426            {
427            case Ogre::LML_TRIVIAL:
428                orxonoxLevel = level::verbose_more;
429                break;
430            case Ogre::LML_NORMAL:
431                orxonoxLevel = level::verbose;
432                break;
433            case Ogre::LML_CRITICAL:
434                orxonoxLevel = level::internal_warning;
435                break;
436            default:
437                orxonoxLevel = level::debug_output;
438            }
439            introduction = "Ogre: ";
440        }
441
442        orxout(orxonoxLevel, context::ogre) << introduction << message << endl;
443    }
444
445    size_t GraphicsManager::getRenderWindowHandle()
446    {
447        size_t windowHnd = 0;
448        renderWindow_->getCustomAttribute("WINDOW", &windowHnd);
449        return windowHnd;
450    }
451
452    bool GraphicsManager::isFullScreen() const
453    {
454        return this->renderWindow_->isFullScreen();
455    }
456
457    unsigned int GraphicsManager::getWindowWidth() const
458    {
459        return this->renderWindow_->getWidth();
460    }
461
462    unsigned int GraphicsManager::getWindowHeight() const
463    {
464        return this->renderWindow_->getHeight();
465    }
466
467    bool GraphicsManager::hasVSyncEnabled() const
468    {
469        Ogre::ConfigOptionMap& options = ogreRoot_->getRenderSystem()->getConfigOptions();
470        Ogre::ConfigOptionMap::iterator it = options.find("VSync");
471        if (it != options.end())
472            return (it->second.currentValue == "Yes");
473        else
474            return false;
475    }
476
477    std::string GraphicsManager::getFSAAMode() const
478    {
479        Ogre::ConfigOptionMap& options = ogreRoot_->getRenderSystem()->getConfigOptions();
480        Ogre::ConfigOptionMap::iterator it = options.find("FSAA");
481        if (it != options.end())
482            return it->second.currentValue;
483        else
484            return "";
485    }
486
487    std::string GraphicsManager::setScreenResolution(unsigned int width, unsigned int height, bool fullscreen)
488    {
489        // workaround to detect if the colour depth should be written to the config file
490        bool bWriteColourDepth = false;
491        Ogre::ConfigOptionMap& options = ogreRoot_->getRenderSystem()->getConfigOptions();
492        Ogre::ConfigOptionMap::iterator it = options.find("Video Mode");
493        if (it != options.end())
494            bWriteColourDepth = (it->second.currentValue.find('@') != std::string::npos);
495
496        if (bWriteColourDepth)
497        {
498            this->ogreRoot_->getRenderSystem()->setConfigOption("Video Mode", multi_cast<std::string>(width)
499                                                                    + " x " + multi_cast<std::string>(height)
500                                                                    + " @ " + multi_cast<std::string>(this->getRenderWindow()->getColourDepth()) + "-bit colour");
501        }
502        else
503        {
504            this->ogreRoot_->getRenderSystem()->setConfigOption("Video Mode", multi_cast<std::string>(width)
505                                                                    + " x " + multi_cast<std::string>(height));
506        }
507
508        this->ogreRoot_->getRenderSystem()->setConfigOption("Full Screen", fullscreen ? "Yes" : "No");
509
510        std::string validate = this->ogreRoot_->getRenderSystem()->validateConfigOptions();
511
512        if (validate == "")
513        {
514            GraphicsManager::getInstance().getRenderWindow()->setFullscreen(fullscreen, width, height);
515            this->ogreRoot_->saveConfig();
516            Core::getInstance().updateOgreConfigTimestamp();
517            // Also reload the input devices
518            InputManager::getInstance().reload();
519        }
520
521        return validate;
522    }
523
524    std::string GraphicsManager::setFSAA(const std::string& mode)
525    {
526        this->ogreRoot_->getRenderSystem()->setConfigOption("FSAA", mode);
527
528        std::string validate = this->ogreRoot_->getRenderSystem()->validateConfigOptions();
529
530        if (validate == "")
531        {
532            //this->ogreRoot_->getRenderSystem()->reinitialise(); // can't use this that easily, because it recreates the render window, invalidating renderWindow_
533            this->ogreRoot_->saveConfig();
534            Core::getInstance().updateOgreConfigTimestamp();
535        }
536
537        return validate;
538    }
539
540    std::string GraphicsManager::setVSync(bool vsync)
541    {
542        this->ogreRoot_->getRenderSystem()->setConfigOption("VSync", vsync ? "Yes" : "No");
543
544        std::string validate = this->ogreRoot_->getRenderSystem()->validateConfigOptions();
545
546        if (validate == "")
547        {
548            //this->ogreRoot_->getRenderSystem()->reinitialise(); // can't use this that easily, because it recreates the render window, invalidating renderWindow_
549            this->ogreRoot_->saveConfig();
550            Core::getInstance().updateOgreConfigTimestamp();
551        }
552
553        return validate;
554    }
555
556    void GraphicsManager::printScreen()
557    {
558        assert(this->renderWindow_);
559        this->renderWindow_->writeContentsToTimestampedFile(PathConfig::getLogPathString() + "screenShot_", ".png");
560    }
561}
Note: See TracBrowser for help on using the repository browser.