Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/libraries/core/GraphicsManager.cc @ 6024

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

Merged core5 branch back to the trunk.
Key features include clean level unloading and an extended XML event system.

Two important notes:
Delete your keybindings.ini files! * or you will still get parser errors when loading the key bindings.
Delete build_dir/lib/modules/libgamestates.module! * or orxonox won't start.
Best thing to do is to delete the build folder ;)

  • Property svn:eol-style set to native
File size: 16.8 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 *      Benjamin Knecht <beni_at_orxonox.net>, (C) 2007
25 *   Co-authors:
26 *      Felix Schulthess
27 *
28 */
29
30#include "GraphicsManager.h"
31
32#include <fstream>
33#include <sstream>
34#include <boost/filesystem.hpp>
35#include <boost/shared_array.hpp>
36
37#include <OgreArchiveFactory.h>
38#include <OgreArchiveManager.h>
39#include <OgreFrameListener.h>
40#include <OgreRoot.h>
41#include <OgreLogManager.h>
42#include <OgreRenderWindow.h>
43#include <OgreRenderSystem.h>
44#include <OgreResourceGroupManager.h>
45#include <OgreTextureManager.h>
46#include <OgreViewport.h>
47#include <OgreWindowEventUtilities.h>
48
49#include "SpecialConfig.h"
50#include "util/Clock.h"
51#include "util/Exception.h"
52#include "util/StringUtils.h"
53#include "util/SubString.h"
54#include "ConsoleCommand.h"
55#include "ConfigValueIncludes.h"
56#include "CoreIncludes.h"
57#include "Game.h"
58#include "GameMode.h"
59#include "Loader.h"
60#include "MemoryArchive.h"
61#include "PathConfig.h"
62#include "WindowEventListener.h"
63#include "XMLFile.h"
64
65namespace orxonox
66{
67    class OgreWindowEventListener : public Ogre::WindowEventListener
68    {
69    public:
70        void windowResized     (Ogre::RenderWindow* rw)
71            { orxonox::WindowEventListener::resizeWindow(rw->getWidth(), rw->getHeight()); }
72        void windowFocusChange (Ogre::RenderWindow* rw)
73            { orxonox::WindowEventListener::changeWindowFocus(); }
74        void windowClosed      (Ogre::RenderWindow* rw)
75            { orxonox::Game::getInstance().stop(); }
76        void windowMoved       (Ogre::RenderWindow* rw)
77            { orxonox::WindowEventListener::moveWindow(); }
78    };
79
80    GraphicsManager* GraphicsManager::singletonPtr_s = 0;
81
82    /**
83    @brief
84        Non-initialising constructor.
85    */
86    GraphicsManager::GraphicsManager(bool bLoadRenderer)
87        : ogreWindowEventListener_(new OgreWindowEventListener())
88#if OGRE_VERSION < 0x010600
89        , memoryArchiveFactory_(new MemoryArchiveFactory())
90#endif
91        , renderWindow_(0)
92        , viewport_(0)
93    {
94        RegisterObject(GraphicsManager);
95
96        this->setConfigValues();
97
98        // Ogre setup procedure (creating Ogre::Root)
99        this->loadOgreRoot();
100        // load all the required plugins for Ogre
101        this->loadOgrePlugins();
102
103        // At first, add the root paths of the data directories as resource locations
104        Ogre::ResourceGroupManager::getSingleton().addResourceLocation(PathConfig::getDataPathString(), "FileSystem", "dataRoot", false);
105        // Load resources
106        resources_.reset(new XMLFile("resources.oxr", "dataRoot"));
107        resources_->setLuaSupport(false);
108        Loader::open(resources_.get());
109
110        // Only for development runs
111        if (PathConfig::isDevelopmentRun())
112        {
113            Ogre::ResourceGroupManager::getSingleton().addResourceLocation(PathConfig::getExternalDataPathString(), "FileSystem", "externalDataRoot", false);
114            extResources_.reset(new XMLFile("resources.oxr", "externalDataRoot"));
115            extResources_->setLuaSupport(false);
116            Loader::open(extResources_.get());
117        }
118
119        if (bLoadRenderer)
120        {
121            // Reads the ogre config and creates the render window
122            this->upgradeToGraphics();
123        }
124    }
125
126    /**
127    @brief
128        Destruction is done by the member scoped_ptrs.
129    */
130    GraphicsManager::~GraphicsManager()
131    {
132        Loader::unload(debugOverlay_.get());
133
134        Ogre::WindowEventUtilities::removeWindowEventListener(renderWindow_, ogreWindowEventListener_.get());
135        // TODO: Destroy the console command
136
137        // Undeclare the resources
138        Loader::unload(resources_.get());
139        if (PathConfig::isDevelopmentRun())
140            Loader::unload(extResources_.get());
141    }
142
143    void GraphicsManager::setConfigValues()
144    {
145        SetConfigValue(ogreConfigFile_,  "ogre.cfg")
146            .description("Location of the Ogre config file");
147        SetConfigValue(ogrePluginsDirectory_, specialConfig::ogrePluginsDirectory)
148            .description("Folder where the Ogre plugins are located.");
149        SetConfigValue(ogrePlugins_, specialConfig::ogrePlugins)
150            .description("Comma separated list of all plugins to load.");
151        SetConfigValue(ogreLogFile_,     "ogre.log")
152            .description("Logfile for messages from Ogre. Use \"\" to suppress log file creation.");
153        SetConfigValue(ogreLogLevelTrivial_ , 5)
154            .description("Corresponding orxonox debug level for ogre Trivial");
155        SetConfigValue(ogreLogLevelNormal_  , 4)
156            .description("Corresponding orxonox debug level for ogre Normal");
157        SetConfigValue(ogreLogLevelCritical_, 2)
158            .description("Corresponding orxonox debug level for ogre Critical");
159    }
160
161    /**
162    @brief
163        Loads the renderer and creates the render window if not yet done so.
164    @remarks
165        This operation is irreversible without recreating the GraphicsManager!
166        So if it throws you HAVE to recreate the GraphicsManager!!!
167        It therefore offers almost no exception safety.
168    */
169    void GraphicsManager::upgradeToGraphics()
170    {
171        if (renderWindow_ != NULL)
172            return;
173
174        this->loadRenderer();
175
176#if OGRE_VERSION < 0x010600
177        // WORKAROUND: There is an incompatibility for particle scripts when trying
178        // to support both Ogre 1.4 and 1.6. The hacky solution is to create
179        // scripts for the 1.6 version and then remove the inserted "particle_system"
180        // keyword. But we need to supply these new scripts as well, which is why
181        // there is an extra Ogre::Archive dealing with it in the memory.
182        using namespace Ogre;
183        ArchiveManager::getSingleton().addArchiveFactory(memoryArchiveFactory_.get());
184        const StringVector& groups = ResourceGroupManager::getSingleton().getResourceGroups();
185        // Travers all groups
186        for (StringVector::const_iterator itGroup = groups.begin(); itGroup != groups.end(); ++itGroup)
187        {
188            FileInfoListPtr files = ResourceGroupManager::getSingleton().findResourceFileInfo(*itGroup, "*.particle");
189            for (FileInfoList::const_iterator itFile = files->begin(); itFile != files->end(); ++itFile)
190            {
191                // open file
192                Ogre::DataStreamPtr input = ResourceGroupManager::getSingleton().openResource(itFile->filename, *itGroup, false);
193                std::stringstream output;
194                // Parse file and replace "particle_system" with nothing
195                while (!input->eof())
196                {
197                    std::string line = input->getLine();
198                    size_t pos = line.find("particle_system");
199                    if (pos != std::string::npos)
200                    {
201                        // 15 is the length of "particle_system"
202                        line.replace(pos, 15, "");
203                    }
204                    output << line << std::endl;
205                }
206                // Add file to the memory archive
207                shared_array<char> data(new char[output.str().size()]);
208                // Debug optimisations
209                const std::string outputStr = output.str();
210                char* rawData = data.get();
211                for (unsigned i = 0; i < outputStr.size(); ++i)
212                    rawData[i] = outputStr[i];
213                MemoryArchive::addFile("particle_scripts_ogre_1.4_" + *itGroup, itFile->filename, data, output.str().size());
214            }
215            if (!files->empty())
216            {
217                // Declare the files, but using a new group
218                ResourceGroupManager::getSingleton().addResourceLocation("particle_scripts_ogre_1.4_" + *itGroup,
219                    "Memory", "particle_scripts_ogre_1.4_" + *itGroup);
220            }
221        }
222#endif
223
224        // Initialise all resources (do this AFTER the renderer has been loaded!)
225        // Note: You can only do this once! Ogre will check whether a resource group has
226        // already been initialised. If you need to load resources later, you will have to
227        // choose another resource group.
228        Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
229    }
230
231    /**
232    @brief
233        Creates the Ogre Root object and sets up the ogre log.
234    */
235    void GraphicsManager::loadOgreRoot()
236    {
237        COUT(3) << "Setting up Ogre..." << std::endl;
238
239        if (ogreConfigFile_ == "")
240        {
241            COUT(2) << "Warning: Ogre config file set to \"\". Defaulting to config.cfg" << std::endl;
242            ModifyConfigValue(ogreConfigFile_, tset, "config.cfg");
243        }
244        if (ogreLogFile_ == "")
245        {
246            COUT(2) << "Warning: Ogre log file set to \"\". Defaulting to ogre.log" << std::endl;
247            ModifyConfigValue(ogreLogFile_, tset, "ogre.log");
248        }
249
250        boost::filesystem::path ogreConfigFilepath(PathConfig::getConfigPath() / this->ogreConfigFile_);
251        boost::filesystem::path ogreLogFilepath(PathConfig::getLogPath() / this->ogreLogFile_);
252
253        // create a new logManager
254        // Ogre::Root will detect that we've already created a Log
255        ogreLogger_.reset(new Ogre::LogManager());
256        COUT(4) << "Ogre LogManager created" << std::endl;
257
258        // create our own log that we can listen to
259        Ogre::Log *myLog;
260        myLog = ogreLogger_->createLog(ogreLogFilepath.string(), true, false, false);
261        COUT(4) << "Ogre Log created" << std::endl;
262
263        myLog->setLogDetail(Ogre::LL_BOREME);
264        myLog->addListener(this);
265
266        COUT(4) << "Creating Ogre Root..." << std::endl;
267
268        // check for config file existence because Ogre displays (caught) exceptions if not
269        if (!boost::filesystem::exists(ogreConfigFilepath))
270        {
271            // create a zero sized file
272            std::ofstream creator;
273            creator.open(ogreConfigFilepath.string().c_str());
274            creator.close();
275        }
276
277        // Leave plugins file empty. We're going to do that part manually later
278        ogreRoot_.reset(new Ogre::Root("", ogreConfigFilepath.string(), ogreLogFilepath.string()));
279
280        COUT(3) << "Ogre set up done." << std::endl;
281    }
282
283    void GraphicsManager::loadOgrePlugins()
284    {
285        // just to make sure the next statement doesn't segfault
286        if (ogrePluginsDirectory_ == "")
287            ogrePluginsDirectory_ = ".";
288
289        boost::filesystem::path folder(ogrePluginsDirectory_);
290        // Do some SubString magic to get the comma separated list of plugins
291        SubString plugins(ogrePlugins_, ",", " ", false, '\\', false, '"', false, '(', ')', false, '\0');
292        // Use backslash paths on Windows! file_string() already does that though.
293        for (unsigned int i = 0; i < plugins.size(); ++i)
294            ogreRoot_->loadPlugin((folder / plugins[i]).file_string());
295    }
296
297    void GraphicsManager::loadRenderer()
298    {
299        CCOUT(4) << "Configuring Renderer" << std::endl;
300
301        if (!ogreRoot_->restoreConfig())
302            if (!ogreRoot_->showConfigDialog())
303                ThrowException(InitialisationFailed, "OGRE graphics configuration dialogue failed.");
304
305        CCOUT(4) << "Creating render window" << std::endl;
306
307        this->renderWindow_ = ogreRoot_->initialise(true, "Orxonox");
308        // Propagate the size of the new winodw
309        this->ogreWindowEventListener_->windowResized(renderWindow_);
310
311        Ogre::WindowEventUtilities::addWindowEventListener(this->renderWindow_, ogreWindowEventListener_.get());
312
313        // create a full screen default viewport
314        // Note: This may throw when adding a viewport with an existing z-order!
315        //       But in our case we only have one viewport for now anyway, therefore
316        //       no ScopeGuards or anything to handle exceptions.
317        this->viewport_ = this->renderWindow_->addViewport(0, 0);
318
319        Ogre::TextureManager::getSingleton().setDefaultNumMipmaps(0);
320
321        // add console commands
322        ccPrintScreen_ = createConsoleCommand(createFunctor(&GraphicsManager::printScreen, this), "printScreen");
323        CommandExecutor::addConsoleCommandShortcut(ccPrintScreen_);
324    }
325
326    void GraphicsManager::loadDebugOverlay()
327    {
328        // Load debug overlay to show info about fps and tick time
329        COUT(4) << "Loading Debug Overlay..." << std::endl;
330        debugOverlay_.reset(new XMLFile("debug.oxo"));
331        Loader::open(debugOverlay_.get());
332    }
333
334    /**
335    @note
336        A note about the Ogre::FrameListener: Even though we don't use them,
337        they still get called. However, the delta times are not correct (except
338        for timeSinceLastFrame, which is the most important). A little research
339        as shown that there is probably only one FrameListener that doesn't even
340        need the time. So we shouldn't run into problems.
341    */
342    void GraphicsManager::update(const Clock& time)
343    {
344        Ogre::FrameEvent evt;
345        evt.timeSinceLastFrame = time.getDeltaTime();
346        evt.timeSinceLastEvent = time.getDeltaTime(); // note: same time, but shouldn't matter anyway
347
348        // don't forget to call _fireFrameStarted to OGRE to make sure
349        // everything goes smoothly
350        ogreRoot_->_fireFrameStarted(evt);
351
352        // Pump messages in all registered RenderWindows
353        // This calls the WindowEventListener objects.
354        Ogre::WindowEventUtilities::messagePump();
355        // make sure the window stays active even when not focused
356        // (probably only necessary on windows)
357        this->renderWindow_->setActive(true);
358
359        // Time before rendering
360        uint64_t timeBeforeTick = time.getRealMicroseconds();
361
362        // Render frame
363        ogreRoot_->_updateAllRenderTargets();
364
365        uint64_t timeAfterTick = time.getRealMicroseconds();
366        // Subtract the time used for rendering from the tick time counter
367        Game::getInstance().subtractTickTime(timeAfterTick - timeBeforeTick);
368
369        // again, just to be sure OGRE works fine
370        ogreRoot_->_fireFrameEnded(evt); // note: uses the same time as _fireFrameStarted
371    }
372
373    void GraphicsManager::setCamera(Ogre::Camera* camera)
374    {
375        this->viewport_->setCamera(camera);
376    }
377
378    /**
379    @brief
380        Method called by the LogListener interface from Ogre.
381        We use it to capture Ogre log messages and handle it ourselves.
382    @param message
383        The message to be logged
384    @param lml
385        The message level the log is using
386    @param maskDebug
387        If we are printing to the console or not
388    @param logName
389        The name of this log (so you can have several listeners
390        for different logs, and identify them)
391    */
392    void GraphicsManager::messageLogged(const std::string& message,
393        Ogre::LogMessageLevel lml, bool maskDebug, const std::string& logName)
394    {
395        int orxonoxLevel;
396        switch (lml)
397        {
398        case Ogre::LML_TRIVIAL:
399            orxonoxLevel = this->ogreLogLevelTrivial_;
400            break;
401        case Ogre::LML_NORMAL:
402            orxonoxLevel = this->ogreLogLevelNormal_;
403            break;
404        case Ogre::LML_CRITICAL:
405            orxonoxLevel = this->ogreLogLevelCritical_;
406            break;
407        default:
408            orxonoxLevel = 0;
409        }
410        OutputHandler::getOutStream().setOutputLevel(orxonoxLevel)
411            << "Ogre: " << message << std::endl;
412    }
413
414    size_t GraphicsManager::getRenderWindowHandle()
415    {
416        size_t windowHnd = 0;
417        renderWindow_->getCustomAttribute("WINDOW", &windowHnd);
418        return windowHnd;
419    }
420
421    bool GraphicsManager::isFullScreen() const
422    {
423        Ogre::ConfigOptionMap& options = ogreRoot_->getRenderSystem()->getConfigOptions();
424        if (options.find("Full Screen") != options.end())
425        {
426            if (options["Full Screen"].currentValue == "Yes")
427                return true;
428            else
429                return false;
430        }
431        else
432        {
433            COUT(0) << "Could not find 'Full Screen' render system option. Fix This!!!" << std::endl;
434            return false;
435        }
436    }
437
438    void GraphicsManager::printScreen()
439    {
440        assert(this->renderWindow_);
441       
442        this->renderWindow_->writeContentsToTimestampedFile(PathConfig::getLogPathString() + "screenShot_", ".jpg");
443    }
444}
Note: See TracBrowser for help on using the repository browser.