Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/orxonox/gamestates/GSGraphics.cc @ 2173

Last change on this file since 2173 was 2171, checked in by landauf, 16 years ago

merged revisions 2111-2170 from objecthierarchy branch back to trunk.

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