Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 1967 was 1891, checked in by rgrieder, 16 years ago

Moved all Ogre related code from GSRoot to GSGraphics.
You should now be able to start the gui, goto ioConsole, then start the gui again and load the level.
gui —> level —> gui doesn't yet work. But I will not dig into that until our object hierarchy has been replaced.

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