Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/output/src/libraries/core/GraphicsManager.cc @ 8967

Last change on this file since 8967 was 8830, checked in by landauf, 13 years ago

added some output (user and internal) throughout the initialization of the game, graphics, and game states

  • Property svn:eol-style set to native
File size: 21.0 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 "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        this->loadOgrePlugins();
186
187        this->loadRenderer();
188
189        // Initialise all resources (do this AFTER the renderer has been loaded!)
190        // Note: You can only do this once! Ogre will check whether a resource group has
191        // already been initialised. If you need to load resources later, you will have to
192        // choose another resource group.
193        Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
194
195        orxout(internal_info) << "GraphicsManager finished upgrade to graphics" << endl;
196    }
197
198    /**
199    @brief
200        Creates the Ogre Root object and sets up the ogre log.
201    */
202    void GraphicsManager::loadOgreRoot()
203    {
204        orxout(internal_info) << "Setting up Ogre..." << endl;
205
206        if (ogreConfigFile_.empty())
207        {
208            orxout(internal_warning) << "Ogre config file set to \"\". Defaulting to config.cfg" << endl;
209            ModifyConfigValue(ogreConfigFile_, tset, "config.cfg");
210        }
211        if (ogreLogFile_.empty())
212        {
213            orxout(internal_warning) << "Ogre log file set to \"\". Defaulting to ogre.log" << endl;
214            ModifyConfigValue(ogreLogFile_, tset, "ogre.log");
215        }
216
217        boost::filesystem::path ogreConfigFilepath(PathConfig::getConfigPath() / this->ogreConfigFile_);
218        boost::filesystem::path ogreLogFilepath(PathConfig::getLogPath() / this->ogreLogFile_);
219
220        // create a new logManager
221        // Ogre::Root will detect that we've already created a Log
222        ogreLogger_ = new Ogre::LogManager();
223        orxout(internal_info) << "Ogre LogManager created" << endl;
224
225        // create our own log that we can listen to
226        Ogre::Log *myLog;
227        myLog = ogreLogger_->createLog(ogreLogFilepath.string(), true, false, false);
228        orxout(internal_info) << "Ogre Log created" << endl;
229
230        myLog->setLogDetail(Ogre::LL_BOREME);
231        myLog->addListener(this);
232
233        orxout(internal_info) << "Creating Ogre Root..." << endl;
234
235        // check for config file existence because Ogre displays (caught) exceptions if not
236        if (!boost::filesystem::exists(ogreConfigFilepath))
237        {
238            // create a zero sized file
239            std::ofstream creator;
240            creator.open(ogreConfigFilepath.string().c_str());
241            creator.close();
242        }
243
244        // Leave plugins file empty. We're going to do that part manually later
245        ogreRoot_ = new Ogre::Root("", ogreConfigFilepath.string(), ogreLogFilepath.string());
246
247        orxout(internal_info) << "Ogre set up done." << endl;
248    }
249
250    void GraphicsManager::loadOgrePlugins()
251    {
252        orxout(internal_info) << "loading ogre plugins" << endl;
253
254        // Plugin path can have many different locations...
255        std::string pluginPath = specialConfig::ogrePluginsDirectory;
256#ifdef DEPENDENCY_PACKAGE_ENABLE
257        if (!PathConfig::buildDirectoryRun())
258        {
259#  if defined(ORXONOX_PLATFORM_WINDOWS)
260            pluginPath = PathConfig::getExecutablePathString();
261#  elif defined(ORXONOX_PLATFORM_APPLE)
262            // TODO: Where are the plugins being installed to?
263            pluginPath = PathConfig::getExecutablePathString();
264#  endif
265        }
266#endif
267
268#ifdef ORXONOX_PLATFORM_WINDOWS
269        // Add OGRE plugin path to the environment. That way one plugin could
270        // also depend on another without problems on Windows
271        const char* currentPATH = getenv("PATH");
272        std::string newPATH = pluginPath;
273        if (currentPATH != NULL)
274            newPATH = std::string(currentPATH) + ';' + newPATH;
275        putenv(const_cast<char*>(("PATH=" + newPATH).c_str()));
276#endif
277
278        // Do some SubString magic to get the comma separated list of plugins
279        SubString plugins(ogrePlugins_, ",", " ", false, '\\', false, '"', false, '{', '}', false, '\0');
280        for (unsigned int i = 0; i < plugins.size(); ++i)
281            ogreRoot_->loadPlugin(pluginPath + '/' + plugins[i]);
282    }
283
284    void GraphicsManager::loadRenderer()
285    {
286        orxout(internal_info) << "GraphicsManager: Configuring Renderer" << endl;
287
288        bool updatedConfig = Core::getInstance().getOgreConfigTimestamp() > Core::getInstance().getLastLevelTimestamp();
289        if (updatedConfig)
290            orxout(user_info)<< "Ogre config file has changed, but no level was started since then. Displaying config dialogue again to verify the changes." << endl;
291
292        if (!ogreRoot_->restoreConfig() || updatedConfig)
293        {
294            if (!ogreRoot_->showConfigDialog())
295                ThrowException(InitialisationFailed, "OGRE graphics configuration dialogue canceled.");
296            else
297                Core::getInstance().updateOgreConfigTimestamp();
298        }
299
300        orxout(internal_info) << "Creating render window" << endl;
301
302        this->renderWindow_ = ogreRoot_->initialise(true, "Orxonox");
303        // Propagate the size of the new winodw
304        this->ogreWindowEventListener_->windowResized(renderWindow_);
305
306        Ogre::WindowEventUtilities::addWindowEventListener(this->renderWindow_, ogreWindowEventListener_);
307
308        // create a full screen default viewport
309        // Note: This may throw when adding a viewport with an existing z-order!
310        //       But in our case we only have one viewport for now anyway, therefore
311        //       no ScopeGuards or anything to handle exceptions.
312        this->viewport_ = this->renderWindow_->addViewport(0, 0);
313
314        Ogre::TextureManager::getSingleton().setDefaultNumMipmaps(Ogre::MIP_UNLIMITED);
315
316        // add console commands
317        ModifyConsoleCommand(__CC_printScreen_name).setFunction(&GraphicsManager::printScreen, this);
318        ModifyConsoleCommand(__CC_GraphicsManager_group, __CC_setScreenResolution_name).setFunction(&GraphicsManager::setScreenResolution, this);
319        ModifyConsoleCommand(__CC_GraphicsManager_group, __CC_setFSAA_name).setFunction(&GraphicsManager::setFSAA, this);
320        ModifyConsoleCommand(__CC_GraphicsManager_group, __CC_setVSync_name).setFunction(&GraphicsManager::setVSync, this);
321    }
322
323    void GraphicsManager::loadDebugOverlay()
324    {
325        // Load debug overlay to show info about fps and tick time
326        orxout(internal_info) << "Loading Debug Overlay..." << endl;
327        debugOverlay_.reset(new XMLFile("debug.oxo"));
328        Loader::open(debugOverlay_.get(), ClassTreeMask(), false);
329    }
330
331    /**
332    @note
333        A note about the Ogre::FrameListener: Even though we don't use them,
334        they still get called.
335    */
336    void GraphicsManager::postUpdate(const Clock& time)
337    {
338        // Time before rendering
339        uint64_t timeBeforeTick = time.getRealMicroseconds();
340
341        // Ogre's time keeping object
342        Ogre::FrameEvent evt;
343
344        // Translate to Ogre float times before the update
345        float temp = lastFrameStartTime_;
346        lastFrameStartTime_ = (float)timeBeforeTick * 0.000001f;
347        evt.timeSinceLastFrame = lastFrameStartTime_ - temp;
348        evt.timeSinceLastEvent = lastFrameStartTime_ - lastFrameEndTime_;
349
350        // Ogre requires the time too
351        ogreRoot_->_fireFrameStarted(evt);
352
353        // Pump messages in all registered RenderWindows
354        // This calls the WindowEventListener objects.
355        Ogre::WindowEventUtilities::messagePump();
356        // Make sure the window stays active even when not focused
357        // (probably only necessary on windows)
358        this->renderWindow_->setActive(true);
359
360        // Render frame
361        ogreRoot_->_updateAllRenderTargets();
362
363        uint64_t timeAfterTick = time.getRealMicroseconds();
364        // Subtract the time used for rendering from the tick time counter
365        Game::getInstance().subtractTickTime((int32_t)(timeAfterTick - timeBeforeTick));
366
367        // Translate to Ogre float times after the update
368        temp = lastFrameEndTime_;
369        lastFrameEndTime_ = (float)timeBeforeTick * 0.000001f;
370        evt.timeSinceLastFrame = lastFrameEndTime_ - temp;
371        evt.timeSinceLastEvent = lastFrameEndTime_ - lastFrameStartTime_;
372
373        // Ogre also needs the time after the frame finished
374        ogreRoot_->_fireFrameEnded(evt);
375    }
376
377    void GraphicsManager::setCamera(Ogre::Camera* camera)
378    {
379        Ogre::Camera* oldCamera = this->viewport_->getCamera();
380
381        this->viewport_->setCamera(camera);
382        GUIManager::getInstance().setCamera(camera);
383
384        for (ObjectList<ViewportEventListener>::iterator it = ObjectList<ViewportEventListener>::begin(); it != ObjectList<ViewportEventListener>::end(); ++it)
385            it->cameraChanged(this->viewport_, oldCamera);
386    }
387
388    /**
389    @brief
390        Method called by the LogListener interface from Ogre.
391        We use it to capture Ogre log messages and handle it ourselves.
392    @param message
393        The message to be logged
394    @param lml
395        The message level the log is using
396    @param maskDebug
397        If we are printing to the console or not
398    @param logName
399        The name of this log (so you can have several listeners
400        for different logs, and identify them)
401    */
402    void GraphicsManager::messageLogged(const std::string& message,
403        Ogre::LogMessageLevel lml, bool maskDebug, const std::string& logName)
404    {
405        OutputLevel orxonoxLevel;
406        std::string introduction;
407        // Do not show caught OGRE exceptions in front
408        if (message.find("EXCEPTION") != std::string::npos)
409        {
410            orxonoxLevel = level::internal_error;
411            introduction = "Ogre, caught exception: ";
412        }
413        else
414        {
415            switch (lml)
416            {
417            case Ogre::LML_TRIVIAL:
418                orxonoxLevel = level::verbose_more;
419                break;
420            case Ogre::LML_NORMAL:
421                orxonoxLevel = level::verbose;
422                break;
423            case Ogre::LML_CRITICAL:
424                orxonoxLevel = level::internal_warning;
425                break;
426            default:
427                orxonoxLevel = level::debug_output;
428            }
429            introduction = "Ogre: ";
430        }
431
432        orxout(orxonoxLevel, context::ogre) << introduction << message << endl;
433    }
434
435    size_t GraphicsManager::getRenderWindowHandle()
436    {
437        size_t windowHnd = 0;
438        renderWindow_->getCustomAttribute("WINDOW", &windowHnd);
439        return windowHnd;
440    }
441
442    bool GraphicsManager::isFullScreen() const
443    {
444        return this->renderWindow_->isFullScreen();
445    }
446
447    unsigned int GraphicsManager::getWindowWidth() const
448    {
449        return this->renderWindow_->getWidth();
450    }
451
452    unsigned int GraphicsManager::getWindowHeight() const
453    {
454        return this->renderWindow_->getHeight();
455    }
456
457    bool GraphicsManager::hasVSyncEnabled() const
458    {
459        Ogre::ConfigOptionMap& options = ogreRoot_->getRenderSystem()->getConfigOptions();
460        Ogre::ConfigOptionMap::iterator it = options.find("VSync");
461        if (it != options.end())
462            return (it->second.currentValue == "Yes");
463        else
464            return false;
465    }
466
467    std::string GraphicsManager::getFSAAMode() const
468    {
469        Ogre::ConfigOptionMap& options = ogreRoot_->getRenderSystem()->getConfigOptions();
470        Ogre::ConfigOptionMap::iterator it = options.find("FSAA");
471        if (it != options.end())
472            return it->second.currentValue;
473        else
474            return "";
475    }
476
477    std::string GraphicsManager::setScreenResolution(unsigned int width, unsigned int height, bool fullscreen)
478    {
479        // workaround to detect if the colour depth should be written to the config file
480        bool bWriteColourDepth = false;
481        Ogre::ConfigOptionMap& options = ogreRoot_->getRenderSystem()->getConfigOptions();
482        Ogre::ConfigOptionMap::iterator it = options.find("Video Mode");
483        if (it != options.end())
484            bWriteColourDepth = (it->second.currentValue.find('@') != std::string::npos);
485
486        if (bWriteColourDepth)
487        {
488            this->ogreRoot_->getRenderSystem()->setConfigOption("Video Mode", multi_cast<std::string>(width)
489                                                                    + " x " + multi_cast<std::string>(height)
490                                                                    + " @ " + multi_cast<std::string>(this->getRenderWindow()->getColourDepth()) + "-bit colour");
491        }
492        else
493        {
494            this->ogreRoot_->getRenderSystem()->setConfigOption("Video Mode", multi_cast<std::string>(width)
495                                                                    + " x " + multi_cast<std::string>(height));
496        }
497
498        this->ogreRoot_->getRenderSystem()->setConfigOption("Full Screen", fullscreen ? "Yes" : "No");
499
500        std::string validate = this->ogreRoot_->getRenderSystem()->validateConfigOptions();
501
502        if (validate == "")
503        {
504            GraphicsManager::getInstance().getRenderWindow()->setFullscreen(fullscreen, width, height);
505            this->ogreRoot_->saveConfig();
506            Core::getInstance().updateOgreConfigTimestamp();
507            // Also reload the input devices
508            InputManager::getInstance().reload();
509        }
510
511        return validate;
512    }
513
514    std::string GraphicsManager::setFSAA(const std::string& mode)
515    {
516        this->ogreRoot_->getRenderSystem()->setConfigOption("FSAA", mode);
517
518        std::string validate = this->ogreRoot_->getRenderSystem()->validateConfigOptions();
519
520        if (validate == "")
521        {
522            //this->ogreRoot_->getRenderSystem()->reinitialise(); // can't use this that easily, because it recreates the render window, invalidating renderWindow_
523            this->ogreRoot_->saveConfig();
524            Core::getInstance().updateOgreConfigTimestamp();
525        }
526
527        return validate;
528    }
529
530    std::string GraphicsManager::setVSync(bool vsync)
531    {
532        this->ogreRoot_->getRenderSystem()->setConfigOption("VSync", vsync ? "Yes" : "No");
533
534        std::string validate = this->ogreRoot_->getRenderSystem()->validateConfigOptions();
535
536        if (validate == "")
537        {
538            //this->ogreRoot_->getRenderSystem()->reinitialise(); // can't use this that easily, because it recreates the render window, invalidating renderWindow_
539            this->ogreRoot_->saveConfig();
540            Core::getInstance().updateOgreConfigTimestamp();
541        }
542
543        return validate;
544    }
545
546    void GraphicsManager::printScreen()
547    {
548        assert(this->renderWindow_);
549        this->renderWindow_->writeContentsToTimestampedFile(PathConfig::getLogPathString() + "screenShot_", ".png");
550    }
551}
Note: See TracBrowser for help on using the repository browser.