Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/presentation/src/orxonox/gamestates/GSGraphics.cc @ 2548

Last change on this file since 2548 was 2548, checked in by scheusso, 16 years ago

Statistics of GSGraphics have a config value more now: avgLength
It defines the time period/age of the statistics data (fps, ticktime)

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