Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/core5/src/libraries/core/GraphicsManager.cc @ 5829

Last change on this file since 5829 was 5829, checked in by landauf, 15 years ago

replaced most occurrences of setObject(o) in combination with createFunctor(f) with the more convenient createFunctor(f, o)

  • Property svn:eol-style set to native
File size: 16.0 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/Exception.h"
51#include "util/StringUtils.h"
52#include "util/SubString.h"
53#include "Clock.h"
54#include "ConsoleCommand.h"
55#include "ConfigValueIncludes.h"
56#include "CoreIncludes.h"
57#include "Core.h"
58#include "Game.h"
59#include "GameMode.h"
60#include "Loader.h"
61#include "MemoryArchive.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(Core::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 (Core::isDevelopmentRun())
112        {
113            Ogre::ResourceGroupManager::getSingleton().addResourceLocation(Core::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        Ogre::WindowEventUtilities::removeWindowEventListener(renderWindow_, ogreWindowEventListener_.get());
133        // TODO: Destroy the console command
134
135        // Undeclare the resources
136        Loader::unload(resources_.get());
137        if (Core::isDevelopmentRun())
138            Loader::unload(extResources_.get());
139    }
140
141    void GraphicsManager::setConfigValues()
142    {
143        SetConfigValue(ogreConfigFile_,  "ogre.cfg")
144            .description("Location of the Ogre config file");
145        SetConfigValue(ogrePluginsDirectory_, specialConfig::ogrePluginsDirectory)
146            .description("Folder where the Ogre plugins are located.");
147        SetConfigValue(ogrePlugins_, specialConfig::ogrePlugins)
148            .description("Comma separated list of all plugins to load.");
149        SetConfigValue(ogreLogFile_,     "ogre.log")
150            .description("Logfile for messages from Ogre. Use \"\" to suppress log file creation.");
151        SetConfigValue(ogreLogLevelTrivial_ , 5)
152            .description("Corresponding orxonox debug level for ogre Trivial");
153        SetConfigValue(ogreLogLevelNormal_  , 4)
154            .description("Corresponding orxonox debug level for ogre Normal");
155        SetConfigValue(ogreLogLevelCritical_, 2)
156            .description("Corresponding orxonox debug level for ogre Critical");
157    }
158
159    /**
160    @brief
161        Loads the renderer and creates the render window if not yet done so.
162    @remarks
163        This operation is irreversible without recreating the GraphicsManager!
164        So if it throws you HAVE to recreate the GraphicsManager!!!
165        It therefore offers almost no exception safety.
166    */
167    void GraphicsManager::upgradeToGraphics()
168    {
169        if (renderWindow_ != NULL)
170            return;
171
172        this->loadRenderer();
173
174#if OGRE_VERSION < 0x010600
175        // WORKAROUND: There is an incompatibility for particle scripts when trying
176        // to support both Ogre 1.4 and 1.6. The hacky solution is to create
177        // scripts for the 1.6 version and then remove the inserted "particle_system"
178        // keyword. But we need to supply these new scripts as well, which is why
179        // there is an extra Ogre::Archive dealing with it in the memory.
180        using namespace Ogre;
181        ArchiveManager::getSingleton().addArchiveFactory(memoryArchiveFactory_.get());
182        const StringVector& groups = ResourceGroupManager::getSingleton().getResourceGroups();
183        // Travers all groups
184        for (StringVector::const_iterator itGroup = groups.begin(); itGroup != groups.end(); ++itGroup)
185        {
186            FileInfoListPtr files = ResourceGroupManager::getSingleton().findResourceFileInfo(*itGroup, "*.particle");
187            for (FileInfoList::const_iterator itFile = files->begin(); itFile != files->end(); ++itFile)
188            {
189                // open file
190                Ogre::DataStreamPtr input = ResourceGroupManager::getSingleton().openResource(itFile->filename, *itGroup, false);
191                std::stringstream output;
192                // Parse file and replace "particle_system" with nothing
193                while (!input->eof())
194                {
195                    std::string line = input->getLine();
196                    size_t pos = line.find("particle_system");
197                    if (pos != std::string::npos)
198                    {
199                        // 15 is the length of "particle_system"
200                        line.replace(pos, 15, "");
201                    }
202                    output << line << std::endl;
203                }
204                // Add file to the memory archive
205                shared_array<char> data(new char[output.str().size()]);
206                // Debug optimisations
207                const std::string outputStr = output.str();
208                char* rawData = data.get();
209                for (unsigned i = 0; i < outputStr.size(); ++i)
210                    rawData[i] = outputStr[i];
211                MemoryArchive::addFile("particle_scripts_ogre_1.4_" + *itGroup, itFile->filename, data, output.str().size());
212            }
213            if (!files->empty())
214            {
215                // Declare the files, but using a new group
216                ResourceGroupManager::getSingleton().addResourceLocation("particle_scripts_ogre_1.4_" + *itGroup,
217                    "Memory", "particle_scripts_ogre_1.4_" + *itGroup);
218            }
219        }
220#endif
221
222        // Initialise all resources (do this AFTER the renderer has been loaded!)
223        // Note: You can only do this once! Ogre will check whether a resource group has
224        // already been initialised. If you need to load resources later, you will have to
225        // choose another resource group.
226        Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
227    }
228
229    /**
230    @brief
231        Creates the Ogre Root object and sets up the ogre log.
232    */
233    void GraphicsManager::loadOgreRoot()
234    {
235        COUT(3) << "Setting up Ogre..." << std::endl;
236
237        if (ogreConfigFile_ == "")
238        {
239            COUT(2) << "Warning: Ogre config file set to \"\". Defaulting to config.cfg" << std::endl;
240            ModifyConfigValue(ogreConfigFile_, tset, "config.cfg");
241        }
242        if (ogreLogFile_ == "")
243        {
244            COUT(2) << "Warning: Ogre log file set to \"\". Defaulting to ogre.log" << std::endl;
245            ModifyConfigValue(ogreLogFile_, tset, "ogre.log");
246        }
247
248        boost::filesystem::path ogreConfigFilepath(Core::getConfigPath() / this->ogreConfigFile_);
249        boost::filesystem::path ogreLogFilepath(Core::getLogPath() / this->ogreLogFile_);
250
251        // create a new logManager
252        // Ogre::Root will detect that we've already created a Log
253        ogreLogger_.reset(new Ogre::LogManager());
254        COUT(4) << "Ogre LogManager created" << std::endl;
255
256        // create our own log that we can listen to
257        Ogre::Log *myLog;
258        myLog = ogreLogger_->createLog(ogreLogFilepath.string(), true, false, false);
259        COUT(4) << "Ogre Log created" << std::endl;
260
261        myLog->setLogDetail(Ogre::LL_BOREME);
262        myLog->addListener(this);
263
264        COUT(4) << "Creating Ogre Root..." << std::endl;
265
266        // check for config file existence because Ogre displays (caught) exceptions if not
267        if (!boost::filesystem::exists(ogreConfigFilepath))
268        {
269            // create a zero sized file
270            std::ofstream creator;
271            creator.open(ogreConfigFilepath.string().c_str());
272            creator.close();
273        }
274
275        // Leave plugins file empty. We're going to do that part manually later
276        ogreRoot_.reset(new Ogre::Root("", ogreConfigFilepath.string(), ogreLogFilepath.string()));
277
278        COUT(3) << "Ogre set up done." << std::endl;
279    }
280
281    void GraphicsManager::loadOgrePlugins()
282    {
283        // just to make sure the next statement doesn't segfault
284        if (ogrePluginsDirectory_ == "")
285            ogrePluginsDirectory_ = ".";
286
287        boost::filesystem::path folder(ogrePluginsDirectory_);
288        // Do some SubString magic to get the comma separated list of plugins
289        SubString plugins(ogrePlugins_, ",", " ", false, '\\', false, '"', false, '(', ')', false, '\0');
290        // Use backslash paths on Windows! file_string() already does that though.
291        for (unsigned int i = 0; i < plugins.size(); ++i)
292            ogreRoot_->loadPlugin((folder / plugins[i]).file_string());
293    }
294
295    void GraphicsManager::loadRenderer()
296    {
297        CCOUT(4) << "Configuring Renderer" << std::endl;
298
299        if (!ogreRoot_->restoreConfig())
300            if (!ogreRoot_->showConfigDialog())
301                ThrowException(InitialisationFailed, "OGRE graphics configuration dialogue failed.");
302
303        CCOUT(4) << "Creating render window" << std::endl;
304
305        this->renderWindow_ = ogreRoot_->initialise(true, "Orxonox");
306        // Propagate the size of the new winodw
307        this->ogreWindowEventListener_->windowResized(renderWindow_);
308
309        Ogre::WindowEventUtilities::addWindowEventListener(this->renderWindow_, ogreWindowEventListener_.get());
310
311        // create a full screen default viewport
312        // Note: This may throw when adding a viewport with an existing z-order!
313        //       But in our case we only have one viewport for now anyway, therefore
314        //       no ScopeGuards or anything to handle exceptions.
315        this->viewport_ = this->renderWindow_->addViewport(0, 0);
316
317        Ogre::TextureManager::getSingleton().setDefaultNumMipmaps(0);
318
319        // add console commands
320        ccPrintScreen_ = createConsoleCommand(createFunctor(&GraphicsManager::printScreen, this), "printScreen");
321        CommandExecutor::addConsoleCommandShortcut(ccPrintScreen_);
322    }
323
324    void GraphicsManager::update(const Clock& time)
325    {
326        Ogre::FrameEvent evt;
327        evt.timeSinceLastFrame = time.getDeltaTime();
328        evt.timeSinceLastEvent = time.getDeltaTime(); // note: same time, but shouldn't matter anyway
329
330        // don't forget to call _fireFrameStarted to OGRE to make sure
331        // everything goes smoothly
332        ogreRoot_->_fireFrameStarted(evt);
333
334        // Pump messages in all registered RenderWindows
335        // This calls the WindowEventListener objects.
336        Ogre::WindowEventUtilities::messagePump();
337        // make sure the window stays active even when not focused
338        // (probably only necessary on windows)
339        this->renderWindow_->setActive(true);
340
341        // Time before rendering
342        uint64_t timeBeforeTick = time.getRealMicroseconds();
343
344        // Render frame
345        ogreRoot_->_updateAllRenderTargets();
346
347        uint64_t timeAfterTick = time.getRealMicroseconds();
348        // Subtract the time used for rendering from the tick time counter
349        Game::getInstance().subtractTickTime(timeAfterTick - timeBeforeTick);
350
351        // again, just to be sure OGRE works fine
352        ogreRoot_->_fireFrameEnded(evt); // note: uses the same time as _fireFrameStarted
353    }
354
355    void GraphicsManager::setCamera(Ogre::Camera* camera)
356    {
357        this->viewport_->setCamera(camera);
358    }
359
360    /**
361    @brief
362        Method called by the LogListener interface from Ogre.
363        We use it to capture Ogre log messages and handle it ourselves.
364    @param message
365        The message to be logged
366    @param lml
367        The message level the log is using
368    @param maskDebug
369        If we are printing to the console or not
370    @param logName
371        The name of this log (so you can have several listeners
372        for different logs, and identify them)
373    */
374    void GraphicsManager::messageLogged(const std::string& message,
375        Ogre::LogMessageLevel lml, bool maskDebug, const std::string& logName)
376    {
377        int orxonoxLevel;
378        switch (lml)
379        {
380        case Ogre::LML_TRIVIAL:
381            orxonoxLevel = this->ogreLogLevelTrivial_;
382            break;
383        case Ogre::LML_NORMAL:
384            orxonoxLevel = this->ogreLogLevelNormal_;
385            break;
386        case Ogre::LML_CRITICAL:
387            orxonoxLevel = this->ogreLogLevelCritical_;
388            break;
389        default:
390            orxonoxLevel = 0;
391        }
392        OutputHandler::getOutStream().setOutputLevel(orxonoxLevel)
393            << "Ogre: " << message << std::endl;
394    }
395
396    size_t GraphicsManager::getRenderWindowHandle()
397    {
398        size_t windowHnd = 0;
399        renderWindow_->getCustomAttribute("WINDOW", &windowHnd);
400        return windowHnd;
401    }
402
403    bool GraphicsManager::isFullScreen() const
404    {
405        Ogre::ConfigOptionMap& options = ogreRoot_->getRenderSystem()->getConfigOptions();
406        if (options.find("Full Screen") != options.end())
407        {
408            if (options["Full Screen"].currentValue == "Yes")
409                return true;
410            else
411                return false;
412        }
413        else
414        {
415            COUT(0) << "Could not find 'Full Screen' render system option. Fix This!!!" << std::endl;
416            return false;
417        }
418    }
419
420    void GraphicsManager::printScreen()
421    {
422        assert(this->renderWindow_);
423       
424        this->renderWindow_->writeContentsToTimestampedFile(Core::getLogPathString() + "screenShot_", ".jpg");
425    }
426}
Note: See TracBrowser for help on using the repository browser.