mirror of
				https://github.com/TES3MP/openmw-tes3mp.git
				synced 2025-10-31 22:56:44 +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 | ||||
|     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 | ||||
|     ) | ||||
|  |  | |||
|  | @ -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
 | ||||
|  |  | |||
|  | @ -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<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); | ||||
|                 // FIXME: mScreenCaptureHandler->getCaptureOperation() causes crash for some reason
 | ||||
|  |  | |||
|  | @ -2,8 +2,6 @@ | |||
| 
 | ||||
| #include <limits> | ||||
| #include <cstdlib> | ||||
| #include <condition_variable> | ||||
| #include <mutex> | ||||
| 
 | ||||
| #include <osg/Light> | ||||
| #include <osg/LightModel> | ||||
|  | @ -13,25 +11,20 @@ | |||
| #include <osg/Group> | ||||
| #include <osg/UserDataContainer> | ||||
| #include <osg/ComputeBoundsVisitor> | ||||
| #include <osg/ShapeDrawable> | ||||
| #include <osg/TextureCubeMap> | ||||
| 
 | ||||
| #include <osgUtil/LineSegmentIntersector> | ||||
| 
 | ||||
| #include <osg/ImageUtils> | ||||
| 
 | ||||
| #include <osgViewer/Viewer> | ||||
| 
 | ||||
| #include <components/nifosg/nifloader.hpp> | ||||
| 
 | ||||
| #include <components/debug/debuglog.hpp> | ||||
| 
 | ||||
| #include <components/misc/stringops.hpp> | ||||
| 
 | ||||
| #include <components/resource/resourcesystem.hpp> | ||||
| #include <components/resource/imagemanager.hpp> | ||||
| #include <components/resource/scenemanager.hpp> | ||||
| #include <components/resource/keyframemanager.hpp> | ||||
| 
 | ||||
| #include <components/shader/shadermanager.hpp> | ||||
| 
 | ||||
| #include <components/settings/settings.hpp> | ||||
|  | @ -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<osg::LightSource> 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<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) | ||||
|     bool RenderingManager::screenshot360(osg::Image* image) | ||||
|     { | ||||
|         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()) | ||||
|         { | ||||
|             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<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(); | ||||
| 
 | ||||
|         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<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 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) | ||||
|     { | ||||
|         if (!worldbb.valid()) return osg::Vec4f(); | ||||
|  |  | |||
|  | @ -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<ObjectPaging> mObjectPaging; | ||||
|         std::unique_ptr<SkyManager> mSky; | ||||
|         std::unique_ptr<FogManager> mFog; | ||||
|         std::unique_ptr<ScreenshotManager> mScreenshotManager; | ||||
|         std::unique_ptr<EffectManager> mEffectManager; | ||||
|         std::unique_ptr<SceneUtil::ShadowManager> mShadowManager; | ||||
|         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) | ||||
|     { | ||||
|         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) | ||||
|  |  | |||
|  | @ -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
 | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue