diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 648660669..fac6fcff2 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -744,17 +744,19 @@ namespace MWRender class NotifyDrawCompletedCallback : public osg::Camera::DrawCallback { public: - NotifyDrawCompletedCallback() - : mDone(false) + NotifyDrawCompletedCallback(unsigned int frame) + : mDone(false), mFrame(frame) { } virtual void operator () (osg::RenderInfo& renderInfo) const { - mMutex.lock(); - mDone = true; - mMutex.unlock(); - mCondition.signal(); + OpenThreads::ScopedLock lock(mMutex); + if (renderInfo.getState()->getFrameStamp()->getFrameNumber() >= mFrame) + { + mDone = true; + mCondition.signal(); + } } void waitTillDone() @@ -769,6 +771,7 @@ namespace MWRender mutable OpenThreads::Condition mCondition; mutable OpenThreads::Mutex mMutex; mutable bool mDone; + unsigned int mFrame; }; bool RenderingManager::screenshot360(osg::Image* image, std::string settingStr) @@ -948,7 +951,7 @@ namespace MWRender mRootNode->addChild(camera); // The draw needs to complete before we can copy back our image. - osg::ref_ptr callback (new NotifyDrawCompletedCallback); + osg::ref_ptr callback (new NotifyDrawCompletedCallback(0)); camera->setFinalDrawCallback(callback); MWBase::Environment::get().getWindowManager()->getLoadingScreen()->loadingOn(false); @@ -967,6 +970,51 @@ namespace MWRender mRootNode->removeChild(camera); } + class ReadImageFromFramebufferCallback : public osg::Drawable::DrawCallback + { + public: + ReadImageFromFramebufferCallback(osg::Image* image, int width, int height) + : mWidth(width), mHeight(height), mImage(image) + { + } + virtual void drawImplementation(osg::RenderInfo& renderInfo,const osg::Drawable* /*drawable*/) const + { + 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::screenshotScreen(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 framenumber 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); diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index d4b0b1840..2efe10cf9 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -144,7 +144,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()); + void screenshotScreen(osg::Image* image, int w, int h); // copie directly from framebuffer and scale to given size + void screenshot(osg::Image* image, int w, int h, osg::Matrixd cameraTransform=osg::Matrixd()); // make a new render at given size bool screenshot360(osg::Image* image, std::string settingStr); struct RayResult diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 49b55f2a9..73be0e6f4 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2554,7 +2554,7 @@ namespace MWWorld void World::screenshot(osg::Image* image, int w, int h) { - mRendering->screenshot(image, w, h); + mRendering->screenshotScreen(image, w, h); } bool World::screenshot360(osg::Image* image, std::string settingStr)