From 93d1c6d7e017a5be163cb820f1bda857a11d588f Mon Sep 17 00:00:00 2001
From: Alexei Kotov <alexdobrohotov@yandex.ru>
Date: Thu, 6 Jun 2024 11:23:05 +0300
Subject: [PATCH] Drop 360-degree screenshot support (#7720)

---
 CHANGELOG.md                                  |   1 +
 apps/openmw/engine.cpp                        |   4 +-
 apps/openmw/mwbase/world.hpp                  |   1 -
 apps/openmw/mwgui/loadingscreen.cpp           |  15 +-
 apps/openmw/mwgui/loadingscreen.hpp           |   3 +-
 apps/openmw/mwinput/actionmanager.cpp         |  25 +-
 apps/openmw/mwinput/actionmanager.hpp         |   6 +-
 apps/openmw/mwinput/inputmanagerimp.cpp       |  11 +-
 apps/openmw/mwinput/inputmanagerimp.hpp       |   6 +-
 apps/openmw/mwrender/renderingmanager.cpp     |  16 +-
 apps/openmw/mwrender/renderingmanager.hpp     |   2 -
 apps/openmw/mwrender/screenshotmanager.cpp    | 235 +-----------------
 apps/openmw/mwrender/screenshotmanager.hpp    |  22 +-
 apps/openmw/mwrender/water.cpp                |  10 -
 apps/openmw/mwrender/water.hpp                |   3 -
 apps/openmw/mwworld/worldimp.cpp              |   5 -
 apps/openmw/mwworld/worldimp.hpp              |   1 -
 components/CMakeLists.txt                     |   1 -
 .../loadinglistener/loadinglistener.hpp       |   2 +-
 components/settings/categories/video.hpp      |   2 -
 components/settings/screenshotsettings.hpp    |  29 ---
 components/settings/settings.cpp              |  41 ---
 components/settings/settings.hpp              |   9 -
 components/settings/settingvalue.hpp          |  20 --
 .../reference/modding/settings/video.rst      |   9 -
 files/settings-default.cfg                    |   4 -
 files/shaders/CMakeLists.txt                  |   2 -
 files/shaders/compatibility/s360.frag         |  21 --
 files/shaders/compatibility/s360.vert         |   9 -
 29 files changed, 31 insertions(+), 484 deletions(-)
 delete mode 100644 components/settings/screenshotsettings.hpp
 delete mode 100644 files/shaders/compatibility/s360.frag
 delete mode 100644 files/shaders/compatibility/s360.vert

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 28185204d5..3a0fcf8d0b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -252,6 +252,7 @@
     Task #7117: Replace boost::scoped_array with std::vector
     Task #7151: Do not use std::strerror to get errno error message
     Task #7394: Drop support for --fs-strict
+    Task #7720: Drop 360-degree screenshot support
 
 0.48.0
 ------
diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp
index 5090039e46..10c0ba5ab0 100644
--- a/apps/openmw/engine.cpp
+++ b/apps/openmw/engine.cpp
@@ -812,8 +812,8 @@ void OMW::Engine::prepareEngine()
         Version::getOpenmwVersionDescription(), shadersSupported, mCfgMgr);
     mEnvironment.setWindowManager(*mWindowManager);
 
-    mInputManager = std::make_unique<MWInput::InputManager>(mWindow, mViewer, mScreenCaptureHandler,
-        mScreenCaptureOperation, keybinderUser, keybinderUserExists, userGameControllerdb, gameControllerdb, mGrab);
+    mInputManager = std::make_unique<MWInput::InputManager>(mWindow, mViewer, mScreenCaptureHandler, keybinderUser,
+        keybinderUserExists, userGameControllerdb, gameControllerdb, mGrab);
     mEnvironment.setInputManager(*mInputManager);
 
     // Create sound system
diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp
index 93dbbd9d50..6ab5ab64fa 100644
--- a/apps/openmw/mwbase/world.hpp
+++ b/apps/openmw/mwbase/world.hpp
@@ -425,7 +425,6 @@ 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) = 0;
 
         /// Find default position inside exterior cell specified by name
         /// \return empty RefId if exterior with given name not exists, the cell's RefId otherwise
diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp
index 3d9cfa5c44..263e676e15 100644
--- a/apps/openmw/mwgui/loadingscreen.cpp
+++ b/apps/openmw/mwgui/loadingscreen.cpp
@@ -39,7 +39,6 @@ namespace MWGui
         , mLastRenderTime(0.0)
         , mLoadingOnTime(0.0)
         , mImportantLabel(false)
-        , mVisible(false)
         , mNestedLoadingCount(0)
         , mProgress(0)
         , mShowWallpaper(true)
@@ -142,7 +141,7 @@ namespace MWGui
         osg::BoundingSphere computeBound(const osg::Node&) const override { return osg::BoundingSphere(); }
     };
 
-    void LoadingScreen::loadingOn(bool visible)
+    void LoadingScreen::loadingOn()
     {
         // Early-out if already on
         if (mNestedLoadingCount++ > 0 && mMainWidget->getVisible())
@@ -161,17 +160,8 @@ namespace MWGui
             mOldIcoMax = ico->getMaximumNumOfObjectsToCompilePerFrame();
         }
 
-        mVisible = visible;
-        mLoadingBox->setVisible(mVisible);
         setVisible(true);
 
-        if (!mVisible)
-        {
-            mShowWallpaper = false;
-            draw();
-            return;
-        }
-
         mShowWallpaper = MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_NoGame;
 
         if (mShowWallpaper)
@@ -186,7 +176,6 @@ namespace MWGui
     {
         if (--mNestedLoadingCount > 0)
             return;
-        mLoadingBox->setVisible(true); // restore
 
         if (mLastRenderTime < mLoadingOnTime)
         {
@@ -327,7 +316,7 @@ namespace MWGui
 
     void LoadingScreen::draw()
     {
-        if (mVisible && !needToDrawLoadingScreen())
+        if (!needToDrawLoadingScreen())
             return;
 
         if (mShowWallpaper && mTimer.time_m() > mLastWallpaperChangeTime + 5000 * 1)
diff --git a/apps/openmw/mwgui/loadingscreen.hpp b/apps/openmw/mwgui/loadingscreen.hpp
index 2cd3f73576..35de331ee0 100644
--- a/apps/openmw/mwgui/loadingscreen.hpp
+++ b/apps/openmw/mwgui/loadingscreen.hpp
@@ -38,7 +38,7 @@ namespace MWGui
 
         /// Overridden from Loading::Listener, see the Loading::Listener documentation for usage details
         void setLabel(const std::string& label, bool important) override;
-        void loadingOn(bool visible = true) override;
+        void loadingOn() override;
         void loadingOff() override;
         void setProgressRange(size_t range) override;
         void setProgress(size_t value) override;
@@ -66,7 +66,6 @@ namespace MWGui
 
         bool mImportantLabel;
 
-        bool mVisible;
         int mNestedLoadingCount;
 
         size_t mProgress;
diff --git a/apps/openmw/mwinput/actionmanager.cpp b/apps/openmw/mwinput/actionmanager.cpp
index 5f9a3fde85..154888c44c 100644
--- a/apps/openmw/mwinput/actionmanager.cpp
+++ b/apps/openmw/mwinput/actionmanager.cpp
@@ -27,13 +27,11 @@
 namespace MWInput
 {
 
-    ActionManager::ActionManager(BindingsManager* bindingsManager,
-        osgViewer::ScreenCaptureHandler::CaptureOperation* screenCaptureOperation,
-        osg::ref_ptr<osgViewer::Viewer> viewer, osg::ref_ptr<osgViewer::ScreenCaptureHandler> screenCaptureHandler)
+    ActionManager::ActionManager(BindingsManager* bindingsManager, osg::ref_ptr<osgViewer::Viewer> viewer,
+        osg::ref_ptr<osgViewer::ScreenCaptureHandler> screenCaptureHandler)
         : mBindingsManager(bindingsManager)
         , mViewer(std::move(viewer))
         , mScreenCaptureHandler(std::move(screenCaptureHandler))
-        , mScreenCaptureOperation(screenCaptureOperation)
         , mTimeIdle(0.f)
     {
     }
@@ -170,23 +168,8 @@ namespace MWInput
 
     void ActionManager::screenshot()
     {
-        const Settings::ScreenshotSettings& settings = Settings::video().mScreenshotType;
-
-        if (settings.mType == Settings::ScreenshotType::Regular)
-        {
-            mScreenCaptureHandler->setFramesToCapture(1);
-            mScreenCaptureHandler->captureNextFrame(*mViewer);
-        }
-        else
-        {
-            osg::ref_ptr<osg::Image> screenshot(new osg::Image);
-
-            if (MWBase::Environment::get().getWorld()->screenshot360(screenshot.get()))
-            {
-                (*mScreenCaptureOperation)(*(screenshot.get()), 0);
-                // FIXME: mScreenCaptureHandler->getCaptureOperation() causes crash for some reason
-            }
-        }
+        mScreenCaptureHandler->setFramesToCapture(1);
+        mScreenCaptureHandler->captureNextFrame(*mViewer);
     }
 
     void ActionManager::toggleMainMenu()
diff --git a/apps/openmw/mwinput/actionmanager.hpp b/apps/openmw/mwinput/actionmanager.hpp
index d78c6906bf..eb21f7ef79 100644
--- a/apps/openmw/mwinput/actionmanager.hpp
+++ b/apps/openmw/mwinput/actionmanager.hpp
@@ -17,9 +17,8 @@ namespace MWInput
     class ActionManager
     {
     public:
-        ActionManager(BindingsManager* bindingsManager,
-            osgViewer::ScreenCaptureHandler::CaptureOperation* screenCaptureOperation,
-            osg::ref_ptr<osgViewer::Viewer> viewer, osg::ref_ptr<osgViewer::ScreenCaptureHandler> screenCaptureHandler);
+        ActionManager(BindingsManager* bindingsManager, osg::ref_ptr<osgViewer::Viewer> viewer,
+            osg::ref_ptr<osgViewer::ScreenCaptureHandler> screenCaptureHandler);
 
         void update(float dt);
 
@@ -48,7 +47,6 @@ namespace MWInput
         BindingsManager* mBindingsManager;
         osg::ref_ptr<osgViewer::Viewer> mViewer;
         osg::ref_ptr<osgViewer::ScreenCaptureHandler> mScreenCaptureHandler;
-        osgViewer::ScreenCaptureHandler::CaptureOperation* mScreenCaptureOperation;
 
         float mTimeIdle;
     };
diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp
index d0d6e7023d..328757a954 100644
--- a/apps/openmw/mwinput/inputmanagerimp.cpp
+++ b/apps/openmw/mwinput/inputmanagerimp.cpp
@@ -25,17 +25,14 @@
 namespace MWInput
 {
     InputManager::InputManager(SDL_Window* window, osg::ref_ptr<osgViewer::Viewer> viewer,
-        osg::ref_ptr<osgViewer::ScreenCaptureHandler> screenCaptureHandler,
-        osgViewer::ScreenCaptureHandler::CaptureOperation* screenCaptureOperation,
-        const std::filesystem::path& userFile, bool userFileExists,
-        const std::filesystem::path& userControllerBindingsFile, const std::filesystem::path& controllerBindingsFile,
-        bool grab)
+        osg::ref_ptr<osgViewer::ScreenCaptureHandler> screenCaptureHandler, const std::filesystem::path& userFile,
+        bool userFileExists, const std::filesystem::path& userControllerBindingsFile,
+        const std::filesystem::path& controllerBindingsFile, bool grab)
         : mControlsDisabled(false)
         , mInputWrapper(std::make_unique<SDLUtil::InputWrapper>(window, viewer, grab))
         , mBindingsManager(std::make_unique<BindingsManager>(userFile, userFileExists))
         , mControlSwitch(std::make_unique<ControlSwitch>())
-        , mActionManager(std::make_unique<ActionManager>(
-              mBindingsManager.get(), screenCaptureOperation, viewer, screenCaptureHandler))
+        , mActionManager(std::make_unique<ActionManager>(mBindingsManager.get(), viewer, screenCaptureHandler))
         , mKeyboardManager(std::make_unique<KeyboardManager>(mBindingsManager.get()))
         , mMouseManager(std::make_unique<MouseManager>(mBindingsManager.get(), mInputWrapper.get(), window))
         , mControllerManager(std::make_unique<ControllerManager>(
diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp
index f8f1411ebf..6131d77c65 100644
--- a/apps/openmw/mwinput/inputmanagerimp.hpp
+++ b/apps/openmw/mwinput/inputmanagerimp.hpp
@@ -49,10 +49,8 @@ namespace MWInput
     {
     public:
         InputManager(SDL_Window* window, osg::ref_ptr<osgViewer::Viewer> viewer,
-            osg::ref_ptr<osgViewer::ScreenCaptureHandler> screenCaptureHandler,
-            osgViewer::ScreenCaptureHandler::CaptureOperation* screenCaptureOperation,
-            const std::filesystem::path& userFile, bool userFileExists,
-            const std::filesystem::path& userControllerBindingsFile,
+            osg::ref_ptr<osgViewer::ScreenCaptureHandler> screenCaptureHandler, const std::filesystem::path& userFile,
+            bool userFileExists, const std::filesystem::path& userControllerBindingsFile,
             const std::filesystem::path& controllerBindingsFile, bool grab);
 
         ~InputManager() final;
diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp
index f6fced3b19..4a5fc18a86 100644
--- a/apps/openmw/mwrender/renderingmanager.cpp
+++ b/apps/openmw/mwrender/renderingmanager.cpp
@@ -521,8 +521,7 @@ namespace MWRender
 
         mCamera = std::make_unique<Camera>(mViewer->getCamera());
 
-        mScreenshotManager
-            = std::make_unique<ScreenshotManager>(viewer, mRootNode, sceneRoot, mResourceSystem, mWater.get());
+        mScreenshotManager = std::make_unique<ScreenshotManager>(viewer);
 
         mViewer->setLightingMode(osgViewer::View::NO_LIGHT);
 
@@ -1019,19 +1018,6 @@ namespace MWRender
         mScreenshotManager->screenshot(image, w, h);
     }
 
-    bool RenderingManager::screenshot360(osg::Image* image)
-    {
-        if (mCamera->isVanityOrPreviewModeEnabled())
-        {
-            Log(Debug::Warning) << "Spherical screenshots are not allowed in preview mode.";
-            return false;
-        }
-
-        mScreenshotManager->screenshot360(image);
-
-        return true;
-    }
-
     osg::Vec4f RenderingManager::getScreenBounds(const osg::BoundingBox& worldbb)
     {
         if (!worldbb.valid())
diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp
index 81adcc85be..2c68c060b3 100644
--- a/apps/openmw/mwrender/renderingmanager.hpp
+++ b/apps/openmw/mwrender/renderingmanager.hpp
@@ -3,7 +3,6 @@
 
 #include <span>
 
-#include <osg/Camera>
 #include <osg/Light>
 #include <osg/ref_ptr>
 
@@ -168,7 +167,6 @@ namespace MWRender
 
         /// Take a screenshot of w*h onto the given image, not including the GUI.
         void screenshot(osg::Image* image, int w, int h);
-        bool screenshot360(osg::Image* image);
 
         struct RayResult
         {
diff --git a/apps/openmw/mwrender/screenshotmanager.cpp b/apps/openmw/mwrender/screenshotmanager.cpp
index f478229daa..2c86a70f23 100644
--- a/apps/openmw/mwrender/screenshotmanager.cpp
+++ b/apps/openmw/mwrender/screenshotmanager.cpp
@@ -3,40 +3,16 @@
 #include <condition_variable>
 #include <mutex>
 
-#include <osg/ImageUtils>
-#include <osg/ShapeDrawable>
-#include <osg/Texture2D>
-#include <osg/TextureCubeMap>
-
-#include <components/loadinglistener/loadinglistener.hpp>
-#include <components/misc/strings/algorithm.hpp>
-#include <components/misc/strings/conversion.hpp>
-#include <components/resource/resourcesystem.hpp>
-#include <components/resource/scenemanager.hpp>
-#include <components/sceneutil/depth.hpp>
-#include <components/settings/values.hpp>
-#include <components/shader/shadermanager.hpp>
 #include <components/stereo/multiview.hpp>
 #include <components/stereo/stereomanager.hpp>
 
 #include "../mwbase/environment.hpp"
-#include "../mwbase/windowmanager.hpp"
 #include "../mwbase/world.hpp"
 
 #include "postprocessor.hpp"
-#include "util.hpp"
-#include "vismask.hpp"
-#include "water.hpp"
 
 namespace MWRender
 {
-    enum class Screenshot360Type
-    {
-        Spherical,
-        Cylindrical,
-        Planet,
-        RawCubemap
-    };
 
     class NotifyDrawCompletedCallback : public osg::Camera::DrawCallback
     {
@@ -113,14 +89,9 @@ namespace MWRender
         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)
+    ScreenshotManager::ScreenshotManager(osgViewer::Viewer* viewer)
         : mViewer(viewer)
-        , mRootNode(std::move(rootNode))
-        , mSceneRoot(std::move(sceneRoot))
         , mDrawCompleteCallback(new NotifyDrawCompletedCallback)
-        , mResourceSystem(resourceSystem)
-        , mWater(water)
     {
     }
 
@@ -135,204 +106,18 @@ namespace MWRender
         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);
-        traversalsAndWait(mViewer->getFrameStamp()->getFrameNumber());
+
+        // Ref https://gitlab.com/OpenMW/openmw/-/issues/6013
+        mDrawCompleteCallback->reset(mViewer->getFrameStamp()->getFrameNumber());
+        mViewer->getCamera()->setFinalDrawCallback(mDrawCompleteCallback);
+        mViewer->eventTraversal();
+        mViewer->updateTraversal();
+        mViewer->renderingTraversals();
+        mDrawCompleteCallback->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);
     }
-
-    bool ScreenshotManager::screenshot360(osg::Image* image)
-    {
-        const Settings::ScreenshotSettings& settings = Settings::video().mScreenshotType;
-
-        Screenshot360Type screenshotMapping = Screenshot360Type::Spherical;
-
-        switch (settings.mType)
-        {
-            case Settings::ScreenshotType::Regular:
-                Log(Debug::Warning) << "Wrong screenshot 360 type: regular.";
-                return false;
-            case Settings::ScreenshotType::Cylindrical:
-                screenshotMapping = Screenshot360Type::Cylindrical;
-                break;
-            case Settings::ScreenshotType::Spherical:
-                screenshotMapping = Screenshot360Type::Spherical;
-                break;
-            case Settings::ScreenshotType::Planet:
-                screenshotMapping = Screenshot360Type::Planet;
-                break;
-            case Settings::ScreenshotType::Cubemap:
-                screenshotMapping = Screenshot360Type::RawCubemap;
-                break;
-        }
-
-        int screenshotW = mViewer->getCamera()->getViewport()->width();
-
-        if (settings.mWidth.has_value())
-            screenshotW = *settings.mWidth;
-
-        int screenshotH = mViewer->getCamera()->getViewport()->height();
-
-        if (settings.mHeight.has_value())
-            screenshotH = *settings.mHeight;
-
-        // planet mapping needs higher resolution
-        const int cubeSize = screenshotMapping == Screenshot360Type::Planet ? screenshotW : screenshotW / 2;
-        const bool rawCubemap = screenshotMapping == Screenshot360Type::RawCubemap;
-
-        if (rawCubemap)
-            screenshotW = cubeSize * 6; // the image will consist of 6 cube sides in a row
-        else if (screenshotMapping == Screenshot360Type::Planet)
-            screenshotH = screenshotW; // use square resolution for planet mapping
-
-        std::vector<osg::ref_ptr<osg::Image>> images;
-        images.reserve(6);
-
-        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)));
-
-        osg::ref_ptr<osg::StateSet> stateset = quad->getOrCreateStateSet();
-
-        Shader::ShaderManager& shaderMgr = mResourceSystem->getSceneManager()->getShaderManager();
-        stateset->setAttributeAndModes(shaderMgr.getProgram("s360"), osg::StateAttribute::ON);
-
-        stateset->addUniform(new osg::Uniform("cubeMap", 0));
-        stateset->addUniform(new osg::Uniform("mapping", static_cast<int>(screenshotMapping)));
-        stateset->setTextureAttributeAndModes(0, cubeTexture, osg::StateAttribute::ON);
-
-        screenshotCamera->addChild(quad);
-
-        renderCameraToImage(screenshotCamera, image, screenshotW, screenshotH);
-
-        return true;
-    }
-
-    void ScreenshotManager::traversalsAndWait(unsigned int frame)
-    {
-        // Ref https://gitlab.com/OpenMW/openmw/-/issues/6013
-        mDrawCompleteCallback->reset(frame);
-        mViewer->getCamera()->setFinalDrawCallback(mDrawCompleteCallback);
-
-        mViewer->eventTraversal();
-        mViewer->updateTraversal();
-        mViewer->renderingTraversals();
-        mDrawCompleteCallback->waitTillDone();
-    }
-
-    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);
-
-        SceneUtil::setCameraClearDepth(camera);
-
-        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);
-        texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
-        texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
-        camera->attach(osg::Camera::COLOR_BUFFER, texture);
-
-        image->setDataType(GL_UNSIGNED_BYTE);
-        image->setPixelFormat(texture->getInternalFormat());
-
-        mRootNode->addChild(camera);
-
-        MWBase::Environment::get().getWindowManager()->getLoadingScreen()->loadingOn(false);
-
-        // The draw needs to complete before we can copy back our image.
-        traversalsAndWait(0);
-
-        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, const osg::Matrixd& cameraTransform)
-    {
-        osg::ref_ptr<osg::Camera> rttCamera(new osg::Camera);
-        const float nearClip = Settings::camera().mNearClip;
-        const float viewDistance = Settings::camera().mViewingDistance;
-        // each cubemap side sees 90 degrees
-        if (SceneUtil::AutoDepth::isReversed())
-            rttCamera->setProjectionMatrix(
-                SceneUtil::getReversedZProjectionMatrixAsPerspectiveInf(90.0, w / float(h), nearClip));
-        else
-            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->getReflectionNode());
-        rttCamera->addChild(mWater->getRefractionNode());
-
-        rttCamera->setCullMask(
-            MWBase::Environment::get().getWindowManager()->getCullMask() & ~(Mask_GUI | Mask_FirstPerson));
-
-        rttCamera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
-
-        renderCameraToImage(rttCamera.get(), image, w, h);
-    }
 }
diff --git a/apps/openmw/mwrender/screenshotmanager.hpp b/apps/openmw/mwrender/screenshotmanager.hpp
index 72e5b91637..f41ccf0045 100644
--- a/apps/openmw/mwrender/screenshotmanager.hpp
+++ b/apps/openmw/mwrender/screenshotmanager.hpp
@@ -1,45 +1,25 @@
 #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 NotifyDrawCompletedCallback;
 
     class ScreenshotManager
     {
     public:
-        ScreenshotManager(osgViewer::Viewer* viewer, osg::ref_ptr<osg::Group> rootNode,
-            osg::ref_ptr<osg::Group> sceneRoot, Resource::ResourceSystem* resourceSystem, Water* water);
+        ScreenshotManager(osgViewer::Viewer* viewer);
         ~ScreenshotManager();
 
         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;
         osg::ref_ptr<NotifyDrawCompletedCallback> mDrawCompleteCallback;
-        Resource::ResourceSystem* mResourceSystem;
-        Water* mWater;
-
-        void traversalsAndWait(unsigned int frame);
-        void renderCameraToImage(osg::Camera* camera, osg::Image* image, int w, int h);
-        void makeCubemapScreenshot(
-            osg::Image* image, int w, int h, const osg::Matrixd& cameraTransform = osg::Matrixd());
     };
 }
 
diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp
index f89c2250b0..c14f031fd1 100644
--- a/apps/openmw/mwrender/water.cpp
+++ b/apps/openmw/mwrender/water.cpp
@@ -568,16 +568,6 @@ namespace MWRender
         updateVisible();
     }
 
-    osg::Node* Water::getReflectionNode()
-    {
-        return mReflection;
-    }
-
-    osg::Node* Water::getRefractionNode()
-    {
-        return mRefraction;
-    }
-
     osg::Vec3d Water::getPosition() const
     {
         return mWaterNode->getPosition();
diff --git a/apps/openmw/mwrender/water.hpp b/apps/openmw/mwrender/water.hpp
index d3241bf3a7..3572f28420 100644
--- a/apps/openmw/mwrender/water.hpp
+++ b/apps/openmw/mwrender/water.hpp
@@ -117,9 +117,6 @@ namespace MWRender
 
         void update(float dt, bool paused);
 
-        osg::Node* getReflectionNode();
-        osg::Node* getRefractionNode();
-
         osg::Vec3d getPosition() const;
 
         void processChangedSettings(const Settings::CategorySettingVector& settings);
diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp
index c649afe6dc..6ba8cee6ea 100644
--- a/apps/openmw/mwworld/worldimp.cpp
+++ b/apps/openmw/mwworld/worldimp.cpp
@@ -2385,11 +2385,6 @@ namespace MWWorld
         mRendering->screenshot(image, w, h);
     }
 
-    bool World::screenshot360(osg::Image* image)
-    {
-        return mRendering->screenshot360(image);
-    }
-
     void World::activateDoor(const MWWorld::Ptr& door)
     {
         auto state = door.getClass().getDoorState(door);
diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp
index ebc92bcdbd..20e33ad66c 100644
--- a/apps/openmw/mwworld/worldimp.hpp
+++ b/apps/openmw/mwworld/worldimp.hpp
@@ -517,7 +517,6 @@ namespace MWWorld
 
         /// \todo this does not belong here
         void screenshot(osg::Image* image, int w, int h) 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
diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt
index 7380fdad68..f669bfdd04 100644
--- a/components/CMakeLists.txt
+++ b/components/CMakeLists.txt
@@ -103,7 +103,6 @@ add_component_dir (settings
     settingvalue
     shadermanager
     values
-    screenshotsettings
     windowmode
     )
 
diff --git a/components/loadinglistener/loadinglistener.hpp b/components/loadinglistener/loadinglistener.hpp
index 423688ad1b..9ccfb25a5c 100644
--- a/components/loadinglistener/loadinglistener.hpp
+++ b/components/loadinglistener/loadinglistener.hpp
@@ -22,7 +22,7 @@ namespace Loading
         /// periodically.
         /// @note It is best to use the ScopedLoad object instead of using loadingOn()/loadingOff() directly,
         ///  so that the loading is exception safe.
-        virtual void loadingOn(bool visible = true) {}
+        virtual void loadingOn() {}
         virtual void loadingOff() {}
 
         /// Set the total range of progress (e.g. the number of objects to load).
diff --git a/components/settings/categories/video.hpp b/components/settings/categories/video.hpp
index cb12ea079c..fed3fd8489 100644
--- a/components/settings/categories/video.hpp
+++ b/components/settings/categories/video.hpp
@@ -3,7 +3,6 @@
 
 #include <components/sdlutil/vsyncmode.hpp>
 #include <components/settings/sanitizerimpl.hpp>
-#include <components/settings/screenshotsettings.hpp>
 #include <components/settings/settingvalue.hpp>
 #include <components/settings/windowmode.hpp>
 
@@ -32,7 +31,6 @@ namespace Settings
         SettingValue<float> mFramerateLimit{ mIndex, "Video", "framerate limit", makeMaxSanitizerFloat(0) };
         SettingValue<float> mContrast{ mIndex, "Video", "contrast", makeMaxStrictSanitizerFloat(0) };
         SettingValue<float> mGamma{ mIndex, "Video", "gamma", makeMaxStrictSanitizerFloat(0) };
-        SettingValue<ScreenshotSettings> mScreenshotType{ mIndex, "Video", "screenshot type" };
     };
 }
 
diff --git a/components/settings/screenshotsettings.hpp b/components/settings/screenshotsettings.hpp
deleted file mode 100644
index 6475ace005..0000000000
--- a/components/settings/screenshotsettings.hpp
+++ /dev/null
@@ -1,29 +0,0 @@
-#ifndef OPENMW_COMPONENTS_SETTINGS_SCREENSHOTSETTINGS_H
-#define OPENMW_COMPONENTS_SETTINGS_SCREENSHOTSETTINGS_H
-
-#include <optional>
-#include <ostream>
-
-namespace Settings
-{
-    enum class ScreenshotType
-    {
-        Regular,
-        Cylindrical,
-        Spherical,
-        Planet,
-        Cubemap,
-    };
-
-    struct ScreenshotSettings
-    {
-        ScreenshotType mType;
-        std::optional<int> mWidth;
-        std::optional<int> mHeight;
-        std::optional<int> mCubeSize;
-
-        auto operator<=>(const ScreenshotSettings& value) const = default;
-    };
-}
-
-#endif
diff --git a/components/settings/settings.cpp b/components/settings/settings.cpp
index bdbd0f7353..ed73f8a800 100644
--- a/components/settings/settings.cpp
+++ b/components/settings/settings.cpp
@@ -119,23 +119,6 @@ namespace Settings
             Log(Debug::Warning) << "Invalid HRTF mode value: " << static_cast<int>(value) << ", fallback to auto (-1)";
             return -1;
         }
-
-        ScreenshotType parseScreenshotType(std::string_view value)
-        {
-            if (value == "regular")
-                return ScreenshotType::Regular;
-            if (value == "spherical")
-                return ScreenshotType::Spherical;
-            if (value == "cylindrical")
-                return ScreenshotType::Cylindrical;
-            if (value == "planet")
-                return ScreenshotType::Planet;
-            if (value == "cubemap")
-                return ScreenshotType::Cubemap;
-
-            Log(Debug::Warning) << "Invalid screenshot type: " << value << ", fallback to regular";
-            return ScreenshotType::Regular;
-        }
     }
 
     CategorySettingValueMap Manager::mDefaultSettings = CategorySettingValueMap();
@@ -576,28 +559,4 @@ namespace Settings
         Log(Debug::Warning) << "Unknown lighting method '" << value << "', returning fallback '" << fallback << "'";
         return SceneUtil::LightingMethod::PerObjectUniform;
     }
-
-    ScreenshotSettings parseScreenshotSettings(std::string_view value)
-    {
-        std::vector<std::string_view> settingArgs;
-        Misc::StringUtils::split(value, settingArgs);
-
-        ScreenshotSettings result;
-
-        if (settingArgs.size() > 0)
-            result.mType = parseScreenshotType(settingArgs[0]);
-        else
-            result.mType = ScreenshotType::Regular;
-
-        if (settingArgs.size() > 1)
-            result.mWidth = std::min(10000, Misc::StringUtils::toNumeric<int>(settingArgs[1], 0));
-
-        if (settingArgs.size() > 2)
-            result.mHeight = std::min(10000, Misc::StringUtils::toNumeric<int>(settingArgs[2], 0));
-
-        if (settingArgs.size() > 3)
-            result.mCubeSize = std::min(5000, Misc::StringUtils::toNumeric<int>(settingArgs[3], 0));
-
-        return result;
-    }
 }
diff --git a/components/settings/settings.hpp b/components/settings/settings.hpp
index bcdcbb16d1..5be2130259 100644
--- a/components/settings/settings.hpp
+++ b/components/settings/settings.hpp
@@ -5,7 +5,6 @@
 #include "gyroscopeaxis.hpp"
 #include "hrtfmode.hpp"
 #include "navmeshrendermode.hpp"
-#include "screenshotsettings.hpp"
 #include "windowmode.hpp"
 
 #include <components/detournavigator/collisionshapetype.hpp>
@@ -264,14 +263,6 @@ namespace Settings
             return SDLUtil::VSyncMode::Disabled;
         return static_cast<SDLUtil::VSyncMode>(value);
     }
-
-    ScreenshotSettings parseScreenshotSettings(std::string_view value);
-
-    template <>
-    inline ScreenshotSettings Manager::getImpl<ScreenshotSettings>(std::string_view setting, std::string_view category)
-    {
-        return parseScreenshotSettings(getString(setting, category));
-    }
 }
 
 #endif // COMPONENTS_SETTINGS_H
diff --git a/components/settings/settingvalue.hpp b/components/settings/settingvalue.hpp
index 8183e8c1ac..b99de6eaa7 100644
--- a/components/settings/settingvalue.hpp
+++ b/components/settings/settingvalue.hpp
@@ -44,7 +44,6 @@ namespace Settings
         HrtfMode,
         WindowMode,
         VSyncMode,
-        ScreenshotSettings,
     };
 
     template <class T>
@@ -176,12 +175,6 @@ namespace Settings
         return SettingValueType::VSyncMode;
     }
 
-    template <>
-    inline constexpr SettingValueType getSettingValueType<ScreenshotSettings>()
-    {
-        return SettingValueType::ScreenshotSettings;
-    }
-
     inline constexpr std::string_view getSettingValueTypeName(SettingValueType type)
     {
         switch (type)
@@ -228,8 +221,6 @@ namespace Settings
                 return "window mode";
             case SettingValueType::VSyncMode:
                 return "vsync mode";
-            case SettingValueType::ScreenshotSettings:
-                return "screenshot settings";
         }
         return "unsupported";
     }
@@ -397,17 +388,6 @@ namespace Settings
                     }
                     return stream;
                 }
-                else if constexpr (std::is_same_v<T, ScreenshotSettings>)
-                {
-                    stream << "ScreenshotSettings{ .mType = " << static_cast<int>(value.mValue.mType);
-                    if (value.mValue.mWidth.has_value())
-                        stream << ", .mWidth = " << *value.mValue.mWidth;
-                    if (value.mValue.mHeight.has_value())
-                        stream << ", .mHeight = " << *value.mValue.mHeight;
-                    if (value.mValue.mCubeSize.has_value())
-                        stream << ", .mCubeSize = " << *value.mValue.mCubeSize;
-                    return stream << " }";
-                }
                 else
                     return stream << value.mValue;
             }
diff --git a/docs/source/reference/modding/settings/video.rst b/docs/source/reference/modding/settings/video.rst
index 46016247ff..76b20bb1da 100644
--- a/docs/source/reference/modding/settings/video.rst
+++ b/docs/source/reference/modding/settings/video.rst
@@ -194,12 +194,3 @@ Gamma is an exponent that makes colors brighter if greater than 1.0 and darker i
 This setting can be changed in the Detail tab of the Video panel of the Options menu.
 It has been reported to not work on some Linux systems, 
 and therefore the in-game setting in the Options menu has been disabled on Linux systems.
-
-screenshot type
----------------
-
-:Type:		screenshot settings
-:Default:	regular
-
-Type of screenshot to take (regular, cylindrical, spherical, planet or cubemap), optionally followed by
-screenshot width, height and cubemap resolution in pixels (e.g. spherical 1600 1000 1200).
diff --git a/files/settings-default.cfg b/files/settings-default.cfg
index 9d15f44401..1ad5131fb0 100644
--- a/files/settings-default.cfg
+++ b/files/settings-default.cfg
@@ -652,10 +652,6 @@ contrast = 1.0
 # Video gamma setting.  (>0.0).  No effect in Linux.
 gamma = 1.0
 
-# Type of screenshot to take (regular, cylindrical, spherical, planet or cubemap), optionally followed by
-# screenshot width, height and cubemap resolution in pixels. (e.g. spherical 1600 1000 1200)
-screenshot type = regular
-
 [Water]
 
 # Enable water shader with reflections and optionally refraction.
diff --git a/files/shaders/CMakeLists.txt b/files/shaders/CMakeLists.txt
index ca0c264ade..bcb1a22fb2 100644
--- a/files/shaders/CMakeLists.txt
+++ b/files/shaders/CMakeLists.txt
@@ -39,8 +39,6 @@ set(SHADER_FILES
     compatibility/objects.frag
     compatibility/terrain.vert
     compatibility/terrain.frag
-    compatibility/s360.frag
-    compatibility/s360.vert
     compatibility/shadows_vertex.glsl
     compatibility/shadows_fragment.glsl
     compatibility/shadowcasting.vert
diff --git a/files/shaders/compatibility/s360.frag b/files/shaders/compatibility/s360.frag
deleted file mode 100644
index 7e7241e174..0000000000
--- a/files/shaders/compatibility/s360.frag
+++ /dev/null
@@ -1,21 +0,0 @@
-#version 120
-
-varying vec2 uv;
-uniform samplerCube cubeMap;
-uniform int mapping;
-
-#include "lib/util/coordinates.glsl"
-
-void main(void)
-{
-    vec3 c;
-
-    if (mapping == 0)
-        c = sphericalCoords(uv);
-    else if (mapping == 1)
-        c = cylindricalCoords(uv);
-    else
-        c = planetCoords(uv);
-
-    gl_FragData[0] = textureCube(cubeMap,c);
-}
diff --git a/files/shaders/compatibility/s360.vert b/files/shaders/compatibility/s360.vert
deleted file mode 100644
index ad96620c3f..0000000000
--- a/files/shaders/compatibility/s360.vert
+++ /dev/null
@@ -1,9 +0,0 @@
-#version 120
-
-varying vec2 uv;
-
-void main(void)
-{
-    gl_Position = gl_Vertex;
-    uv = (gl_Vertex.xy * vec2(1.0,-1.0) + vec2(1.0)) / 2;
-}