Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

Fixed install target:

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