Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/buildsystem3/src/orxonox/gamestates/GSGraphics.cc @ 2664

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

Merged buildsystem2 to buildsystem3.

Note: Bare merge, just resolved conflicts. To testing, no nothing.

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