Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/buildsystem2/src/orxonox/gamestates/GSGraphics.cc @ 2522

Last change on this file since 2522 was 2512, checked in by rgrieder, 17 years ago

Fixed build due to merge

  • Property svn:eol-style set to native
File size: 20.6 KB
RevLine 
[1661]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 *   Co-authors:
25 *      ...
26 *
27 */
28
29#include "OrxonoxStableHeaders.h"
30#include "GSGraphics.h"
31
[1686]32#include <fstream>
33#include <OgreConfigFile.h>
[1661]34#include <OgreFrameListener.h>
35#include <OgreRoot.h>
[1891]36#include <OgreLogManager.h>
[1686]37#include <OgreException.h>
38#include <OgreRenderWindow.h>
[1828]39#include <OgreRenderSystem.h>
[1686]40#include <OgreTextureManager.h>
41#include <OgreViewport.h>
[1661]42#include <OgreWindowEventUtilities.h>
43
[1755]44#include "util/Debug.h"
[1764]45#include "util/Exception.h"
[2509]46#include "util/String.h"
47#include "util/SubString.h"
[1662]48#include "core/ConsoleCommand.h"
49#include "core/ConfigValueIncludes.h"
[1686]50#include "core/CoreIncludes.h"
[2087]51#include "core/Core.h"
[1661]52#include "core/input/InputManager.h"
[1788]53#include "core/input/KeyBinder.h"
54#include "core/input/ExtendedInputState.h"
[2087]55#include "core/Loader.h"
56#include "core/XMLFile.h"
[1661]57#include "overlays/console/InGameConsole.h"
58#include "gui/GUIManager.h"
[1686]59#include "tools/WindowEventListener.h"
60#include "Settings.h"
61
62// for compatibility
[1662]63#include "GraphicsEngine.h"
[1661]64
65namespace orxonox
66{
67    GSGraphics::GSGraphics()
[1689]68        : GameState<GSRoot>("graphics")
[1891]69        , renderWindow_(0)
70        , viewport_(0)
[2087]71        , bWindowEventListenerUpdateRequired_(false)
[1661]72        , inputManager_(0)
73        , console_(0)
74        , guiManager_(0)
[1891]75        , ogreRoot_(0)
76        , ogreLogger_(0)
77        , graphicsEngine_(0)
[1788]78        , masterKeyBinder_(0)
[1672]79        , frameCount_(0)
[1674]80        , statisticsRefreshCycle_(0)
81        , statisticsStartTime_(0)
82        , statisticsStartCount_(0)
83        , tickTime_(0)
[2087]84        , debugOverlay_(0)
[1661]85    {
[1686]86        RegisterRootObject(GSGraphics);
[1891]87        setConfigValues();
[1661]88    }
89
90    GSGraphics::~GSGraphics()
91    {
92    }
93
94    void GSGraphics::setConfigValues()
95    {
[2103]96        SetConfigValue(resourceFile_,    "resources.cfg")
97            .description("Location of the resources file in the data path.");
98        SetConfigValue(ogreConfigFile_,  "ogre.cfg")
99            .description("Location of the Ogre config file");
[2509]100        SetConfigValue(ogrePluginsFolder_, ".")
101            .description("Folder where the Ogre plugins are located.");
102        SetConfigValue(ogrePlugins_, "RenderSystem_GL, Plugin_ParticleFX")
103            .description("Comma separated list of all plugins to load.");
[2103]104        SetConfigValue(ogreLogFile_,     "ogre.log")
105            .description("Logfile for messages from Ogre. Use \"\" to suppress log file creation.");
106        SetConfigValue(ogreLogLevelTrivial_ , 5)
107            .description("Corresponding orxonox debug level for ogre Trivial");
108        SetConfigValue(ogreLogLevelNormal_  , 4)
109            .description("Corresponding orxonox debug level for ogre Normal");
110        SetConfigValue(ogreLogLevelCritical_, 2)
111            .description("Corresponding orxonox debug level for ogre Critical");
112        SetConfigValue(statisticsRefreshCycle_, 200000)
113            .description("Sets the time in microseconds interval at which average fps, etc. get updated.");
114        SetConfigValue(defaultMasterKeybindings_, "def_masterKeybindings.ini")
115            .description("Filename of default master keybindings.");
[1661]116    }
117
118    void GSGraphics::enter()
119    {
[2087]120        Core::setShowsGraphics(true);
[1696]121
[1891]122        // initialise graphics engine. Doesn't load the render window yet!
123        graphicsEngine_ = new GraphicsEngine();
[1674]124
[1891]125        // Ogre setup procedure
126        setupOgre();
[2509]127        // load all the required plugins for Ogre
128        loadOgrePlugins();
129        // read resource declaration file
[1686]130        this->declareResources();
[2509]131        // Reads ogre config and creates the render window
132        this->loadRenderer();
133
[1661]134        // TODO: Spread this so that this call only initialises things needed for the Console and GUI
[1686]135        this->initialiseResources();
[1661]136
[2087]137        // We want to get informed whenever an object of type WindowEventListener is created
138        // in order to later update the window size.
139        bWindowEventListenerUpdateRequired_ = false;
140        RegisterConstructionCallback(GSGraphics, orxonox::WindowEventListener, requestWindowEventListenerUpdate);
[1686]141
[2087]142        // load debug overlay
143        COUT(3) << "Loading Debug Overlay..." << std::endl;
144        this->debugOverlay_ = new XMLFile(Settings::getDataPath() + "overlay/debug.oxo");
145        Loader::open(debugOverlay_);
[1686]146
[1661]147        // Calls the InputManager which sets up the input devices.
148        // The render window width and height are used to set up the mouse movement.
149        inputManager_ = new InputManager();
[1686]150        size_t windowHnd = 0;
151        this->renderWindow_->getCustomAttribute("WINDOW", &windowHnd);
152        inputManager_->initialise(windowHnd, renderWindow_->getWidth(), renderWindow_->getHeight(), true);
[1788]153        // Configure master input state with a KeyBinder
[2103]154        masterKeyBinder_ = new KeyBinder();
155        masterKeyBinder_->loadBindings("masterKeybindings.ini", defaultMasterKeybindings_);
156        inputManager_->getMasterInputState()->addKeyHandler(masterKeyBinder_);
[1661]157
158        // Load the InGameConsole
159        console_ = new InGameConsole();
[2087]160        console_->initialise(this->renderWindow_->getWidth(), this->renderWindow_->getHeight());
[1661]161
162        // load the CEGUI interface
163        guiManager_ = new GUIManager();
[1686]164        guiManager_->initialise(this->renderWindow_);
[1674]165
166        // reset frame counter
167        this->frameCount_ = 0;
168        this->tickTime_ = 0;
169        statisticsStartTime_ = 0;
170        statisticsStartCount_ = 0;
[1686]171
172        // add console commands
173        FunctorMember<GSGraphics>* functor1 = createFunctor(&GSGraphics::printScreen);
174        functor1->setObject(this);
175        CommandExecutor::addConsoleCommandShortcut(createConsoleCommand(functor1, "printScreen"));
[1661]176    }
177
178    void GSGraphics::leave()
179    {
[1824]180        using namespace Ogre;
181
[1891]182        // remove our WindowEventListener first to avoid bad calls after the window has been destroyed
[1878]183        Ogre::WindowEventUtilities::removeWindowEventListener(this->renderWindow_, this);
184
[1662]185        delete this->guiManager_;
[1661]186
[1662]187        delete this->console_;
[1661]188
[1788]189        //inputManager_->getMasterInputState()->removeKeyHandler(this->masterKeyBinder_);
[2103]190        delete this->masterKeyBinder_;
[1662]191        delete this->inputManager_;
192
[2087]193        Loader::unload(this->debugOverlay_);
194        delete this->debugOverlay_;
195
[1824]196        // destroy render window
197        RenderSystem* renderer = this->ogreRoot_->getRenderSystem();
198        renderer->destroyRenderWindow("Orxonox");
[1696]199
[1891]200        /*** CODE SNIPPET, UNUSED ***/
[1824]201        // Does the opposite of initialise()
[1891]202        //ogreRoot_->shutdown();
[1824]203        // Remove all resources and resource groups
[1825]204        //StringVector groups = ResourceGroupManager::getSingleton().getResourceGroups();
205        //for (StringVector::iterator it = groups.begin(); it != groups.end(); ++it)
206        //{
207        //    ResourceGroupManager::getSingleton().destroyResourceGroup(*it);
208        //}
[1824]209
[1825]210        //ParticleSystemManager::getSingleton().removeAllTemplates();
[1824]211
212        // Shutdown the render system
[1825]213        //this->ogreRoot_->setRenderSystem(0);
[1824]214
[1891]215        delete this->ogreRoot_;
216
217#if ORXONOX_PLATFORM == ORXONOX_PLATFORM_WIN32
218        // delete the ogre log and the logManager (since we have created it).
219        this->ogreLogger_->getDefaultLog()->removeListener(this);
220        this->ogreLogger_->destroyLog(Ogre::LogManager::getSingleton().getDefaultLog());
221        delete this->ogreLogger_;
222#endif
223
224        delete graphicsEngine_;
225
[2087]226        Core::setShowsGraphics(false);
[1661]227    }
228
[1662]229    /**
230        Main loop of the orxonox game.
231        We use the Ogre::Timer to measure time since it uses the most precise
232        method an a platform (however the windows timer lacks time when under
233        heavy kernel load!).
234        There is a simple mechanism to measure the average time spent in our
235        ticks as it may indicate performance issues.
236        A note about the Ogre::FrameListener: Even though we don't use them,
237        they still get called. However, the delta times are not correct (except
238        for timeSinceLastFrame, which is the most important). A little research
239        as shown that there is probably only one FrameListener that doesn't even
240        need the time. So we shouldn't run into problems.
241    */
[1674]242    void GSGraphics::ticked(const Clock& time)
[1661]243    {
[1724]244        unsigned long long timeBeforeTick = time.getRealMicroseconds();
[1674]245        float dt = time.getDeltaTime();
246
[1672]247        this->inputManager_->tick(dt);
248        // tick console
249        this->console_->tick(dt);
[1674]250        this->tickChild(time);
[2087]251
252        if (this->bWindowEventListenerUpdateRequired_)
253        {
254            // Update all WindowEventListeners for the case a new one was created.
255            this->windowResized(this->renderWindow_);
256            this->bWindowEventListenerUpdateRequired_ = false;
257        }
258
[1724]259        unsigned long long timeAfterTick = time.getRealMicroseconds();
[1661]260
[1674]261        tickTime_ += (unsigned int)(timeAfterTick - timeBeforeTick);
262        if (timeAfterTick > statisticsStartTime_ + statisticsRefreshCycle_)
263        {
264            GraphicsEngine::getInstance().setAverageTickTime(
265                (float)tickTime_ * 0.001f / (frameCount_ - statisticsStartCount_));
266            float avgFPS = (float)(frameCount_ - statisticsStartCount_)
267                / (timeAfterTick - statisticsStartTime_) * 1000000.0;
268            GraphicsEngine::getInstance().setAverageFramesPerSecond(avgFPS);
[1661]269
[1674]270            tickTime_ = 0;
271            statisticsStartCount_ = frameCount_;
272            statisticsStartTime_  = timeAfterTick;
273        }
[1661]274
[1672]275        // don't forget to call _fireFrameStarted in ogre to make sure
276        // everything goes smoothly
277        Ogre::FrameEvent evt;
278        evt.timeSinceLastFrame = dt;
279        evt.timeSinceLastEvent = dt; // note: same time, but shouldn't matter anyway
280        ogreRoot_->_fireFrameStarted(evt);
[1661]281
[1672]282        // Pump messages in all registered RenderWindows
283        // This calls the WindowEventListener objects.
284        Ogre::WindowEventUtilities::messagePump();
285        // make sure the window stays active even when not focused
286        // (probably only necessary on windows)
[1686]287        this->renderWindow_->setActive(true);
[1661]288
[1672]289        // render
290        ogreRoot_->_updateAllRenderTargets();
[1661]291
[1672]292        // again, just to be sure ogre works fine
293        ogreRoot_->_fireFrameEnded(evt); // note: uses the same time as _fireFrameStarted
[1661]294
[1672]295        ++frameCount_;
[1661]296    }
[1686]297
[1891]298    /**
299    @brief
300        Creates the Ogre Root object and sets up the ogre log.
301    */
302    void GSGraphics::setupOgre()
303    {
304        COUT(3) << "Setting up Ogre..." << std::endl;
305
306        // TODO: LogManager doesn't work on oli platform. The why is yet unknown.
307#if ORXONOX_PLATFORM == ORXONOX_PLATFORM_WIN32
308        // create a new logManager
309        ogreLogger_ = new Ogre::LogManager();
310        COUT(4) << "Ogre LogManager created" << std::endl;
311
312        // create our own log that we can listen to
313        Ogre::Log *myLog;
314        if (this->ogreLogFile_ == "")
315            myLog = ogreLogger_->createLog("ogre.log", true, false, true);
316        else
317            myLog = ogreLogger_->createLog(this->ogreLogFile_, true, false, false);
318        COUT(4) << "Ogre Log created" << std::endl;
319
320        myLog->setLogDetail(Ogre::LL_BOREME);
321        myLog->addListener(this);
322#endif
323
324        // Root will detect that we've already created a Log
325        COUT(4) << "Creating Ogre Root..." << std::endl;
326
327        if (ogreConfigFile_ == "")
328        {
329            COUT(2) << "Warning: Ogre config file set to \"\". Defaulting to config.cfg" << std::endl;
330            ModifyConfigValue(ogreConfigFile_, tset, "config.cfg");
331        }
332        if (ogreLogFile_ == "")
333        {
334            COUT(2) << "Warning: Ogre log file set to \"\". Defaulting to ogre.log" << std::endl;
335            ModifyConfigValue(ogreLogFile_, tset, "ogre.log");
336        }
337
338        // check for config file existence because Ogre displays (caught) exceptions if not
339        std::ifstream probe;
340        probe.open(ogreConfigFile_.c_str());
341        if (!probe)
342        {
343            // create a zero sized file
344            std::ofstream creator;
345            creator.open(ogreConfigFile_.c_str());
346            creator.close();
347        }
348        else
349            probe.close();
350
[2509]351        // Leave plugins file empty. We're going to do that part manually later
352        ogreRoot_ = new Ogre::Root("", ogreConfigFile_, ogreLogFile_);
[1891]353
354#if 0 // Ogre 1.4.3 doesn't yet support setDebugOutputEnabled(.)
355#if ORXONOX_PLATFORM != ORXONOX_PLATFORM_WIN32
356        // tame the ogre ouput so we don't get all the mess in the console
357        Ogre::Log* defaultLog = Ogre::LogManager::getSingleton().getDefaultLog();
358        defaultLog->setDebugOutputEnabled(false);
359        defaultLog->setLogDetail(Ogre::LL_BOREME);
360        defaultLog->addListener(this);
361#endif
362#endif
363
364        COUT(3) << "Ogre set up done." << std::endl;
365    }
366
[2509]367    void GSGraphics::loadOgrePlugins()
368    {
369        // just to make sure the next statement doesn't segfault
370        if (ogrePluginsFolder_ == "")
371            ogrePluginsFolder_ = ".";
372
373#if ORXONOX_PLATFORM == ORXONOX_PLATFORM_WIN32
374        convertToWindowsPath(&ogrePluginsFolder_);
375#else
376        convertToUnixPath(&ogrePluginsFolder_);
377#endif
378
379        // Do some SubString magic to get the comma separated list of plugins
380        SubString plugins(ogrePlugins_, ",", " ", false, 92, false, 34, false, 40, 41, false, '\0');
381        for (unsigned int i = 0; i < plugins.size(); ++i)
382            ogreRoot_->loadPlugin(ogrePluginsFolder_ + plugins[i]);
383    }
384
[1686]385    void GSGraphics::declareResources()
386    {
387        CCOUT(4) << "Declaring Resources" << std::endl;
388        //TODO: Specify layout of data file and maybe use xml-loader
389        //TODO: Work with ressource groups (should be generated by a special loader)
390
391        if (resourceFile_ == "")
392        {
393            COUT(2) << "Warning: Ogre resource file set to \"\". Defaulting to resources.cfg" << std::endl;
394            ModifyConfigValue(resourceFile_, tset, "resources.cfg");
395        }
396
397        // Load resource paths from data file using configfile ressource type
398        Ogre::ConfigFile cf;
399        try
400        {
401            cf.load(Settings::getDataPath() + resourceFile_);
402        }
403        catch (...)
404        {
405            //COUT(1) << ex.getFullDescription() << std::endl;
406            COUT(0) << "Have you forgotten to set the data path in orxnox.ini?" << std::endl;
407            throw;
408        }
409
410        // Go through all sections & settings in the file
411        Ogre::ConfigFile::SectionIterator seci = cf.getSectionIterator();
412
413        std::string secName, typeName, archName;
414        while (seci.hasMoreElements())
415        {
416            try
417            {
418                secName = seci.peekNextKey();
419                Ogre::ConfigFile::SettingsMultiMap *settings = seci.getNext();
420                Ogre::ConfigFile::SettingsMultiMap::iterator i;
421                for (i = settings->begin(); i != settings->end(); ++i)
422                {
423                    typeName = i->first; // for instance "FileSystem" or "Zip"
424                    archName = i->second; // name (and location) of archive
425
426                    Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
427                        std::string(Settings::getDataPath() + archName), typeName, secName);
428                }
429            }
430            catch (Ogre::Exception& ex)
431            {
432                COUT(1) << ex.getFullDescription() << std::endl;
433            }
434        }
435    }
436
437    void GSGraphics::loadRenderer()
438    {
439        CCOUT(4) << "Configuring Renderer" << std::endl;
440
441        if (!ogreRoot_->restoreConfig())
442            if (!ogreRoot_->showConfigDialog())
443                ThrowException(InitialisationFailed, "Could not show Ogre configuration dialogue.");
444
445        CCOUT(4) << "Creating render window" << std::endl;
446
[1824]447        this->renderWindow_ = ogreRoot_->initialise(true, "Orxonox");
[1686]448
449        Ogre::WindowEventUtilities::addWindowEventListener(this->renderWindow_, this);
450
[1820]451        Ogre::TextureManager::getSingleton().setDefaultNumMipmaps(0);
[1686]452
453        // create a full screen default viewport
454        this->viewport_ = this->renderWindow_->addViewport(0, 0);
455    }
456
457    void GSGraphics::initialiseResources()
458    {
459        CCOUT(4) << "Initialising resources" << std::endl;
460        //TODO: Do NOT load all the groups, why are we doing that? And do we really do that? initialise != load...
[1824]461        //try
462        //{
[1686]463            Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
464            /*Ogre::StringVector str = Ogre::ResourceGroupManager::getSingleton().getResourceGroups();
465            for (unsigned int i = 0; i < str.size(); i++)
466            {
467            Ogre::ResourceGroupManager::getSingleton().loadResourceGroup(str[i]);
468            }*/
[1824]469        //}
470        //catch (...)
471        //{
472        //    CCOUT(2) << "Error: There was a serious error when initialising the resources." << std::endl;
473        //    throw;
474        //}
[1686]475    }
476
[1891]477    /**
478    @brief
479        Method called by the LogListener interface from Ogre.
480        We use it to capture Ogre log messages and handle it ourselves.
481    @param message
482        The message to be logged
483    @param lml
484        The message level the log is using
485    @param maskDebug
486        If we are printing to the console or not
487    @param logName
488        The name of this log (so you can have several listeners
489        for different logs, and identify them)
490    */
491    void GSGraphics::messageLogged(const std::string& message,
492        Ogre::LogMessageLevel lml, bool maskDebug, const std::string& logName)
493    {
494        int orxonoxLevel;
495        switch (lml)
496        {
497        case Ogre::LML_TRIVIAL:
498            orxonoxLevel = this->ogreLogLevelTrivial_;
499            break;
500        case Ogre::LML_NORMAL:
501            orxonoxLevel = this->ogreLogLevelNormal_;
502            break;
503        case Ogre::LML_CRITICAL:
504            orxonoxLevel = this->ogreLogLevelCritical_;
505            break;
506        default:
507            orxonoxLevel = 0;
508        }
509        OutputHandler::getOutStream().setOutputLevel(orxonoxLevel)
510            << "Ogre: " << message << std::endl;
511    }
[1686]512
513    /**
514    @brief
515        Window has moved.
516    @param rw
517        The render window it occured in
518    */
519    void GSGraphics::windowMoved(Ogre::RenderWindow *rw)
520    {
[1755]521        for (ObjectList<orxonox::WindowEventListener>::iterator it = ObjectList<orxonox::WindowEventListener>::begin(); it; ++it)
[1686]522            it->windowMoved();
523    }
524
525    /**
526    @brief
527        Window has resized.
528    @param rw
529        The render window it occured in
530    @note
531        GraphicsEngine has a render window stored itself. This is the same
532        as rw. But we have to be careful when using multiple render windows!
533    */
534    void GSGraphics::windowResized(Ogre::RenderWindow *rw)
535    {
[1755]536        for (ObjectList<orxonox::WindowEventListener>::iterator it = ObjectList<orxonox::WindowEventListener>::begin(); it; ++it)
[1686]537            it->windowResized(this->renderWindow_->getWidth(), this->renderWindow_->getHeight());
[2087]538
539        // OIS needs this under linux even if we only use relative input measurement.
540        if (this->inputManager_)
541            this->inputManager_->setWindowExtents(renderWindow_->getWidth(), renderWindow_->getHeight());
[1686]542    }
543
544    /**
545    @brief
546        Window focus has changed.
547    @param rw
548        The render window it occured in
549    */
[1878]550    void GSGraphics::windowFocusChange(Ogre::RenderWindow *rw)
[1686]551    {
[1755]552        for (ObjectList<orxonox::WindowEventListener>::iterator it = ObjectList<orxonox::WindowEventListener>::begin(); it; ++it)
[1686]553            it->windowFocusChanged();
[1878]554
555        // instruct InputManager to clear the buffers (core library so we cannot use the interface)
[2087]556        if (this->inputManager_)
557            this->inputManager_->clearBuffers();
[1686]558    }
559
560    /**
561    @brief
562        Window was closed.
563    @param rw
564        The render window it occured in
565    */
566    void GSGraphics::windowClosed(Ogre::RenderWindow *rw)
567    {
568        this->requestState("root");
569    }
570
571    void GSGraphics::printScreen()
572    {
573        if (this->renderWindow_)
574        {
575            this->renderWindow_->writeContentsToTimestampedFile("shot_", ".jpg");
576        }
577    }
[1661]578}
Note: See TracBrowser for help on using the repository browser.