Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/buildsystem2/src/orxonox/gamestates/GSGraphics.cc @ 2639

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

Cleanup in OrxonoxConfig.h.in. Made use of various CMake features like CheckInclude or CheckCompiles to determine some options and macros in the config header file.

Also removed util/Integers.h and placed the code directory in OrxonoxConfig.h.in.

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