Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

Merged buildsystem3 containing buildsystem2 containing Adi's buildsystem branch back to the trunk.
Please update the media directory if you were not using buildsystem3 before.

  • 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").file_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.file_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.file_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.file_string(), ogreLogFilepath.file_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        for (unsigned int i = 0; i < plugins.size(); ++i)
339            ogreRoot_->loadPlugin((folder / plugins[i]).file_string());
340    }
341
342    void GSGraphics::declareResources()
343    {
344        CCOUT(4) << "Declaring Resources" << std::endl;
345        //TODO: Specify layout of data file and maybe use xml-loader
346        //TODO: Work with ressource groups (should be generated by a special loader)
347
348        if (resourceFile_ == "")
349        {
350            COUT(2) << "Warning: Ogre resource file set to \"\". Defaulting to resources.cfg" << std::endl;
351            ModifyConfigValue(resourceFile_, tset, "resources.cfg");
352        }
353
354        // Load resource paths from data file using configfile ressource type
355        Ogre::ConfigFile cf;
356        try
357        {
358            cf.load((Core::getMediaPath() / resourceFile_).file_string());
359        }
360        catch (...)
361        {
362            //COUT(1) << ex.getFullDescription() << std::endl;
363            COUT(0) << "Have you forgotten to set the data path in orxnox.ini?" << std::endl;
364            throw;
365        }
366
367        // Go through all sections & settings in the file
368        Ogre::ConfigFile::SectionIterator seci = cf.getSectionIterator();
369
370        std::string secName, typeName, archName;
371        while (seci.hasMoreElements())
372        {
373            try
374            {
375                secName = seci.peekNextKey();
376                Ogre::ConfigFile::SettingsMultiMap *settings = seci.getNext();
377                Ogre::ConfigFile::SettingsMultiMap::iterator i;
378                for (i = settings->begin(); i != settings->end(); ++i)
379                {
380                    typeName = i->first; // for instance "FileSystem" or "Zip"
381                    archName = i->second; // name (and location) of archive
382
383                    Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
384                        (Core::getMediaPath() / archName).directory_string(), typeName, secName);
385                }
386            }
387            catch (Ogre::Exception& ex)
388            {
389                COUT(1) << ex.getFullDescription() << std::endl;
390            }
391        }
392    }
393
394    void GSGraphics::loadRenderer()
395    {
396        CCOUT(4) << "Configuring Renderer" << std::endl;
397
398        if (!ogreRoot_->restoreConfig())
399            if (!ogreRoot_->showConfigDialog())
400                ThrowException(InitialisationFailed, "Could not show Ogre configuration dialogue.");
401
402        CCOUT(4) << "Creating render window" << std::endl;
403
404        this->renderWindow_ = ogreRoot_->initialise(true, "Orxonox");
405
406        Ogre::WindowEventUtilities::addWindowEventListener(this->renderWindow_, this);
407
408        Ogre::TextureManager::getSingleton().setDefaultNumMipmaps(0);
409
410        // create a full screen default viewport
411        this->viewport_ = this->renderWindow_->addViewport(0, 0);
412
413        if (this->graphicsEngine_)
414            this->graphicsEngine_->setViewport(this->viewport_);
415    }
416
417    void GSGraphics::initialiseResources()
418    {
419        CCOUT(4) << "Initialising resources" << std::endl;
420        //TODO: Do NOT load all the groups, why are we doing that? And do we really do that? initialise != load...
421        //try
422        //{
423            Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
424            /*Ogre::StringVector str = Ogre::ResourceGroupManager::getSingleton().getResourceGroups();
425            for (unsigned int i = 0; i < str.size(); i++)
426            {
427            Ogre::ResourceGroupManager::getSingleton().loadResourceGroup(str[i]);
428            }*/
429        //}
430        //catch (...)
431        //{
432        //    CCOUT(2) << "Error: There was a serious error when initialising the resources." << std::endl;
433        //    throw;
434        //}
435    }
436
437    /**
438    @brief
439        Method called by the LogListener interface from Ogre.
440        We use it to capture Ogre log messages and handle it ourselves.
441    @param message
442        The message to be logged
443    @param lml
444        The message level the log is using
445    @param maskDebug
446        If we are printing to the console or not
447    @param logName
448        The name of this log (so you can have several listeners
449        for different logs, and identify them)
450    */
451    void GSGraphics::messageLogged(const std::string& message,
452        Ogre::LogMessageLevel lml, bool maskDebug, const std::string& logName)
453    {
454        int orxonoxLevel;
455        switch (lml)
456        {
457        case Ogre::LML_TRIVIAL:
458            orxonoxLevel = this->ogreLogLevelTrivial_;
459            break;
460        case Ogre::LML_NORMAL:
461            orxonoxLevel = this->ogreLogLevelNormal_;
462            break;
463        case Ogre::LML_CRITICAL:
464            orxonoxLevel = this->ogreLogLevelCritical_;
465            break;
466        default:
467            orxonoxLevel = 0;
468        }
469        OutputHandler::getOutStream().setOutputLevel(orxonoxLevel)
470            << "Ogre: " << message << std::endl;
471    }
472
473    /**
474    @brief
475        Window has moved.
476    @param rw
477        The render window it occured in
478    */
479    void GSGraphics::windowMoved(Ogre::RenderWindow *rw)
480    {
481        for (ObjectList<orxonox::WindowEventListener>::iterator it = ObjectList<orxonox::WindowEventListener>::begin(); it; ++it)
482            it->windowMoved();
483    }
484
485    /**
486    @brief
487        Window has resized.
488    @param rw
489        The render window it occured in
490    @note
491        GraphicsEngine has a render window stored itself. This is the same
492        as rw. But we have to be careful when using multiple render windows!
493    */
494    void GSGraphics::windowResized(Ogre::RenderWindow *rw)
495    {
496        for (ObjectList<orxonox::WindowEventListener>::iterator it = ObjectList<orxonox::WindowEventListener>::begin(); it; ++it)
497            it->windowResized(this->renderWindow_->getWidth(), this->renderWindow_->getHeight());
498
499        // OIS needs this under linux even if we only use relative input measurement.
500        if (this->inputManager_)
501            this->inputManager_->setWindowExtents(renderWindow_->getWidth(), renderWindow_->getHeight());
502    }
503
504    /**
505    @brief
506        Window focus has changed.
507    @param rw
508        The render window it occured in
509    */
510    void GSGraphics::windowFocusChange(Ogre::RenderWindow *rw)
511    {
512        for (ObjectList<orxonox::WindowEventListener>::iterator it = ObjectList<orxonox::WindowEventListener>::begin(); it; ++it)
513            it->windowFocusChanged();
514
515        // instruct InputManager to clear the buffers (core library so we cannot use the interface)
516        if (this->inputManager_)
517            this->inputManager_->clearBuffers();
518    }
519
520    /**
521    @brief
522        Window was closed.
523    @param rw
524        The render window it occured in
525    */
526    void GSGraphics::windowClosed(Ogre::RenderWindow *rw)
527    {
528        this->requestState("root");
529    }
530
531    void GSGraphics::printScreen()
532    {
533        if (this->renderWindow_)
534        {
535            this->renderWindow_->writeContentsToTimestampedFile("shot_", ".jpg");
536        }
537    }
538}
Note: See TracBrowser for help on using the repository browser.