Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/questsystem5/src/orxonox/gamestates/GSGraphics.cc @ 3016

Last change on this file since 3016 was 2908, checked in by dafrick, 16 years ago

Reverted to revision 2906 (because I'm too stupid to merge correctly, 2nd try will follow shortly. ;))

  • Property svn:eol-style set to native
File size: 19.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:
[2908]25 *      ...
[1661]26 *
27 */
28
29#include "OrxonoxStableHeaders.h"
30#include "GSGraphics.h"
31
[2908]32#include <fstream>
[2710]33#include <boost/filesystem.hpp>
[2908]34
35#include <OgreCompositorManager.h>
36#include <OgreConfigFile.h>
37#include <OgreFrameListener.h>
38#include <OgreRoot.h>
39#include <OgreLogManager.h>
40#include <OgreException.h>
[1686]41#include <OgreRenderWindow.h>
[2908]42#include <OgreRenderSystem.h>
43#include <OgreTextureManager.h>
44#include <OgreViewport.h>
45#include <OgreWindowEventUtilities.h>
[1661]46
[2908]47#include "SpecialConfig.h"
[1755]48#include "util/Debug.h"
[2908]49#include "util/Exception.h"
50#include "util/String.h"
51#include "util/SubString.h"
52#include "core/ConsoleCommand.h"
[2907]53#include "core/ConfigValueIncludes.h"
[2908]54#include "core/CoreIncludes.h"
[2907]55#include "core/Core.h"
[1661]56#include "core/input/InputManager.h"
[1788]57#include "core/input/KeyBinder.h"
[2908]58#include "core/input/ExtendedInputState.h"
[2087]59#include "core/Loader.h"
60#include "core/XMLFile.h"
[1661]61#include "overlays/console/InGameConsole.h"
62#include "gui/GUIManager.h"
[2908]63#include "tools/WindowEventListener.h"
[1686]64
[2908]65// for compatibility
66#include "GraphicsEngine.h"
67
[1661]68namespace orxonox
69{
[2908]70    GSGraphics::GSGraphics()
71        : GameState<GSRoot>("graphics")
72        , renderWindow_(0)
73        , viewport_(0)
74        , bWindowEventListenerUpdateRequired_(false)
[1661]75        , inputManager_(0)
76        , console_(0)
77        , guiManager_(0)
[2908]78        , ogreRoot_(0)
79        , ogreLogger_(0)
80        , graphicsEngine_(0)
[1788]81        , masterKeyBinder_(0)
[2087]82        , debugOverlay_(0)
[1661]83    {
[1686]84        RegisterRootObject(GSGraphics);
[2908]85        setConfigValues();
[1661]86    }
87
88    GSGraphics::~GSGraphics()
89    {
90    }
91
92    void GSGraphics::setConfigValues()
93    {
[2908]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(ogrePluginsFolder_, ORXONOX_OGRE_PLUGINS_FOLDER)
99            .description("Folder where the Ogre plugins are located.");
100        SetConfigValue(ogrePlugins_, ORXONOX_OGRE_PLUGINS)
101            .description("Comma separated list of all plugins to load.");
102        SetConfigValue(ogreLogFile_,     "ogre.log")
103            .description("Logfile for messages from Ogre. Use \"\" to suppress log file creation.");
104        SetConfigValue(ogreLogLevelTrivial_ , 5)
105            .description("Corresponding orxonox debug level for ogre Trivial");
106        SetConfigValue(ogreLogLevelNormal_  , 4)
107            .description("Corresponding orxonox debug level for ogre Normal");
108        SetConfigValue(ogreLogLevelCritical_, 2)
109            .description("Corresponding orxonox debug level for ogre Critical");
[1661]110    }
111
[2908]112    void GSGraphics::enter()
[1661]113    {
[2908]114        Core::setShowsGraphics(true);
[1696]115
[2908]116        // initialise graphics engine. Doesn't load the render window yet!
117        graphicsEngine_ = new GraphicsEngine();
[1674]118
[2908]119        // Ogre setup procedure
120        setupOgre();
121        // load all the required plugins for Ogre
122        loadOgrePlugins();
123        // read resource declaration file
124        this->declareResources();
125        // Reads ogre config and creates the render window
126        this->loadRenderer();
[2710]127
[2908]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
[2087]136        // load debug overlay
137        COUT(3) << "Loading Debug Overlay..." << std::endl;
[2759]138        this->debugOverlay_ = new XMLFile((Core::getMediaPath() / "overlay" / "debug.oxo").string());
[2087]139        Loader::open(debugOverlay_);
[1686]140
[2908]141        // Calls the InputManager which sets up the input devices.
[2907]142        // The render window width and height are used to set up the mouse movement.
[2908]143        inputManager_ = new InputManager();
[2907]144        size_t windowHnd = 0;
[2908]145        this->renderWindow_->getCustomAttribute("WINDOW", &windowHnd);
146        inputManager_->initialise(windowHnd, renderWindow_->getWidth(), renderWindow_->getHeight(), true);
147        // Configure master input state with a KeyBinder
[2103]148        masterKeyBinder_ = new KeyBinder();
[2710]149        masterKeyBinder_->loadBindings("masterKeybindings.ini");
[2908]150        inputManager_->getMasterInputState()->addKeyHandler(masterKeyBinder_);
[1661]151
152        // Load the InGameConsole
153        console_ = new InGameConsole();
[2908]154        console_->initialise(this->renderWindow_->getWidth(), this->renderWindow_->getHeight());
[1661]155
156        // load the CEGUI interface
157        guiManager_ = new GUIManager();
[2908]158        guiManager_->initialise(this->renderWindow_);
[1674]159
[2908]160        // add console commands
161        FunctorMember<GSGraphics>* functor1 = createFunctor(&GSGraphics::printScreen);
162        functor1->setObject(this);
163        ccPrintScreen_ = createConsoleCommand(functor1, "printScreen");
164        CommandExecutor::addConsoleCommandShortcut(ccPrintScreen_);
[1661]165    }
166
[2908]167    void GSGraphics::leave()
[1661]168    {
[2908]169        using namespace Ogre;
[1824]170
[2908]171        delete this->ccPrintScreen_;
[2662]172
[2908]173        // remove our WindowEventListener first to avoid bad calls after the window has been destroyed
174        Ogre::WindowEventUtilities::removeWindowEventListener(this->renderWindow_, this);
[1878]175
[1662]176        delete this->guiManager_;
[2908]177
[1662]178        delete this->console_;
[1661]179
[2908]180        //inputManager_->getMasterInputState()->removeKeyHandler(this->masterKeyBinder_);
181        delete this->masterKeyBinder_;
182        delete this->inputManager_;
183
[2087]184        Loader::unload(this->debugOverlay_);
185        delete this->debugOverlay_;
186
[2908]187        // unload all compositors
188        Ogre::CompositorManager::getSingleton().removeAll();
[2662]189
[2908]190        // destroy render window
191        RenderSystem* renderer = this->ogreRoot_->getRenderSystem();
192        renderer->destroyRenderWindow("Orxonox");
[1696]193
[2908]194        /*** CODE SNIPPET, UNUSED ***/
195        // Does the opposite of initialise()
196        //ogreRoot_->shutdown();
197        // Remove all resources and resource groups
198        //StringVector groups = ResourceGroupManager::getSingleton().getResourceGroups();
199        //for (StringVector::iterator it = groups.begin(); it != groups.end(); ++it)
200        //{
201        //    ResourceGroupManager::getSingleton().destroyResourceGroup(*it);
202        //}
[1824]203
[2908]204        //ParticleSystemManager::getSingleton().removeAllTemplates();
[1824]205
[2908]206        // Shutdown the render system
207        //this->ogreRoot_->setRenderSystem(0);
208
209        delete this->ogreRoot_;
210
211        // delete the ogre log and the logManager (since we have created it).
212        this->ogreLogger_->getDefaultLog()->removeListener(this);
213        this->ogreLogger_->destroyLog(Ogre::LogManager::getSingleton().getDefaultLog());
214        delete this->ogreLogger_;
215
216        delete graphicsEngine_;
217
218        Core::setShowsGraphics(false);
[1661]219    }
220
[1662]221    /**
[2662]222    @note
[1662]223        A note about the Ogre::FrameListener: Even though we don't use them,
224        they still get called. However, the delta times are not correct (except
225        for timeSinceLastFrame, which is the most important). A little research
226        as shown that there is probably only one FrameListener that doesn't even
227        need the time. So we shouldn't run into problems.
228    */
[2908]229    void GSGraphics::ticked(const Clock& time)
[1661]230    {
[2908]231        uint64_t timeBeforeTick = time.getRealMicroseconds();
232
233        float dt = time.getDeltaTime();
234
235        this->inputManager_->tick(dt);
236        // tick console
237        this->console_->tick(dt);
238        this->tickChild(time);
239
240        if (this->bWindowEventListenerUpdateRequired_)
[2087]241        {
[2908]242            // Update all WindowEventListeners for the case a new one was created.
243            this->windowResized(this->renderWindow_);
244            this->bWindowEventListenerUpdateRequired_ = false;
[2087]245        }
246
[2908]247        uint64_t timeAfterTick = time.getRealMicroseconds();
[1661]248
[2908]249        // Also add our tick time to the list in GSRoot
250        this->getParent()->addTickTime(timeAfterTick - timeBeforeTick);
[1661]251
[2908]252        // Update statistics overlay. Note that the values only change periodically in GSRoot.
253        GraphicsEngine::getInstance().setAverageFramesPerSecond(this->getParent()->getAvgFPS());
254        GraphicsEngine::getInstance().setAverageTickTime(this->getParent()->getAvgTickTime());
[1661]255
[2908]256        // don't forget to call _fireFrameStarted in ogre to make sure
257        // everything goes smoothly
258        Ogre::FrameEvent evt;
259        evt.timeSinceLastFrame = dt;
260        evt.timeSinceLastEvent = dt; // note: same time, but shouldn't matter anyway
261        ogreRoot_->_fireFrameStarted(evt);
[1661]262
[2908]263        // Pump messages in all registered RenderWindows
264        // This calls the WindowEventListener objects.
265        Ogre::WindowEventUtilities::messagePump();
266        // make sure the window stays active even when not focused
267        // (probably only necessary on windows)
268        this->renderWindow_->setActive(true);
269
270        // render
271        ogreRoot_->_updateAllRenderTargets();
272
273        // again, just to be sure ogre works fine
274        ogreRoot_->_fireFrameEnded(evt); // note: uses the same time as _fireFrameStarted
[1661]275    }
[1686]276
[1891]277    /**
278    @brief
[2908]279        Creates the Ogre Root object and sets up the ogre log.
280    */
281    void GSGraphics::setupOgre()
282    {
283        COUT(3) << "Setting up Ogre..." << std::endl;
284
285        if (ogreConfigFile_ == "")
286        {
287            COUT(2) << "Warning: Ogre config file set to \"\". Defaulting to config.cfg" << std::endl;
288            ModifyConfigValue(ogreConfigFile_, tset, "config.cfg");
289        }
290        if (ogreLogFile_ == "")
291        {
292            COUT(2) << "Warning: Ogre log file set to \"\". Defaulting to ogre.log" << std::endl;
293            ModifyConfigValue(ogreLogFile_, tset, "ogre.log");
294        }
295
296        boost::filesystem::path ogreConfigFilepath(Core::getConfigPath() / this->ogreConfigFile_);
297        boost::filesystem::path ogreLogFilepath(Core::getLogPath() / this->ogreLogFile_);
298
299        // create a new logManager
300        // Ogre::Root will detect that we've already created a Log
301        ogreLogger_ = new Ogre::LogManager();
302        COUT(4) << "Ogre LogManager created" << std::endl;
303
304        // create our own log that we can listen to
305        Ogre::Log *myLog;
306        myLog = ogreLogger_->createLog(ogreLogFilepath.string(), true, false, false);
307        COUT(4) << "Ogre Log created" << std::endl;
308
309        myLog->setLogDetail(Ogre::LL_BOREME);
310        myLog->addListener(this);
311
312        COUT(4) << "Creating Ogre Root..." << std::endl;
313
314        // check for config file existence because Ogre displays (caught) exceptions if not
315        if (!boost::filesystem::exists(ogreConfigFilepath))
316        {
317            // create a zero sized file
318            std::ofstream creator;
319            creator.open(ogreConfigFilepath.string().c_str());
320            creator.close();
321        }
322
323        // Leave plugins file empty. We're going to do that part manually later
324        ogreRoot_ = new Ogre::Root("", ogreConfigFilepath.string(), ogreLogFilepath.string());
325
326        COUT(3) << "Ogre set up done." << std::endl;
327    }
328
329    void GSGraphics::loadOgrePlugins()
330    {
331        // just to make sure the next statement doesn't segfault
332        if (ogrePluginsFolder_ == "")
333            ogrePluginsFolder_ = ".";
334
335        boost::filesystem::path folder(ogrePluginsFolder_);
336        // Do some SubString magic to get the comma separated list of plugins
337        SubString plugins(ogrePlugins_, ",", " ", false, 92, false, 34, false, 40, 41, false, '\0');
338        // Use backslash paths on Windows! file_string() already does that though.
339        for (unsigned int i = 0; i < plugins.size(); ++i)
340            ogreRoot_->loadPlugin((folder / plugins[i]).file_string());
341    }
342
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((Core::getMediaPath() / resourceFile_).string());
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                        (Core::getMediaPath() / archName).string(), 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
405        this->renderWindow_ = ogreRoot_->initialise(true, "Orxonox");
406
407        Ogre::WindowEventUtilities::addWindowEventListener(this->renderWindow_, this);
408
409        Ogre::TextureManager::getSingleton().setDefaultNumMipmaps(0);
410
411        // create a full screen default viewport
412        this->viewport_ = this->renderWindow_->addViewport(0, 0);
413
414        if (this->graphicsEngine_)
415            this->graphicsEngine_->setViewport(this->viewport_);
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...
422        //try
423        //{
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            }*/
430        //}
431        //catch (...)
432        //{
433        //    CCOUT(2) << "Error: There was a serious error when initialising the resources." << std::endl;
434        //    throw;
435        //}
436    }
437
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    }
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    {
482        for (ObjectList<orxonox::WindowEventListener>::iterator it = ObjectList<orxonox::WindowEventListener>::begin(); it; ++it)
483            it->windowMoved();
484    }
485
486    /**
487    @brief
[1686]488        Window has resized.
489    @param rw
490        The render window it occured in
491    @note
[2908]492        GraphicsEngine has a render window stored itself. This is the same
[1686]493        as rw. But we have to be careful when using multiple render windows!
494    */
[2908]495    void GSGraphics::windowResized(Ogre::RenderWindow *rw)
[1686]496    {
[2908]497        for (ObjectList<orxonox::WindowEventListener>::iterator it = ObjectList<orxonox::WindowEventListener>::begin(); it; ++it)
498            it->windowResized(this->renderWindow_->getWidth(), this->renderWindow_->getHeight());
499
[2087]500        // OIS needs this under linux even if we only use relative input measurement.
501        if (this->inputManager_)
[2908]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    */
[2908]511    void GSGraphics::windowFocusChange(Ogre::RenderWindow *rw)
[1686]512    {
[2908]513        for (ObjectList<orxonox::WindowEventListener>::iterator it = ObjectList<orxonox::WindowEventListener>::begin(); it; ++it)
514            it->windowFocusChanged();
515
[1878]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
[2908]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.