Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/presentation2/src/libraries/core/GraphicsManager.cc @ 6385

Last change on this file since 6385 was 6385, checked in by rgrieder, 14 years ago

The free PNG format is probably more suitable for screen shots than TIFF.
About quality: I created a PNG and a TIFF image from the same frame and stored them as BMP with GIMP afterwards. Then I let diffutils compare it —> No difference at all.

  • Property svn:eol-style set to native
File size: 16.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 *      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
101        // At first, add the root paths of the data directories as resource locations
102        Ogre::ResourceGroupManager::getSingleton().addResourceLocation(PathConfig::getDataPathString(), "FileSystem", "dataRoot", false);
103        // Load resources
104        resources_.reset(new XMLFile("resources.oxr", "dataRoot"));
105        resources_->setLuaSupport(false);
106        Loader::open(resources_.get());
107
108        // Only for development runs
109        if (PathConfig::isDevelopmentRun())
110        {
111            Ogre::ResourceGroupManager::getSingleton().addResourceLocation(PathConfig::getExternalDataPathString(), "FileSystem", "externalDataRoot", false);
112            extResources_.reset(new XMLFile("resources.oxr", "externalDataRoot"));
113            extResources_->setLuaSupport(false);
114            Loader::open(extResources_.get());
115        }
116
117        if (bLoadRenderer)
118        {
119            // Reads the ogre config and creates the render window
120            this->upgradeToGraphics();
121        }
122    }
123
124    /**
125    @brief
126        Destruction is done by the member scoped_ptrs.
127    */
128    GraphicsManager::~GraphicsManager()
129    {
130        Loader::unload(debugOverlay_.get());
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 (PathConfig::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        // load all the required plugins for Ogre
173        this->loadOgrePlugins();
174
175        this->loadRenderer();
176
177#if OGRE_VERSION < 0x010600
178        // WORKAROUND: There is an incompatibility for particle scripts when trying
179        // to support both Ogre 1.4 and 1.6. The hacky solution is to create
180        // scripts for the 1.6 version and then remove the inserted "particle_system"
181        // keyword. But we need to supply these new scripts as well, which is why
182        // there is an extra Ogre::Archive dealing with it in the memory.
183        using namespace Ogre;
184        ArchiveManager::getSingleton().addArchiveFactory(memoryArchiveFactory_.get());
185        const StringVector& groups = ResourceGroupManager::getSingleton().getResourceGroups();
186        // Travers all groups
187        for (StringVector::const_iterator itGroup = groups.begin(); itGroup != groups.end(); ++itGroup)
188        {
189            FileInfoListPtr files = ResourceGroupManager::getSingleton().findResourceFileInfo(*itGroup, "*.particle");
190            for (FileInfoList::const_iterator itFile = files->begin(); itFile != files->end(); ++itFile)
191            {
192                // open file
193                Ogre::DataStreamPtr input = ResourceGroupManager::getSingleton().openResource(itFile->filename, *itGroup, false);
194                std::stringstream output;
195                // Parse file and replace "particle_system" with nothing
196                while (!input->eof())
197                {
198                    std::string line = input->getLine();
199                    size_t pos = line.find("particle_system");
200                    if (pos != std::string::npos)
201                    {
202                        // 15 is the length of "particle_system"
203                        line.replace(pos, 15, "");
204                    }
205                    output << line << std::endl;
206                }
207                // Add file to the memory archive
208                shared_array<char> data(new char[output.str().size()]);
209                // Debug optimisations
210                const std::string outputStr = output.str();
211                char* rawData = data.get();
212                for (unsigned i = 0; i < outputStr.size(); ++i)
213                    rawData[i] = outputStr[i];
214                MemoryArchive::addFile("particle_scripts_ogre_1.4_" + *itGroup, itFile->filename, data, output.str().size());
215            }
216            if (!files->empty())
217            {
218                // Declare the files, but using a new group
219                ResourceGroupManager::getSingleton().addResourceLocation("particle_scripts_ogre_1.4_" + *itGroup,
220                    "Memory", "particle_scripts_ogre_1.4_" + *itGroup);
221            }
222        }
223#endif
224
225        // Initialise all resources (do this AFTER the renderer has been loaded!)
226        // Note: You can only do this once! Ogre will check whether a resource group has
227        // already been initialised. If you need to load resources later, you will have to
228        // choose another resource group.
229        Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
230    }
231
232    /**
233    @brief
234        Creates the Ogre Root object and sets up the ogre log.
235    */
236    void GraphicsManager::loadOgreRoot()
237    {
238        COUT(3) << "Setting up Ogre..." << std::endl;
239
240        if (ogreConfigFile_ == "")
241        {
242            COUT(2) << "Warning: Ogre config file set to \"\". Defaulting to config.cfg" << std::endl;
243            ModifyConfigValue(ogreConfigFile_, tset, "config.cfg");
244        }
245        if (ogreLogFile_ == "")
246        {
247            COUT(2) << "Warning: Ogre log file set to \"\". Defaulting to ogre.log" << std::endl;
248            ModifyConfigValue(ogreLogFile_, tset, "ogre.log");
249        }
250
251        boost::filesystem::path ogreConfigFilepath(PathConfig::getConfigPath() / this->ogreConfigFile_);
252        boost::filesystem::path ogreLogFilepath(PathConfig::getLogPath() / this->ogreLogFile_);
253
254        // create a new logManager
255        // Ogre::Root will detect that we've already created a Log
256        ogreLogger_.reset(new Ogre::LogManager());
257        COUT(4) << "Ogre LogManager created" << std::endl;
258
259        // create our own log that we can listen to
260        Ogre::Log *myLog;
261        myLog = ogreLogger_->createLog(ogreLogFilepath.string(), true, false, false);
262        COUT(4) << "Ogre Log created" << std::endl;
263
264        myLog->setLogDetail(Ogre::LL_BOREME);
265        myLog->addListener(this);
266
267        COUT(4) << "Creating Ogre Root..." << std::endl;
268
269        // check for config file existence because Ogre displays (caught) exceptions if not
270        if (!boost::filesystem::exists(ogreConfigFilepath))
271        {
272            // create a zero sized file
273            std::ofstream creator;
274            creator.open(ogreConfigFilepath.string().c_str());
275            creator.close();
276        }
277
278        // Leave plugins file empty. We're going to do that part manually later
279        ogreRoot_.reset(new Ogre::Root("", ogreConfigFilepath.string(), ogreLogFilepath.string()));
280
281        COUT(3) << "Ogre set up done." << std::endl;
282    }
283
284    void GraphicsManager::loadOgrePlugins()
285    {
286        // just to make sure the next statement doesn't segfault
287        if (ogrePluginsDirectory_ == "")
288            ogrePluginsDirectory_ = ".";
289
290        boost::filesystem::path folder(ogrePluginsDirectory_);
291        // Do some SubString magic to get the comma separated list of plugins
292        SubString plugins(ogrePlugins_, ",", " ", false, '\\', false, '"', false, '(', ')', false, '\0');
293        // Use backslash paths on Windows! file_string() already does that though.
294        for (unsigned int i = 0; i < plugins.size(); ++i)
295            ogreRoot_->loadPlugin((folder / plugins[i]).file_string());
296    }
297
298    void GraphicsManager::loadRenderer()
299    {
300        CCOUT(4) << "Configuring Renderer" << std::endl;
301
302        if (!ogreRoot_->restoreConfig())
303            if (!ogreRoot_->showConfigDialog())
304                ThrowException(InitialisationFailed, "OGRE graphics configuration dialogue failed.");
305
306        CCOUT(4) << "Creating render window" << std::endl;
307
308        this->renderWindow_ = ogreRoot_->initialise(true, "Orxonox");
309        // Propagate the size of the new winodw
310        this->ogreWindowEventListener_->windowResized(renderWindow_);
311
312        Ogre::WindowEventUtilities::addWindowEventListener(this->renderWindow_, ogreWindowEventListener_.get());
313
314        // create a full screen default viewport
315        // Note: This may throw when adding a viewport with an existing z-order!
316        //       But in our case we only have one viewport for now anyway, therefore
317        //       no ScopeGuards or anything to handle exceptions.
318        this->viewport_ = this->renderWindow_->addViewport(0, 0);
319
320        Ogre::TextureManager::getSingleton().setDefaultNumMipmaps(0);
321
322        // add console commands
323        ccPrintScreen_ = createConsoleCommand(createFunctor(&GraphicsManager::printScreen, this), "printScreen");
324        CommandExecutor::addConsoleCommandShortcut(ccPrintScreen_);
325    }
326
327    void GraphicsManager::loadDebugOverlay()
328    {
329        // Load debug overlay to show info about fps and tick time
330        COUT(4) << "Loading Debug Overlay..." << std::endl;
331        debugOverlay_.reset(new XMLFile("debug.oxo"));
332        Loader::open(debugOverlay_.get());
333    }
334
335    /**
336    @note
337        A note about the Ogre::FrameListener: Even though we don't use them,
338        they still get called. However, the delta times are not correct (except
339        for timeSinceLastFrame, which is the most important). A little research
340        as shown that there is probably only one FrameListener that doesn't even
341        need the time. So we shouldn't run into problems.
342    */
343    void GraphicsManager::postUpdate(const Clock& time)
344    {
345        Ogre::FrameEvent evt;
346        evt.timeSinceLastFrame = time.getDeltaTime();
347        evt.timeSinceLastEvent = time.getDeltaTime(); // note: same time, but shouldn't matter anyway
348
349        // don't forget to call _fireFrameStarted to OGRE to make sure
350        // everything goes smoothly
351        ogreRoot_->_fireFrameStarted(evt);
352
353        // Pump messages in all registered RenderWindows
354        // This calls the WindowEventListener objects.
355        Ogre::WindowEventUtilities::messagePump();
356        // make sure the window stays active even when not focused
357        // (probably only necessary on windows)
358        this->renderWindow_->setActive(true);
359
360        // Time before rendering
361        uint64_t timeBeforeTick = time.getRealMicroseconds();
362
363        // Render frame
364        ogreRoot_->_updateAllRenderTargets();
365
366        uint64_t timeAfterTick = time.getRealMicroseconds();
367        // Subtract the time used for rendering from the tick time counter
368        Game::getInstance().subtractTickTime(timeAfterTick - timeBeforeTick);
369
370        // again, just to be sure OGRE works fine
371        ogreRoot_->_fireFrameEnded(evt); // note: uses the same time as _fireFrameStarted
372    }
373
374    void GraphicsManager::setCamera(Ogre::Camera* camera)
375    {
376        this->viewport_->setCamera(camera);
377    }
378
379    /**
380    @brief
381        Method called by the LogListener interface from Ogre.
382        We use it to capture Ogre log messages and handle it ourselves.
383    @param message
384        The message to be logged
385    @param lml
386        The message level the log is using
387    @param maskDebug
388        If we are printing to the console or not
389    @param logName
390        The name of this log (so you can have several listeners
391        for different logs, and identify them)
392    */
393    void GraphicsManager::messageLogged(const std::string& message,
394        Ogre::LogMessageLevel lml, bool maskDebug, const std::string& logName)
395    {
396        int orxonoxLevel;
397        switch (lml)
398        {
399        case Ogre::LML_TRIVIAL:
400            orxonoxLevel = this->ogreLogLevelTrivial_;
401            break;
402        case Ogre::LML_NORMAL:
403            orxonoxLevel = this->ogreLogLevelNormal_;
404            break;
405        case Ogre::LML_CRITICAL:
406            orxonoxLevel = this->ogreLogLevelCritical_;
407            break;
408        default:
409            orxonoxLevel = 0;
410        }
411        OutputHandler::getOutStream(orxonoxLevel)
412            << "Ogre: " << message << std::endl;
413    }
414
415    size_t GraphicsManager::getRenderWindowHandle()
416    {
417        size_t windowHnd = 0;
418        renderWindow_->getCustomAttribute("WINDOW", &windowHnd);
419        return windowHnd;
420    }
421
422    bool GraphicsManager::isFullScreen() const
423    {
424        Ogre::ConfigOptionMap& options = ogreRoot_->getRenderSystem()->getConfigOptions();
425        if (options.find("Full Screen") != options.end())
426        {
427            if (options["Full Screen"].currentValue == "Yes")
428                return true;
429            else
430                return false;
431        }
432        else
433        {
434            COUT(0) << "Could not find 'Full Screen' render system option. Fix This!!!" << std::endl;
435            return false;
436        }
437    }
438
439    void GraphicsManager::printScreen()
440    {
441        assert(this->renderWindow_);
442       
443        this->renderWindow_->writeContentsToTimestampedFile(PathConfig::getLogPathString() + "screenShot_", ".png");
444        this->renderWindow_->writeContentsToTimestampedFile(PathConfig::getLogPathString() + "screenShot_", ".tif");
445    }
446}
Note: See TracBrowser for help on using the repository browser.