Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/objecthierarchy/src/orxonox/gamestates/GSGraphics.cc @ 2007

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

Fixed the linux mouse movement bug. OIS has to warp the pointer to the middle of the screen. That means it needs to know the screen extents after all.

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