mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-02-01 02:45:32 +00:00
Implement savegame screenshots
This commit is contained in:
parent
786ed6ca5b
commit
295aed3533
9 changed files with 89 additions and 2 deletions
|
@ -412,6 +412,7 @@ namespace MWBase
|
||||||
virtual void playVideo(const std::string& name, bool allowSkipping) = 0;
|
virtual void playVideo(const std::string& name, bool allowSkipping) = 0;
|
||||||
virtual void stopVideo() = 0;
|
virtual void stopVideo() = 0;
|
||||||
virtual void frameStarted (float dt, bool paused) = 0;
|
virtual void frameStarted (float dt, bool paused) = 0;
|
||||||
|
virtual void screenshot (Ogre::Image& image, int w, int h) = 0;
|
||||||
|
|
||||||
/// Find default position inside exterior cell specified by name
|
/// Find default position inside exterior cell specified by name
|
||||||
/// \return false if exterior with given name not exists, true otherwise
|
/// \return false if exterior with given name not exists, true otherwise
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
#include "savegamedialog.hpp"
|
#include "savegamedialog.hpp"
|
||||||
#include "widgets.hpp"
|
#include "widgets.hpp"
|
||||||
|
|
||||||
|
#include <OgreImage.h>
|
||||||
|
#include <OgreTextureManager.h>
|
||||||
|
|
||||||
#include <components/misc/stringops.hpp>
|
#include <components/misc/stringops.hpp>
|
||||||
|
|
||||||
#include <components/settings/settings.hpp>
|
#include <components/settings/settings.hpp>
|
||||||
|
@ -166,6 +169,7 @@ namespace MWGui
|
||||||
if (pos == MyGUI::ITEM_NONE)
|
if (pos == MyGUI::ITEM_NONE)
|
||||||
{
|
{
|
||||||
mInfoText->setCaption("");
|
mInfoText->setCaption("");
|
||||||
|
mScreenshot->setImageTexture("");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,5 +203,29 @@ namespace MWGui
|
||||||
<< " " << hour << " " << (pm ? "#{sSaveMenuHelp05}" : "#{sSaveMenuHelp04}");
|
<< " " << hour << " " << (pm ? "#{sSaveMenuHelp05}" : "#{sSaveMenuHelp04}");
|
||||||
|
|
||||||
mInfoText->setCaptionWithReplacing(text.str());
|
mInfoText->setCaptionWithReplacing(text.str());
|
||||||
|
|
||||||
|
// Decode screenshot
|
||||||
|
std::vector<char> data = slot->mProfile.mScreenshot; // MemoryDataStream doesn't work with const data :(
|
||||||
|
Ogre::DataStreamPtr stream(new Ogre::MemoryDataStream(&data[0], data.size()));
|
||||||
|
Ogre::Image image;
|
||||||
|
image.load(stream, "jpg");
|
||||||
|
|
||||||
|
const std::string textureName = "@savegame_screenshot";
|
||||||
|
Ogre::TexturePtr texture;
|
||||||
|
texture = Ogre::TextureManager::getSingleton().getByName(textureName);
|
||||||
|
mScreenshot->setImageTexture("");
|
||||||
|
if (texture.isNull())
|
||||||
|
{
|
||||||
|
texture = Ogre::TextureManager::getSingleton().createManual(textureName,
|
||||||
|
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
|
||||||
|
Ogre::TEX_TYPE_2D,
|
||||||
|
image.getWidth(), image.getHeight(), 0, Ogre::PF_BYTE_RGBA, Ogre::TU_DYNAMIC_WRITE_ONLY);
|
||||||
|
}
|
||||||
|
texture->unload();
|
||||||
|
texture->setWidth(image.getWidth());
|
||||||
|
texture->setHeight(image.getHeight());
|
||||||
|
texture->loadImage(image);
|
||||||
|
|
||||||
|
mScreenshot->setImageTexture(textureName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -289,6 +289,9 @@ void RenderingManager::rotateObject(const MWWorld::Ptr &ptr)
|
||||||
void
|
void
|
||||||
RenderingManager::updateObjectCell(const MWWorld::Ptr &old, const MWWorld::Ptr &cur)
|
RenderingManager::updateObjectCell(const MWWorld::Ptr &old, const MWWorld::Ptr &cur)
|
||||||
{
|
{
|
||||||
|
Ogre::Image im;
|
||||||
|
im.encode(".jpg");
|
||||||
|
|
||||||
Ogre::SceneNode *child =
|
Ogre::SceneNode *child =
|
||||||
mRendering.getScene()->getSceneNode(old.getRefData().getHandle());
|
mRendering.getScene()->getSceneNode(old.getRefData().getHandle());
|
||||||
|
|
||||||
|
@ -966,6 +969,38 @@ Animation* RenderingManager::getAnimation(const MWWorld::Ptr &ptr)
|
||||||
return anim;
|
return anim;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RenderingManager::screenshot(Image &image, int w, int h)
|
||||||
|
{
|
||||||
|
// Create a temporary render target. We do not use the RenderWindow since we want a specific size.
|
||||||
|
// Also, the GUI should not be visible (and it is only rendered on the RenderWindow's primary viewport)
|
||||||
|
const std::string tempName = "@temp";
|
||||||
|
Ogre::TexturePtr texture = Ogre::TextureManager::getSingleton().createManual(tempName,
|
||||||
|
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, Ogre::TEX_TYPE_2D, w, h, 0, Ogre::PF_R8G8B8, Ogre::TU_RENDERTARGET);
|
||||||
|
|
||||||
|
float oldAspect = mRendering.getCamera()->getAspectRatio();
|
||||||
|
|
||||||
|
mRendering.getCamera()->setAspectRatio(w / static_cast<float>(h));
|
||||||
|
|
||||||
|
Ogre::RenderTarget* rt = texture->getBuffer()->getRenderTarget();
|
||||||
|
Ogre::Viewport* vp = rt->addViewport(mRendering.getCamera());
|
||||||
|
vp->setBackgroundColour(mRendering.getViewport()->getBackgroundColour());
|
||||||
|
vp->setOverlaysEnabled(false);
|
||||||
|
vp->setVisibilityMask(mRendering.getViewport()->getVisibilityMask());
|
||||||
|
rt->update();
|
||||||
|
|
||||||
|
Ogre::PixelFormat pf = rt->suggestPixelFormat();
|
||||||
|
|
||||||
|
std::vector<Ogre::uchar> data;
|
||||||
|
data.resize(w * h * Ogre::PixelUtil::getNumElemBytes(pf));
|
||||||
|
|
||||||
|
Ogre::PixelBox pb(w, h, 1, pf, &data[0]);
|
||||||
|
rt->copyContentsToMemory(pb);
|
||||||
|
|
||||||
|
image.loadDynamicImage(&data[0], w, h, pf);
|
||||||
|
|
||||||
|
Ogre::TextureManager::getSingleton().remove(tempName);
|
||||||
|
mRendering.getCamera()->setAspectRatio(oldAspect);
|
||||||
|
}
|
||||||
|
|
||||||
void RenderingManager::playVideo(const std::string& name, bool allowSkipping)
|
void RenderingManager::playVideo(const std::string& name, bool allowSkipping)
|
||||||
{
|
{
|
||||||
|
|
|
@ -213,6 +213,7 @@ public:
|
||||||
void playVideo(const std::string& name, bool allowSkipping);
|
void playVideo(const std::string& name, bool allowSkipping);
|
||||||
void stopVideo();
|
void stopVideo();
|
||||||
void frameStarted(float dt, bool paused);
|
void frameStarted(float dt, bool paused);
|
||||||
|
void screenshot(Ogre::Image& image, int w, int h);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void windowResized(int x, int y);
|
virtual void windowResized(int x, int y);
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
|
|
||||||
#include <components/settings/settings.hpp>
|
#include <components/settings/settings.hpp>
|
||||||
|
|
||||||
|
#include <OgreImage.h>
|
||||||
|
|
||||||
#include "../mwbase/environment.hpp"
|
#include "../mwbase/environment.hpp"
|
||||||
#include "../mwbase/world.hpp"
|
#include "../mwbase/world.hpp"
|
||||||
#include "../mwbase/journal.hpp"
|
#include "../mwbase/journal.hpp"
|
||||||
|
@ -151,6 +153,13 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot
|
||||||
profile.mTimePlayed = mTimePlayed;
|
profile.mTimePlayed = mTimePlayed;
|
||||||
profile.mDescription = description;
|
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());
|
||||||
|
|
||||||
if (!slot)
|
if (!slot)
|
||||||
slot = mCharacterManager.getCurrentCharacter()->createSlot (profile);
|
slot = mCharacterManager.getCurrentCharacter()->createSlot (profile);
|
||||||
else
|
else
|
||||||
|
|
|
@ -1810,6 +1810,11 @@ namespace MWWorld
|
||||||
mRendering->frameStarted(dt, paused);
|
mRendering->frameStarted(dt, paused);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void World::screenshot(Ogre::Image &image, int w, int h)
|
||||||
|
{
|
||||||
|
mRendering->screenshot(image, w, h);
|
||||||
|
}
|
||||||
|
|
||||||
void World::activateDoor(const MWWorld::Ptr& door)
|
void World::activateDoor(const MWWorld::Ptr& door)
|
||||||
{
|
{
|
||||||
if (mDoorStates.find(door) != mDoorStates.end())
|
if (mDoorStates.find(door) != mDoorStates.end())
|
||||||
|
|
|
@ -504,6 +504,7 @@ namespace MWWorld
|
||||||
virtual void playVideo(const std::string& name, bool allowSkipping);
|
virtual void playVideo(const std::string& name, bool allowSkipping);
|
||||||
virtual void stopVideo();
|
virtual void stopVideo();
|
||||||
virtual void frameStarted (float dt, bool paused);
|
virtual void frameStarted (float dt, bool paused);
|
||||||
|
virtual void screenshot (Ogre::Image& image, int w, int h);
|
||||||
|
|
||||||
/// Find center of exterior cell above land surface
|
/// Find center of exterior cell above land surface
|
||||||
/// \return false if exterior with given name not exists, true otherwise
|
/// \return false if exterior with given name not exists, true otherwise
|
||||||
|
|
|
@ -19,6 +19,11 @@ void ESM::SavedGame::load (ESMReader &esm)
|
||||||
|
|
||||||
while (esm.isNextSub ("DEPE"))
|
while (esm.isNextSub ("DEPE"))
|
||||||
mContentFiles.push_back (esm.getHString());
|
mContentFiles.push_back (esm.getHString());
|
||||||
|
|
||||||
|
esm.getSubNameIs("SCRN");
|
||||||
|
esm.getSubHeader();
|
||||||
|
mScreenshot.resize(esm.getSubSize());
|
||||||
|
esm.getExact(&mScreenshot[0], mScreenshot.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESM::SavedGame::save (ESMWriter &esm) const
|
void ESM::SavedGame::save (ESMWriter &esm) const
|
||||||
|
@ -35,4 +40,7 @@ void ESM::SavedGame::save (ESMWriter &esm) const
|
||||||
iter!=mContentFiles.end(); ++iter)
|
iter!=mContentFiles.end(); ++iter)
|
||||||
esm.writeHNString ("DEPE", *iter);
|
esm.writeHNString ("DEPE", *iter);
|
||||||
|
|
||||||
|
esm.startSubRecord("SCRN");
|
||||||
|
esm.write(&mScreenshot[0], mScreenshot.size());
|
||||||
|
esm.endRecord("SCRN");
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,8 +31,7 @@ namespace ESM
|
||||||
TimeStamp mInGameTime;
|
TimeStamp mInGameTime;
|
||||||
double mTimePlayed;
|
double mTimePlayed;
|
||||||
std::string mDescription;
|
std::string mDescription;
|
||||||
|
std::vector<char> mScreenshot; // raw jpg-encoded data
|
||||||
/// \todo add field for screenshot
|
|
||||||
|
|
||||||
void load (ESMReader &esm);
|
void load (ESMReader &esm);
|
||||||
void save (ESMWriter &esm) const;
|
void save (ESMWriter &esm) const;
|
||||||
|
|
Loading…
Reference in a new issue