Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 2668 was 2662, checked in by rgrieder, 16 years ago

Merged presentation branch back to trunk.

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