Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/objecthierarchy2/src/orxonox/gamestates/GSGraphics.cc @ 2518

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