mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-02-21 18:09:39 +00:00
Move screenshots handling to the separate class
This commit is contained in:
parent
dc82cb61f4
commit
799bd3379c
9 changed files with 386 additions and 299 deletions
|
@ -19,7 +19,7 @@ source_group(game FILES ${GAME} ${GAME_HEADER})
|
||||||
|
|
||||||
add_openmw_dir (mwrender
|
add_openmw_dir (mwrender
|
||||||
actors objects renderingmanager animation rotatecontroller sky npcanimation vismask
|
actors objects renderingmanager animation rotatecontroller sky npcanimation vismask
|
||||||
creatureanimation effectmanager util renderinginterface pathgrid rendermode weaponanimation
|
creatureanimation effectmanager util renderinginterface pathgrid rendermode weaponanimation screenshotmanager
|
||||||
bulletdebugdraw globalmap characterpreview camera viewovershoulder localmap water terrainstorage ripplesimulation
|
bulletdebugdraw globalmap characterpreview camera viewovershoulder localmap water terrainstorage ripplesimulation
|
||||||
renderbin actoranimation landmanager navmesh actorspaths recastmesh fogmanager objectpaging
|
renderbin actoranimation landmanager navmesh actorspaths recastmesh fogmanager objectpaging
|
||||||
)
|
)
|
||||||
|
|
|
@ -497,7 +497,7 @@ namespace MWBase
|
||||||
|
|
||||||
/// \todo this does not belong here
|
/// \todo this does not belong here
|
||||||
virtual void screenshot (osg::Image* image, int w, int h) = 0;
|
virtual void screenshot (osg::Image* image, int w, int h) = 0;
|
||||||
virtual bool screenshot360 (osg::Image* image, std::string settingStr) = 0;
|
virtual bool screenshot360 (osg::Image* image) = 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
|
||||||
|
|
|
@ -333,12 +333,8 @@ namespace MWInput
|
||||||
|
|
||||||
void ActionManager::screenshot()
|
void ActionManager::screenshot()
|
||||||
{
|
{
|
||||||
bool regularScreenshot = true;
|
const std::string& settingStr = Settings::Manager::getString("screenshot type", "Video");
|
||||||
|
bool regularScreenshot = settingStr.size() == 0 || settingStr.compare("regular") == 0;
|
||||||
std::string settingStr;
|
|
||||||
|
|
||||||
settingStr = Settings::Manager::getString("screenshot type","Video");
|
|
||||||
regularScreenshot = settingStr.size() == 0 || settingStr.compare("regular") == 0;
|
|
||||||
|
|
||||||
if (regularScreenshot)
|
if (regularScreenshot)
|
||||||
{
|
{
|
||||||
|
@ -349,7 +345,7 @@ namespace MWInput
|
||||||
{
|
{
|
||||||
osg::ref_ptr<osg::Image> screenshot (new osg::Image);
|
osg::ref_ptr<osg::Image> screenshot (new osg::Image);
|
||||||
|
|
||||||
if (MWBase::Environment::get().getWorld()->screenshot360(screenshot.get(), settingStr))
|
if (MWBase::Environment::get().getWorld()->screenshot360(screenshot.get()))
|
||||||
{
|
{
|
||||||
(*mScreenCaptureOperation) (*(screenshot.get()), 0);
|
(*mScreenCaptureOperation) (*(screenshot.get()), 0);
|
||||||
// FIXME: mScreenCaptureHandler->getCaptureOperation() causes crash for some reason
|
// FIXME: mScreenCaptureHandler->getCaptureOperation() causes crash for some reason
|
||||||
|
|
|
@ -2,8 +2,6 @@
|
||||||
|
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <condition_variable>
|
|
||||||
#include <mutex>
|
|
||||||
|
|
||||||
#include <osg/Light>
|
#include <osg/Light>
|
||||||
#include <osg/LightModel>
|
#include <osg/LightModel>
|
||||||
|
@ -13,25 +11,20 @@
|
||||||
#include <osg/Group>
|
#include <osg/Group>
|
||||||
#include <osg/UserDataContainer>
|
#include <osg/UserDataContainer>
|
||||||
#include <osg/ComputeBoundsVisitor>
|
#include <osg/ComputeBoundsVisitor>
|
||||||
#include <osg/ShapeDrawable>
|
|
||||||
#include <osg/TextureCubeMap>
|
|
||||||
|
|
||||||
#include <osgUtil/LineSegmentIntersector>
|
#include <osgUtil/LineSegmentIntersector>
|
||||||
|
|
||||||
#include <osg/ImageUtils>
|
|
||||||
|
|
||||||
#include <osgViewer/Viewer>
|
#include <osgViewer/Viewer>
|
||||||
|
|
||||||
#include <components/nifosg/nifloader.hpp>
|
#include <components/nifosg/nifloader.hpp>
|
||||||
|
|
||||||
#include <components/debug/debuglog.hpp>
|
#include <components/debug/debuglog.hpp>
|
||||||
|
|
||||||
#include <components/misc/stringops.hpp>
|
|
||||||
|
|
||||||
#include <components/resource/resourcesystem.hpp>
|
#include <components/resource/resourcesystem.hpp>
|
||||||
#include <components/resource/imagemanager.hpp>
|
#include <components/resource/imagemanager.hpp>
|
||||||
#include <components/resource/scenemanager.hpp>
|
#include <components/resource/scenemanager.hpp>
|
||||||
#include <components/resource/keyframemanager.hpp>
|
#include <components/resource/keyframemanager.hpp>
|
||||||
|
|
||||||
#include <components/shader/shadermanager.hpp>
|
#include <components/shader/shadermanager.hpp>
|
||||||
|
|
||||||
#include <components/settings/settings.hpp>
|
#include <components/settings/settings.hpp>
|
||||||
|
@ -74,7 +67,7 @@
|
||||||
#include "recastmesh.hpp"
|
#include "recastmesh.hpp"
|
||||||
#include "fogmanager.hpp"
|
#include "fogmanager.hpp"
|
||||||
#include "objectpaging.hpp"
|
#include "objectpaging.hpp"
|
||||||
|
#include "screenshotmanager.hpp"
|
||||||
|
|
||||||
namespace MWRender
|
namespace MWRender
|
||||||
{
|
{
|
||||||
|
@ -311,6 +304,8 @@ namespace MWRender
|
||||||
if (Settings::Manager::getBool("view over shoulder", "Camera"))
|
if (Settings::Manager::getBool("view over shoulder", "Camera"))
|
||||||
mViewOverShoulderController.reset(new ViewOverShoulderController(mCamera.get()));
|
mViewOverShoulderController.reset(new ViewOverShoulderController(mCamera.get()));
|
||||||
|
|
||||||
|
mScreenshotManager.reset(new ScreenshotManager(viewer, mRootNode, sceneRoot, mResourceSystem, mWater.get()));
|
||||||
|
|
||||||
mViewer->setLightingMode(osgViewer::View::NO_LIGHT);
|
mViewer->setLightingMode(osgViewer::View::NO_LIGHT);
|
||||||
|
|
||||||
osg::ref_ptr<osg::LightSource> source = new osg::LightSource;
|
osg::ref_ptr<osg::LightSource> source = new osg::LightSource;
|
||||||
|
@ -695,298 +690,31 @@ namespace MWRender
|
||||||
mSky->setWaterHeight(height);
|
mSky->setWaterHeight(height);
|
||||||
}
|
}
|
||||||
|
|
||||||
class NotifyDrawCompletedCallback : public osg::Camera::DrawCallback
|
void RenderingManager::screenshot(osg::Image* image, int w, int h)
|
||||||
{
|
{
|
||||||
public:
|
mScreenshotManager->screenshot(image, w, h);
|
||||||
NotifyDrawCompletedCallback(unsigned int frame)
|
}
|
||||||
: mDone(false), mFrame(frame)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void operator () (osg::RenderInfo& renderInfo) const override
|
bool RenderingManager::screenshot360(osg::Image* image)
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(mMutex);
|
|
||||||
if (renderInfo.getState()->getFrameStamp()->getFrameNumber() >= mFrame)
|
|
||||||
{
|
|
||||||
mDone = true;
|
|
||||||
mCondition.notify_one();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void waitTillDone()
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> lock(mMutex);
|
|
||||||
if (mDone)
|
|
||||||
return;
|
|
||||||
mCondition.wait(lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
mutable std::condition_variable mCondition;
|
|
||||||
mutable std::mutex mMutex;
|
|
||||||
mutable bool mDone;
|
|
||||||
unsigned int mFrame;
|
|
||||||
};
|
|
||||||
|
|
||||||
bool RenderingManager::screenshot360(osg::Image* image, std::string settingStr)
|
|
||||||
{
|
{
|
||||||
int screenshotW = mViewer->getCamera()->getViewport()->width();
|
|
||||||
int screenshotH = mViewer->getCamera()->getViewport()->height();
|
|
||||||
int screenshotMapping = 0;
|
|
||||||
|
|
||||||
std::vector<std::string> settingArgs;
|
|
||||||
Misc::StringUtils::split(settingStr, settingArgs);
|
|
||||||
|
|
||||||
if (settingArgs.size() > 0)
|
|
||||||
{
|
|
||||||
std::string typeStrings[4] = {"spherical","cylindrical","planet","cubemap"};
|
|
||||||
bool found = false;
|
|
||||||
|
|
||||||
for (int i = 0; i < 4; ++i)
|
|
||||||
if (settingArgs[0].compare(typeStrings[i]) == 0)
|
|
||||||
{
|
|
||||||
screenshotMapping = i;
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!found)
|
|
||||||
{
|
|
||||||
Log(Debug::Warning) << "Wrong screenshot type: " << settingArgs[0] << ".";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// planet mapping needs higher resolution
|
|
||||||
int cubeSize = screenshotMapping == 2 ? screenshotW : screenshotW / 2;
|
|
||||||
|
|
||||||
if (settingArgs.size() > 1)
|
|
||||||
screenshotW = std::min(10000,std::atoi(settingArgs[1].c_str()));
|
|
||||||
|
|
||||||
if (settingArgs.size() > 2)
|
|
||||||
screenshotH = std::min(10000,std::atoi(settingArgs[2].c_str()));
|
|
||||||
|
|
||||||
if (settingArgs.size() > 3)
|
|
||||||
cubeSize = std::min(5000,std::atoi(settingArgs[3].c_str()));
|
|
||||||
|
|
||||||
if (mCamera->isVanityOrPreviewModeEnabled())
|
if (mCamera->isVanityOrPreviewModeEnabled())
|
||||||
{
|
{
|
||||||
Log(Debug::Warning) << "Spherical screenshots are not allowed in preview mode.";
|
Log(Debug::Warning) << "Spherical screenshots are not allowed in preview mode.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool rawCubemap = screenshotMapping == 3;
|
|
||||||
|
|
||||||
if (rawCubemap)
|
|
||||||
screenshotW = cubeSize * 6; // the image will consist of 6 cube sides in a row
|
|
||||||
else if (screenshotMapping == 2)
|
|
||||||
screenshotH = screenshotW; // use square resolution for planet mapping
|
|
||||||
|
|
||||||
std::vector<osg::ref_ptr<osg::Image>> images;
|
|
||||||
|
|
||||||
for (int i = 0; i < 6; ++i)
|
|
||||||
images.push_back(new osg::Image);
|
|
||||||
|
|
||||||
osg::Vec3 directions[6] = {
|
|
||||||
rawCubemap ? osg::Vec3(1,0,0) : osg::Vec3(0,0,1),
|
|
||||||
osg::Vec3(0,0,-1),
|
|
||||||
osg::Vec3(-1,0,0),
|
|
||||||
rawCubemap ? osg::Vec3(0,0,1) : osg::Vec3(1,0,0),
|
|
||||||
osg::Vec3(0,1,0),
|
|
||||||
osg::Vec3(0,-1,0)};
|
|
||||||
|
|
||||||
double rotations[] = {
|
|
||||||
-osg::PI / 2.0,
|
|
||||||
osg::PI / 2.0,
|
|
||||||
osg::PI,
|
|
||||||
0,
|
|
||||||
osg::PI / 2.0,
|
|
||||||
osg::PI / 2.0};
|
|
||||||
|
|
||||||
double fovBackup = mFieldOfView;
|
|
||||||
mFieldOfView = 90.0; // each cubemap side sees 90 degrees
|
|
||||||
|
|
||||||
int maskBackup = mPlayerAnimation->getObjectRoot()->getNodeMask();
|
int maskBackup = mPlayerAnimation->getObjectRoot()->getNodeMask();
|
||||||
|
|
||||||
if (mCamera->isFirstPerson())
|
if (mCamera->isFirstPerson())
|
||||||
mPlayerAnimation->getObjectRoot()->setNodeMask(0);
|
mPlayerAnimation->getObjectRoot()->setNodeMask(0);
|
||||||
|
|
||||||
for (int i = 0; i < 6; ++i) // for each cubemap side
|
mScreenshotManager->screenshot360(image);
|
||||||
{
|
|
||||||
osg::Matrixd transform = osg::Matrixd::rotate(osg::Vec3(0,0,-1),directions[i]);
|
|
||||||
|
|
||||||
if (!rawCubemap)
|
|
||||||
transform *= osg::Matrixd::rotate(rotations[i],osg::Vec3(0,0,-1));
|
|
||||||
|
|
||||||
osg::Image *sideImage = images[i].get();
|
|
||||||
screenshot(sideImage,cubeSize,cubeSize,transform);
|
|
||||||
|
|
||||||
if (!rawCubemap)
|
|
||||||
sideImage->flipHorizontal();
|
|
||||||
}
|
|
||||||
|
|
||||||
mPlayerAnimation->getObjectRoot()->setNodeMask(maskBackup);
|
mPlayerAnimation->getObjectRoot()->setNodeMask(maskBackup);
|
||||||
mFieldOfView = fovBackup;
|
|
||||||
|
|
||||||
if (rawCubemap) // for raw cubemap don't run on GPU, just merge the images
|
|
||||||
{
|
|
||||||
image->allocateImage(cubeSize * 6,cubeSize,images[0]->r(),images[0]->getPixelFormat(),images[0]->getDataType());
|
|
||||||
|
|
||||||
for (int i = 0; i < 6; ++i)
|
|
||||||
osg::copyImage(images[i].get(),0,0,0,images[i]->s(),images[i]->t(),images[i]->r(),image,i * cubeSize,0,0);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// run on GPU now:
|
|
||||||
|
|
||||||
osg::ref_ptr<osg::TextureCubeMap> cubeTexture (new osg::TextureCubeMap);
|
|
||||||
cubeTexture->setResizeNonPowerOfTwoHint(false);
|
|
||||||
|
|
||||||
cubeTexture->setFilter(osg::Texture::MIN_FILTER,osg::Texture::NEAREST);
|
|
||||||
cubeTexture->setFilter(osg::Texture::MAG_FILTER,osg::Texture::NEAREST);
|
|
||||||
|
|
||||||
cubeTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
|
|
||||||
cubeTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
|
|
||||||
|
|
||||||
for (int i = 0; i < 6; ++i)
|
|
||||||
cubeTexture->setImage(i,images[i].get());
|
|
||||||
|
|
||||||
osg::ref_ptr<osg::Camera> screenshotCamera (new osg::Camera);
|
|
||||||
osg::ref_ptr<osg::ShapeDrawable> quad (new osg::ShapeDrawable(new osg::Box(osg::Vec3(0,0,0),2.0)));
|
|
||||||
|
|
||||||
std::map<std::string, std::string> defineMap;
|
|
||||||
|
|
||||||
Shader::ShaderManager& shaderMgr = mResourceSystem->getSceneManager()->getShaderManager();
|
|
||||||
osg::ref_ptr<osg::Shader> fragmentShader (shaderMgr.getShader("s360_fragment.glsl",defineMap,osg::Shader::FRAGMENT));
|
|
||||||
osg::ref_ptr<osg::Shader> vertexShader (shaderMgr.getShader("s360_vertex.glsl", defineMap, osg::Shader::VERTEX));
|
|
||||||
osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet;
|
|
||||||
|
|
||||||
osg::ref_ptr<osg::Program> program (new osg::Program);
|
|
||||||
program->addShader(fragmentShader);
|
|
||||||
program->addShader(vertexShader);
|
|
||||||
stateset->setAttributeAndModes(program, osg::StateAttribute::ON);
|
|
||||||
|
|
||||||
stateset->addUniform(new osg::Uniform("cubeMap",0));
|
|
||||||
stateset->addUniform(new osg::Uniform("mapping",screenshotMapping));
|
|
||||||
stateset->setTextureAttributeAndModes(0,cubeTexture,osg::StateAttribute::ON);
|
|
||||||
|
|
||||||
quad->setStateSet(stateset);
|
|
||||||
quad->setUpdateCallback(nullptr);
|
|
||||||
|
|
||||||
screenshotCamera->addChild(quad);
|
|
||||||
|
|
||||||
renderCameraToImage(screenshotCamera,image,screenshotW,screenshotH);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderingManager::renderCameraToImage(osg::Camera *camera, osg::Image *image, int w, int h)
|
|
||||||
{
|
|
||||||
camera->setNodeMask(Mask_RenderToTexture);
|
|
||||||
camera->attach(osg::Camera::COLOR_BUFFER, image);
|
|
||||||
camera->setRenderOrder(osg::Camera::PRE_RENDER);
|
|
||||||
camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF);
|
|
||||||
camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT,osg::Camera::PIXEL_BUFFER_RTT);
|
|
||||||
|
|
||||||
camera->setViewport(0, 0, w, h);
|
|
||||||
|
|
||||||
osg::ref_ptr<osg::Texture2D> texture (new osg::Texture2D);
|
|
||||||
texture->setInternalFormat(GL_RGB);
|
|
||||||
texture->setTextureSize(w,h);
|
|
||||||
texture->setResizeNonPowerOfTwoHint(false);
|
|
||||||
texture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
|
|
||||||
texture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
|
|
||||||
camera->attach(osg::Camera::COLOR_BUFFER,texture);
|
|
||||||
|
|
||||||
image->setDataType(GL_UNSIGNED_BYTE);
|
|
||||||
image->setPixelFormat(texture->getInternalFormat());
|
|
||||||
|
|
||||||
mRootNode->addChild(camera);
|
|
||||||
|
|
||||||
// The draw needs to complete before we can copy back our image.
|
|
||||||
osg::ref_ptr<NotifyDrawCompletedCallback> callback (new NotifyDrawCompletedCallback(0));
|
|
||||||
camera->setFinalDrawCallback(callback);
|
|
||||||
|
|
||||||
MWBase::Environment::get().getWindowManager()->getLoadingScreen()->loadingOn(false);
|
|
||||||
|
|
||||||
mViewer->eventTraversal();
|
|
||||||
mViewer->updateTraversal();
|
|
||||||
mViewer->renderingTraversals();
|
|
||||||
callback->waitTillDone();
|
|
||||||
|
|
||||||
MWBase::Environment::get().getWindowManager()->getLoadingScreen()->loadingOff();
|
|
||||||
|
|
||||||
// now that we've "used up" the current frame, get a fresh framenumber for the next frame() following after the screenshot is completed
|
|
||||||
mViewer->advance(mViewer->getFrameStamp()->getSimulationTime());
|
|
||||||
|
|
||||||
camera->removeChildren(0, camera->getNumChildren());
|
|
||||||
mRootNode->removeChild(camera);
|
|
||||||
}
|
|
||||||
|
|
||||||
class ReadImageFromFramebufferCallback : public osg::Drawable::DrawCallback
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ReadImageFromFramebufferCallback(osg::Image* image, int width, int height)
|
|
||||||
: mWidth(width), mHeight(height), mImage(image)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
void drawImplementation(osg::RenderInfo& renderInfo,const osg::Drawable* /*drawable*/) const override
|
|
||||||
{
|
|
||||||
int screenW = renderInfo.getCurrentCamera()->getViewport()->width();
|
|
||||||
int screenH = renderInfo.getCurrentCamera()->getViewport()->height();
|
|
||||||
double imageaspect = (double)mWidth/(double)mHeight;
|
|
||||||
int leftPadding = std::max(0, static_cast<int>(screenW - screenH * imageaspect) / 2);
|
|
||||||
int topPadding = std::max(0, static_cast<int>(screenH - screenW / imageaspect) / 2);
|
|
||||||
int width = screenW - leftPadding*2;
|
|
||||||
int height = screenH - topPadding*2;
|
|
||||||
mImage->readPixels(leftPadding, topPadding, width, height, GL_RGB, GL_UNSIGNED_BYTE);
|
|
||||||
mImage->scaleImage(mWidth, mHeight, 1);
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
int mWidth;
|
|
||||||
int mHeight;
|
|
||||||
osg::ref_ptr<osg::Image> mImage;
|
|
||||||
};
|
|
||||||
|
|
||||||
void RenderingManager::screenshotFramebuffer(osg::Image* image, int w, int h)
|
|
||||||
{
|
|
||||||
osg::Camera* camera = mViewer->getCamera();
|
|
||||||
osg::ref_ptr<osg::Drawable> tempDrw = new osg::Drawable;
|
|
||||||
tempDrw->setDrawCallback(new ReadImageFromFramebufferCallback(image, w, h));
|
|
||||||
tempDrw->setCullingActive(false);
|
|
||||||
tempDrw->getOrCreateStateSet()->setRenderBinDetails(100, "RenderBin", osg::StateSet::USE_RENDERBIN_DETAILS); // so its after all scene bins but before POST_RENDER gui camera
|
|
||||||
camera->addChild(tempDrw);
|
|
||||||
osg::ref_ptr<NotifyDrawCompletedCallback> callback (new NotifyDrawCompletedCallback(mViewer->getFrameStamp()->getFrameNumber()));
|
|
||||||
camera->setFinalDrawCallback(callback);
|
|
||||||
mViewer->eventTraversal();
|
|
||||||
mViewer->updateTraversal();
|
|
||||||
mViewer->renderingTraversals();
|
|
||||||
callback->waitTillDone();
|
|
||||||
// now that we've "used up" the current frame, get a fresh frame number for the next frame() following after the screenshot is completed
|
|
||||||
mViewer->advance(mViewer->getFrameStamp()->getSimulationTime());
|
|
||||||
camera->removeChild(tempDrw);
|
|
||||||
camera->setFinalDrawCallback(nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RenderingManager::screenshot(osg::Image *image, int w, int h, osg::Matrixd cameraTransform)
|
|
||||||
{
|
|
||||||
osg::ref_ptr<osg::Camera> rttCamera (new osg::Camera);
|
|
||||||
rttCamera->setProjectionMatrixAsPerspective(mFieldOfView, w/float(h), mNearClip, mViewDistance);
|
|
||||||
rttCamera->setViewMatrix(mViewer->getCamera()->getViewMatrix() * cameraTransform);
|
|
||||||
|
|
||||||
rttCamera->setUpdateCallback(new NoTraverseCallback);
|
|
||||||
rttCamera->addChild(mSceneRoot);
|
|
||||||
|
|
||||||
rttCamera->addChild(mWater->getReflectionCamera());
|
|
||||||
rttCamera->addChild(mWater->getRefractionCamera());
|
|
||||||
|
|
||||||
rttCamera->setCullMask(mViewer->getCamera()->getCullMask() & (~Mask_GUI));
|
|
||||||
|
|
||||||
rttCamera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
||||||
|
|
||||||
renderCameraToImage(rttCamera.get(),image,w,h);
|
|
||||||
}
|
|
||||||
|
|
||||||
osg::Vec4f RenderingManager::getScreenBounds(const osg::BoundingBox &worldbb)
|
osg::Vec4f RenderingManager::getScreenBounds(const osg::BoundingBox &worldbb)
|
||||||
{
|
{
|
||||||
if (!worldbb.valid()) return osg::Vec4f();
|
if (!worldbb.valid()) return osg::Vec4f();
|
||||||
|
|
|
@ -74,6 +74,7 @@ namespace MWRender
|
||||||
class StateUpdater;
|
class StateUpdater;
|
||||||
|
|
||||||
class EffectManager;
|
class EffectManager;
|
||||||
|
class ScreenshotManager;
|
||||||
class FogManager;
|
class FogManager;
|
||||||
class SkyManager;
|
class SkyManager;
|
||||||
class NpcAnimation;
|
class NpcAnimation;
|
||||||
|
@ -148,9 +149,8 @@ namespace MWRender
|
||||||
void setWaterHeight(float level);
|
void setWaterHeight(float level);
|
||||||
|
|
||||||
/// Take a screenshot of w*h onto the given image, not including the GUI.
|
/// Take a screenshot of w*h onto the given image, not including the GUI.
|
||||||
void screenshot(osg::Image* image, int w, int h, osg::Matrixd cameraTransform=osg::Matrixd()); // make a new render at given size
|
void screenshot(osg::Image* image, int w, int h);
|
||||||
void screenshotFramebuffer(osg::Image* image, int w, int h); // copy directly from framebuffer and scale to given size
|
bool screenshot360(osg::Image* image);
|
||||||
bool screenshot360(osg::Image* image, std::string settingStr);
|
|
||||||
|
|
||||||
struct RayResult
|
struct RayResult
|
||||||
{
|
{
|
||||||
|
@ -248,8 +248,6 @@ namespace MWRender
|
||||||
|
|
||||||
void reportStats() const;
|
void reportStats() const;
|
||||||
|
|
||||||
void renderCameraToImage(osg::Camera *camera, osg::Image *image, int w, int h);
|
|
||||||
|
|
||||||
void updateNavMesh();
|
void updateNavMesh();
|
||||||
|
|
||||||
void updateRecastMesh();
|
void updateRecastMesh();
|
||||||
|
@ -281,6 +279,7 @@ namespace MWRender
|
||||||
std::unique_ptr<ObjectPaging> mObjectPaging;
|
std::unique_ptr<ObjectPaging> mObjectPaging;
|
||||||
std::unique_ptr<SkyManager> mSky;
|
std::unique_ptr<SkyManager> mSky;
|
||||||
std::unique_ptr<FogManager> mFog;
|
std::unique_ptr<FogManager> mFog;
|
||||||
|
std::unique_ptr<ScreenshotManager> mScreenshotManager;
|
||||||
std::unique_ptr<EffectManager> mEffectManager;
|
std::unique_ptr<EffectManager> mEffectManager;
|
||||||
std::unique_ptr<SceneUtil::ShadowManager> mShadowManager;
|
std::unique_ptr<SceneUtil::ShadowManager> mShadowManager;
|
||||||
osg::ref_ptr<NpcAnimation> mPlayerAnimation;
|
osg::ref_ptr<NpcAnimation> mPlayerAnimation;
|
||||||
|
|
324
apps/openmw/mwrender/screenshotmanager.cpp
Normal file
324
apps/openmw/mwrender/screenshotmanager.cpp
Normal file
|
@ -0,0 +1,324 @@
|
||||||
|
#include "screenshotmanager.hpp"
|
||||||
|
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
#include <osg/ImageUtils>
|
||||||
|
#include <osg/ShapeDrawable>
|
||||||
|
#include <osg/Texture2D>
|
||||||
|
#include <osg/TextureCubeMap>
|
||||||
|
|
||||||
|
#include <components/misc/stringops.hpp>
|
||||||
|
#include <components/resource/resourcesystem.hpp>
|
||||||
|
#include <components/resource/scenemanager.hpp>
|
||||||
|
#include <components/shader/shadermanager.hpp>
|
||||||
|
|
||||||
|
#include <components/settings/settings.hpp>
|
||||||
|
|
||||||
|
#include "../mwgui/loadingscreen.hpp"
|
||||||
|
#include "../mwbase/environment.hpp"
|
||||||
|
#include "../mwbase/windowmanager.hpp"
|
||||||
|
|
||||||
|
#include "util.hpp"
|
||||||
|
#include "vismask.hpp"
|
||||||
|
#include "water.hpp"
|
||||||
|
|
||||||
|
namespace MWRender
|
||||||
|
{
|
||||||
|
enum Screenshot360Type
|
||||||
|
{
|
||||||
|
Spherical,
|
||||||
|
Cylindrical,
|
||||||
|
Planet,
|
||||||
|
RawCubemap
|
||||||
|
};
|
||||||
|
|
||||||
|
class NotifyDrawCompletedCallback : public osg::Camera::DrawCallback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NotifyDrawCompletedCallback(unsigned int frame)
|
||||||
|
: mDone(false), mFrame(frame)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator () (osg::RenderInfo& renderInfo) const override
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mMutex);
|
||||||
|
if (renderInfo.getState()->getFrameStamp()->getFrameNumber() >= mFrame)
|
||||||
|
{
|
||||||
|
mDone = true;
|
||||||
|
mCondition.notify_one();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void waitTillDone()
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(mMutex);
|
||||||
|
if (mDone)
|
||||||
|
return;
|
||||||
|
mCondition.wait(lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
mutable std::condition_variable mCondition;
|
||||||
|
mutable std::mutex mMutex;
|
||||||
|
mutable bool mDone;
|
||||||
|
unsigned int mFrame;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ReadImageFromFramebufferCallback : public osg::Drawable::DrawCallback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ReadImageFromFramebufferCallback(osg::Image* image, int width, int height)
|
||||||
|
: mWidth(width), mHeight(height), mImage(image)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
void drawImplementation(osg::RenderInfo& renderInfo,const osg::Drawable* /*drawable*/) const override
|
||||||
|
{
|
||||||
|
int screenW = renderInfo.getCurrentCamera()->getViewport()->width();
|
||||||
|
int screenH = renderInfo.getCurrentCamera()->getViewport()->height();
|
||||||
|
double imageaspect = (double)mWidth/(double)mHeight;
|
||||||
|
int leftPadding = std::max(0, static_cast<int>(screenW - screenH * imageaspect) / 2);
|
||||||
|
int topPadding = std::max(0, static_cast<int>(screenH - screenW / imageaspect) / 2);
|
||||||
|
int width = screenW - leftPadding*2;
|
||||||
|
int height = screenH - topPadding*2;
|
||||||
|
mImage->readPixels(leftPadding, topPadding, width, height, GL_RGB, GL_UNSIGNED_BYTE);
|
||||||
|
mImage->scaleImage(mWidth, mHeight, 1);
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
int mWidth;
|
||||||
|
int mHeight;
|
||||||
|
osg::ref_ptr<osg::Image> mImage;
|
||||||
|
};
|
||||||
|
|
||||||
|
ScreenshotManager::ScreenshotManager(osgViewer::Viewer* viewer, osg::ref_ptr<osg::Group> rootNode, osg::ref_ptr<osg::Group> sceneRoot, Resource::ResourceSystem* resourceSystem, Water* water)
|
||||||
|
: mViewer(viewer)
|
||||||
|
, mRootNode(rootNode)
|
||||||
|
, mSceneRoot(sceneRoot)
|
||||||
|
, mResourceSystem(resourceSystem)
|
||||||
|
, mWater(water)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScreenshotManager::screenshot(osg::Image* image, int w, int h)
|
||||||
|
{
|
||||||
|
osg::Camera* camera = mViewer->getCamera();
|
||||||
|
osg::ref_ptr<osg::Drawable> tempDrw = new osg::Drawable;
|
||||||
|
tempDrw->setDrawCallback(new ReadImageFromFramebufferCallback(image, w, h));
|
||||||
|
tempDrw->setCullingActive(false);
|
||||||
|
tempDrw->getOrCreateStateSet()->setRenderBinDetails(100, "RenderBin", osg::StateSet::USE_RENDERBIN_DETAILS); // so its after all scene bins but before POST_RENDER gui camera
|
||||||
|
camera->addChild(tempDrw);
|
||||||
|
osg::ref_ptr<NotifyDrawCompletedCallback> callback (new NotifyDrawCompletedCallback(mViewer->getFrameStamp()->getFrameNumber()));
|
||||||
|
camera->setFinalDrawCallback(callback);
|
||||||
|
mViewer->eventTraversal();
|
||||||
|
mViewer->updateTraversal();
|
||||||
|
mViewer->renderingTraversals();
|
||||||
|
callback->waitTillDone();
|
||||||
|
// now that we've "used up" the current frame, get a fresh frame number for the next frame() following after the screenshot is completed
|
||||||
|
mViewer->advance(mViewer->getFrameStamp()->getSimulationTime());
|
||||||
|
camera->removeChild(tempDrw);
|
||||||
|
camera->setFinalDrawCallback(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScreenshotManager::screenshot360(osg::Image* image)
|
||||||
|
{
|
||||||
|
int screenshotW = mViewer->getCamera()->getViewport()->width();
|
||||||
|
int screenshotH = mViewer->getCamera()->getViewport()->height();
|
||||||
|
Screenshot360Type screenshotMapping = Spherical;
|
||||||
|
|
||||||
|
const std::string& settingStr = Settings::Manager::getString("screenshot type", "Video");
|
||||||
|
std::vector<std::string> settingArgs;
|
||||||
|
Misc::StringUtils::split(settingStr, settingArgs);
|
||||||
|
|
||||||
|
if (settingArgs.size() > 0)
|
||||||
|
{
|
||||||
|
std::string typeStrings[4] = {"spherical", "cylindrical", "planet", "cubemap"};
|
||||||
|
bool found = false;
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; ++i)
|
||||||
|
{
|
||||||
|
if (settingArgs[0].compare(typeStrings[i]) == 0)
|
||||||
|
{
|
||||||
|
screenshotMapping = static_cast<Screenshot360Type>(i);
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found)
|
||||||
|
{
|
||||||
|
Log(Debug::Warning) << "Wrong screenshot type: " << settingArgs[0] << ".";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// planet mapping needs higher resolution
|
||||||
|
int cubeSize = screenshotMapping == Planet ? screenshotW : screenshotW / 2;
|
||||||
|
|
||||||
|
if (settingArgs.size() > 1)
|
||||||
|
screenshotW = std::min(10000, std::atoi(settingArgs[1].c_str()));
|
||||||
|
|
||||||
|
if (settingArgs.size() > 2)
|
||||||
|
screenshotH = std::min(10000, std::atoi(settingArgs[2].c_str()));
|
||||||
|
|
||||||
|
if (settingArgs.size() > 3)
|
||||||
|
cubeSize = std::min(5000, std::atoi(settingArgs[3].c_str()));
|
||||||
|
|
||||||
|
bool rawCubemap = screenshotMapping == RawCubemap;
|
||||||
|
|
||||||
|
if (rawCubemap)
|
||||||
|
screenshotW = cubeSize * 6; // the image will consist of 6 cube sides in a row
|
||||||
|
else if (screenshotMapping == Planet)
|
||||||
|
screenshotH = screenshotW; // use square resolution for planet mapping
|
||||||
|
|
||||||
|
std::vector<osg::ref_ptr<osg::Image>> images;
|
||||||
|
|
||||||
|
for (int i = 0; i < 6; ++i)
|
||||||
|
images.push_back(new osg::Image);
|
||||||
|
|
||||||
|
osg::Vec3 directions[6] = {
|
||||||
|
rawCubemap ? osg::Vec3(1,0,0) : osg::Vec3(0,0,1),
|
||||||
|
osg::Vec3(0,0,-1),
|
||||||
|
osg::Vec3(-1,0,0),
|
||||||
|
rawCubemap ? osg::Vec3(0,0,1) : osg::Vec3(1,0,0),
|
||||||
|
osg::Vec3(0,1,0),
|
||||||
|
osg::Vec3(0,-1,0)};
|
||||||
|
|
||||||
|
double rotations[] = {
|
||||||
|
-osg::PI / 2.0,
|
||||||
|
osg::PI / 2.0,
|
||||||
|
osg::PI,
|
||||||
|
0,
|
||||||
|
osg::PI / 2.0,
|
||||||
|
osg::PI / 2.0 };
|
||||||
|
|
||||||
|
for (int i = 0; i < 6; ++i) // for each cubemap side
|
||||||
|
{
|
||||||
|
osg::Matrixd transform = osg::Matrixd::rotate(osg::Vec3(0,0,-1), directions[i]);
|
||||||
|
|
||||||
|
if (!rawCubemap)
|
||||||
|
transform *= osg::Matrixd::rotate(rotations[i],osg::Vec3(0,0,-1));
|
||||||
|
|
||||||
|
osg::Image *sideImage = images[i].get();
|
||||||
|
makeCubemapScreenshot(sideImage, cubeSize, cubeSize, transform);
|
||||||
|
|
||||||
|
if (!rawCubemap)
|
||||||
|
sideImage->flipHorizontal();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rawCubemap) // for raw cubemap don't run on GPU, just merge the images
|
||||||
|
{
|
||||||
|
image->allocateImage(cubeSize * 6,cubeSize,images[0]->r(),images[0]->getPixelFormat(),images[0]->getDataType());
|
||||||
|
|
||||||
|
for (int i = 0; i < 6; ++i)
|
||||||
|
osg::copyImage(images[i].get(),0,0,0,images[i]->s(),images[i]->t(),images[i]->r(),image,i * cubeSize,0,0);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// run on GPU now:
|
||||||
|
osg::ref_ptr<osg::TextureCubeMap> cubeTexture (new osg::TextureCubeMap);
|
||||||
|
cubeTexture->setResizeNonPowerOfTwoHint(false);
|
||||||
|
|
||||||
|
cubeTexture->setFilter(osg::Texture::MIN_FILTER,osg::Texture::NEAREST);
|
||||||
|
cubeTexture->setFilter(osg::Texture::MAG_FILTER,osg::Texture::NEAREST);
|
||||||
|
|
||||||
|
cubeTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
|
||||||
|
cubeTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
|
||||||
|
|
||||||
|
for (int i = 0; i < 6; ++i)
|
||||||
|
cubeTexture->setImage(i, images[i].get());
|
||||||
|
|
||||||
|
osg::ref_ptr<osg::Camera> screenshotCamera(new osg::Camera);
|
||||||
|
osg::ref_ptr<osg::ShapeDrawable> quad(new osg::ShapeDrawable(new osg::Box(osg::Vec3(0,0,0), 2.0)));
|
||||||
|
|
||||||
|
std::map<std::string, std::string> defineMap;
|
||||||
|
|
||||||
|
Shader::ShaderManager& shaderMgr = mResourceSystem->getSceneManager()->getShaderManager();
|
||||||
|
osg::ref_ptr<osg::Shader> fragmentShader(shaderMgr.getShader("s360_fragment.glsl", defineMap,osg::Shader::FRAGMENT));
|
||||||
|
osg::ref_ptr<osg::Shader> vertexShader(shaderMgr.getShader("s360_vertex.glsl", defineMap, osg::Shader::VERTEX));
|
||||||
|
osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet;
|
||||||
|
|
||||||
|
osg::ref_ptr<osg::Program> program(new osg::Program);
|
||||||
|
program->addShader(fragmentShader);
|
||||||
|
program->addShader(vertexShader);
|
||||||
|
stateset->setAttributeAndModes(program, osg::StateAttribute::ON);
|
||||||
|
|
||||||
|
stateset->addUniform(new osg::Uniform("cubeMap", 0));
|
||||||
|
stateset->addUniform(new osg::Uniform("mapping", screenshotMapping));
|
||||||
|
stateset->setTextureAttributeAndModes(0, cubeTexture, osg::StateAttribute::ON);
|
||||||
|
|
||||||
|
quad->setStateSet(stateset);
|
||||||
|
quad->setUpdateCallback(nullptr);
|
||||||
|
|
||||||
|
screenshotCamera->addChild(quad);
|
||||||
|
|
||||||
|
renderCameraToImage(screenshotCamera, image, screenshotW, screenshotH);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScreenshotManager::renderCameraToImage(osg::Camera *camera, osg::Image *image, int w, int h)
|
||||||
|
{
|
||||||
|
camera->setNodeMask(Mask_RenderToTexture);
|
||||||
|
camera->attach(osg::Camera::COLOR_BUFFER, image);
|
||||||
|
camera->setRenderOrder(osg::Camera::PRE_RENDER);
|
||||||
|
camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF);
|
||||||
|
camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT,osg::Camera::PIXEL_BUFFER_RTT);
|
||||||
|
|
||||||
|
camera->setViewport(0, 0, w, h);
|
||||||
|
|
||||||
|
osg::ref_ptr<osg::Texture2D> texture (new osg::Texture2D);
|
||||||
|
texture->setInternalFormat(GL_RGB);
|
||||||
|
texture->setTextureSize(w,h);
|
||||||
|
texture->setResizeNonPowerOfTwoHint(false);
|
||||||
|
texture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
|
||||||
|
texture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
|
||||||
|
camera->attach(osg::Camera::COLOR_BUFFER,texture);
|
||||||
|
|
||||||
|
image->setDataType(GL_UNSIGNED_BYTE);
|
||||||
|
image->setPixelFormat(texture->getInternalFormat());
|
||||||
|
|
||||||
|
mRootNode->addChild(camera);
|
||||||
|
|
||||||
|
// The draw needs to complete before we can copy back our image.
|
||||||
|
osg::ref_ptr<NotifyDrawCompletedCallback> callback (new NotifyDrawCompletedCallback(0));
|
||||||
|
camera->setFinalDrawCallback(callback);
|
||||||
|
|
||||||
|
MWBase::Environment::get().getWindowManager()->getLoadingScreen()->loadingOn(false);
|
||||||
|
|
||||||
|
mViewer->eventTraversal();
|
||||||
|
mViewer->updateTraversal();
|
||||||
|
mViewer->renderingTraversals();
|
||||||
|
callback->waitTillDone();
|
||||||
|
|
||||||
|
MWBase::Environment::get().getWindowManager()->getLoadingScreen()->loadingOff();
|
||||||
|
|
||||||
|
// now that we've "used up" the current frame, get a fresh framenumber for the next frame() following after the screenshot is completed
|
||||||
|
mViewer->advance(mViewer->getFrameStamp()->getSimulationTime());
|
||||||
|
|
||||||
|
camera->removeChildren(0, camera->getNumChildren());
|
||||||
|
mRootNode->removeChild(camera);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScreenshotManager::makeCubemapScreenshot(osg::Image *image, int w, int h, osg::Matrixd cameraTransform)
|
||||||
|
{
|
||||||
|
osg::ref_ptr<osg::Camera> rttCamera (new osg::Camera);
|
||||||
|
float nearClip = Settings::Manager::getFloat("near clip", "Camera");
|
||||||
|
float viewDistance = Settings::Manager::getFloat("viewing distance", "Camera");
|
||||||
|
// each cubemap side sees 90 degrees
|
||||||
|
rttCamera->setProjectionMatrixAsPerspective(90.0, w/float(h), nearClip, viewDistance);
|
||||||
|
rttCamera->setViewMatrix(mViewer->getCamera()->getViewMatrix() * cameraTransform);
|
||||||
|
|
||||||
|
rttCamera->setUpdateCallback(new NoTraverseCallback);
|
||||||
|
rttCamera->addChild(mSceneRoot);
|
||||||
|
|
||||||
|
rttCamera->addChild(mWater->getReflectionCamera());
|
||||||
|
rttCamera->addChild(mWater->getRefractionCamera());
|
||||||
|
|
||||||
|
rttCamera->setCullMask(mViewer->getCamera()->getCullMask() & (~Mask_GUI));
|
||||||
|
|
||||||
|
rttCamera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
|
|
||||||
|
renderCameraToImage(rttCamera.get(),image,w,h);
|
||||||
|
}
|
||||||
|
}
|
40
apps/openmw/mwrender/screenshotmanager.hpp
Normal file
40
apps/openmw/mwrender/screenshotmanager.hpp
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
#ifndef MWRENDER_SCREENSHOTMANAGER_H
|
||||||
|
#define MWRENDER_SCREENSHOTMANAGER_H
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include <osg/Group>
|
||||||
|
#include <osg/ref_ptr>
|
||||||
|
|
||||||
|
#include <osgViewer/Viewer>
|
||||||
|
|
||||||
|
namespace Resource
|
||||||
|
{
|
||||||
|
class ResourceSystem;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace MWRender
|
||||||
|
{
|
||||||
|
class Water;
|
||||||
|
|
||||||
|
class ScreenshotManager
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ScreenshotManager(osgViewer::Viewer* viewer, osg::ref_ptr<osg::Group> rootNode, osg::ref_ptr<osg::Group> sceneRoot, Resource::ResourceSystem* resourceSystem, Water* water);
|
||||||
|
|
||||||
|
void screenshot(osg::Image* image, int w, int h);
|
||||||
|
bool screenshot360(osg::Image* image);
|
||||||
|
|
||||||
|
private:
|
||||||
|
osg::ref_ptr<osgViewer::Viewer> mViewer;
|
||||||
|
osg::ref_ptr<osg::Group> mRootNode;
|
||||||
|
osg::ref_ptr<osg::Group> mSceneRoot;
|
||||||
|
Resource::ResourceSystem* mResourceSystem;
|
||||||
|
Water* mWater;
|
||||||
|
|
||||||
|
void renderCameraToImage(osg::Camera *camera, osg::Image *image, int w, int h);
|
||||||
|
void makeCubemapScreenshot(osg::Image* image, int w, int h, osg::Matrixd cameraTransform=osg::Matrixd());
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -2536,12 +2536,12 @@ namespace MWWorld
|
||||||
|
|
||||||
void World::screenshot(osg::Image* image, int w, int h)
|
void World::screenshot(osg::Image* image, int w, int h)
|
||||||
{
|
{
|
||||||
mRendering->screenshotFramebuffer(image, w, h);
|
mRendering->screenshot(image, w, h);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool World::screenshot360(osg::Image* image, std::string settingStr)
|
bool World::screenshot360(osg::Image* image)
|
||||||
{
|
{
|
||||||
return mRendering->screenshot360(image,settingStr);
|
return mRendering->screenshot360(image);
|
||||||
}
|
}
|
||||||
|
|
||||||
void World::activateDoor(const MWWorld::Ptr& door)
|
void World::activateDoor(const MWWorld::Ptr& door)
|
||||||
|
|
|
@ -597,7 +597,7 @@ namespace MWWorld
|
||||||
|
|
||||||
/// \todo this does not belong here
|
/// \todo this does not belong here
|
||||||
void screenshot (osg::Image* image, int w, int h) override;
|
void screenshot (osg::Image* image, int w, int h) override;
|
||||||
bool screenshot360 (osg::Image* image, std::string settingStr) override;
|
bool screenshot360 (osg::Image* image) override;
|
||||||
|
|
||||||
/// 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
|
||||||
|
|
Loading…
Reference in a new issue