Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 8351 was 8351, checked in by rgrieder, 13 years ago

Merged kicklib2 branch back to trunk (includes former branches ois_update, mac_osx and kicklib).

Notes for updating

Linux:
You don't need an extra package for CEGUILua and Tolua, it's already shipped with CEGUI.
However you do need to make sure that the OgreRenderer is installed too with CEGUI 0.7 (may be a separate package).
Also, Orxonox now recognises if you install the CgProgramManager (a separate package available on newer Ubuntu on Debian systems).

Windows:
Download the new dependency packages versioned 6.0 and use these. If you have problems with that or if you don't like the in game console problem mentioned below, you can download the new 4.3 version of the packages (only available for Visual Studio 2005/2008).

Key new features:

  • *Support for Mac OS X*
  • Visual Studio 2010 support
  • Bullet library update to 2.77
  • OIS library update to 1.3
  • Support for CEGUI 0.7 —> Support for Arch Linux and even SuSE
  • Improved install target
  • Compiles now with GCC 4.6
  • Ogre Cg Shader plugin activated for Linux if available
  • And of course lots of bug fixes

There are also some regressions:

  • No support for CEGUI 0.5, Ogre 1.4 and boost 1.35 - 1.39 any more
  • In game console is not working in main menu for CEGUI 0.7
  • Tolua (just the C lib, not the application) and CEGUILua libraries are no longer in our repository. —> You will need to get these as well when compiling Orxonox
  • And of course lots of new bugs we don't yet know about
  • Property svn:eol-style set to native
File size: 20.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 "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    /**
97    @brief
98        Non-initialising constructor.
99    */
100    GraphicsManager::GraphicsManager(bool bLoadRenderer)
101        : ogreWindowEventListener_(new OgreWindowEventListener())
102        , renderWindow_(0)
103        , viewport_(0)
104        , lastFrameStartTime_(0.0f)
105        , lastFrameEndTime_(0.0f)
106    {
107        RegisterObject(GraphicsManager);
108
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::open(resources_.get());
120
121        // Only for development runs
122        if (PathConfig::isDevelopmentRun())
123            Ogre::ResourceGroupManager::getSingleton().addResourceLocation(PathConfig::getExternalDataPathString(), "FileSystem");
124
125        extResources_.reset(new XMLFile("resources.oxr"));
126        extResources_->setLuaSupport(false);
127        Loader::open(extResources_.get());
128
129        if (bLoadRenderer)
130        {
131            // Reads the ogre config and creates the render window
132            this->upgradeToGraphics();
133        }
134    }
135
136    /**
137    @brief
138        Destruction is done by the member scoped_ptrs.
139    */
140    GraphicsManager::~GraphicsManager()
141    {
142        Loader::unload(debugOverlay_.get());
143
144        Ogre::WindowEventUtilities::removeWindowEventListener(renderWindow_, ogreWindowEventListener_.get());
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::unload(resources_.get());
152        Loader::unload(extResources_.get());
153    }
154
155    void GraphicsManager::setConfigValues()
156    {
157        SetConfigValue(ogreConfigFile_,  "ogre.cfg")
158            .description("Location of the Ogre config file");
159        SetConfigValue(ogrePlugins_, specialConfig::ogrePlugins)
160            .description("Comma separated list of all plugins to load.");
161        SetConfigValue(ogreLogFile_,     "ogre.log")
162            .description("Logfile for messages from Ogre. Use \"\" to suppress log file creation.");
163        SetConfigValue(ogreLogLevelTrivial_ , 5)
164            .description("Corresponding orxonox debug level for ogre Trivial");
165        SetConfigValue(ogreLogLevelNormal_  , 4)
166            .description("Corresponding orxonox debug level for ogre Normal");
167        SetConfigValue(ogreLogLevelCritical_, 2)
168            .description("Corresponding orxonox debug level for ogre Critical");
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        // 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
196    /**
197    @brief
198        Creates the Ogre Root object and sets up the ogre log.
199    */
200    void GraphicsManager::loadOgreRoot()
201    {
202        COUT(3) << "Setting up Ogre..." << std::endl;
203
204        if (ogreConfigFile_.empty())
205        {
206            COUT(2) << "Warning: Ogre config file set to \"\". Defaulting to config.cfg" << std::endl;
207            ModifyConfigValue(ogreConfigFile_, tset, "config.cfg");
208        }
209        if (ogreLogFile_.empty())
210        {
211            COUT(2) << "Warning: Ogre log file set to \"\". Defaulting to ogre.log" << std::endl;
212            ModifyConfigValue(ogreLogFile_, tset, "ogre.log");
213        }
214
215        boost::filesystem::path ogreConfigFilepath(PathConfig::getConfigPath() / this->ogreConfigFile_);
216        boost::filesystem::path ogreLogFilepath(PathConfig::getLogPath() / this->ogreLogFile_);
217
218        // create a new logManager
219        // Ogre::Root will detect that we've already created a Log
220        ogreLogger_.reset(new Ogre::LogManager());
221        COUT(4) << "Ogre LogManager created" << std::endl;
222
223        // create our own log that we can listen to
224        Ogre::Log *myLog;
225        myLog = ogreLogger_->createLog(ogreLogFilepath.string(), true, false, false);
226        COUT(4) << "Ogre Log created" << std::endl;
227
228        myLog->setLogDetail(Ogre::LL_BOREME);
229        myLog->addListener(this);
230
231        COUT(4) << "Creating Ogre Root..." << std::endl;
232
233        // check for config file existence because Ogre displays (caught) exceptions if not
234        if (!boost::filesystem::exists(ogreConfigFilepath))
235        {
236            // create a zero sized file
237            std::ofstream creator;
238            creator.open(ogreConfigFilepath.string().c_str());
239            creator.close();
240        }
241
242        // Leave plugins file empty. We're going to do that part manually later
243        ogreRoot_.reset(new Ogre::Root("", ogreConfigFilepath.string(), ogreLogFilepath.string()));
244
245        COUT(3) << "Ogre set up done." << std::endl;
246    }
247
248    void GraphicsManager::loadOgrePlugins()
249    {
250        // Plugin path can have many different locations...
251        std::string pluginPath = specialConfig::ogrePluginsDirectory;
252#ifdef DEPENDENCY_PACKAGE_ENABLE
253        if (!PathConfig::isDevelopmentRun())
254        {
255#  if defined(ORXONOX_PLATFORM_WINDOWS)
256            pluginPath = PathConfig::getExecutablePathString();
257#  elif defined(ORXONOX_PLATFORM_APPLE)
258            // TODO: Where are the plugins being installed to?
259            pluginPath = PathConfig::getExecutablePathString();
260#  endif
261        }
262#endif
263
264#ifdef ORXONOX_PLATFORM_WINDOWS
265        // Add OGRE plugin path to the environment. That way one plugin could
266        // also depend on another without problems on Windows
267        const char* currentPATH = getenv("PATH");
268        std::string newPATH = pluginPath;
269        if (currentPATH != NULL)
270            newPATH = std::string(currentPATH) + ';' + newPATH;
271        putenv(const_cast<char*>(("PATH=" + newPATH).c_str()));
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        CCOUT(4) << "Configuring Renderer" << std::endl;
283
284        bool updatedConfig = Core::getInstance().getOgreConfigTimestamp() > Core::getInstance().getLastLevelTimestamp();
285        if (updatedConfig)
286            COUT(2) << "Ogre config file has changed, but no level was started since then. Displaying config dialogue again to verify the changes." << std::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().updateOgreConfigTimestamp();
294        }
295
296        CCOUT(4) << "Creating render window" << std::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_.get());
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 console commands
313        ModifyConsoleCommand(__CC_printScreen_name).setFunction(&GraphicsManager::printScreen, this);
314        ModifyConsoleCommand(__CC_GraphicsManager_group, __CC_setScreenResolution_name).setFunction(&GraphicsManager::setScreenResolution, this);
315        ModifyConsoleCommand(__CC_GraphicsManager_group, __CC_setFSAA_name).setFunction(&GraphicsManager::setFSAA, this);
316        ModifyConsoleCommand(__CC_GraphicsManager_group, __CC_setVSync_name).setFunction(&GraphicsManager::setVSync, this);
317    }
318
319    void GraphicsManager::loadDebugOverlay()
320    {
321        // Load debug overlay to show info about fps and tick time
322        COUT(4) << "Loading Debug Overlay..." << std::endl;
323        debugOverlay_.reset(new XMLFile("debug.oxo"));
324        Loader::open(debugOverlay_.get());
325    }
326
327    /**
328    @note
329        A note about the Ogre::FrameListener: Even though we don't use them,
330        they still get called.
331    */
332    void GraphicsManager::postUpdate(const Clock& time)
333    {
334        // Time before rendering
335        uint64_t timeBeforeTick = time.getRealMicroseconds();
336
337        // Ogre's time keeping object
338        Ogre::FrameEvent evt;
339
340        // Translate to Ogre float times before the update
341        float temp = lastFrameStartTime_;
342        lastFrameStartTime_ = (float)timeBeforeTick * 0.000001f;
343        evt.timeSinceLastFrame = lastFrameStartTime_ - temp;
344        evt.timeSinceLastEvent = lastFrameStartTime_ - lastFrameEndTime_;
345
346        // Ogre requires the time too
347        ogreRoot_->_fireFrameStarted(evt);
348
349        // Pump messages in all registered RenderWindows
350        // This calls the WindowEventListener objects.
351        Ogre::WindowEventUtilities::messagePump();
352        // Make sure the window stays active even when not focused
353        // (probably only necessary on windows)
354        this->renderWindow_->setActive(true);
355
356        // Render frame
357        ogreRoot_->_updateAllRenderTargets();
358
359        uint64_t timeAfterTick = time.getRealMicroseconds();
360        // Subtract the time used for rendering from the tick time counter
361        Game::getInstance().subtractTickTime((int32_t)(timeAfterTick - timeBeforeTick));
362
363        // Translate to Ogre float times after the update
364        temp = lastFrameEndTime_;
365        lastFrameEndTime_ = (float)timeBeforeTick * 0.000001f;
366        evt.timeSinceLastFrame = lastFrameEndTime_ - temp;
367        evt.timeSinceLastEvent = lastFrameEndTime_ - lastFrameStartTime_;
368
369        // Ogre also needs the time after the frame finished
370        ogreRoot_->_fireFrameEnded(evt);
371    }
372
373    void GraphicsManager::setCamera(Ogre::Camera* camera)
374    {
375        Ogre::Camera* oldCamera = this->viewport_->getCamera();
376
377        this->viewport_->setCamera(camera);
378        GUIManager::getInstance().setCamera(camera);
379
380        for (ObjectList<ViewportEventListener>::iterator it = ObjectList<ViewportEventListener>::begin(); it != ObjectList<ViewportEventListener>::end(); ++it)
381            it->cameraChanged(this->viewport_, oldCamera);
382    }
383
384    /**
385    @brief
386        Method called by the LogListener interface from Ogre.
387        We use it to capture Ogre log messages and handle it ourselves.
388    @param message
389        The message to be logged
390    @param lml
391        The message level the log is using
392    @param maskDebug
393        If we are printing to the console or not
394    @param logName
395        The name of this log (so you can have several listeners
396        for different logs, and identify them)
397    */
398    void GraphicsManager::messageLogged(const std::string& message,
399        Ogre::LogMessageLevel lml, bool maskDebug, const std::string& logName)
400    {
401        int orxonoxLevel;
402        std::string introduction;
403        // Do not show caught OGRE exceptions in front
404        if (message.find("EXCEPTION") != std::string::npos)
405        {
406            orxonoxLevel = OutputLevel::Debug;
407            introduction = "Ogre, caught exception: ";
408        }
409        else
410        {
411            switch (lml)
412            {
413            case Ogre::LML_TRIVIAL:
414                orxonoxLevel = this->ogreLogLevelTrivial_;
415                break;
416            case Ogre::LML_NORMAL:
417                orxonoxLevel = this->ogreLogLevelNormal_;
418                break;
419            case Ogre::LML_CRITICAL:
420                orxonoxLevel = this->ogreLogLevelCritical_;
421                break;
422            default:
423                orxonoxLevel = 0;
424            }
425            introduction = "Ogre: ";
426        }
427        OutputHandler::getOutStream(orxonoxLevel)
428            << introduction << message << std::endl;
429    }
430
431    size_t GraphicsManager::getRenderWindowHandle()
432    {
433        size_t windowHnd = 0;
434        renderWindow_->getCustomAttribute("WINDOW", &windowHnd);
435        return windowHnd;
436    }
437
438    bool GraphicsManager::isFullScreen() const
439    {
440        return this->renderWindow_->isFullScreen();
441    }
442
443    unsigned int GraphicsManager::getWindowWidth() const
444    {
445        return this->renderWindow_->getWidth();
446    }
447
448    unsigned int GraphicsManager::getWindowHeight() const
449    {
450        return this->renderWindow_->getHeight();
451    }
452
453    bool GraphicsManager::hasVSyncEnabled() const
454    {
455        Ogre::ConfigOptionMap& options = ogreRoot_->getRenderSystem()->getConfigOptions();
456        Ogre::ConfigOptionMap::iterator it = options.find("VSync");
457        if (it != options.end())
458            return (it->second.currentValue == "Yes");
459        else
460            return false;
461    }
462
463    std::string GraphicsManager::getFSAAMode() const
464    {
465        Ogre::ConfigOptionMap& options = ogreRoot_->getRenderSystem()->getConfigOptions();
466        Ogre::ConfigOptionMap::iterator it = options.find("FSAA");
467        if (it != options.end())
468            return it->second.currentValue;
469        else
470            return "";
471    }
472
473    std::string GraphicsManager::setScreenResolution(unsigned int width, unsigned int height, bool fullscreen)
474    {
475        // workaround to detect if the colour depth should be written to the config file
476        bool bWriteColourDepth = false;
477        Ogre::ConfigOptionMap& options = ogreRoot_->getRenderSystem()->getConfigOptions();
478        Ogre::ConfigOptionMap::iterator it = options.find("Video Mode");
479        if (it != options.end())
480            bWriteColourDepth = (it->second.currentValue.find('@') != std::string::npos);
481
482        if (bWriteColourDepth)
483        {
484            this->ogreRoot_->getRenderSystem()->setConfigOption("Video Mode", multi_cast<std::string>(width)
485                                                                    + " x " + multi_cast<std::string>(height)
486                                                                    + " @ " + multi_cast<std::string>(this->getRenderWindow()->getColourDepth()) + "-bit colour");
487        }
488        else
489        {
490            this->ogreRoot_->getRenderSystem()->setConfigOption("Video Mode", multi_cast<std::string>(width)
491                                                                    + " x " + multi_cast<std::string>(height));
492        }
493
494        this->ogreRoot_->getRenderSystem()->setConfigOption("Full Screen", fullscreen ? "Yes" : "No");
495
496        std::string validate = this->ogreRoot_->getRenderSystem()->validateConfigOptions();
497
498        if (validate == "")
499        {
500            GraphicsManager::getInstance().getRenderWindow()->setFullscreen(fullscreen, width, height);
501            this->ogreRoot_->saveConfig();
502            Core::getInstance().updateOgreConfigTimestamp();
503            // Also reload the input devices
504            InputManager::getInstance().reload();
505        }
506
507        return validate;
508    }
509
510    std::string GraphicsManager::setFSAA(const std::string& mode)
511    {
512        this->ogreRoot_->getRenderSystem()->setConfigOption("FSAA", mode);
513
514        std::string validate = this->ogreRoot_->getRenderSystem()->validateConfigOptions();
515
516        if (validate == "")
517        {
518            //this->ogreRoot_->getRenderSystem()->reinitialise(); // can't use this that easily, because it recreates the render window, invalidating renderWindow_
519            this->ogreRoot_->saveConfig();
520            Core::getInstance().updateOgreConfigTimestamp();
521        }
522
523        return validate;
524    }
525
526    std::string GraphicsManager::setVSync(bool vsync)
527    {
528        this->ogreRoot_->getRenderSystem()->setConfigOption("VSync", vsync ? "Yes" : "No");
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().updateOgreConfigTimestamp();
537        }
538
539        return validate;
540    }
541
542    void GraphicsManager::printScreen()
543    {
544        assert(this->renderWindow_);
545        this->renderWindow_->writeContentsToTimestampedFile(PathConfig::getLogPathString() + "screenShot_", ".png");
546    }
547}
Note: See TracBrowser for help on using the repository browser.