diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index a4c3b91361..f96ddb27f8 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -19,7 +19,7 @@ source_group(game FILES ${GAME} ${GAME_HEADER}) add_openmw_dir (mwrender 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 renderbin actoranimation landmanager navmesh actorspaths recastmesh fogmanager objectpaging ) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 958fcfb0ea..a3b035a8dd 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -497,7 +497,7 @@ namespace MWBase /// \todo this does not belong here 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 /// \return false if exterior with given name not exists, true otherwise diff --git a/apps/openmw/mwinput/actionmanager.cpp b/apps/openmw/mwinput/actionmanager.cpp index b29aa58a29..e0fcc5ccfc 100644 --- a/apps/openmw/mwinput/actionmanager.cpp +++ b/apps/openmw/mwinput/actionmanager.cpp @@ -333,12 +333,8 @@ namespace MWInput void ActionManager::screenshot() { - bool regularScreenshot = true; - - std::string settingStr; - - settingStr = Settings::Manager::getString("screenshot type","Video"); - regularScreenshot = settingStr.size() == 0 || settingStr.compare("regular") == 0; + const std::string& settingStr = Settings::Manager::getString("screenshot type", "Video"); + bool regularScreenshot = settingStr.size() == 0 || settingStr.compare("regular") == 0; if (regularScreenshot) { @@ -349,7 +345,7 @@ namespace MWInput { osg::ref_ptr 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); // FIXME: mScreenCaptureHandler->getCaptureOperation() causes crash for some reason diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 217f0b73ca..6ce431d2e2 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -2,8 +2,6 @@ #include #include -#include -#include #include #include @@ -13,25 +11,20 @@ #include #include #include -#include -#include #include -#include - #include #include #include -#include - #include #include #include #include + #include #include @@ -74,7 +67,7 @@ #include "recastmesh.hpp" #include "fogmanager.hpp" #include "objectpaging.hpp" - +#include "screenshotmanager.hpp" namespace MWRender { @@ -311,6 +304,8 @@ namespace MWRender if (Settings::Manager::getBool("view over shoulder", "Camera")) mViewOverShoulderController.reset(new ViewOverShoulderController(mCamera.get())); + mScreenshotManager.reset(new ScreenshotManager(viewer, mRootNode, sceneRoot, mResourceSystem, mWater.get())); + mViewer->setLightingMode(osgViewer::View::NO_LIGHT); osg::ref_ptr source = new osg::LightSource; @@ -695,298 +690,31 @@ namespace MWRender mSky->setWaterHeight(height); } - class NotifyDrawCompletedCallback : public osg::Camera::DrawCallback + void RenderingManager::screenshot(osg::Image* image, int w, int h) { - public: - NotifyDrawCompletedCallback(unsigned int frame) - : mDone(false), mFrame(frame) - { - } + mScreenshotManager->screenshot(image, w, h); + } - void operator () (osg::RenderInfo& renderInfo) const override - { - std::lock_guard lock(mMutex); - if (renderInfo.getState()->getFrameStamp()->getFrameNumber() >= mFrame) - { - mDone = true; - mCondition.notify_one(); - } - } - - void waitTillDone() - { - std::unique_lock 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) + bool RenderingManager::screenshot360(osg::Image* image) { - int screenshotW = mViewer->getCamera()->getViewport()->width(); - int screenshotH = mViewer->getCamera()->getViewport()->height(); - int screenshotMapping = 0; - - std::vector 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()) { Log(Debug::Warning) << "Spherical screenshots are not allowed in preview mode."; 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> 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(); if (mCamera->isFirstPerson()) mPlayerAnimation->getObjectRoot()->setNodeMask(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(); - screenshot(sideImage,cubeSize,cubeSize,transform); - - if (!rawCubemap) - sideImage->flipHorizontal(); - } + mScreenshotManager->screenshot360(image); 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 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 screenshotCamera (new osg::Camera); - osg::ref_ptr quad (new osg::ShapeDrawable(new osg::Box(osg::Vec3(0,0,0),2.0))); - - std::map defineMap; - - Shader::ShaderManager& shaderMgr = mResourceSystem->getSceneManager()->getShaderManager(); - osg::ref_ptr fragmentShader (shaderMgr.getShader("s360_fragment.glsl",defineMap,osg::Shader::FRAGMENT)); - osg::ref_ptr vertexShader (shaderMgr.getShader("s360_vertex.glsl", defineMap, osg::Shader::VERTEX)); - osg::ref_ptr stateset = new osg::StateSet; - - osg::ref_ptr 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 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 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 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(screenW - screenH * imageaspect) / 2); - int topPadding = std::max(0, static_cast(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 mImage; - }; - - void RenderingManager::screenshotFramebuffer(osg::Image* image, int w, int h) - { - osg::Camera* camera = mViewer->getCamera(); - osg::ref_ptr 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 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 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) { if (!worldbb.valid()) return osg::Vec4f(); diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index ef28cf544e..39d1a0194e 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -74,6 +74,7 @@ namespace MWRender class StateUpdater; class EffectManager; + class ScreenshotManager; class FogManager; class SkyManager; class NpcAnimation; @@ -148,9 +149,8 @@ namespace MWRender 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, osg::Matrixd cameraTransform=osg::Matrixd()); // make a new render at given size - void screenshotFramebuffer(osg::Image* image, int w, int h); // copy directly from framebuffer and scale to given size - bool screenshot360(osg::Image* image, std::string settingStr); + void screenshot(osg::Image* image, int w, int h); + bool screenshot360(osg::Image* image); struct RayResult { @@ -248,8 +248,6 @@ namespace MWRender void reportStats() const; - void renderCameraToImage(osg::Camera *camera, osg::Image *image, int w, int h); - void updateNavMesh(); void updateRecastMesh(); @@ -281,6 +279,7 @@ namespace MWRender std::unique_ptr mObjectPaging; std::unique_ptr mSky; std::unique_ptr mFog; + std::unique_ptr mScreenshotManager; std::unique_ptr mEffectManager; std::unique_ptr mShadowManager; osg::ref_ptr mPlayerAnimation; diff --git a/apps/openmw/mwrender/screenshotmanager.cpp b/apps/openmw/mwrender/screenshotmanager.cpp new file mode 100644 index 0000000000..89b225da42 --- /dev/null +++ b/apps/openmw/mwrender/screenshotmanager.cpp @@ -0,0 +1,324 @@ +#include "screenshotmanager.hpp" + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#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 lock(mMutex); + if (renderInfo.getState()->getFrameStamp()->getFrameNumber() >= mFrame) + { + mDone = true; + mCondition.notify_one(); + } + } + + void waitTillDone() + { + std::unique_lock 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(screenW - screenH * imageaspect) / 2); + int topPadding = std::max(0, static_cast(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 mImage; + }; + + ScreenshotManager::ScreenshotManager(osgViewer::Viewer* viewer, osg::ref_ptr rootNode, osg::ref_ptr 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 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 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 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(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> 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 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 screenshotCamera(new osg::Camera); + osg::ref_ptr quad(new osg::ShapeDrawable(new osg::Box(osg::Vec3(0,0,0), 2.0))); + + std::map defineMap; + + Shader::ShaderManager& shaderMgr = mResourceSystem->getSceneManager()->getShaderManager(); + osg::ref_ptr fragmentShader(shaderMgr.getShader("s360_fragment.glsl", defineMap,osg::Shader::FRAGMENT)); + osg::ref_ptr vertexShader(shaderMgr.getShader("s360_vertex.glsl", defineMap, osg::Shader::VERTEX)); + osg::ref_ptr stateset = new osg::StateSet; + + osg::ref_ptr 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 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 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 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); + } +} diff --git a/apps/openmw/mwrender/screenshotmanager.hpp b/apps/openmw/mwrender/screenshotmanager.hpp new file mode 100644 index 0000000000..2ac50bdf0c --- /dev/null +++ b/apps/openmw/mwrender/screenshotmanager.hpp @@ -0,0 +1,40 @@ +#ifndef MWRENDER_SCREENSHOTMANAGER_H +#define MWRENDER_SCREENSHOTMANAGER_H + +#include + +#include +#include + +#include + +namespace Resource +{ + class ResourceSystem; +} + +namespace MWRender +{ + class Water; + + class ScreenshotManager + { + public: + ScreenshotManager(osgViewer::Viewer* viewer, osg::ref_ptr rootNode, osg::ref_ptr sceneRoot, Resource::ResourceSystem* resourceSystem, Water* water); + + void screenshot(osg::Image* image, int w, int h); + bool screenshot360(osg::Image* image); + + private: + osg::ref_ptr mViewer; + osg::ref_ptr mRootNode; + osg::ref_ptr 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 diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 8b95ee1221..bf5b6db738 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2536,12 +2536,12 @@ namespace MWWorld 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) diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 9f05014133..79c8a4980e 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -597,7 +597,7 @@ namespace MWWorld /// \todo this does not belong here 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 /// \return false if exterior with given name not exists, true otherwise