Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 7903 was 7874, checked in by landauf, 14 years ago

WindowEventListener now declares if the window's focus is active or not.
CEGUI stops highlighting menu items if the window's focus is lost, i.e. after pressing alt+tab

  • Property svn:eol-style set to native
File size: 17.5 KB
RevLine 
[612]1/*
2 *   ORXONOX - the hottest 3D action shooter ever to exist
[1293]3 *                    > www.orxonox.net <
[612]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
[1349]11 *   of the License, or (at your option) any later version.
12 *
[612]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:
[1535]23 *      Reto Grieder
[1755]24 *      Benjamin Knecht <beni_at_orxonox.net>, (C) 2007
[612]25 *   Co-authors:
[1755]26 *      Felix Schulthess
[612]27 *
28 */
[1035]29
[2801]30#include "GraphicsManager.h"
[612]31
[2801]32#include <fstream>
[5695]33#include <sstream>
[2801]34#include <boost/filesystem.hpp>
[5695]35#include <boost/shared_array.hpp>
[2801]36
[5695]37#include <OgreArchiveFactory.h>
38#include <OgreArchiveManager.h>
[2801]39#include <OgreFrameListener.h>
40#include <OgreRoot.h>
41#include <OgreLogManager.h>
[1755]42#include <OgreRenderWindow.h>
[2801]43#include <OgreRenderSystem.h>
[5695]44#include <OgreResourceGroupManager.h>
[2801]45#include <OgreTextureManager.h>
46#include <OgreViewport.h>
47#include <OgreWindowEventUtilities.h>
[1538]48
[2801]49#include "SpecialConfig.h"
[5929]50#include "util/Clock.h"
[2801]51#include "util/Exception.h"
[3280]52#include "util/StringUtils.h"
[2801]53#include "util/SubString.h"
[3346]54#include "ConfigValueIncludes.h"
55#include "CoreIncludes.h"
[7870]56#include "Core.h"
[3346]57#include "Game.h"
58#include "GameMode.h"
[5695]59#include "Loader.h"
60#include "MemoryArchive.h"
[5929]61#include "PathConfig.h"
[3346]62#include "WindowEventListener.h"
[5695]63#include "XMLFile.h"
[7284]64#include "command/ConsoleCommand.h"
[1032]65
[1625]66namespace orxonox
67{
[7284]68    static const std::string __CC_printScreen_name = "printScreen";
69    DeclareConsoleCommand(__CC_printScreen_name, &prototype::void__void);
70
[3327]71    class OgreWindowEventListener : public Ogre::WindowEventListener
[2801]72    {
[3327]73    public:
74        void windowResized     (Ogre::RenderWindow* rw)
75            { orxonox::WindowEventListener::resizeWindow(rw->getWidth(), rw->getHeight()); }
76        void windowFocusChange (Ogre::RenderWindow* rw)
[7874]77            { orxonox::WindowEventListener::changeWindowFocus(rw->isActive()); }
[3327]78        void windowClosed      (Ogre::RenderWindow* rw)
79            { orxonox::Game::getInstance().stop(); }
80        void windowMoved       (Ogre::RenderWindow* rw)
81            { orxonox::WindowEventListener::moveWindow(); }
[2801]82    };
[1032]83
[3366]84    GraphicsManager* GraphicsManager::singletonPtr_s = 0;
[1293]85
[1755]86    /**
87    @brief
[2801]88        Non-initialising constructor.
[1755]89    */
[5695]90    GraphicsManager::GraphicsManager(bool bLoadRenderer)
91        : ogreWindowEventListener_(new OgreWindowEventListener())
92#if OGRE_VERSION < 0x010600
93        , memoryArchiveFactory_(new MemoryArchiveFactory())
94#endif
[2801]95        , renderWindow_(0)
96        , viewport_(0)
[1024]97    {
[2801]98        RegisterObject(GraphicsManager);
99
100        this->setConfigValues();
[612]101
[5695]102        // Ogre setup procedure (creating Ogre::Root)
103        this->loadOgreRoot();
[2801]104
[5695]105        // At first, add the root paths of the data directories as resource locations
[6417]106        Ogre::ResourceGroupManager::getSingleton().addResourceLocation(PathConfig::getDataPathString(), "FileSystem");
[5695]107        // Load resources
[6417]108        resources_.reset(new XMLFile("DefaultResources.oxr"));
[5695]109        resources_->setLuaSupport(false);
110        Loader::open(resources_.get());
111
112        // Only for development runs
[5929]113        if (PathConfig::isDevelopmentRun())
[3280]114        {
[6417]115            Ogre::ResourceGroupManager::getSingleton().addResourceLocation(PathConfig::getExternalDataPathString(), "FileSystem");
116            extResources_.reset(new XMLFile("resources.oxr"));
[5695]117            extResources_->setLuaSupport(false);
118            Loader::open(extResources_.get());
119        }
[2801]120
[5695]121        if (bLoadRenderer)
[3280]122        {
[5695]123            // Reads the ogre config and creates the render window
124            this->upgradeToGraphics();
[3280]125        }
[2801]126    }
127
[1755]128    /**
129    @brief
[5695]130        Destruction is done by the member scoped_ptrs.
[1755]131    */
[2801]132    GraphicsManager::~GraphicsManager()
[1535]133    {
[5929]134        Loader::unload(debugOverlay_.get());
135
[5695]136        Ogre::WindowEventUtilities::removeWindowEventListener(renderWindow_, ogreWindowEventListener_.get());
[7284]137        ModifyConsoleCommand(__CC_printScreen_name).resetFunction();
[5929]138
139        // Undeclare the resources
140        Loader::unload(resources_.get());
141        if (PathConfig::isDevelopmentRun())
142            Loader::unload(extResources_.get());
[1535]143    }
144
[2801]145    void GraphicsManager::setConfigValues()
[1535]146    {
[2801]147        SetConfigValue(ogreConfigFile_,  "ogre.cfg")
148            .description("Location of the Ogre config file");
[5695]149        SetConfigValue(ogrePluginsDirectory_, specialConfig::ogrePluginsDirectory)
[2801]150            .description("Folder where the Ogre plugins are located.");
[5695]151        SetConfigValue(ogrePlugins_, specialConfig::ogrePlugins)
[2801]152            .description("Comma separated list of all plugins to load.");
153        SetConfigValue(ogreLogFile_,     "ogre.log")
154            .description("Logfile for messages from Ogre. Use \"\" to suppress log file creation.");
155        SetConfigValue(ogreLogLevelTrivial_ , 5)
156            .description("Corresponding orxonox debug level for ogre Trivial");
157        SetConfigValue(ogreLogLevelNormal_  , 4)
158            .description("Corresponding orxonox debug level for ogre Normal");
159        SetConfigValue(ogreLogLevelCritical_, 2)
160            .description("Corresponding orxonox debug level for ogre Critical");
[1535]161    }
[612]162
[5695]163    /**
164    @brief
165        Loads the renderer and creates the render window if not yet done so.
166    @remarks
167        This operation is irreversible without recreating the GraphicsManager!
168        So if it throws you HAVE to recreate the GraphicsManager!!!
169        It therefore offers almost no exception safety.
170    */
171    void GraphicsManager::upgradeToGraphics()
[2801]172    {
[5695]173        if (renderWindow_ != NULL)
174            return;
[2801]175
[6075]176        // load all the required plugins for Ogre
177        this->loadOgrePlugins();
178
[5695]179        this->loadRenderer();
[2801]180
[5695]181#if OGRE_VERSION < 0x010600
182        // WORKAROUND: There is an incompatibility for particle scripts when trying
183        // to support both Ogre 1.4 and 1.6. The hacky solution is to create
184        // scripts for the 1.6 version and then remove the inserted "particle_system"
185        // keyword. But we need to supply these new scripts as well, which is why
[5747]186        // there is an extra Ogre::Archive dealing with it in the memory.
[5695]187        using namespace Ogre;
188        ArchiveManager::getSingleton().addArchiveFactory(memoryArchiveFactory_.get());
189        const StringVector& groups = ResourceGroupManager::getSingleton().getResourceGroups();
190        // Travers all groups
191        for (StringVector::const_iterator itGroup = groups.begin(); itGroup != groups.end(); ++itGroup)
192        {
193            FileInfoListPtr files = ResourceGroupManager::getSingleton().findResourceFileInfo(*itGroup, "*.particle");
194            for (FileInfoList::const_iterator itFile = files->begin(); itFile != files->end(); ++itFile)
195            {
196                // open file
197                Ogre::DataStreamPtr input = ResourceGroupManager::getSingleton().openResource(itFile->filename, *itGroup, false);
198                std::stringstream output;
199                // Parse file and replace "particle_system" with nothing
200                while (!input->eof())
201                {
202                    std::string line = input->getLine();
203                    size_t pos = line.find("particle_system");
204                    if (pos != std::string::npos)
205                    {
206                        // 15 is the length of "particle_system"
207                        line.replace(pos, 15, "");
208                    }
209                    output << line << std::endl;
210                }
211                // Add file to the memory archive
212                shared_array<char> data(new char[output.str().size()]);
213                // Debug optimisations
[6417]214                const std::string& outputStr = output.str();
[5695]215                char* rawData = data.get();
216                for (unsigned i = 0; i < outputStr.size(); ++i)
217                    rawData[i] = outputStr[i];
218                MemoryArchive::addFile("particle_scripts_ogre_1.4_" + *itGroup, itFile->filename, data, output.str().size());
219            }
220            if (!files->empty())
221            {
222                // Declare the files, but using a new group
223                ResourceGroupManager::getSingleton().addResourceLocation("particle_scripts_ogre_1.4_" + *itGroup,
224                    "Memory", "particle_scripts_ogre_1.4_" + *itGroup);
225            }
226        }
227#endif
[2801]228
[5695]229        // Initialise all resources (do this AFTER the renderer has been loaded!)
230        // Note: You can only do this once! Ogre will check whether a resource group has
231        // already been initialised. If you need to load resources later, you will have to
232        // choose another resource group.
233        Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
[2801]234    }
235
[1755]236    /**
237    @brief
[2801]238        Creates the Ogre Root object and sets up the ogre log.
[1755]239    */
[5695]240    void GraphicsManager::loadOgreRoot()
[1538]241    {
[2801]242        COUT(3) << "Setting up Ogre..." << std::endl;
243
[6417]244        if (ogreConfigFile_.empty())
[2801]245        {
246            COUT(2) << "Warning: Ogre config file set to \"\". Defaulting to config.cfg" << std::endl;
247            ModifyConfigValue(ogreConfigFile_, tset, "config.cfg");
248        }
[6417]249        if (ogreLogFile_.empty())
[2801]250        {
251            COUT(2) << "Warning: Ogre log file set to \"\". Defaulting to ogre.log" << std::endl;
252            ModifyConfigValue(ogreLogFile_, tset, "ogre.log");
253        }
254
[5929]255        boost::filesystem::path ogreConfigFilepath(PathConfig::getConfigPath() / this->ogreConfigFile_);
256        boost::filesystem::path ogreLogFilepath(PathConfig::getLogPath() / this->ogreLogFile_);
[2801]257
258        // create a new logManager
259        // Ogre::Root will detect that we've already created a Log
[5695]260        ogreLogger_.reset(new Ogre::LogManager());
[2801]261        COUT(4) << "Ogre LogManager created" << std::endl;
262
263        // create our own log that we can listen to
264        Ogre::Log *myLog;
[5695]265        myLog = ogreLogger_->createLog(ogreLogFilepath.string(), true, false, false);
[2801]266        COUT(4) << "Ogre Log created" << std::endl;
267
268        myLog->setLogDetail(Ogre::LL_BOREME);
269        myLog->addListener(this);
270
271        COUT(4) << "Creating Ogre Root..." << std::endl;
272
273        // check for config file existence because Ogre displays (caught) exceptions if not
274        if (!boost::filesystem::exists(ogreConfigFilepath))
275        {
276            // create a zero sized file
277            std::ofstream creator;
278            creator.open(ogreConfigFilepath.string().c_str());
279            creator.close();
280        }
281
282        // Leave plugins file empty. We're going to do that part manually later
[5695]283        ogreRoot_.reset(new Ogre::Root("", ogreConfigFilepath.string(), ogreLogFilepath.string()));
[2801]284
285        COUT(3) << "Ogre set up done." << std::endl;
[1538]286    }
[2801]287
288    void GraphicsManager::loadOgrePlugins()
289    {
290        // just to make sure the next statement doesn't segfault
[6417]291        if (ogrePluginsDirectory_.empty())
292            ogrePluginsDirectory_ = '.';
[2801]293
[5695]294        boost::filesystem::path folder(ogrePluginsDirectory_);
[2801]295        // Do some SubString magic to get the comma separated list of plugins
[7284]296        SubString plugins(ogrePlugins_, ",", " ", false, '\\', false, '"', false, '{', '}', false, '\0');
[2801]297        // Use backslash paths on Windows! file_string() already does that though.
298        for (unsigned int i = 0; i < plugins.size(); ++i)
299            ogreRoot_->loadPlugin((folder / plugins[i]).file_string());
300    }
301
302    void GraphicsManager::loadRenderer()
303    {
304        CCOUT(4) << "Configuring Renderer" << std::endl;
305
[7870]306        if (!ogreRoot_->restoreConfig() || Core::getInstance().getOgreConfigTimestamp() > Core::getInstance().getLastLevelTimestamp())
307        {
[2801]308            if (!ogreRoot_->showConfigDialog())
[7868]309                ThrowException(InitialisationFailed, "OGRE graphics configuration dialogue canceled.");
[7870]310            else
311                Core::getInstance().updateOgreConfigTimestamp();
312        }
[2801]313
314        CCOUT(4) << "Creating render window" << std::endl;
315
316        this->renderWindow_ = ogreRoot_->initialise(true, "Orxonox");
[5695]317        // Propagate the size of the new winodw
[3327]318        this->ogreWindowEventListener_->windowResized(renderWindow_);
[2801]319
[5695]320        Ogre::WindowEventUtilities::addWindowEventListener(this->renderWindow_, ogreWindowEventListener_.get());
[2801]321
[5695]322        // create a full screen default viewport
323        // Note: This may throw when adding a viewport with an existing z-order!
324        //       But in our case we only have one viewport for now anyway, therefore
325        //       no ScopeGuards or anything to handle exceptions.
326        this->viewport_ = this->renderWindow_->addViewport(0, 0);
327
[6524]328        Ogre::TextureManager::getSingleton().setDefaultNumMipmaps(Ogre::MIP_UNLIMITED);
[2801]329
[5695]330        // add console commands
[7284]331        ModifyConsoleCommand(__CC_printScreen_name).setFunction(&GraphicsManager::printScreen, this);
[2801]332    }
333
[5929]334    void GraphicsManager::loadDebugOverlay()
335    {
336        // Load debug overlay to show info about fps and tick time
337        COUT(4) << "Loading Debug Overlay..." << std::endl;
338        debugOverlay_.reset(new XMLFile("debug.oxo"));
339        Loader::open(debugOverlay_.get());
340    }
341
342    /**
343    @note
344        A note about the Ogre::FrameListener: Even though we don't use them,
345        they still get called. However, the delta times are not correct (except
346        for timeSinceLastFrame, which is the most important). A little research
347        as shown that there is probably only one FrameListener that doesn't even
348        need the time. So we shouldn't run into problems.
349    */
[6417]350    void GraphicsManager::postUpdate(const Clock& time)
[2801]351    {
[5695]352        Ogre::FrameEvent evt;
353        evt.timeSinceLastFrame = time.getDeltaTime();
354        evt.timeSinceLastEvent = time.getDeltaTime(); // note: same time, but shouldn't matter anyway
355
356        // don't forget to call _fireFrameStarted to OGRE to make sure
357        // everything goes smoothly
358        ogreRoot_->_fireFrameStarted(evt);
359
360        // Pump messages in all registered RenderWindows
361        // This calls the WindowEventListener objects.
362        Ogre::WindowEventUtilities::messagePump();
363        // make sure the window stays active even when not focused
364        // (probably only necessary on windows)
365        this->renderWindow_->setActive(true);
366
367        // Time before rendering
368        uint64_t timeBeforeTick = time.getRealMicroseconds();
369
370        // Render frame
371        ogreRoot_->_updateAllRenderTargets();
372
373        uint64_t timeAfterTick = time.getRealMicroseconds();
374        // Subtract the time used for rendering from the tick time counter
[6502]375        Game::getInstance().subtractTickTime((int32_t)(timeAfterTick - timeBeforeTick));
[5695]376
377        // again, just to be sure OGRE works fine
378        ogreRoot_->_fireFrameEnded(evt); // note: uses the same time as _fireFrameStarted
[2801]379    }
380
[5695]381    void GraphicsManager::setCamera(Ogre::Camera* camera)
382    {
383        this->viewport_->setCamera(camera);
384    }
385
[2801]386    /**
387    @brief
388        Method called by the LogListener interface from Ogre.
389        We use it to capture Ogre log messages and handle it ourselves.
390    @param message
391        The message to be logged
392    @param lml
393        The message level the log is using
394    @param maskDebug
395        If we are printing to the console or not
396    @param logName
397        The name of this log (so you can have several listeners
398        for different logs, and identify them)
399    */
400    void GraphicsManager::messageLogged(const std::string& message,
401        Ogre::LogMessageLevel lml, bool maskDebug, const std::string& logName)
402    {
403        int orxonoxLevel;
[6417]404        std::string introduction;
405        // Do not show caught OGRE exceptions in front
406        if (message.find("EXCEPTION") != std::string::npos)
[2801]407        {
[6417]408            orxonoxLevel = OutputLevel::Debug;
409            introduction = "Ogre, caught exception: ";
[2801]410        }
[6417]411        else
412        {
413            switch (lml)
414            {
415            case Ogre::LML_TRIVIAL:
416                orxonoxLevel = this->ogreLogLevelTrivial_;
417                break;
418            case Ogre::LML_NORMAL:
419                orxonoxLevel = this->ogreLogLevelNormal_;
420                break;
421            case Ogre::LML_CRITICAL:
422                orxonoxLevel = this->ogreLogLevelCritical_;
423                break;
424            default:
425                orxonoxLevel = 0;
426            }
427            introduction = "Ogre: ";
428        }
[6105]429        OutputHandler::getOutStream(orxonoxLevel)
[6417]430            << introduction << message << std::endl;
[2801]431    }
432
[5695]433    size_t GraphicsManager::getRenderWindowHandle()
434    {
435        size_t windowHnd = 0;
436        renderWindow_->getCustomAttribute("WINDOW", &windowHnd);
437        return windowHnd;
438    }
439
440    bool GraphicsManager::isFullScreen() const
441    {
442        Ogre::ConfigOptionMap& options = ogreRoot_->getRenderSystem()->getConfigOptions();
443        if (options.find("Full Screen") != options.end())
444        {
445            if (options["Full Screen"].currentValue == "Yes")
446                return true;
447            else
448                return false;
449        }
450        else
451        {
452            COUT(0) << "Could not find 'Full Screen' render system option. Fix This!!!" << std::endl;
453            return false;
454        }
455    }
456
[2801]457    void GraphicsManager::printScreen()
458    {
459        assert(this->renderWindow_);
[6417]460        this->renderWindow_->writeContentsToTimestampedFile(PathConfig::getLogPathString() + "screenShot_", ".png");
[2801]461    }
[612]462}
Note: See TracBrowser for help on using the repository browser.