From 2a85a22dba62c875cb579a414be90729e092626a Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 3 Jun 2015 16:40:16 +0200 Subject: [PATCH] Write savegame screenshot --- apps/openmw/mwbase/world.hpp | 3 +- apps/openmw/mwrender/renderingmanager.cpp | 59 +++++++++++++++++++++++ apps/openmw/mwrender/renderingmanager.hpp | 3 ++ apps/openmw/mwstate/statemanagerimp.cpp | 41 ++++++++++++---- apps/openmw/mwstate/statemanagerimp.hpp | 2 + apps/openmw/mwworld/worldimp.cpp | 4 +- apps/openmw/mwworld/worldimp.hpp | 2 +- 7 files changed, 101 insertions(+), 13 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index f258eb300..bb48afc9d 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -23,6 +23,7 @@ namespace osg { class Vec3f; class Quat; + class Image; } namespace Loading @@ -438,7 +439,7 @@ namespace MWBase virtual void reattachPlayerCamera() = 0; /// \todo this does not belong here - virtual void screenshot (Ogre::Image& image, int w, int h) = 0; + virtual void screenshot (osg::Image* image, int w, int h) = 0; /// Find default position inside exterior cell specified by name /// \return false if exterior with given name not exists, true otherwise diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 6205c3ad8..7af21a5a6 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -379,6 +379,65 @@ namespace MWRender mWater->setHeight(height); } + class NotifyDrawCompletedCallback : public osg::Camera::DrawCallback + { + public: + virtual void operator () (osg::RenderInfo& renderInfo) const + { + mCondition.signal(); + } + + mutable OpenThreads::Condition mCondition; + }; + + void RenderingManager::screenshot(osg::Image *image, int w, int h) + { + int oldCullMask = mViewer->getCamera()->getCullMask(); + mViewer->getCamera()->setCullMask(oldCullMask & (~Mask_GUI)); + + osg::ref_ptr rttCamera (new osg::Camera); + rttCamera->setNodeMask(Mask_RenderToTexture); + rttCamera->attach(osg::Camera::COLOR_BUFFER, image); + rttCamera->setRenderOrder(osg::Camera::PRE_RENDER); + rttCamera->setReferenceFrame(osg::Camera::ABSOLUTE_RF); + rttCamera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT, osg::Camera::PIXEL_BUFFER_RTT); + rttCamera->setClearColor(mViewer->getCamera()->getClearColor()); + rttCamera->setClearMask(mViewer->getCamera()->getClearMask()); + rttCamera->setProjectionMatrixAsPerspective(mFieldOfView, w/float(h), mNearClip, mViewDistance); + rttCamera->setViewMatrix(mViewer->getCamera()->getViewMatrix()); + rttCamera->setViewport(0, 0, w, h); + rttCamera->setGraphicsContext(mViewer->getCamera()->getGraphicsContext()); + + osg::ref_ptr texture (new osg::Texture2D); + texture->setInternalFormat(GL_RGB); + texture->setTextureSize(w, h); + texture->setResizeNonPowerOfTwoHint(false); + rttCamera->attach(osg::Camera::COLOR_BUFFER, texture); + + image->setDataType(GL_UNSIGNED_BYTE); + image->setPixelFormat(texture->getInternalFormat()); + + rttCamera->addChild(mLightRoot); + + mRootNode->addChild(rttCamera); + + mViewer->frame(); + + // The draw needs to complete before we can copy back our image. + osg::ref_ptr callback (new NotifyDrawCompletedCallback); + rttCamera->setFinalDrawCallback(callback); + OpenThreads::Mutex m; + m.lock(); + callback->mCondition.wait(&m); + m.unlock(); + + rttCamera->removeChildren(0, rttCamera->getNumChildren()); + rttCamera->setGraphicsContext(NULL); + mRootNode->removeChild(rttCamera); + + mViewer->getCamera()->setCullMask(oldCullMask); + } + void RenderingManager::getCameraToViewportRay(float nX, float nY, osg::Vec3f &origin, osg::Vec3f &dest) { osg::Matrix viewProj = mViewer->getCamera()->getViewMatrix() * mViewer->getCamera()->getProjectionMatrix(); diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index c22d3c7bf..4452ae9f8 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -82,6 +82,9 @@ namespace MWRender void setWaterEnabled(bool enabled); void setWaterHeight(float level); + /// Take a screenshot of w*h onto the given image, not including the GUI. + void screenshot(osg::Image* image, int w, int h); + /// Return the object under the mouse cursor / crosshair position, given by nX and nY normalized screen coordinates, /// where (0,0) is the top left corner. MWWorld::Ptr getFacedObject(const float nX, const float nY, float maxDistance, bool ignorePlayer); diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index d403590dd..ce4d8f958 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -10,7 +10,9 @@ #include -#include +#include + +#include #include #include @@ -179,14 +181,7 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot profile.mTimePlayed = mTimePlayed; profile.mDescription = description; - /* - int screenshotW = 259*2, screenshotH = 133*2; // *2 to get some nice antialiasing - Ogre::Image screenshot; - world.screenshot(screenshot, screenshotW, screenshotH); - Ogre::DataStreamPtr encoded = screenshot.encode("jpg"); - profile.mScreenshot.resize(encoded->size()); - encoded->read(&profile.mScreenshot[0], encoded->size()); - */ + writeScreenshot(profile.mScreenshot); if (!slot) slot = getCurrentCharacter()->createSlot (profile); @@ -572,3 +567,31 @@ bool MWState::StateManager::verifyProfile(const ESM::SavedGame& profile) const } return true; } + +void MWState::StateManager::writeScreenshot(std::vector &imageData) const +{ + int screenshotW = 259*2, screenshotH = 133*2; // *2 to get some nice antialiasing + + osg::ref_ptr screenshot (new osg::Image); + + MWBase::Environment::get().getWorld()->screenshot(screenshot.get(), screenshotW, screenshotH); + + osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension("jpg"); + if (!readerwriter) + { + std::cerr << "Unable to write screenshot, can't find a jpg ReaderWriter" << std::endl; + return; + } + + std::ostringstream ostream; + osgDB::ReaderWriter::WriteResult result = readerwriter->writeImage(*screenshot, ostream); + if (!result.success()) + { + std::cerr << "Unable to write screenshot: " << result.message() << std::endl; + return; + } + + std::string data = ostream.str(); + imageData = std::vector(data.begin(), data.end()); + +} diff --git a/apps/openmw/mwstate/statemanagerimp.hpp b/apps/openmw/mwstate/statemanagerimp.hpp index 37f38f8df..59dc919d1 100644 --- a/apps/openmw/mwstate/statemanagerimp.hpp +++ b/apps/openmw/mwstate/statemanagerimp.hpp @@ -25,6 +25,8 @@ namespace MWState bool verifyProfile (const ESM::SavedGame& profile) const; + void writeScreenshot (std::vector& imageData) const; + std::map buildContentFileIndexMap (const ESM::ESMReader& reader) const; public: diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 7528e9286..7bc46c078 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2128,9 +2128,9 @@ namespace MWWorld return mRendering->getAnimation(ptr); } - void World::screenshot(Ogre::Image &image, int w, int h) + void World::screenshot(osg::Image* image, int w, int h) { - //mRendering->screenshot(image, w, h); + mRendering->screenshot(image, w, h); } void World::activateDoor(const MWWorld::Ptr& door) diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 4c4df2c99..20d6bf9a1 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -524,7 +524,7 @@ namespace MWWorld virtual void reattachPlayerCamera(); /// \todo this does not belong here - virtual void screenshot (Ogre::Image& image, int w, int h); + virtual void screenshot (osg::Image* image, int w, int h); /// Find center of exterior cell above land surface /// \return false if exterior with given name not exists, true otherwise