Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 2549 was 2549, checked in by rgrieder, 15 years ago

Moved tick time measurement to GSRoot. The values get combined with the one from GSGraphics.

  • Property svn:eol-style set to native
File size: 19.0 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        , debugOverlay_(0)
79    {
80        RegisterRootObject(GSGraphics);
81        setConfigValues();
82    }
83
84    GSGraphics::~GSGraphics()
85    {
86    }
87
88    void GSGraphics::setConfigValues()
89    {
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.");
106    }
107
108    void GSGraphics::enter()
109    {
110        Core::setShowsGraphics(true);
111
112        // initialise graphics engine. Doesn't load the render window yet!
113        graphicsEngine_ = new GraphicsEngine();
114
115        // Ogre setup procedure
116        setupOgre();
117        this->declareResources();
118        this->loadRenderer();    // creates the render window
119        // TODO: Spread this so that this call only initialises things needed for the Console and GUI
120        this->initialiseResources();
121
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);
126
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_);
131
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();
135        size_t windowHnd = 0;
136        this->renderWindow_->getCustomAttribute("WINDOW", &windowHnd);
137        inputManager_->initialise(windowHnd, renderWindow_->getWidth(), renderWindow_->getHeight(), true);
138        // Configure master input state with a KeyBinder
139        masterKeyBinder_ = new KeyBinder();
140        masterKeyBinder_->loadBindings("masterKeybindings.ini", defaultMasterKeybindings_);
141        inputManager_->getMasterInputState()->addKeyHandler(masterKeyBinder_);
142
143        // Load the InGameConsole
144        console_ = new InGameConsole();
145        console_->initialise(this->renderWindow_->getWidth(), this->renderWindow_->getHeight());
146
147        // load the CEGUI interface
148        guiManager_ = new GUIManager();
149        guiManager_->initialise(this->renderWindow_);
150
151        // add console commands
152        FunctorMember<GSGraphics>* functor1 = createFunctor(&GSGraphics::printScreen);
153        functor1->setObject(this);
154        ccPrintScreen_ = createConsoleCommand(functor1, "printScreen");
155        CommandExecutor::addConsoleCommandShortcut(ccPrintScreen_);
156    }
157
158    void GSGraphics::leave()
159    {
160        using namespace Ogre;
161
162        delete this->ccPrintScreen_;
163
164        // remove our WindowEventListener first to avoid bad calls after the window has been destroyed
165        Ogre::WindowEventUtilities::removeWindowEventListener(this->renderWindow_, this);
166
167        delete this->guiManager_;
168
169        delete this->console_;
170
171        //inputManager_->getMasterInputState()->removeKeyHandler(this->masterKeyBinder_);
172        delete this->masterKeyBinder_;
173        delete this->inputManager_;
174
175        Loader::unload(this->debugOverlay_);
176        delete this->debugOverlay_;
177
178        // unload all compositors
179        Ogre::CompositorManager::getSingleton().removeAll();
180
181        // destroy render window
182        RenderSystem* renderer = this->ogreRoot_->getRenderSystem();
183        renderer->destroyRenderWindow("Orxonox");
184
185        /*** CODE SNIPPET, UNUSED ***/
186        // Does the opposite of initialise()
187        //ogreRoot_->shutdown();
188        // Remove all resources and resource groups
189        //StringVector groups = ResourceGroupManager::getSingleton().getResourceGroups();
190        //for (StringVector::iterator it = groups.begin(); it != groups.end(); ++it)
191        //{
192        //    ResourceGroupManager::getSingleton().destroyResourceGroup(*it);
193        //}
194
195        //ParticleSystemManager::getSingleton().removeAllTemplates();
196
197        // Shutdown the render system
198        //this->ogreRoot_->setRenderSystem(0);
199
200        delete this->ogreRoot_;
201
202#if ORXONOX_PLATFORM == ORXONOX_PLATFORM_WIN32
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_;
207#endif
208
209        delete graphicsEngine_;
210
211        Core::setShowsGraphics(false);
212    }
213
214    /**
215    @note
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    */
222    void GSGraphics::ticked(const Clock& time)
223    {
224        uint64_t timeBeforeTick = time.getRealMicroseconds();
225
226        float dt = time.getDeltaTime();
227
228        this->inputManager_->tick(dt);
229        // tick console
230        this->console_->tick(dt);
231        this->tickChild(time);
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
240        uint64_t timeAfterTick = time.getRealMicroseconds();
241
242        // Also add our tick time to the list in GSRoot
243        this->getParent()->addTickTime(timeAfterTick - timeBeforeTick);
244
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());
248
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);
255
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)
261        this->renderWindow_->setActive(true);
262
263        // render
264        ogreRoot_->_updateAllRenderTargets();
265
266        // again, just to be sure ogre works fine
267        ogreRoot_->_fireFrameEnded(evt); // note: uses the same time as _fireFrameStarted
268    }
269
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.
279#if ORXONOX_PLATFORM == ORXONOX_PLATFORM_WIN32
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);
294#endif
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
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
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
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    {
497        for (ObjectList<orxonox::WindowEventListener>::iterator it = ObjectList<orxonox::WindowEventListener>::begin(); it; ++it)
498            it->windowResized(this->renderWindow_->getWidth(), this->renderWindow_->getHeight());
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());
503    }
504
505    /**
506    @brief
507        Window focus has changed.
508    @param rw
509        The render window it occured in
510    */
511    void GSGraphics::windowFocusChange(Ogre::RenderWindow *rw)
512    {
513        for (ObjectList<orxonox::WindowEventListener>::iterator it = ObjectList<orxonox::WindowEventListener>::begin(); it; ++it)
514            it->windowFocusChanged();
515
516        // instruct InputManager to clear the buffers (core library so we cannot use the interface)
517        if (this->inputManager_)
518            this->inputManager_->clearBuffers();
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    }
539}
Note: See TracBrowser for help on using the repository browser.