Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 2992 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
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 <OgreCompositorManager.h>
36#include <OgreConfigFile.h>
37#include <OgreFrameListener.h>
38#include <OgreRoot.h>
39#include <OgreLogManager.h>
40#include <OgreException.h>
41#include <OgreRenderWindow.h>
42#include <OgreRenderSystem.h>
43#include <OgreTextureManager.h>
44#include <OgreViewport.h>
45#include <OgreWindowEventUtilities.h>
46
47#include "SpecialConfig.h"
48#include "util/Debug.h"
49#include "util/Exception.h"
50#include "util/String.h"
51#include "util/SubString.h"
52#include "core/ConsoleCommand.h"
53#include "core/ConfigValueIncludes.h"
54#include "core/CoreIncludes.h"
55#include "core/Core.h"
56#include "core/input/InputManager.h"
57#include "core/input/KeyBinder.h"
58#include "core/input/ExtendedInputState.h"
59#include "core/Loader.h"
60#include "core/XMLFile.h"
61#include "overlays/console/InGameConsole.h"
62#include "gui/GUIManager.h"
63#include "tools/WindowEventListener.h"
64
65// for compatibility
66#include "GraphicsEngine.h"
67
68namespace orxonox
69{
70    GSGraphics::GSGraphics()
71        : GameState<GSRoot>("graphics")
72        , renderWindow_(0)
73        , viewport_(0)
74        , bWindowEventListenerUpdateRequired_(false)
75        , inputManager_(0)
76        , console_(0)
77        , guiManager_(0)
78        , ogreRoot_(0)
79        , ogreLogger_(0)
80        , graphicsEngine_(0)
81        , masterKeyBinder_(0)
82        , debugOverlay_(0)
83    {
84        RegisterRootObject(GSGraphics);
85        setConfigValues();
86    }
87
88    GSGraphics::~GSGraphics()
89    {
90    }
91
92    void GSGraphics::setConfigValues()
93    {
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");
110    }
111
112    void GSGraphics::enter()
113    {
114        Core::setShowsGraphics(true);
115
116        // initialise graphics engine. Doesn't load the render window yet!
117        graphicsEngine_ = new GraphicsEngine();
118
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();
127
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
136        // load debug overlay
137        COUT(3) << "Loading Debug Overlay..." << std::endl;
138        this->debugOverlay_ = new XMLFile((Core::getMediaPath() / "overlay" / "debug.oxo").string());
139        Loader::open(debugOverlay_);
140
141        // Calls the InputManager which sets up the input devices.
142        // The render window width and height are used to set up the mouse movement.
143        inputManager_ = new InputManager();
144        size_t windowHnd = 0;
145        this->renderWindow_->getCustomAttribute("WINDOW", &windowHnd);
146        inputManager_->initialise(windowHnd, renderWindow_->getWidth(), renderWindow_->getHeight(), true);
147        // Configure master input state with a KeyBinder
148        masterKeyBinder_ = new KeyBinder();
149        masterKeyBinder_->loadBindings("masterKeybindings.ini");
150        inputManager_->getMasterInputState()->addKeyHandler(masterKeyBinder_);
151
152        // Load the InGameConsole
153        console_ = new InGameConsole();
154        console_->initialise(this->renderWindow_->getWidth(), this->renderWindow_->getHeight());
155
156        // load the CEGUI interface
157        guiManager_ = new GUIManager();
158        guiManager_->initialise(this->renderWindow_);
159
160        // add console commands
161        FunctorMember<GSGraphics>* functor1 = createFunctor(&GSGraphics::printScreen);
162        functor1->setObject(this);
163        ccPrintScreen_ = createConsoleCommand(functor1, "printScreen");
164        CommandExecutor::addConsoleCommandShortcut(ccPrintScreen_);
165    }
166
167    void GSGraphics::leave()
168    {
169        using namespace Ogre;
170
171        delete this->ccPrintScreen_;
172
173        // remove our WindowEventListener first to avoid bad calls after the window has been destroyed
174        Ogre::WindowEventUtilities::removeWindowEventListener(this->renderWindow_, this);
175
176        delete this->guiManager_;
177
178        delete this->console_;
179
180        //inputManager_->getMasterInputState()->removeKeyHandler(this->masterKeyBinder_);
181        delete this->masterKeyBinder_;
182        delete this->inputManager_;
183
184        Loader::unload(this->debugOverlay_);
185        delete this->debugOverlay_;
186
187        // unload all compositors
188        Ogre::CompositorManager::getSingleton().removeAll();
189
190        // destroy render window
191        RenderSystem* renderer = this->ogreRoot_->getRenderSystem();
192        renderer->destroyRenderWindow("Orxonox");
193
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        //}
203
204        //ParticleSystemManager::getSingleton().removeAllTemplates();
205
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);
219    }
220
221    /**
222    @note
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    */
229    void GSGraphics::ticked(const Clock& time)
230    {
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_)
241        {
242            // Update all WindowEventListeners for the case a new one was created.
243            this->windowResized(this->renderWindow_);
244            this->bWindowEventListenerUpdateRequired_ = false;
245        }
246
247        uint64_t timeAfterTick = time.getRealMicroseconds();
248
249        // Also add our tick time to the list in GSRoot
250        this->getParent()->addTickTime(timeAfterTick - timeBeforeTick);
251
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());
255
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);
262
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
275    }
276
277    /**
278    @brief
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
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.