mirror of
https://github.com/OpenMW/openmw.git
synced 2025-03-30 19:36:40 +00:00
Merge branch 'Stereo-MR' into 'master'
Stereo See merge request OpenMW/openmw!1757
This commit is contained in:
commit
275bebb066
55 changed files with 3499 additions and 429 deletions
|
@ -378,7 +378,7 @@ variables: &tests-targets
|
|||
- $env:CCACHE_BASEDIR = Get-Location
|
||||
- $env:CCACHE_DIR = "$(Get-Location)\ccache"
|
||||
- New-Item -Type Directory -Force -Path $env:CCACHE_DIR
|
||||
- sh CI/before_script.msvc.sh -c $config -p Win64 -v 2019 -k -V -N -b -t -C
|
||||
- sh CI/before_script.msvc.sh -c $config -p Win64 -v 2019 -k -V -N -b -t -C $multiview
|
||||
- cd MSVC2019_64_Ninja
|
||||
- .\ActivateMSVC.ps1
|
||||
- cmake --build . --config $config --target ($targets.Split(','))
|
||||
|
@ -424,6 +424,14 @@ Windows_Ninja_Engine_Release:
|
|||
<<: *engine-targets
|
||||
config: "Release"
|
||||
|
||||
Windows_Ninja_Engine_Release_MultiView:
|
||||
extends:
|
||||
- .Windows_Ninja_Base
|
||||
variables:
|
||||
<<: *engine-targets
|
||||
multiview: "-M"
|
||||
config: "Release"
|
||||
|
||||
Windows_Ninja_Engine_Debug:
|
||||
extends:
|
||||
- .Windows_Ninja_Base
|
||||
|
@ -506,7 +514,7 @@ Windows_Ninja_Tests_RelWithDebInfo:
|
|||
- $env:CCACHE_BASEDIR = Get-Location
|
||||
- $env:CCACHE_DIR = "$(Get-Location)\ccache"
|
||||
- New-Item -Type Directory -Force -Path $env:CCACHE_DIR
|
||||
- sh CI/before_script.msvc.sh -c $config -p Win64 -v 2019 -k -V -b -t -C
|
||||
- sh CI/before_script.msvc.sh -c $config -p Win64 -v 2019 -k -V -b -t -C $multiview
|
||||
- cd MSVC2019_64
|
||||
- cmake --build . --config $config --target ($targets.Split(','))
|
||||
- ccache --show-stats
|
||||
|
|
|
@ -75,6 +75,7 @@ TEST_FRAMEWORK=""
|
|||
GOOGLE_INSTALL_ROOT=""
|
||||
INSTALL_PREFIX="."
|
||||
BUILD_BENCHMARKS=""
|
||||
OSG_MULTIVIEW_BUILD=""
|
||||
|
||||
ACTIVATE_MSVC=""
|
||||
SINGLE_CONFIG=""
|
||||
|
@ -139,7 +140,10 @@ while [ $# -gt 0 ]; do
|
|||
|
||||
b )
|
||||
BUILD_BENCHMARKS=true ;;
|
||||
|
||||
|
||||
M )
|
||||
OSG_MULTIVIEW_BUILD=true ;;
|
||||
|
||||
h )
|
||||
cat <<EOF
|
||||
Usage: $0 [-cdehkpuvVi]
|
||||
|
@ -178,6 +182,8 @@ Options:
|
|||
CMake install prefix
|
||||
-b
|
||||
Build benchmarks
|
||||
-M
|
||||
Use a multiview build of OSG
|
||||
EOF
|
||||
wrappedExit 0
|
||||
;;
|
||||
|
@ -514,6 +520,16 @@ fi
|
|||
|
||||
ICU_VER="70_1"
|
||||
|
||||
OSG_ARCHIVE_NAME="OSGoS 3.6.5"
|
||||
OSG_ARCHIVE="OSGoS-3.6.5-b02abe2-msvc${MSVC_REAL_YEAR}-win${BITS}"
|
||||
OSG_ARCHIVE_REPO_URL="https://gitlab.com/OpenMW/openmw-deps/-/raw/main"
|
||||
if ! [ -z $OSG_MULTIVIEW_BUILD ]; then
|
||||
OSG_ARCHIVE_NAME="OSG-3.6-multiview"
|
||||
OSG_ARCHIVE="OSG-3.6-multiview-ee297dce0-msvc${MSVC_REAL_YEAR}-win${BITS}"
|
||||
OSG_ARCHIVE_REPO_URL="https://gitlab.com/madsbuvi/openmw-deps/-/raw/openmw-vr-ovr_multiview"
|
||||
fi
|
||||
|
||||
|
||||
echo
|
||||
echo "==================================="
|
||||
echo "Starting prebuild on MSVC${MSVC_DISPLAY_YEAR} WIN${BITS}"
|
||||
|
@ -565,15 +581,15 @@ if [ -z $SKIP_DOWNLOAD ]; then
|
|||
"https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/OpenAL-Soft-1.20.1.zip" \
|
||||
"OpenAL-Soft-1.20.1.zip"
|
||||
|
||||
# OSGoS
|
||||
download "OSGoS 3.6.5" \
|
||||
"https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/OSGoS-3.6.5-b02abe2-msvc${MSVC_REAL_YEAR}-win${BITS}.7z" \
|
||||
"OSGoS-3.6.5-b02abe2-msvc${MSVC_REAL_YEAR}-win${BITS}.7z"
|
||||
# OSGoS https://gitlab.com/madsbuvi/openmw-deps/-/raw/openmw-vr-ovr_multiview/windows/OSG-3.6-multiview-ee297dce0-msvc${MSVC_REAL_YEAR}-win${BITS}.7z
|
||||
download "${OSG_ARCHIVE_NAME}" \
|
||||
"${OSG_ARCHIVE_REPO_URL}/windows/${OSG_ARCHIVE}.7z" \
|
||||
"${OSG_ARCHIVE}.7z"
|
||||
|
||||
if [ -n "$PDBS" ]; then
|
||||
download "OSGoS symbols" \
|
||||
"https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/OSGoS-3.6.5-b02abe2-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z" \
|
||||
"OSGoS-3.6.5-b02abe2-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z"
|
||||
download "${OSG_ARCHIVE_NAME} symbols" \
|
||||
"${OSG_ARCHIVE_REPO_URL}/windows/${OSG_ARCHIVE}-sym.7z" \
|
||||
"${OSG_ARCHIVE}-sym.7z"
|
||||
fi
|
||||
|
||||
# SDL2
|
||||
|
@ -781,7 +797,7 @@ printf "OpenAL-Soft 1.20.1... "
|
|||
cd $DEPS
|
||||
echo
|
||||
# OSGoS
|
||||
printf "OSGoS 3.6.5... "
|
||||
printf "${OSG_ARCHIVE_NAME}... "
|
||||
{
|
||||
cd $DEPS_INSTALL
|
||||
if [ -d OSG ] && \
|
||||
|
@ -792,9 +808,9 @@ printf "OSGoS 3.6.5... "
|
|||
printf "Exists. "
|
||||
elif [ -z $SKIP_EXTRACT ]; then
|
||||
rm -rf OSG
|
||||
eval 7z x -y "${DEPS}/OSGoS-3.6.5-b02abe2-msvc${MSVC_REAL_YEAR}-win${BITS}.7z" $STRIP
|
||||
[ -n "$PDBS" ] && eval 7z x -y "${DEPS}/OSGoS-3.6.5-b02abe2-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z" $STRIP
|
||||
mv "OSGoS-3.6.5-b02abe2-msvc${MSVC_REAL_YEAR}-win${BITS}" OSG
|
||||
eval 7z x -y "${DEPS}/${OSG_ARCHIVE}.7z" $STRIP
|
||||
[ -n "$PDBS" ] && eval 7z x -y "${DEPS}/${OSG_ARCHIVE}-sym.7z" $STRIP
|
||||
mv "${OSG_ARCHIVE}" OSG
|
||||
fi
|
||||
OSG_SDK="$(real_pwd)/OSG"
|
||||
add_cmake_opts -DOSG_DIR="$OSG_SDK"
|
||||
|
@ -804,8 +820,13 @@ printf "OSGoS 3.6.5... "
|
|||
else
|
||||
SUFFIX=""
|
||||
fi
|
||||
add_runtime_dlls $CONFIGURATION "$(pwd)/OSG/bin/"{OpenThreads,zlib,libpng}${SUFFIX}.dll \
|
||||
"$(pwd)/OSG/bin/osg"{,Animation,DB,FX,GA,Particle,Text,Util,Viewer,Shadow}${SUFFIX}.dll
|
||||
if ! [ -z $OSG_MULTIVIEW_BUILD ]; then
|
||||
add_runtime_dlls $CONFIGURATION "$(pwd)/OSG/bin/"{ot21-OpenThreads,zlib,libpng16}${SUFFIX}.dll \
|
||||
"$(pwd)/OSG/bin/osg162-osg"{,Animation,DB,FX,GA,Particle,Text,Util,Viewer,Shadow}${SUFFIX}.dll
|
||||
else
|
||||
add_runtime_dlls $CONFIGURATION "$(pwd)/OSG/bin/"{OpenThreads,zlib,libpng}${SUFFIX}.dll \
|
||||
"$(pwd)/OSG/bin/osg"{,Animation,DB,FX,GA,Particle,Text,Util,Viewer,Shadow}${SUFFIX}.dll
|
||||
fi
|
||||
add_osg_dlls $CONFIGURATION "$(pwd)/OSG/bin/osgPlugins-3.6.5/osgdb_"{bmp,dds,freetype,jpeg,osg,png,tga}${SUFFIX}.dll
|
||||
add_osg_dlls $CONFIGURATION "$(pwd)/OSG/bin/osgPlugins-3.6.5/osgdb_serializers_osg"{,animation,fx,ga,particle,text,util,viewer,shadow}${SUFFIX}.dll
|
||||
done
|
||||
|
|
|
@ -410,6 +410,11 @@ if(OSG_STATIC)
|
|||
add_definitions(-DOSG_LIBRARY_STATIC)
|
||||
endif()
|
||||
|
||||
include(cmake/CheckOsgMultiview.cmake)
|
||||
if(HAVE_MULTIVIEW)
|
||||
add_definitions(-DOSG_HAS_MULTIVIEW)
|
||||
endif(HAVE_MULTIVIEW)
|
||||
|
||||
set(BOOST_COMPONENTS system filesystem program_options iostreams)
|
||||
if(WIN32)
|
||||
set(BOOST_COMPONENTS ${BOOST_COMPONENTS} locale)
|
||||
|
|
|
@ -30,6 +30,9 @@
|
|||
|
||||
#include <components/compiler/extensions0.hpp>
|
||||
|
||||
#include <components/stereo/stereomanager.hpp>
|
||||
#include <components/stereo/multiview.hpp>
|
||||
|
||||
#include <components/sceneutil/workqueue.hpp>
|
||||
|
||||
#include <components/files/configurationmanager.hpp>
|
||||
|
@ -40,6 +43,7 @@
|
|||
|
||||
#include <components/sceneutil/screencapture.hpp>
|
||||
#include <components/sceneutil/depth.hpp>
|
||||
#include <components/sceneutil/color.hpp>
|
||||
#include <components/sceneutil/util.hpp>
|
||||
|
||||
#include "mwinput/inputmanagerimp.hpp"
|
||||
|
@ -251,6 +255,18 @@ namespace
|
|||
Log(Debug::Info) << "OpenGL Version: " << glGetString(GL_VERSION);
|
||||
}
|
||||
};
|
||||
|
||||
class InitializeStereoOperation final : public osg::GraphicsOperation
|
||||
{
|
||||
public:
|
||||
InitializeStereoOperation() : GraphicsOperation("InitializeStereoOperation", false)
|
||||
{}
|
||||
|
||||
void operator()(osg::GraphicsContext* graphicsContext) override
|
||||
{
|
||||
Stereo::Manager::instance().initializeStereo(graphicsContext);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void OMW::Engine::executeLocalScripts()
|
||||
|
@ -413,6 +429,9 @@ OMW::Engine::Engine(Files::ConfigurationManager& configurationManager)
|
|||
, mEncoding(ToUTF8::WINDOWS_1252)
|
||||
, mEncoder(nullptr)
|
||||
, mScreenCaptureOperation(nullptr)
|
||||
, mSelectDepthFormatOperation(new SceneUtil::SelectDepthFormatOperation())
|
||||
, mSelectColorFormatOperation(new SceneUtil::Color::SelectColorFormatOperation())
|
||||
, mStereoManager(nullptr)
|
||||
, mSkipMenu (false)
|
||||
, mUseSound (true)
|
||||
, mCompileAll (false)
|
||||
|
@ -448,6 +467,8 @@ OMW::Engine::~Engine()
|
|||
if (mScreenCaptureOperation != nullptr)
|
||||
mScreenCaptureOperation->stop();
|
||||
|
||||
mStereoManager = nullptr;
|
||||
|
||||
mEnvironment.cleanup();
|
||||
|
||||
delete mScriptContext;
|
||||
|
@ -640,6 +661,15 @@ void OMW::Engine::createWindow(Settings::Manager& settings)
|
|||
if (Debug::shouldDebugOpenGL())
|
||||
realizeOperations->add(new Debug::EnableGLDebugOperation());
|
||||
|
||||
realizeOperations->add(mSelectDepthFormatOperation);
|
||||
realizeOperations->add(mSelectColorFormatOperation);
|
||||
|
||||
if (Stereo::getStereo())
|
||||
{
|
||||
realizeOperations->add(new InitializeStereoOperation());
|
||||
Stereo::setVertexBufferHint();
|
||||
}
|
||||
|
||||
mViewer->realize();
|
||||
|
||||
mViewer->getEventQueue()->getCurrentEventState()->setWindowRectangle(0, 0, graphicsWindow->getTraits()->width, graphicsWindow->getTraits()->height);
|
||||
|
@ -674,11 +704,13 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
|
|||
mEnvironment.setStateManager (
|
||||
std::make_unique<MWState::StateManager> (mCfgMgr.getUserDataPath() / "saves", mContentFiles));
|
||||
|
||||
createWindow(settings);
|
||||
mStereoManager = std::make_unique<Stereo::Manager>(mViewer);
|
||||
|
||||
osg::ref_ptr<osg::Group> rootNode (new osg::Group);
|
||||
osg::ref_ptr<osg::Group> rootNode(new osg::Group);
|
||||
mViewer->setSceneData(rootNode);
|
||||
|
||||
createWindow(settings);
|
||||
|
||||
mVFS = std::make_unique<VFS::Manager>(mFSStrict);
|
||||
|
||||
VFS::registerArchives(mVFS.get(), mFileCollections, mArchives, true);
|
||||
|
@ -757,22 +789,6 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
|
|||
|
||||
osg::ref_ptr<osg::GLExtensions> exts = osg::GLExtensions::Get(0, false);
|
||||
bool shadersSupported = exts && (exts->glslLanguageVersion >= 1.2f);
|
||||
bool enableReverseZ = false;
|
||||
|
||||
if (Settings::Manager::getBool("reverse z", "Camera"))
|
||||
{
|
||||
if (exts && exts->isClipControlSupported)
|
||||
{
|
||||
enableReverseZ = true;
|
||||
Log(Debug::Info) << "Using reverse-z depth buffer";
|
||||
}
|
||||
else
|
||||
Log(Debug::Warning) << "GL_ARB_clip_control not supported: disabling reverse-z depth buffer";
|
||||
}
|
||||
else
|
||||
Log(Debug::Info) << "Using standard depth buffer";
|
||||
|
||||
SceneUtil::AutoDepth::setReversed(enableReverseZ);
|
||||
|
||||
#if OSG_VERSION_LESS_THAN(3, 6, 6)
|
||||
// hack fix for https://github.com/openscenegraph/OpenSceneGraph/issues/1028
|
||||
|
@ -784,6 +800,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
|
|||
osg::ref_ptr<osg::Group> guiRoot = new osg::Group;
|
||||
guiRoot->setName("GUI Root");
|
||||
guiRoot->setNodeMask(MWRender::Mask_GUI);
|
||||
mStereoManager->disableStereoForNode(guiRoot);
|
||||
rootNode->addChild(guiRoot);
|
||||
|
||||
auto windowMgr = std::make_unique<MWGui::WindowManager>(mWindow, mViewer, guiRoot, mResourceSystem.get(), mWorkQueue.get(),
|
||||
|
@ -794,7 +811,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
|
|||
mEnvironment.setWindowManager (std::move(windowMgr));
|
||||
|
||||
auto inputMgr = std::make_unique<MWInput::InputManager>(mWindow, mViewer, mScreenCaptureHandler, mScreenCaptureOperation, keybinderUser, keybinderUserExists, userGameControllerdb, gameControllerdb, mGrab);
|
||||
mEnvironment.setInputManager (std::move(inputMgr));
|
||||
mEnvironment.setInputManager(std::move(inputMgr));
|
||||
|
||||
// Create sound system
|
||||
mEnvironment.setSoundManager (std::make_unique<MWSound::SoundManager>(mVFS.get(), mUseSound));
|
||||
|
|
|
@ -39,6 +39,11 @@ namespace MWLua
|
|||
class LuaManager;
|
||||
}
|
||||
|
||||
namespace Stereo
|
||||
{
|
||||
class Manager;
|
||||
}
|
||||
|
||||
namespace Files
|
||||
{
|
||||
struct ConfigurationManager;
|
||||
|
@ -49,6 +54,16 @@ namespace osgViewer
|
|||
class ScreenCaptureHandler;
|
||||
}
|
||||
|
||||
namespace SceneUtil
|
||||
{
|
||||
class SelectDepthFormatOperation;
|
||||
|
||||
namespace Color
|
||||
{
|
||||
class SelectColorFormatOperation;
|
||||
}
|
||||
}
|
||||
|
||||
struct SDL_Window;
|
||||
|
||||
namespace OMW
|
||||
|
@ -69,9 +84,14 @@ namespace OMW
|
|||
osg::ref_ptr<osgViewer::Viewer> mViewer;
|
||||
osg::ref_ptr<osgViewer::ScreenCaptureHandler> mScreenCaptureHandler;
|
||||
osg::ref_ptr<SceneUtil::AsyncScreenCaptureOperation> mScreenCaptureOperation;
|
||||
osg::ref_ptr<SceneUtil::SelectDepthFormatOperation> mSelectDepthFormatOperation;
|
||||
osg::ref_ptr<SceneUtil::Color::SelectColorFormatOperation> mSelectColorFormatOperation;
|
||||
std::string mCellName;
|
||||
std::vector<std::string> mContentFiles;
|
||||
std::vector<std::string> mGroundcoverFiles;
|
||||
|
||||
std::unique_ptr<Stereo::Manager> mStereoManager;
|
||||
|
||||
bool mSkipMenu;
|
||||
bool mUseSound;
|
||||
bool mCompileAll;
|
||||
|
|
|
@ -367,6 +367,12 @@ namespace MWBase
|
|||
virtual void forceLootMode(const MWWorld::Ptr& ptr) = 0;
|
||||
|
||||
virtual void asyncPrepareSaveMap() = 0;
|
||||
|
||||
/// Sets the cull masks for all applicable views
|
||||
virtual void setCullMask(uint32_t mask) = 0;
|
||||
|
||||
/// Same as viewer->getCamera()->getCullMask(), provided for consistency.
|
||||
virtual uint32_t getCullMask() = 0;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -748,7 +748,11 @@ namespace MWGui
|
|||
|
||||
// ------------------------------------------------------------------------------------------
|
||||
MapWindow::MapWindow(CustomMarkerCollection &customMarkers, DragAndDrop* drag, MWRender::LocalMap* localMapRender, SceneUtil::WorkQueue* workQueue)
|
||||
#ifdef USE_OPENXR
|
||||
: WindowPinnableBase("openmw_map_window_vr.layout")
|
||||
#else
|
||||
: WindowPinnableBase("openmw_map_window.layout")
|
||||
#endif
|
||||
, LocalMapBase(customMarkers, localMapRender)
|
||||
, NoDrop(drag, mMainWidget)
|
||||
, mGlobalMap(nullptr)
|
||||
|
|
|
@ -573,17 +573,17 @@ namespace MWGui
|
|||
void WindowManager::enableScene(bool enable)
|
||||
{
|
||||
unsigned int disablemask = MWRender::Mask_GUI|MWRender::Mask_PreCompile;
|
||||
if (!enable && mViewer->getCamera()->getCullMask() != disablemask)
|
||||
if (!enable && getCullMask() != disablemask)
|
||||
{
|
||||
mOldUpdateMask = mViewer->getUpdateVisitor()->getTraversalMask();
|
||||
mOldCullMask = mViewer->getCamera()->getCullMask();
|
||||
mOldCullMask = getCullMask();
|
||||
mViewer->getUpdateVisitor()->setTraversalMask(disablemask);
|
||||
mViewer->getCamera()->setCullMask(disablemask);
|
||||
setCullMask(disablemask);
|
||||
}
|
||||
else if (enable && mViewer->getCamera()->getCullMask() == disablemask)
|
||||
else if (enable && getCullMask() == disablemask)
|
||||
{
|
||||
mViewer->getUpdateVisitor()->setTraversalMask(mOldUpdateMask);
|
||||
mViewer->getCamera()->setCullMask(mOldCullMask);
|
||||
setCullMask(mOldCullMask);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1227,6 +1227,21 @@ namespace MWGui
|
|||
updateVisible();
|
||||
}
|
||||
|
||||
void WindowManager::setCullMask(uint32_t mask)
|
||||
{
|
||||
mViewer->getCamera()->setCullMask(mask);
|
||||
|
||||
// We could check whether stereo is enabled here, but these methods are
|
||||
// trivial and have no effect in mono or multiview so just call them regardless.
|
||||
mViewer->getCamera()->setCullMaskLeft(mask);
|
||||
mViewer->getCamera()->setCullMaskRight(mask);
|
||||
}
|
||||
|
||||
uint32_t WindowManager::getCullMask()
|
||||
{
|
||||
return mViewer->getCamera()->getCullMask();
|
||||
}
|
||||
|
||||
void WindowManager::popGuiMode(bool noSound)
|
||||
{
|
||||
if (mDragAndDrop && mDragAndDrop->mIsOnDragAndDrop)
|
||||
|
|
|
@ -582,6 +582,9 @@ namespace MWGui
|
|||
void handleScheduledMessageBoxes();
|
||||
|
||||
void pushGuiMode(GuiMode mode, const MWWorld::Ptr& arg, bool force);
|
||||
|
||||
void setCullMask(uint32_t mask) override;
|
||||
uint32_t getCullMask() override;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <components/resource/resourcesystem.hpp>
|
||||
#include <components/sceneutil/lightmanager.hpp>
|
||||
#include <components/sceneutil/shadow.hpp>
|
||||
#include <components/sceneutil/rtt.hpp>
|
||||
#include <components/settings/settings.hpp>
|
||||
#include <components/sceneutil/nodecallback.hpp>
|
||||
#include <components/sceneutil/depth.hpp>
|
||||
|
@ -41,9 +42,10 @@ namespace MWRender
|
|||
class DrawOnceCallback : public SceneUtil::NodeCallback<DrawOnceCallback>
|
||||
{
|
||||
public:
|
||||
DrawOnceCallback ()
|
||||
DrawOnceCallback(osg::Node* subgraph)
|
||||
: mRendered(false)
|
||||
, mLastRenderedFrame(0)
|
||||
, mSubgraph(subgraph)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -61,6 +63,9 @@ namespace MWRender
|
|||
|
||||
nv->setFrameStamp(fs);
|
||||
|
||||
// Update keyframe controllers in the scene graph first...
|
||||
// RTTNode does not continue update traversal, so manually continue the update traversal since we need it.
|
||||
mSubgraph->accept(*nv);
|
||||
traverse(node, nv);
|
||||
|
||||
nv->setFrameStamp(previousFramestamp);
|
||||
|
@ -84,6 +89,7 @@ namespace MWRender
|
|||
private:
|
||||
bool mRendered;
|
||||
unsigned int mLastRenderedFrame;
|
||||
osg::ref_ptr<osg::Node> mSubgraph;
|
||||
};
|
||||
|
||||
|
||||
|
@ -138,6 +144,96 @@ namespace MWRender
|
|||
}
|
||||
};
|
||||
|
||||
class CharacterPreviewRTTNode : public SceneUtil::RTTNode
|
||||
{
|
||||
static constexpr float fovYDegrees = 12.3f;
|
||||
static constexpr float znear = 0.1f;
|
||||
static constexpr float zfar = 10000.f;
|
||||
|
||||
public:
|
||||
CharacterPreviewRTTNode(uint32_t sizeX, uint32_t sizeY)
|
||||
: RTTNode(sizeX, sizeY, Settings::Manager::getInt("antialiasing", "Video"), false, 0, StereoAwareness::Unaware_MultiViewShaders)
|
||||
, mAspectRatio(static_cast<float>(sizeX) / static_cast<float>(sizeY))
|
||||
{
|
||||
if (SceneUtil::AutoDepth::isReversed())
|
||||
mPerspectiveMatrix = static_cast<osg::Matrixf>(SceneUtil::getReversedZProjectionMatrixAsPerspective(fovYDegrees, mAspectRatio, znear, zfar));
|
||||
else
|
||||
mPerspectiveMatrix = osg::Matrixf::perspective(fovYDegrees, mAspectRatio, znear, zfar);
|
||||
mGroup->getOrCreateStateSet()->addUniform(new osg::Uniform("projectionMatrix", mPerspectiveMatrix));
|
||||
mViewMatrix = osg::Matrixf::identity();
|
||||
setColorBufferInternalFormat(GL_RGBA);
|
||||
}
|
||||
|
||||
void setDefaults(osg::Camera* camera) override
|
||||
{
|
||||
|
||||
// hints that the camera is not relative to the master camera
|
||||
camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF);
|
||||
camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT, osg::Camera::PIXEL_BUFFER_RTT);
|
||||
camera->setClearColor(osg::Vec4(0.f, 0.f, 0.f, 0.f));
|
||||
camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
camera->setViewport(0, 0, width(), height());
|
||||
camera->setRenderOrder(osg::Camera::PRE_RENDER);
|
||||
camera->setName("CharacterPreview");
|
||||
camera->setComputeNearFarMode(osg::Camera::COMPUTE_NEAR_FAR_USING_BOUNDING_VOLUMES);
|
||||
camera->setCullMask(~(Mask_UpdateVisitor));
|
||||
SceneUtil::setCameraClearDepth(camera);
|
||||
|
||||
// hints that the camera is not relative to the master camera
|
||||
camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF);
|
||||
camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT, osg::Camera::PIXEL_BUFFER_RTT);
|
||||
camera->setClearColor(osg::Vec4(0.f, 0.f, 0.f, 0.f));
|
||||
camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
camera->setProjectionMatrixAsPerspective(fovYDegrees, mAspectRatio, znear, zfar);
|
||||
camera->setViewport(0, 0, width(), height());
|
||||
camera->setRenderOrder(osg::Camera::PRE_RENDER);
|
||||
#ifdef OSG_HAS_MULTIVIEW
|
||||
if (shouldDoTextureArray())
|
||||
{
|
||||
auto* viewUniform = new osg::Uniform(osg::Uniform::FLOAT_MAT4, "viewMatrixMultiView", 2);
|
||||
auto* projUniform = new osg::Uniform(osg::Uniform::FLOAT_MAT4, "projectionMatrixMultiView", 2);
|
||||
viewUniform->setElement(0, osg::Matrix::identity());
|
||||
viewUniform->setElement(1, osg::Matrix::identity());
|
||||
projUniform->setElement(0, mPerspectiveMatrix);
|
||||
projUniform->setElement(1, mPerspectiveMatrix);
|
||||
mGroup->getOrCreateStateSet()->addUniform(viewUniform, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
|
||||
mGroup->getOrCreateStateSet()->addUniform(projUniform, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
|
||||
}
|
||||
#endif
|
||||
|
||||
camera->setNodeMask(Mask_RenderToTexture);
|
||||
camera->addChild(mGroup);
|
||||
};
|
||||
|
||||
void apply(osg::Camera* camera) override
|
||||
{
|
||||
if(mCameraStateset)
|
||||
camera->setStateSet(mCameraStateset);
|
||||
camera->setViewMatrix(mViewMatrix);
|
||||
};
|
||||
|
||||
void addChild(osg::Node* node)
|
||||
{
|
||||
mGroup->addChild(node);
|
||||
}
|
||||
|
||||
void setCameraStateset(osg::StateSet* stateset)
|
||||
{
|
||||
mCameraStateset = stateset;
|
||||
}
|
||||
|
||||
void setViewMatrix(const osg::Matrixf& viewMatrix)
|
||||
{
|
||||
mViewMatrix = viewMatrix;
|
||||
}
|
||||
|
||||
osg::ref_ptr<osg::Group> mGroup = new osg::Group;
|
||||
osg::Matrixf mPerspectiveMatrix;
|
||||
osg::Matrixf mViewMatrix;
|
||||
osg::ref_ptr<osg::StateSet> mCameraStateset;
|
||||
float mAspectRatio;
|
||||
};
|
||||
|
||||
CharacterPreview::CharacterPreview(osg::Group* parent, Resource::ResourceSystem* resourceSystem,
|
||||
const MWWorld::Ptr& character, int sizeX, int sizeY, const osg::Vec3f& position, const osg::Vec3f& lookAt)
|
||||
: mParent(parent)
|
||||
|
@ -149,31 +245,11 @@ namespace MWRender
|
|||
, mSizeX(sizeX)
|
||||
, mSizeY(sizeY)
|
||||
{
|
||||
mTexture = new osg::Texture2D;
|
||||
mTexture->setTextureSize(sizeX, sizeY);
|
||||
mTexture->setInternalFormat(GL_RGBA);
|
||||
mTexture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
|
||||
mTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
|
||||
|
||||
mTextureStateSet = new osg::StateSet;
|
||||
mTextureStateSet->setAttribute(new osg::BlendFunc(osg::BlendFunc::ONE, osg::BlendFunc::ONE_MINUS_SRC_ALPHA));
|
||||
|
||||
mCamera = new osg::Camera;
|
||||
// hints that the camera is not relative to the master camera
|
||||
mCamera->setReferenceFrame(osg::Camera::ABSOLUTE_RF);
|
||||
mCamera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT, osg::Camera::PIXEL_BUFFER_RTT);
|
||||
mCamera->setClearColor(osg::Vec4(0.f, 0.f, 0.f, 0.f));
|
||||
mCamera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
mCamera->setViewport(0, 0, sizeX, sizeY);
|
||||
mCamera->setRenderOrder(osg::Camera::PRE_RENDER);
|
||||
mCamera->attach(osg::Camera::COLOR_BUFFER, mTexture, 0, 0, false, Settings::Manager::getInt("antialiasing", "Video"));
|
||||
mCamera->setName("CharacterPreview");
|
||||
mCamera->setComputeNearFarMode(osg::Camera::COMPUTE_NEAR_FAR_USING_BOUNDING_VOLUMES);
|
||||
mCamera->setCullMask(~(Mask_UpdateVisitor));
|
||||
|
||||
mCamera->setNodeMask(Mask_RenderToTexture);
|
||||
|
||||
SceneUtil::setCameraClearDepth(mCamera);
|
||||
mRTTNode = new CharacterPreviewRTTNode(sizeX, sizeY);
|
||||
mRTTNode->setNodeMask(Mask_RenderToTexture);
|
||||
|
||||
bool ffp = mResourceSystem->getSceneManager()->getLightingMethod() == SceneUtil::LightingMethod::FFP;
|
||||
|
||||
|
@ -191,14 +267,6 @@ namespace MWRender
|
|||
defaultMat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 0.f));
|
||||
stateset->setAttribute(defaultMat);
|
||||
|
||||
const float fovYDegrees = 12.3f;
|
||||
const float aspectRatio = static_cast<float>(sizeX) / static_cast<float>(sizeY);
|
||||
const float znear = 0.1f;
|
||||
const float zfar = 10000.f;
|
||||
mCamera->setProjectionMatrixAsPerspective(fovYDegrees, aspectRatio, znear, zfar);
|
||||
osg::Matrixf projectionMatrix = SceneUtil::AutoDepth::isReversed() ? static_cast<osg::Matrixf>(SceneUtil::getReversedZProjectionMatrixAsPerspective(fovYDegrees, aspectRatio, znear, zfar)) : static_cast<osg::Matrixf>(mCamera->getProjectionMatrix());
|
||||
stateset->addUniform(new osg::Uniform("projectionMatrix", projectionMatrix));
|
||||
|
||||
SceneUtil::ShadowManager::disableShadowsForStateSet(stateset);
|
||||
|
||||
// assign large value to effectively turn off fog
|
||||
|
@ -266,23 +334,22 @@ namespace MWRender
|
|||
|
||||
lightManager->addChild(lightSource);
|
||||
|
||||
mCamera->addChild(lightManager);
|
||||
mRTTNode->addChild(lightManager);
|
||||
|
||||
mNode = new osg::PositionAttitudeTransform;
|
||||
lightManager->addChild(mNode);
|
||||
|
||||
mDrawOnceCallback = new DrawOnceCallback;
|
||||
mCamera->addUpdateCallback(mDrawOnceCallback);
|
||||
mDrawOnceCallback = new DrawOnceCallback(mRTTNode->mGroup);
|
||||
mRTTNode->addUpdateCallback(mDrawOnceCallback);
|
||||
|
||||
mParent->addChild(mCamera);
|
||||
mParent->addChild(mRTTNode);
|
||||
|
||||
mCharacter.mCell = nullptr;
|
||||
}
|
||||
|
||||
CharacterPreview::~CharacterPreview ()
|
||||
{
|
||||
mCamera->removeChildren(0, mCamera->getNumChildren());
|
||||
mParent->removeChild(mCamera);
|
||||
mParent->removeChild(mRTTNode);
|
||||
}
|
||||
|
||||
int CharacterPreview::getTextureWidth() const
|
||||
|
@ -308,7 +375,7 @@ namespace MWRender
|
|||
|
||||
osg::ref_ptr<osg::Texture2D> CharacterPreview::getTexture()
|
||||
{
|
||||
return mTexture;
|
||||
return static_cast<osg::Texture2D*>(mRTTNode->getColorTexture(nullptr));
|
||||
}
|
||||
|
||||
void CharacterPreview::rebuild()
|
||||
|
@ -325,7 +392,7 @@ namespace MWRender
|
|||
|
||||
void CharacterPreview::redraw()
|
||||
{
|
||||
mCamera->setNodeMask(Mask_RenderToTexture);
|
||||
mRTTNode->setNodeMask(Mask_RenderToTexture);
|
||||
mDrawOnceCallback->redrawNextFrame();
|
||||
}
|
||||
|
||||
|
@ -346,7 +413,7 @@ namespace MWRender
|
|||
osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet;
|
||||
mViewport = new osg::Viewport(0, mSizeY-sizeY, std::min(mSizeX, sizeX), std::min(mSizeY, sizeY));
|
||||
stateset->setAttributeAndModes(mViewport);
|
||||
mCamera->setStateSet(stateset);
|
||||
mRTTNode->setCameraStateset(stateset);
|
||||
|
||||
redraw();
|
||||
}
|
||||
|
@ -433,10 +500,11 @@ namespace MWRender
|
|||
// Set the traversal number from the last draw, so that the frame switch used for RigGeometry double buffering works correctly
|
||||
visitor.setTraversalNumber(mDrawOnceCallback->getLastRenderedFrame());
|
||||
|
||||
osg::Node::NodeMask nodeMask = mCamera->getNodeMask();
|
||||
mCamera->setNodeMask(~0u);
|
||||
mCamera->accept(visitor);
|
||||
mCamera->setNodeMask(nodeMask);
|
||||
auto* camera = mRTTNode->getCamera(nullptr);
|
||||
osg::Node::NodeMask nodeMask = camera->getNodeMask();
|
||||
camera->setNodeMask(~0u);
|
||||
camera->accept(visitor);
|
||||
camera->setNodeMask(nodeMask);
|
||||
|
||||
if (intersector->containsIntersections())
|
||||
{
|
||||
|
@ -459,7 +527,8 @@ namespace MWRender
|
|||
|
||||
mNode->setScale(scale);
|
||||
|
||||
mCamera->setViewMatrixAsLookAt(mPosition * scale.z(), mLookAt * scale.z(), osg::Vec3f(0,0,1));
|
||||
auto viewMatrix = osg::Matrixf::lookAt(mPosition * scale.z(), mLookAt * scale.z(), osg::Vec3f(0, 0, 1));
|
||||
mRTTNode->setViewMatrix(viewMatrix);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------------
|
||||
|
@ -492,7 +561,7 @@ namespace MWRender
|
|||
rebuild();
|
||||
}
|
||||
|
||||
class UpdateCameraCallback : public SceneUtil::NodeCallback<UpdateCameraCallback, osg::Camera*>
|
||||
class UpdateCameraCallback : public SceneUtil::NodeCallback<UpdateCameraCallback, CharacterPreviewRTTNode*>
|
||||
{
|
||||
public:
|
||||
UpdateCameraCallback(osg::ref_ptr<const osg::Node> nodeToFollow, const osg::Vec3& posOffset, const osg::Vec3& lookAtOffset)
|
||||
|
@ -502,10 +571,10 @@ namespace MWRender
|
|||
{
|
||||
}
|
||||
|
||||
void operator()(osg::Camera* cam, osg::NodeVisitor* nv)
|
||||
void operator()(CharacterPreviewRTTNode* node, osg::NodeVisitor* nv)
|
||||
{
|
||||
// Update keyframe controllers in the scene graph first...
|
||||
traverse(cam, nv);
|
||||
traverse(node, nv);
|
||||
|
||||
// Now update camera utilizing the updated head position
|
||||
osg::NodePathList nodepaths = mNodeToFollow->getParentalNodePaths();
|
||||
|
@ -514,7 +583,8 @@ namespace MWRender
|
|||
osg::Matrix worldMat = osg::computeLocalToWorld(nodepaths[0]);
|
||||
osg::Vec3 headOffset = worldMat.getTrans();
|
||||
|
||||
cam->setViewMatrixAsLookAt(headOffset + mPosOffset, headOffset + mLookAtOffset, osg::Vec3(0,0,1));
|
||||
auto viewMatrix = osg::Matrixf::lookAt(headOffset + mPosOffset, headOffset + mLookAtOffset, osg::Vec3(0, 0, 1));
|
||||
node->setViewMatrix(viewMatrix);
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -531,13 +601,13 @@ namespace MWRender
|
|||
|
||||
// attach camera to follow the head node
|
||||
if (mUpdateCameraCallback)
|
||||
mCamera->removeUpdateCallback(mUpdateCameraCallback);
|
||||
mRTTNode->removeUpdateCallback(mUpdateCameraCallback);
|
||||
|
||||
const osg::Node* head = mAnimation->getNode("Bip01 Head");
|
||||
if (head)
|
||||
{
|
||||
mUpdateCameraCallback = new UpdateCameraCallback(head, mPosition, mLookAt);
|
||||
mCamera->addUpdateCallback(mUpdateCameraCallback);
|
||||
mRTTNode->addUpdateCallback(mUpdateCameraCallback);
|
||||
}
|
||||
else
|
||||
Log(Debug::Error) << "Error: Bip01 Head node not found";
|
||||
|
|
|
@ -26,6 +26,7 @@ namespace MWRender
|
|||
|
||||
class NpcAnimation;
|
||||
class DrawOnceCallback;
|
||||
class CharacterPreviewRTTNode;
|
||||
|
||||
class CharacterPreview
|
||||
{
|
||||
|
@ -56,10 +57,9 @@ namespace MWRender
|
|||
|
||||
osg::ref_ptr<osg::Group> mParent;
|
||||
Resource::ResourceSystem* mResourceSystem;
|
||||
osg::ref_ptr<osg::Texture2D> mTexture;
|
||||
osg::ref_ptr<osg::StateSet> mTextureStateSet;
|
||||
osg::ref_ptr<osg::Camera> mCamera;
|
||||
osg::ref_ptr<DrawOnceCallback> mDrawOnceCallback;
|
||||
osg::ref_ptr<CharacterPreviewRTTNode> mRTTNode;
|
||||
|
||||
osg::Vec3f mPosition;
|
||||
osg::Vec3f mLookAt;
|
||||
|
|
|
@ -15,12 +15,14 @@
|
|||
#include <components/esm3/fogstate.hpp>
|
||||
#include <components/esm3/loadcell.hpp>
|
||||
#include <components/misc/constants.hpp>
|
||||
#include <components/stereo/multiview.hpp>
|
||||
#include <components/settings/settings.hpp>
|
||||
#include <components/sceneutil/visitor.hpp>
|
||||
#include <components/sceneutil/shadow.hpp>
|
||||
#include <components/sceneutil/depth.hpp>
|
||||
#include <components/sceneutil/lightmanager.hpp>
|
||||
#include <components/sceneutil/nodecallback.hpp>
|
||||
#include <components/sceneutil/rtt.hpp>
|
||||
#include <components/files/memorystream.hpp>
|
||||
#include <components/resource/scenemanager.hpp>
|
||||
|
||||
|
@ -34,36 +36,6 @@
|
|||
|
||||
namespace
|
||||
{
|
||||
|
||||
class CameraLocalUpdateCallback : public SceneUtil::NodeCallback<CameraLocalUpdateCallback, osg::Camera*>
|
||||
{
|
||||
public:
|
||||
CameraLocalUpdateCallback(MWRender::LocalMap* parent)
|
||||
: mRendered(false)
|
||||
, mParent(parent)
|
||||
{
|
||||
}
|
||||
|
||||
void operator()(osg::Camera* node, osg::NodeVisitor*)
|
||||
{
|
||||
if (mRendered)
|
||||
node->setNodeMask(0);
|
||||
|
||||
if (!mRendered)
|
||||
{
|
||||
mRendered = true;
|
||||
mParent->markForRemoval(node);
|
||||
}
|
||||
|
||||
// Note, we intentionally do not traverse children here. The map camera's scene data is the same as the master camera's,
|
||||
// so it has been updated already.
|
||||
}
|
||||
|
||||
private:
|
||||
bool mRendered;
|
||||
MWRender::LocalMap* mParent;
|
||||
};
|
||||
|
||||
float square(float val)
|
||||
{
|
||||
return val*val;
|
||||
|
@ -82,6 +54,28 @@ namespace
|
|||
|
||||
namespace MWRender
|
||||
{
|
||||
class LocalMapRenderToTexture: public SceneUtil::RTTNode
|
||||
{
|
||||
public:
|
||||
LocalMapRenderToTexture(osg::Node* sceneRoot, int res, int mapWorldSize,
|
||||
float x, float y, const osg::Vec3d& upVector, float zmin, float zmax);
|
||||
|
||||
void setDefaults(osg::Camera* camera) override;
|
||||
|
||||
bool isActive() { return mActive; }
|
||||
void setIsActive(bool active) { mActive = active; }
|
||||
|
||||
osg::Node* mSceneRoot;
|
||||
osg::Matrix mProjectionMatrix;
|
||||
osg::Matrix mViewMatrix;
|
||||
bool mActive;
|
||||
};
|
||||
|
||||
class CameraLocalUpdateCallback : public SceneUtil::NodeCallback<CameraLocalUpdateCallback, LocalMapRenderToTexture*>
|
||||
{
|
||||
public:
|
||||
void operator()(LocalMapRenderToTexture* node, osg::NodeVisitor* nv);
|
||||
};
|
||||
|
||||
LocalMap::LocalMap(osg::Group* root)
|
||||
: mRoot(root)
|
||||
|
@ -104,10 +98,8 @@ LocalMap::LocalMap(osg::Group* root)
|
|||
|
||||
LocalMap::~LocalMap()
|
||||
{
|
||||
for (auto& camera : mActiveCameras)
|
||||
removeCamera(camera);
|
||||
for (auto& camera : mCamerasPendingRemoval)
|
||||
removeCamera(camera);
|
||||
for (auto& rtt : mLocalMapRTTs)
|
||||
mRoot->removeChild(rtt);
|
||||
}
|
||||
|
||||
const osg::Vec2f LocalMap::rotatePoint(const osg::Vec2f& point, const osg::Vec2f& center, const float angle)
|
||||
|
@ -173,93 +165,14 @@ void LocalMap::saveFogOfWar(MWWorld::CellStore* cell)
|
|||
}
|
||||
}
|
||||
|
||||
osg::ref_ptr<osg::Camera> LocalMap::createOrthographicCamera(float x, float y, float width, float height, const osg::Vec3d& upVector, float zmin, float zmax)
|
||||
void LocalMap::setupRenderToTexture(int segment_x, int segment_y, float left, float top, const osg::Vec3d& upVector, float zmin, float zmax)
|
||||
{
|
||||
osg::ref_ptr<osg::Camera> camera (new osg::Camera);
|
||||
mLocalMapRTTs.emplace_back(new LocalMapRenderToTexture(mSceneRoot, mMapResolution, mMapWorldSize, left, top, upVector, zmin, zmax));
|
||||
|
||||
if (SceneUtil::AutoDepth::isReversed())
|
||||
camera->setProjectionMatrix(SceneUtil::getReversedZProjectionMatrixAsOrtho(-width/2, width/2, -height/2, height/2, 5, (zmax-zmin) + 10));
|
||||
else
|
||||
camera->setProjectionMatrixAsOrtho(-width/2, width/2, -height/2, height/2, 5, (zmax-zmin) + 10);
|
||||
mRoot->addChild(mLocalMapRTTs.back());
|
||||
|
||||
camera->setComputeNearFarMode(osg::Camera::DO_NOT_COMPUTE_NEAR_FAR);
|
||||
camera->setViewMatrixAsLookAt(osg::Vec3d(x, y, zmax + 5), osg::Vec3d(x, y, zmin), upVector);
|
||||
camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF_INHERIT_VIEWPOINT);
|
||||
camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT, osg::Camera::PIXEL_BUFFER_RTT);
|
||||
camera->setClearColor(osg::Vec4(0.f, 0.f, 0.f, 1.f));
|
||||
camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
||||
camera->setRenderOrder(osg::Camera::PRE_RENDER);
|
||||
|
||||
camera->setCullMask(Mask_Scene | Mask_SimpleWater | Mask_Terrain | Mask_Object | Mask_Static);
|
||||
camera->setNodeMask(Mask_RenderToTexture);
|
||||
|
||||
// Disable small feature culling, it's not going to be reliable for this camera
|
||||
osg::Camera::CullingMode cullingMode = (osg::Camera::DEFAULT_CULLING|osg::Camera::FAR_PLANE_CULLING) & ~(osg::Camera::SMALL_FEATURE_CULLING);
|
||||
camera->setCullingMode(cullingMode);
|
||||
|
||||
SceneUtil::setCameraClearDepth(camera);
|
||||
|
||||
osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet;
|
||||
stateset->setAttribute(new osg::PolygonMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::FILL), osg::StateAttribute::OVERRIDE);
|
||||
stateset->addUniform(new osg::Uniform("projectionMatrix", static_cast<osg::Matrixf>(camera->getProjectionMatrix())), osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
|
||||
|
||||
// assign large value to effectively turn off fog
|
||||
// shaders don't respect glDisable(GL_FOG)
|
||||
osg::ref_ptr<osg::Fog> fog (new osg::Fog);
|
||||
fog->setStart(10000000);
|
||||
fog->setEnd(10000000);
|
||||
stateset->setAttributeAndModes(fog, osg::StateAttribute::OFF|osg::StateAttribute::OVERRIDE);
|
||||
|
||||
osg::ref_ptr<osg::LightModel> lightmodel = new osg::LightModel;
|
||||
lightmodel->setAmbientIntensity(osg::Vec4(0.3f, 0.3f, 0.3f, 1.f));
|
||||
stateset->setAttributeAndModes(lightmodel, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
|
||||
|
||||
osg::ref_ptr<osg::Light> light = new osg::Light;
|
||||
light->setPosition(osg::Vec4(-0.3f, -0.3f, 0.7f, 0.f));
|
||||
light->setDiffuse(osg::Vec4(0.7f, 0.7f, 0.7f, 1.f));
|
||||
light->setAmbient(osg::Vec4(0,0,0,1));
|
||||
light->setSpecular(osg::Vec4(0,0,0,0));
|
||||
light->setLightNum(0);
|
||||
light->setConstantAttenuation(1.f);
|
||||
light->setLinearAttenuation(0.f);
|
||||
light->setQuadraticAttenuation(0.f);
|
||||
|
||||
osg::ref_ptr<osg::LightSource> lightSource = new osg::LightSource;
|
||||
lightSource->setLight(light);
|
||||
|
||||
lightSource->setStateSetModes(*stateset, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
|
||||
|
||||
SceneUtil::ShadowManager::disableShadowsForStateSet(stateset);
|
||||
|
||||
// override sun for local map
|
||||
SceneUtil::configureStateSetSunOverride(static_cast<SceneUtil::LightManager*>(mSceneRoot.get()), light, stateset);
|
||||
|
||||
camera->addChild(lightSource);
|
||||
camera->setStateSet(stateset);
|
||||
camera->setViewport(0, 0, mMapResolution, mMapResolution);
|
||||
camera->setUpdateCallback(new CameraLocalUpdateCallback(this));
|
||||
|
||||
return camera;
|
||||
}
|
||||
|
||||
void LocalMap::setupRenderToTexture(osg::ref_ptr<osg::Camera> camera, int x, int y)
|
||||
{
|
||||
osg::ref_ptr<osg::Texture2D> texture (new osg::Texture2D);
|
||||
texture->setTextureSize(mMapResolution, mMapResolution);
|
||||
texture->setInternalFormat(GL_RGB);
|
||||
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);
|
||||
|
||||
SceneUtil::attachAlphaToCoverageFriendlyFramebufferToCamera(camera, osg::Camera::COLOR_BUFFER, texture);
|
||||
|
||||
camera->addChild(mSceneRoot);
|
||||
mRoot->addChild(camera);
|
||||
mActiveCameras.push_back(camera);
|
||||
|
||||
MapSegment& segment = mInterior? mInteriorSegments[std::make_pair(x, y)] : mExteriorSegments[std::make_pair(x, y)];
|
||||
segment.mMapTexture = texture;
|
||||
MapSegment& segment = mInterior? mInteriorSegments[std::make_pair(segment_x, segment_y)] : mExteriorSegments[std::make_pair(segment_x, segment_y)];
|
||||
segment.mMapTexture = static_cast<osg::Texture2D*>(mLocalMapRTTs.back()->getColorTexture(nullptr));
|
||||
}
|
||||
|
||||
void LocalMap::requestMap(const MWWorld::CellStore* cell)
|
||||
|
@ -321,33 +234,19 @@ osg::ref_ptr<osg::Texture2D> LocalMap::getFogOfWarTexture(int x, int y)
|
|||
return found->second.mFogOfWarTexture;
|
||||
}
|
||||
|
||||
void LocalMap::removeCamera(osg::Camera *cam)
|
||||
{
|
||||
cam->removeChildren(0, cam->getNumChildren());
|
||||
mRoot->removeChild(cam);
|
||||
}
|
||||
|
||||
void LocalMap::markForRemoval(osg::Camera *cam)
|
||||
{
|
||||
CameraVector::iterator found = std::find(mActiveCameras.begin(), mActiveCameras.end(), cam);
|
||||
if (found == mActiveCameras.end())
|
||||
{
|
||||
Log(Debug::Error) << "Error: trying to remove an inactive camera";
|
||||
return;
|
||||
}
|
||||
mActiveCameras.erase(found);
|
||||
mCamerasPendingRemoval.push_back(cam);
|
||||
}
|
||||
|
||||
void LocalMap::cleanupCameras()
|
||||
{
|
||||
if (mCamerasPendingRemoval.empty())
|
||||
return;
|
||||
|
||||
for (auto& camera : mCamerasPendingRemoval)
|
||||
removeCamera(camera);
|
||||
|
||||
mCamerasPendingRemoval.clear();
|
||||
auto it = mLocalMapRTTs.begin();
|
||||
while (it != mLocalMapRTTs.end())
|
||||
{
|
||||
if (!(*it)->isActive())
|
||||
{
|
||||
mRoot->removeChild(*it);
|
||||
it = mLocalMapRTTs.erase(it);
|
||||
}
|
||||
else
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
void LocalMap::requestExteriorMap(const MWWorld::CellStore* cell)
|
||||
|
@ -361,9 +260,9 @@ void LocalMap::requestExteriorMap(const MWWorld::CellStore* cell)
|
|||
float zmin = bound.center().z() - bound.radius();
|
||||
float zmax = bound.center().z() + bound.radius();
|
||||
|
||||
osg::ref_ptr<osg::Camera> camera = createOrthographicCamera(x*mMapWorldSize + mMapWorldSize/2.f, y*mMapWorldSize + mMapWorldSize/2.f, mMapWorldSize, mMapWorldSize,
|
||||
osg::Vec3d(0,1,0), zmin, zmax);
|
||||
setupRenderToTexture(camera, cell->getCell()->getGridX(), cell->getCell()->getGridY());
|
||||
setupRenderToTexture(cell->getCell()->getGridX(), cell->getCell()->getGridY(),
|
||||
x * mMapWorldSize + mMapWorldSize / 2.f, y * mMapWorldSize + mMapWorldSize / 2.f,
|
||||
osg::Vec3d(0, 1, 0), zmin, zmax);
|
||||
|
||||
MapSegment& segment = mExteriorSegments[std::make_pair(cell->getCell()->getGridX(), cell->getCell()->getGridY())];
|
||||
if (!segment.mFogOfWarImage)
|
||||
|
@ -501,11 +400,8 @@ void LocalMap::requestInteriorMap(const MWWorld::CellStore* cell)
|
|||
|
||||
osg::Vec2f pos = osg::Vec2f(rotatedCenter.x(), rotatedCenter.y()) + center;
|
||||
|
||||
osg::ref_ptr<osg::Camera> camera = createOrthographicCamera(pos.x(), pos.y(),
|
||||
mMapWorldSize, mMapWorldSize,
|
||||
osg::Vec3f(north.x(), north.y(), 0.f), zMin, zMax);
|
||||
|
||||
setupRenderToTexture(camera, x, y);
|
||||
setupRenderToTexture(x, y, pos.x(), pos.y(),
|
||||
osg::Vec3f(north.x(), north.y(), 0.f), zMin, zMax);
|
||||
|
||||
auto coords = std::make_pair(x,y);
|
||||
MapSegment& segment = mInteriorSegments[coords];
|
||||
|
@ -682,6 +578,7 @@ void LocalMap::MapSegment::createFogOfWarTexture()
|
|||
mFogOfWarTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
|
||||
mFogOfWarTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
|
||||
mFogOfWarTexture->setUnRefImageDataAfterApply(false);
|
||||
mFogOfWarTexture->setImage(mFogOfWarImage);
|
||||
}
|
||||
|
||||
void LocalMap::MapSegment::initFogOfWar()
|
||||
|
@ -697,7 +594,6 @@ void LocalMap::MapSegment::initFogOfWar()
|
|||
memcpy(mFogOfWarImage->data(), &data[0], data.size()*4);
|
||||
|
||||
createFogOfWarTexture();
|
||||
mFogOfWarTexture->setImage(mFogOfWarImage);
|
||||
}
|
||||
|
||||
void LocalMap::MapSegment::loadFogOfWar(const ESM::FogTexture &esm)
|
||||
|
@ -730,7 +626,6 @@ void LocalMap::MapSegment::loadFogOfWar(const ESM::FogTexture &esm)
|
|||
mFogOfWarImage->dirty();
|
||||
|
||||
createFogOfWarTexture();
|
||||
mFogOfWarTexture->setImage(mFogOfWarImage);
|
||||
mHasFogState = true;
|
||||
}
|
||||
|
||||
|
@ -762,4 +657,107 @@ void LocalMap::MapSegment::saveFogOfWar(ESM::FogTexture &fog) const
|
|||
fog.mImageData = std::vector<char>(data.begin(), data.end());
|
||||
}
|
||||
|
||||
LocalMapRenderToTexture::LocalMapRenderToTexture(osg::Node* sceneRoot, int res, int mapWorldSize, float x, float y, const osg::Vec3d& upVector, float zmin, float zmax)
|
||||
: RTTNode(res, res, 0, false, 0, StereoAwareness::Unaware_MultiViewShaders)
|
||||
, mSceneRoot(sceneRoot)
|
||||
, mActive(true)
|
||||
{
|
||||
if (SceneUtil::AutoDepth::isReversed())
|
||||
mProjectionMatrix = SceneUtil::getReversedZProjectionMatrixAsOrtho(-mapWorldSize / 2, mapWorldSize / 2, -mapWorldSize / 2, mapWorldSize / 2, 5, (zmax - zmin) + 10);
|
||||
else
|
||||
mProjectionMatrix.makeOrtho(-mapWorldSize / 2, mapWorldSize / 2, -mapWorldSize / 2, mapWorldSize / 2, 5, (zmax - zmin) + 10);
|
||||
|
||||
mViewMatrix.makeLookAt(osg::Vec3d(x, y, zmax + 5), osg::Vec3d(x, y, zmin), upVector);
|
||||
|
||||
setUpdateCallback(new CameraLocalUpdateCallback);
|
||||
}
|
||||
|
||||
void LocalMapRenderToTexture::setDefaults(osg::Camera* camera)
|
||||
{
|
||||
// Disable small feature culling, it's not going to be reliable for this camera
|
||||
osg::Camera::CullingMode cullingMode = (osg::Camera::DEFAULT_CULLING | osg::Camera::FAR_PLANE_CULLING) & ~(osg::Camera::SMALL_FEATURE_CULLING);
|
||||
camera->setCullingMode(cullingMode);
|
||||
|
||||
SceneUtil::setCameraClearDepth(camera);
|
||||
camera->setComputeNearFarMode(osg::Camera::DO_NOT_COMPUTE_NEAR_FAR);
|
||||
camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF_INHERIT_VIEWPOINT);
|
||||
camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT, osg::Camera::PIXEL_BUFFER_RTT);
|
||||
camera->setClearColor(osg::Vec4(0.f, 0.f, 0.f, 1.f));
|
||||
camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
||||
camera->setRenderOrder(osg::Camera::PRE_RENDER);
|
||||
|
||||
camera->setCullMask(Mask_Scene | Mask_SimpleWater | Mask_Terrain | Mask_Object | Mask_Static);
|
||||
camera->setCullMaskLeft(Mask_Scene | Mask_SimpleWater | Mask_Terrain | Mask_Object | Mask_Static);
|
||||
camera->setCullMaskRight(Mask_Scene | Mask_SimpleWater | Mask_Terrain | Mask_Object | Mask_Static);
|
||||
camera->setNodeMask(Mask_RenderToTexture);
|
||||
camera->setProjectionMatrix(mProjectionMatrix);
|
||||
camera->setViewMatrix(mViewMatrix);
|
||||
|
||||
auto* stateset = camera->getOrCreateStateSet();
|
||||
|
||||
stateset->setAttribute(new osg::PolygonMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::FILL), osg::StateAttribute::OVERRIDE);
|
||||
stateset->addUniform(new osg::Uniform("projectionMatrix", static_cast<osg::Matrixf>(mProjectionMatrix)), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
|
||||
|
||||
if (Stereo::getMultiview())
|
||||
{
|
||||
auto* viewUniform = new osg::Uniform(osg::Uniform::FLOAT_MAT4, "viewMatrixMultiView", 2);
|
||||
auto* projUniform = new osg::Uniform(osg::Uniform::FLOAT_MAT4, "projectionMatrixMultiView", 2);
|
||||
viewUniform->setElement(0, osg::Matrix::identity());
|
||||
viewUniform->setElement(1, osg::Matrix::identity());
|
||||
projUniform->setElement(0, mProjectionMatrix);
|
||||
projUniform->setElement(1, mProjectionMatrix);
|
||||
stateset->addUniform(viewUniform, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
|
||||
stateset->addUniform(projUniform, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
|
||||
}
|
||||
|
||||
// assign large value to effectively turn off fog
|
||||
// shaders don't respect glDisable(GL_FOG)
|
||||
osg::ref_ptr<osg::Fog> fog(new osg::Fog);
|
||||
fog->setStart(10000000);
|
||||
fog->setEnd(10000000);
|
||||
stateset->setAttributeAndModes(fog, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE);
|
||||
|
||||
osg::ref_ptr<osg::LightModel> lightmodel = new osg::LightModel;
|
||||
lightmodel->setAmbientIntensity(osg::Vec4(0.3f, 0.3f, 0.3f, 1.f));
|
||||
stateset->setAttributeAndModes(lightmodel, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
|
||||
|
||||
osg::ref_ptr<osg::Light> light = new osg::Light;
|
||||
light->setPosition(osg::Vec4(-0.3f, -0.3f, 0.7f, 0.f));
|
||||
light->setDiffuse(osg::Vec4(0.7f, 0.7f, 0.7f, 1.f));
|
||||
light->setAmbient(osg::Vec4(0, 0, 0, 1));
|
||||
light->setSpecular(osg::Vec4(0, 0, 0, 0));
|
||||
light->setLightNum(0);
|
||||
light->setConstantAttenuation(1.f);
|
||||
light->setLinearAttenuation(0.f);
|
||||
light->setQuadraticAttenuation(0.f);
|
||||
|
||||
osg::ref_ptr<osg::LightSource> lightSource = new osg::LightSource;
|
||||
lightSource->setLight(light);
|
||||
|
||||
lightSource->setStateSetModes(*stateset, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
|
||||
|
||||
SceneUtil::ShadowManager::disableShadowsForStateSet(stateset);
|
||||
|
||||
// override sun for local map
|
||||
SceneUtil::configureStateSetSunOverride(static_cast<SceneUtil::LightManager*>(mSceneRoot), light, stateset);
|
||||
|
||||
camera->addChild(lightSource);
|
||||
camera->addChild(mSceneRoot);
|
||||
}
|
||||
|
||||
void CameraLocalUpdateCallback::operator()(LocalMapRenderToTexture* node, osg::NodeVisitor* nv)
|
||||
{
|
||||
if (!node->isActive())
|
||||
node->setNodeMask(0);
|
||||
|
||||
if (node->isActive())
|
||||
{
|
||||
node->setIsActive(false);
|
||||
}
|
||||
|
||||
// Rtt-nodes do not forward update traversal to their cameras so we can traverse safely.
|
||||
// Traverse in case there are nested callbacks.
|
||||
traverse(node, nv);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -30,6 +30,8 @@ namespace osg
|
|||
|
||||
namespace MWRender
|
||||
{
|
||||
class LocalMapRenderToTexture;
|
||||
|
||||
///
|
||||
/// \brief Local map rendering
|
||||
///
|
||||
|
@ -58,13 +60,6 @@ namespace MWRender
|
|||
|
||||
osg::ref_ptr<osg::Texture2D> getFogOfWarTexture (int x, int y);
|
||||
|
||||
void removeCamera(osg::Camera* cam);
|
||||
|
||||
/**
|
||||
* Indicates a camera has been queued for rendering and can be cleaned up in the next frame. For internal use only.
|
||||
*/
|
||||
void markForRemoval(osg::Camera* cam);
|
||||
|
||||
/**
|
||||
* Removes cameras that have already been rendered. Should be called every frame to ensure that
|
||||
* we do not render the same map more than once. Note, this cleanup is difficult to implement in an
|
||||
|
@ -104,11 +99,8 @@ namespace MWRender
|
|||
osg::ref_ptr<osg::Group> mRoot;
|
||||
osg::ref_ptr<osg::Node> mSceneRoot;
|
||||
|
||||
typedef std::vector< osg::ref_ptr<osg::Camera> > CameraVector;
|
||||
|
||||
CameraVector mActiveCameras;
|
||||
|
||||
CameraVector mCamerasPendingRemoval;
|
||||
typedef std::vector< osg::ref_ptr<LocalMapRenderToTexture> > RTTVector;
|
||||
RTTVector mLocalMapRTTs;
|
||||
|
||||
typedef std::set<std::pair<int, int> > Grid;
|
||||
Grid mCurrentGrid;
|
||||
|
@ -152,8 +144,7 @@ namespace MWRender
|
|||
void requestExteriorMap(const MWWorld::CellStore* cell);
|
||||
void requestInteriorMap(const MWWorld::CellStore* cell);
|
||||
|
||||
osg::ref_ptr<osg::Camera> createOrthographicCamera(float left, float top, float width, float height, const osg::Vec3d& upVector, float zmin, float zmax);
|
||||
void setupRenderToTexture(osg::ref_ptr<osg::Camera> camera, int x, int y);
|
||||
void setupRenderToTexture(int segment_x, int segment_y, float left, float top, const osg::Vec3d& upVector, float zmin, float zmax);
|
||||
|
||||
bool mInterior;
|
||||
osg::BoundingBox mBounds;
|
||||
|
|
|
@ -6,15 +6,20 @@
|
|||
#include <osg/Camera>
|
||||
#include <osg/Callback>
|
||||
#include <osg/Texture2D>
|
||||
#include <osg/Texture2DArray>
|
||||
#include <osg/FrameBufferObject>
|
||||
|
||||
#include <osgViewer/Viewer>
|
||||
|
||||
#include <components/settings/settings.hpp>
|
||||
#include <components/sceneutil/depth.hpp>
|
||||
#include <components/sceneutil/color.hpp>
|
||||
#include <components/sceneutil/nodecallback.hpp>
|
||||
#include <components/debug/debuglog.hpp>
|
||||
|
||||
#include <components/stereo/multiview.hpp>
|
||||
#include <components/stereo/stereomanager.hpp>
|
||||
|
||||
#include "vismask.hpp"
|
||||
|
||||
namespace
|
||||
|
@ -38,8 +43,8 @@ namespace
|
|||
class CullCallback : public SceneUtil::NodeCallback<CullCallback, osg::Node*, osgUtil::CullVisitor*>
|
||||
{
|
||||
public:
|
||||
CullCallback()
|
||||
: mLastFrameNumber(0)
|
||||
CullCallback(MWRender::PostProcessor* pp)
|
||||
: mPostProcessor(pp)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -47,36 +52,21 @@ namespace
|
|||
{
|
||||
osgUtil::RenderStage* renderStage = cv->getCurrentRenderStage();
|
||||
|
||||
unsigned int frame = cv->getTraversalNumber();
|
||||
if (frame != mLastFrameNumber)
|
||||
if (!mPostProcessor->getMsaaFbo())
|
||||
{
|
||||
mLastFrameNumber = frame;
|
||||
|
||||
MWRender::PostProcessor* postProcessor = dynamic_cast<MWRender::PostProcessor*>(cv->getCurrentCamera()->getUserData());
|
||||
|
||||
if (!postProcessor)
|
||||
{
|
||||
Log(Debug::Error) << "Failed retrieving user data for master camera: FBO setup failed";
|
||||
traverse(node, cv);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!postProcessor->getMsaaFbo())
|
||||
{
|
||||
renderStage->setFrameBufferObject(postProcessor->getFbo());
|
||||
}
|
||||
else
|
||||
{
|
||||
renderStage->setMultisampleResolveFramebufferObject(postProcessor->getFbo());
|
||||
renderStage->setFrameBufferObject(postProcessor->getMsaaFbo());
|
||||
}
|
||||
renderStage->setFrameBufferObject(mPostProcessor->getFbo());
|
||||
}
|
||||
else
|
||||
{
|
||||
renderStage->setMultisampleResolveFramebufferObject(mPostProcessor->getFbo());
|
||||
renderStage->setFrameBufferObject(mPostProcessor->getMsaaFbo());
|
||||
}
|
||||
|
||||
traverse(node, cv);
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned int mLastFrameNumber;
|
||||
MWRender::PostProcessor* mPostProcessor;
|
||||
};
|
||||
|
||||
struct ResizedCallback : osg::GraphicsContext::ResizedCallback
|
||||
|
@ -157,15 +147,13 @@ namespace MWRender
|
|||
PostProcessor::PostProcessor(osgViewer::Viewer* viewer, osg::Group* rootNode)
|
||||
: mViewer(viewer)
|
||||
, mRootNode(new osg::Group)
|
||||
, mDepthFormat(GL_DEPTH24_STENCIL8_EXT)
|
||||
{
|
||||
bool softParticles = Settings::Manager::getBool("soft particles", "Shaders");
|
||||
|
||||
if (!SceneUtil::AutoDepth::isReversed() && !softParticles)
|
||||
if (!SceneUtil::AutoDepth::isReversed() && !softParticles && !Stereo::getStereo())
|
||||
return;
|
||||
|
||||
osg::GraphicsContext* gc = viewer->getCamera()->getGraphicsContext();
|
||||
unsigned int contextID = gc->getState()->getContextID();
|
||||
osg::GLExtensions* ext = gc->getState()->get<osg::GLExtensions>();
|
||||
|
||||
constexpr char errPreamble[] = "Postprocessing and floating point depth buffers disabled: ";
|
||||
|
@ -184,24 +172,19 @@ namespace MWRender
|
|||
|
||||
if (SceneUtil::AutoDepth::isReversed())
|
||||
{
|
||||
if (osg::isGLExtensionSupported(contextID, "GL_ARB_depth_buffer_float"))
|
||||
mDepthFormat = GL_DEPTH32F_STENCIL8;
|
||||
else if (osg::isGLExtensionSupported(contextID, "GL_NV_depth_buffer_float"))
|
||||
mDepthFormat = GL_DEPTH32F_STENCIL8_NV;
|
||||
else
|
||||
if(SceneUtil::AutoDepth::depthSourceType() != GL_FLOAT_32_UNSIGNED_INT_24_8_REV)
|
||||
{
|
||||
// TODO: Once we have post-processing implemented we want to skip this return and continue with setup.
|
||||
// Rendering to a FBO to fullscreen geometry has overhead (especially when MSAA is enabled) and there are no
|
||||
// benefits if no floating point depth formats are supported.
|
||||
Log(Debug::Warning) << errPreamble << "'GL_ARB_depth_buffer_float' and 'GL_NV_depth_buffer_float' unsupported.";
|
||||
|
||||
if (!softParticles)
|
||||
if (!softParticles && !Stereo::getStereo())
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int width = viewer->getCamera()->getViewport()->width();
|
||||
int height = viewer->getCamera()->getViewport()->height();
|
||||
auto* traits = gc->getTraits();
|
||||
int width = traits->width;
|
||||
int height = traits->height;
|
||||
|
||||
createTexturesAndCamera(width, height);
|
||||
resize(width, height);
|
||||
|
@ -210,15 +193,17 @@ namespace MWRender
|
|||
mRootNode->addChild(rootNode);
|
||||
mViewer->setSceneData(mRootNode);
|
||||
|
||||
// We need to manually set the FBO and resolve FBO during the cull callback. If we were using a separate
|
||||
// RTT camera this would not be needed.
|
||||
mViewer->getCamera()->addCullCallback(new CullCallback);
|
||||
mViewer->getCamera()->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);
|
||||
mViewer->getCamera()->attach(osg::Camera::COLOR_BUFFER0, mSceneTex);
|
||||
if (!Stereo::getStereo())
|
||||
{
|
||||
// We need to manually set the FBO and resolve FBO during the cull callback. If we were using a separate
|
||||
// RTT camera this would not be needed.
|
||||
mViewer->getCamera()->addCullCallback(new CullCallback(this));
|
||||
mViewer->getCamera()->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);
|
||||
mViewer->getCamera()->attach(osg::Camera::COLOR_BUFFER0, mSceneTex);
|
||||
mViewer->getCamera()->attach(osg::Camera::PACKED_DEPTH_STENCIL_BUFFER, mDepthTex);
|
||||
}
|
||||
|
||||
mViewer->getCamera()->getGraphicsContext()->setResizedCallback(new ResizedCallback(this));
|
||||
mViewer->getCamera()->setUserData(this);
|
||||
}
|
||||
|
||||
void PostProcessor::resize(int width, int height)
|
||||
|
@ -260,15 +245,89 @@ namespace MWRender
|
|||
|
||||
mViewer->getCamera()->resize(width, height);
|
||||
mHUDCamera->resize(width, height);
|
||||
|
||||
if (Stereo::getStereo())
|
||||
Stereo::Manager::instance().screenResolutionChanged();
|
||||
}
|
||||
|
||||
class HUDCameraStatesetUpdater final : public SceneUtil::StateSetUpdater
|
||||
{
|
||||
public:
|
||||
public:
|
||||
HUDCameraStatesetUpdater(osg::ref_ptr<osg::Camera> HUDCamera, osg::ref_ptr<osg::Program> program, osg::ref_ptr<osg::Texture2D> sceneTex)
|
||||
: mHUDCamera(HUDCamera)
|
||||
, mProgram(program)
|
||||
, mSceneTex(sceneTex)
|
||||
{
|
||||
}
|
||||
|
||||
void setDefaults(osg::StateSet* stateset) override
|
||||
{
|
||||
stateset->setTextureAttributeAndModes(0, mSceneTex, osg::StateAttribute::ON);
|
||||
stateset->setAttributeAndModes(mProgram, osg::StateAttribute::ON);
|
||||
stateset->addUniform(new osg::Uniform("sceneTex", 0));
|
||||
stateset->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
|
||||
stateset->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
|
||||
|
||||
if (osg::DisplaySettings::instance()->getStereo())
|
||||
{
|
||||
stateset->setAttribute(new osg::Viewport);
|
||||
stateset->addUniform(new osg::Uniform("viewportIndex", 0));
|
||||
}
|
||||
}
|
||||
|
||||
void apply(osg::StateSet* stateset, osg::NodeVisitor* nv) override
|
||||
{
|
||||
if (Stereo::getMultiview())
|
||||
{
|
||||
auto& multiviewFbo = Stereo::Manager::instance().multiviewFramebuffer();
|
||||
stateset->setTextureAttributeAndModes(0, multiviewFbo->multiviewColorBuffer(), osg::StateAttribute::ON);
|
||||
}
|
||||
}
|
||||
|
||||
void applyLeft(osg::StateSet* stateset, osgUtil::CullVisitor* cv) override
|
||||
{
|
||||
auto& multiviewFbo = Stereo::Manager::instance().multiviewFramebuffer();
|
||||
stateset->setTextureAttributeAndModes(0, multiviewFbo->layerColorBuffer(0), osg::StateAttribute::ON);
|
||||
|
||||
auto viewport = static_cast<osg::Viewport*>(stateset->getAttribute(osg::StateAttribute::VIEWPORT));
|
||||
auto fullViewport = mHUDCamera->getViewport();
|
||||
viewport->setViewport(
|
||||
0,
|
||||
0,
|
||||
fullViewport->width() / 2,
|
||||
fullViewport->height()
|
||||
);
|
||||
}
|
||||
|
||||
void applyRight(osg::StateSet* stateset, osgUtil::CullVisitor* cv) override
|
||||
{
|
||||
auto& multiviewFbo = Stereo::Manager::instance().multiviewFramebuffer();
|
||||
stateset->setTextureAttributeAndModes(0, multiviewFbo->layerColorBuffer(1), osg::StateAttribute::ON);
|
||||
|
||||
auto viewport = static_cast<osg::Viewport*>(stateset->getAttribute(osg::StateAttribute::VIEWPORT));
|
||||
auto fullViewport = mHUDCamera->getViewport();
|
||||
viewport->setViewport(
|
||||
fullViewport->width() / 2,
|
||||
0,
|
||||
fullViewport->width() / 2,
|
||||
fullViewport->height()
|
||||
);
|
||||
}
|
||||
|
||||
private:
|
||||
osg::ref_ptr<osg::Camera> mHUDCamera;
|
||||
osg::ref_ptr<osg::Program> mProgram;
|
||||
osg::ref_ptr<osg::Texture2D> mSceneTex;
|
||||
};
|
||||
|
||||
void PostProcessor::createTexturesAndCamera(int width, int height)
|
||||
{
|
||||
mDepthTex = new osg::Texture2D;
|
||||
mDepthTex->setTextureSize(width, height);
|
||||
mDepthTex->setSourceFormat(GL_DEPTH_STENCIL_EXT);
|
||||
mDepthTex->setSourceType(SceneUtil::isFloatingPointDepthFormat(getDepthFormat()) ? GL_FLOAT_32_UNSIGNED_INT_24_8_REV : GL_UNSIGNED_INT_24_8_EXT);
|
||||
mDepthTex->setInternalFormat(mDepthFormat);
|
||||
mDepthTex->setSourceFormat(SceneUtil::AutoDepth::depthSourceFormat());
|
||||
mDepthTex->setSourceType(SceneUtil::AutoDepth::depthSourceType());
|
||||
mDepthTex->setInternalFormat(SceneUtil::AutoDepth::depthInternalFormat());
|
||||
mDepthTex->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::NEAREST);
|
||||
mDepthTex->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::NEAREST);
|
||||
mDepthTex->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
|
||||
|
@ -283,9 +342,9 @@ namespace MWRender
|
|||
|
||||
mSceneTex = new osg::Texture2D;
|
||||
mSceneTex->setTextureSize(width, height);
|
||||
mSceneTex->setSourceFormat(GL_RGB);
|
||||
mSceneTex->setSourceType(GL_UNSIGNED_BYTE);
|
||||
mSceneTex->setInternalFormat(GL_RGB);
|
||||
mSceneTex->setSourceFormat(SceneUtil::Color::colorSourceFormat());
|
||||
mSceneTex->setSourceType(SceneUtil::Color::colorSourceType());
|
||||
mSceneTex->setInternalFormat(SceneUtil::Color::colorInternalFormat());
|
||||
mSceneTex->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::NEAREST);
|
||||
mSceneTex->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::NEAREST);
|
||||
mSceneTex->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
|
||||
|
@ -296,6 +355,7 @@ namespace MWRender
|
|||
mHUDCamera->setReferenceFrame(osg::Camera::ABSOLUTE_RF);
|
||||
mHUDCamera->setRenderOrder(osg::Camera::POST_RENDER);
|
||||
mHUDCamera->setClearColor(osg::Vec4(0.45, 0.45, 0.14, 1.0));
|
||||
mHUDCamera->setClearMask(0);
|
||||
mHUDCamera->setProjectionMatrix(osg::Matrix::ortho2D(0, 1, 0, 1));
|
||||
mHUDCamera->setAllowEventFocus(false);
|
||||
mHUDCamera->setViewport(0, 0, width, height);
|
||||
|
@ -325,8 +385,28 @@ namespace MWRender
|
|||
}
|
||||
)GLSL";
|
||||
|
||||
constexpr char fragSrcMultiview[] = R"GLSL(
|
||||
#version 330 compatibility
|
||||
|
||||
#extension GL_EXT_texture_array : require
|
||||
|
||||
varying vec2 uv;
|
||||
uniform sampler2DArray sceneTex;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec3 array_uv = vec3(uv.x * 2, uv.y, 0);
|
||||
if(array_uv.x >= 1.0)
|
||||
{
|
||||
array_uv.x -= 1.0;
|
||||
array_uv.z = 1;
|
||||
}
|
||||
gl_FragData[0] = texture2DArray(sceneTex, array_uv);
|
||||
}
|
||||
)GLSL";
|
||||
|
||||
osg::ref_ptr<osg::Shader> vertShader = new osg::Shader(osg::Shader::VERTEX, vertSrc);
|
||||
osg::ref_ptr<osg::Shader> fragShader = new osg::Shader(osg::Shader::FRAGMENT, fragSrc);
|
||||
osg::ref_ptr<osg::Shader> fragShader = new osg::Shader(osg::Shader::FRAGMENT, Stereo::getMultiview() ? fragSrcMultiview : fragSrc);
|
||||
|
||||
osg::ref_ptr<osg::Program> program = new osg::Program;
|
||||
program->addShader(vertShader);
|
||||
|
@ -334,13 +414,8 @@ namespace MWRender
|
|||
|
||||
mHUDCamera->addChild(createFullScreenTri());
|
||||
mHUDCamera->setNodeMask(Mask_RenderToTexture);
|
||||
|
||||
auto* stateset = mHUDCamera->getOrCreateStateSet();
|
||||
stateset->setTextureAttributeAndModes(0, mSceneTex, osg::StateAttribute::ON);
|
||||
stateset->setAttributeAndModes(program, osg::StateAttribute::ON);
|
||||
stateset->addUniform(new osg::Uniform("sceneTex", 0));
|
||||
stateset->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
|
||||
stateset->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
|
||||
mHUDCamera->setCullCallback(new HUDCameraStatesetUpdater(mHUDCamera, program, mSceneTex));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -7,11 +7,18 @@
|
|||
#include <osg/Camera>
|
||||
#include <osg/ref_ptr>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace osgViewer
|
||||
{
|
||||
class Viewer;
|
||||
}
|
||||
|
||||
namespace Stereo
|
||||
{
|
||||
class MultiviewFramebuffer;
|
||||
}
|
||||
|
||||
namespace MWRender
|
||||
{
|
||||
class PostProcessor : public osg::Referenced
|
||||
|
@ -23,7 +30,6 @@ namespace MWRender
|
|||
auto getFbo() { return mFbo; }
|
||||
auto getFirstPersonRBProxy() { return mFirstPersonDepthRBProxy; }
|
||||
|
||||
int getDepthFormat() { return mDepthFormat; }
|
||||
osg::ref_ptr<osg::Texture2D> getOpaqueDepthTex() { return mOpaqueDepthTex; }
|
||||
|
||||
void resize(int width, int height);
|
||||
|
@ -35,6 +41,7 @@ namespace MWRender
|
|||
osg::ref_ptr<osg::Group> mRootNode;
|
||||
osg::ref_ptr<osg::Camera> mHUDCamera;
|
||||
|
||||
std::shared_ptr<Stereo::MultiviewFramebuffer> mMultiviewFbo;
|
||||
osg::ref_ptr<osg::FrameBufferObject> mMsaaFbo;
|
||||
osg::ref_ptr<osg::FrameBufferObject> mFbo;
|
||||
osg::ref_ptr<osg::RenderBuffer> mFirstPersonDepthRBProxy;
|
||||
|
@ -42,9 +49,8 @@ namespace MWRender
|
|||
osg::ref_ptr<osg::Texture2D> mSceneTex;
|
||||
osg::ref_ptr<osg::Texture2D> mDepthTex;
|
||||
osg::ref_ptr<osg::Texture2D> mOpaqueDepthTex;
|
||||
|
||||
int mDepthFormat;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
|
|
@ -21,6 +21,9 @@
|
|||
|
||||
#include <components/debug/debuglog.hpp>
|
||||
|
||||
#include <components/stereo/stereomanager.hpp>
|
||||
#include <components/stereo/multiview.hpp>
|
||||
|
||||
#include <components/resource/resourcesystem.hpp>
|
||||
#include <components/resource/imagemanager.hpp>
|
||||
#include <components/resource/keyframemanager.hpp>
|
||||
|
@ -31,6 +34,7 @@
|
|||
#include <components/settings/settings.hpp>
|
||||
|
||||
#include <components/sceneutil/depth.hpp>
|
||||
#include <components/sceneutil/visitor.hpp>
|
||||
#include <components/sceneutil/lightmanager.hpp>
|
||||
#include <components/sceneutil/statesetupdater.hpp>
|
||||
#include <components/sceneutil/positionattitudetransform.hpp>
|
||||
|
@ -45,6 +49,7 @@
|
|||
|
||||
#include <components/detournavigator/navigator.hpp>
|
||||
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
#include "../mwworld/cellstore.hpp"
|
||||
#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/groundcoverstore.hpp"
|
||||
|
@ -70,6 +75,54 @@
|
|||
|
||||
namespace MWRender
|
||||
{
|
||||
class PerViewUniformStateUpdater final : public SceneUtil::StateSetUpdater
|
||||
{
|
||||
public:
|
||||
public:
|
||||
PerViewUniformStateUpdater()
|
||||
{
|
||||
}
|
||||
|
||||
void setDefaults(osg::StateSet* stateset) override
|
||||
{
|
||||
stateset->addUniform(new osg::Uniform("projectionMatrix", osg::Matrixf{}));
|
||||
}
|
||||
|
||||
void apply(osg::StateSet* stateset, osg::NodeVisitor* nv) override
|
||||
{
|
||||
auto* uProjectionMatrix = stateset->getUniform("projectionMatrix");
|
||||
if (uProjectionMatrix)
|
||||
uProjectionMatrix->set(mProjectionMatrix);
|
||||
}
|
||||
|
||||
void applyLeft(osg::StateSet* stateset, osgUtil::CullVisitor* nv) override
|
||||
{
|
||||
auto* uProjectionMatrix = stateset->getUniform("projectionMatrix");
|
||||
if (uProjectionMatrix)
|
||||
uProjectionMatrix->set(Stereo::Manager::instance().computeEyeProjection(0, SceneUtil::AutoDepth::isReversed()));
|
||||
}
|
||||
|
||||
void applyRight(osg::StateSet* stateset, osgUtil::CullVisitor* nv) override
|
||||
{
|
||||
auto* uProjectionMatrix = stateset->getUniform("projectionMatrix");
|
||||
if (uProjectionMatrix)
|
||||
uProjectionMatrix->set(Stereo::Manager::instance().computeEyeProjection(1, SceneUtil::AutoDepth::isReversed()));
|
||||
}
|
||||
|
||||
void setProjectionMatrix(const osg::Matrixf& projectionMatrix)
|
||||
{
|
||||
mProjectionMatrix = projectionMatrix;
|
||||
}
|
||||
|
||||
const osg::Matrixf& projectionMatrix() const
|
||||
{
|
||||
return mProjectionMatrix;
|
||||
}
|
||||
|
||||
private:
|
||||
osg::Matrixf mProjectionMatrix;
|
||||
};
|
||||
|
||||
class SharedUniformStateUpdater : public SceneUtil::StateSetUpdater
|
||||
{
|
||||
public:
|
||||
|
@ -82,9 +135,8 @@ namespace MWRender
|
|||
{
|
||||
}
|
||||
|
||||
void setDefaults(osg::StateSet *stateset) override
|
||||
void setDefaults(osg::StateSet* stateset) override
|
||||
{
|
||||
stateset->addUniform(new osg::Uniform("projectionMatrix", osg::Matrixf{}));
|
||||
stateset->addUniform(new osg::Uniform("linearFac", 0.f));
|
||||
stateset->addUniform(new osg::Uniform("near", 0.f));
|
||||
stateset->addUniform(new osg::Uniform("far", 0.f));
|
||||
|
@ -98,10 +150,6 @@ namespace MWRender
|
|||
|
||||
void apply(osg::StateSet* stateset, osg::NodeVisitor* nv) override
|
||||
{
|
||||
auto* uProjectionMatrix = stateset->getUniform("projectionMatrix");
|
||||
if (uProjectionMatrix)
|
||||
uProjectionMatrix->set(mProjectionMatrix);
|
||||
|
||||
auto* uLinearFac = stateset->getUniform("linearFac");
|
||||
if (uLinearFac)
|
||||
uLinearFac->set(mLinearFac);
|
||||
|
@ -130,11 +178,6 @@ namespace MWRender
|
|||
}
|
||||
}
|
||||
|
||||
void setProjectionMatrix(const osg::Matrixf& projectionMatrix)
|
||||
{
|
||||
mProjectionMatrix = projectionMatrix;
|
||||
}
|
||||
|
||||
void setLinearFac(float linearFac)
|
||||
{
|
||||
mLinearFac = linearFac;
|
||||
|
@ -167,7 +210,6 @@ namespace MWRender
|
|||
|
||||
|
||||
private:
|
||||
osg::Matrixf mProjectionMatrix;
|
||||
float mLinearFac;
|
||||
float mNear;
|
||||
float mFar;
|
||||
|
@ -312,14 +354,16 @@ namespace MWRender
|
|||
auto lightingMethod = SceneUtil::LightManager::getLightingMethodFromString(Settings::Manager::getString("lighting method", "Shaders"));
|
||||
|
||||
resourceSystem->getSceneManager()->setParticleSystemMask(MWRender::Mask_ParticleSystem);
|
||||
// Shadows and radial fog have problems with fixed-function mode
|
||||
// Shadows and radial fog have problems with fixed-function mode.
|
||||
bool forceShaders = Settings::Manager::getBool("radial fog", "Shaders")
|
||||
|| Settings::Manager::getBool("soft particles", "Shaders")
|
||||
|| Settings::Manager::getBool("force shaders", "Shaders")
|
||||
|| Settings::Manager::getBool("enable shadows", "Shadows")
|
||||
|| lightingMethod != SceneUtil::LightingMethod::FFP
|
||||
|| reverseZ;
|
||||
|| reverseZ
|
||||
|| Stereo::getMultiview();
|
||||
resourceSystem->getSceneManager()->setForceShaders(forceShaders);
|
||||
|
||||
// FIXME: calling dummy method because terrain needs to know whether lighting is clamped
|
||||
resourceSystem->getSceneManager()->setClampLighting(Settings::Manager::getBool("clamp lighting", "Shaders"));
|
||||
resourceSystem->getSceneManager()->setAutoUseNormalMaps(Settings::Manager::getBool("auto use object normal maps", "Shaders"));
|
||||
|
@ -368,6 +412,8 @@ namespace MWRender
|
|||
globalDefines["preLightEnv"] = Settings::Manager::getBool("apply lighting to environment maps", "Shaders") ? "1" : "0";
|
||||
globalDefines["radialFog"] = Settings::Manager::getBool("radial fog", "Shaders") ? "1" : "0";
|
||||
globalDefines["useGPUShader4"] = "0";
|
||||
globalDefines["useOVR_multiview"] = "0";
|
||||
globalDefines["numViews"] = "1";
|
||||
|
||||
for (auto itr = lightDefines.begin(); itr != lightDefines.end(); itr++)
|
||||
globalDefines[itr->first] = itr->second;
|
||||
|
@ -453,16 +499,19 @@ namespace MWRender
|
|||
mSharedUniformStateUpdater = new SharedUniformStateUpdater(groundcover);
|
||||
rootNode->addUpdateCallback(mSharedUniformStateUpdater);
|
||||
|
||||
mPerViewUniformStateUpdater = new PerViewUniformStateUpdater();
|
||||
rootNode->addCullCallback(mPerViewUniformStateUpdater);
|
||||
|
||||
mPostProcessor = new PostProcessor(viewer, mRootNode);
|
||||
resourceSystem->getSceneManager()->setDepthFormat(mPostProcessor->getDepthFormat());
|
||||
resourceSystem->getSceneManager()->setDepthFormat(SceneUtil::AutoDepth::depthInternalFormat());
|
||||
resourceSystem->getSceneManager()->setOpaqueDepthTex(mPostProcessor->getOpaqueDepthTex());
|
||||
|
||||
if (reverseZ && !SceneUtil::isFloatingPointDepthFormat(mPostProcessor->getDepthFormat()))
|
||||
if (reverseZ && !SceneUtil::isFloatingPointDepthFormat(SceneUtil::AutoDepth::depthInternalFormat()))
|
||||
Log(Debug::Warning) << "Floating point depth format not in use but reverse-z buffer is enabled, consider disabling it.";
|
||||
|
||||
// water goes after terrain for correct waterculling order
|
||||
mWater.reset(new Water(sceneRoot->getParent(0), sceneRoot, mResourceSystem, mViewer->getIncrementalCompileOperation(), resourcePath));
|
||||
|
||||
|
||||
mCamera.reset(new Camera(mViewer->getCamera()));
|
||||
|
||||
mScreenshotManager.reset(new ScreenshotManager(viewer, mRootNode, sceneRoot, mResourceSystem, mWater.get()));
|
||||
|
@ -514,7 +563,8 @@ namespace MWRender
|
|||
mViewer->getCamera()->setComputeNearFarMode(osg::Camera::DO_NOT_COMPUTE_NEAR_FAR);
|
||||
mViewer->getCamera()->setCullingMode(cullingMode);
|
||||
|
||||
mViewer->getCamera()->setCullMask(~(Mask_UpdateVisitor|Mask_SimpleWater));
|
||||
auto mask = ~(Mask_UpdateVisitor | Mask_SimpleWater);
|
||||
MWBase::Environment::get().getWindowManager()->setCullMask(mask);
|
||||
NifOsg::Loader::setHiddenNodeMask(Mask_UpdateVisitor);
|
||||
NifOsg::Loader::setIntersectionDisabledNodeMask(Mask_Effect);
|
||||
Nif::NIFFile::setLoadUnsupportedFiles(Settings::Manager::getBool("load unsupported nif files", "Models"));
|
||||
|
@ -537,6 +587,7 @@ namespace MWRender
|
|||
|
||||
SceneUtil::setCameraClearDepth(mViewer->getCamera());
|
||||
|
||||
|
||||
updateProjectionMatrix();
|
||||
|
||||
mViewer->getCamera()->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
||||
|
@ -753,14 +804,15 @@ namespace MWRender
|
|||
}
|
||||
else if (mode == Render_Scene)
|
||||
{
|
||||
unsigned int mask = mViewer->getCamera()->getCullMask();
|
||||
auto* wm = MWBase::Environment::get().getWindowManager();
|
||||
unsigned int mask = wm->getCullMask();
|
||||
bool enabled = !(mask&sToggleWorldMask);
|
||||
if (enabled)
|
||||
mask |= sToggleWorldMask;
|
||||
else
|
||||
mask &= ~sToggleWorldMask;
|
||||
mWater->showWorld(enabled);
|
||||
mViewer->getCamera()->setCullMask(mask);
|
||||
wm->setCullMask(mask);
|
||||
return enabled;
|
||||
}
|
||||
else if (mode == Render_NavMesh)
|
||||
|
@ -1155,14 +1207,23 @@ namespace MWRender
|
|||
if (SceneUtil::AutoDepth::isReversed())
|
||||
{
|
||||
mSharedUniformStateUpdater->setLinearFac(-mNearClip / (mViewDistance - mNearClip) - 1.f);
|
||||
mSharedUniformStateUpdater->setProjectionMatrix(SceneUtil::getReversedZProjectionMatrixAsPerspective(fov, aspect, mNearClip, mViewDistance));
|
||||
mPerViewUniformStateUpdater->setProjectionMatrix(SceneUtil::getReversedZProjectionMatrixAsPerspective(fov, aspect, mNearClip, mViewDistance));
|
||||
}
|
||||
else
|
||||
mSharedUniformStateUpdater->setProjectionMatrix(mViewer->getCamera()->getProjectionMatrix());
|
||||
mPerViewUniformStateUpdater->setProjectionMatrix(mViewer->getCamera()->getProjectionMatrix());
|
||||
|
||||
mSharedUniformStateUpdater->setNear(mNearClip);
|
||||
mSharedUniformStateUpdater->setFar(mViewDistance);
|
||||
mSharedUniformStateUpdater->setScreenRes(width, height);
|
||||
if (Stereo::getStereo())
|
||||
{
|
||||
auto res = Stereo::Manager::instance().eyeResolution();
|
||||
mSharedUniformStateUpdater->setScreenRes(res.x(), res.y());
|
||||
Stereo::Manager::instance().setMasterProjectionMatrix(mPerViewUniformStateUpdater->projectionMatrix());
|
||||
}
|
||||
else
|
||||
{
|
||||
mSharedUniformStateUpdater->setScreenRes(width, height);
|
||||
}
|
||||
|
||||
// Since our fog is not radial yet, we should take FOV in account, otherwise terrain near viewing distance may disappear.
|
||||
// Limit FOV here just for sure, otherwise viewing distance can be too high.
|
||||
|
|
|
@ -76,6 +76,7 @@ namespace MWRender
|
|||
{
|
||||
class StateUpdater;
|
||||
class SharedUniformStateUpdater;
|
||||
class PerViewUniformStateUpdater;
|
||||
|
||||
class EffectManager;
|
||||
class ScreenshotManager;
|
||||
|
@ -97,7 +98,7 @@ namespace MWRender
|
|||
class RenderingManager : public MWRender::RenderingInterface
|
||||
{
|
||||
public:
|
||||
RenderingManager(osgViewer::Viewer* viewer, osg::ref_ptr<osg::Group> rootNode,
|
||||
RenderingManager(osgViewer::Viewer* viewer, osg::ref_ptr<osg::Group> rootNode,
|
||||
Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue,
|
||||
const std::string& resourcePath, DetourNavigator::Navigator& navigator, const MWWorld::GroundcoverStore& groundcoverStore);
|
||||
~RenderingManager();
|
||||
|
@ -255,6 +256,7 @@ namespace MWRender
|
|||
|
||||
void updateRecastMesh();
|
||||
|
||||
|
||||
osg::ref_ptr<osgUtil::IntersectionVisitor> getIntersectionVisitor(osgUtil::Intersector* intersector, bool ignorePlayer, bool ignoreActors);
|
||||
|
||||
osg::ref_ptr<osgUtil::IntersectionVisitor> mIntersectionVisitor;
|
||||
|
@ -292,6 +294,7 @@ namespace MWRender
|
|||
|
||||
osg::ref_ptr<StateUpdater> mStateUpdater;
|
||||
osg::ref_ptr<SharedUniformStateUpdater> mSharedUniformStateUpdater;
|
||||
osg::ref_ptr<PerViewUniformStateUpdater> mPerViewUniformStateUpdater;
|
||||
|
||||
osg::Vec4f mAmbientColor;
|
||||
float mMinimumAmbientLuminance;
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
#include <components/resource/scenemanager.hpp>
|
||||
#include <components/shader/shadermanager.hpp>
|
||||
#include <components/sceneutil/depth.hpp>
|
||||
#include <components/stereo/stereomanager.hpp>
|
||||
#include <components/stereo/multiview.hpp>
|
||||
|
||||
#include <components/settings/settings.hpp>
|
||||
|
||||
|
@ -85,6 +87,12 @@ namespace MWRender
|
|||
{
|
||||
int screenW = renderInfo.getCurrentCamera()->getViewport()->width();
|
||||
int screenH = renderInfo.getCurrentCamera()->getViewport()->height();
|
||||
if (Stereo::getStereo())
|
||||
{
|
||||
auto eyeRes = Stereo::Manager::instance().eyeResolution();
|
||||
screenW = eyeRes.x();
|
||||
screenH = eyeRes.y();
|
||||
}
|
||||
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);
|
||||
|
@ -94,13 +102,19 @@ namespace MWRender
|
|||
// Ensure we are reading from the resolved framebuffer and not the multisampled render buffer. Also ensure that the readbuffer is set correctly with rendeirng to FBO.
|
||||
// glReadPixel() cannot read from multisampled targets
|
||||
PostProcessor* postProcessor = dynamic_cast<PostProcessor*>(renderInfo.getCurrentCamera()->getUserData());
|
||||
osg::GLExtensions* ext = osg::GLExtensions::Get(renderInfo.getContextID(), false);
|
||||
|
||||
if (postProcessor && postProcessor->getFbo())
|
||||
if (ext)
|
||||
{
|
||||
osg::GLExtensions* ext = osg::GLExtensions::Get(renderInfo.getContextID(), false);
|
||||
if (ext)
|
||||
osg::FrameBufferObject* fbo = nullptr;
|
||||
if (Stereo::getStereo())
|
||||
fbo = Stereo::Manager::instance().multiviewFramebuffer()->layerFbo(0);
|
||||
else if (postProcessor)
|
||||
fbo = postProcessor->getFbo();
|
||||
|
||||
if (fbo)
|
||||
{
|
||||
ext->glBindFramebuffer(GL_FRAMEBUFFER_EXT, postProcessor->getFbo()->getHandle(renderInfo.getContextID()));
|
||||
ext->glBindFramebuffer(GL_FRAMEBUFFER_EXT, fbo->getHandle(renderInfo.getContextID()));
|
||||
renderInfo.getState()->glReadBuffer(GL_COLOR_ATTACHMENT0_EXT);
|
||||
}
|
||||
}
|
||||
|
@ -346,7 +360,7 @@ namespace MWRender
|
|||
rttCamera->addChild(mWater->getReflectionNode());
|
||||
rttCamera->addChild(mWater->getRefractionNode());
|
||||
|
||||
rttCamera->setCullMask(mViewer->getCamera()->getCullMask() & ~(Mask_GUI|Mask_FirstPerson));
|
||||
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);
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
#include <components/misc/rng.hpp>
|
||||
#include <components/misc/resourcehelpers.hpp>
|
||||
#include <components/stereo/stereomanager.hpp>
|
||||
|
||||
#include <components/nifosg/particle.hpp>
|
||||
|
||||
|
@ -332,8 +333,10 @@ namespace MWRender
|
|||
|
||||
if (mSceneManager->getForceShaders())
|
||||
{
|
||||
auto vertex = mSceneManager->getShaderManager().getShader("sky_vertex.glsl", {}, osg::Shader::VERTEX);
|
||||
auto fragment = mSceneManager->getShaderManager().getShader("sky_fragment.glsl", {}, osg::Shader::FRAGMENT);
|
||||
Shader::ShaderManager::DefineMap defines = {};
|
||||
Stereo::Manager::instance().shaderStereoDefines(defines);
|
||||
auto vertex = mSceneManager->getShaderManager().getShader("sky_vertex.glsl", defines, osg::Shader::VERTEX);
|
||||
auto fragment = mSceneManager->getShaderManager().getShader("sky_fragment.glsl", defines, osg::Shader::FRAGMENT);
|
||||
auto program = mSceneManager->getShaderManager().getProgram(vertex, fragment);
|
||||
mEarlyRenderBinRoot->getOrCreateStateSet()->addUniform(new osg::Uniform("pass", -1));
|
||||
mEarlyRenderBinRoot->getOrCreateStateSet()->setAttributeAndModes(program, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <osgParticle/Particle>
|
||||
|
||||
#include <components/misc/rng.hpp>
|
||||
#include <components/stereo/stereomanager.hpp>
|
||||
|
||||
#include <components/resource/scenemanager.hpp>
|
||||
#include <components/resource/imagemanager.hpp>
|
||||
|
@ -602,6 +603,38 @@ namespace MWRender
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
class SkyMultiviewStatesetUpdater: public SceneUtil::StateSetUpdater
|
||||
{
|
||||
public:
|
||||
SkyMultiviewStatesetUpdater()
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void setDefaults(osg::StateSet* stateset)
|
||||
{
|
||||
stateset->addUniform(new osg::Uniform(osg::Uniform::FLOAT_MAT4, "viewMatrixMultiView", 2), osg::StateAttribute::OVERRIDE);
|
||||
}
|
||||
|
||||
virtual void apply(osg::StateSet* stateset, osg::NodeVisitor* /*nv*/)
|
||||
{
|
||||
auto* viewMatrixMultiViewUniform = stateset->getUniform("viewMatrixMultiView");
|
||||
auto& sm = Stereo::Manager::instance();
|
||||
|
||||
for (int view : {0, 1})
|
||||
{
|
||||
auto viewOffsetMatrix = sm.computeEyeViewOffset(view);
|
||||
for (int col : {0, 1, 2})
|
||||
viewOffsetMatrix(3, col) = 0;
|
||||
|
||||
viewMatrixMultiViewUniform->setElement(view, viewOffsetMatrix);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
CameraRelativeTransform::CameraRelativeTransform()
|
||||
{
|
||||
// Culling works in node-local space, not in camera space, so we can't cull this node correctly
|
||||
|
@ -610,6 +643,7 @@ namespace MWRender
|
|||
setCullingActive(false);
|
||||
|
||||
addCullCallback(new CameraRelativeTransformCullCallback);
|
||||
addCullCallback(new SkyMultiviewStatesetUpdater);
|
||||
}
|
||||
|
||||
CameraRelativeTransform::CameraRelativeTransform(const CameraRelativeTransform& copy, const osg::CopyOp& copyop)
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <osg/PositionAttitudeTransform>
|
||||
#include <osg/ClipNode>
|
||||
#include <osg/FrontFace>
|
||||
#include <osg/ViewportIndexed>
|
||||
|
||||
#include <osgDB/ReadFile>
|
||||
|
||||
|
@ -33,6 +34,7 @@
|
|||
|
||||
#include <components/misc/constants.hpp>
|
||||
#include <components/misc/stringops.hpp>
|
||||
#include <components/stereo/stereomanager.hpp>
|
||||
|
||||
#include <components/nifosg/controller.hpp>
|
||||
|
||||
|
@ -44,6 +46,8 @@
|
|||
|
||||
#include "../mwworld/cellstore.hpp"
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
|
||||
#include "vismask.hpp"
|
||||
#include "ripplesimulation.hpp"
|
||||
#include "renderbin.hpp"
|
||||
|
@ -166,7 +170,7 @@ private:
|
|||
class InheritViewPointCallback : public SceneUtil::NodeCallback<InheritViewPointCallback, osg::Node*, osgUtil::CullVisitor*>
|
||||
{
|
||||
public:
|
||||
InheritViewPointCallback() {}
|
||||
InheritViewPointCallback() {}
|
||||
|
||||
void operator()(osg::Node* node, osgUtil::CullVisitor* cv)
|
||||
{
|
||||
|
@ -260,7 +264,7 @@ class Refraction : public SceneUtil::RTTNode
|
|||
{
|
||||
public:
|
||||
Refraction(uint32_t rttSize)
|
||||
: RTTNode(rttSize, rttSize, 1, false)
|
||||
: RTTNode(rttSize, rttSize, 0, false, 1, StereoAwareness::Aware)
|
||||
, mNodeMask(Refraction::sDefaultCullMask)
|
||||
{
|
||||
mClipCullNode = new ClipCullNode;
|
||||
|
@ -335,7 +339,7 @@ class Reflection : public SceneUtil::RTTNode
|
|||
{
|
||||
public:
|
||||
Reflection(uint32_t rttSize, bool isInterior)
|
||||
: RTTNode(rttSize, rttSize, 0, false)
|
||||
: RTTNode(rttSize, rttSize, 0, false, 0, StereoAwareness::Aware)
|
||||
{
|
||||
setInterior(isInterior);
|
||||
mClipCullNode = new ClipCullNode;
|
||||
|
@ -458,6 +462,7 @@ Water::Water(osg::Group *parent, osg::Group* sceneRoot, Resource::ResourceSystem
|
|||
mWaterGeom->setDrawCallback(new DepthClampCallback);
|
||||
mWaterGeom->setNodeMask(Mask_Water);
|
||||
mWaterGeom->setDataVariance(osg::Object::STATIC);
|
||||
mWaterGeom->setName("Water Geometry");
|
||||
|
||||
mWaterNode = new osg::PositionAttitudeTransform;
|
||||
mWaterNode->setName("Water Root");
|
||||
|
@ -468,6 +473,7 @@ Water::Water(osg::Group *parent, osg::Group* sceneRoot, Resource::ResourceSystem
|
|||
osg::ref_ptr<osg::Geometry> geom2 (osg::clone(mWaterGeom.get(), osg::CopyOp::DEEP_COPY_NODES));
|
||||
createSimpleWaterStateSet(geom2, Fallback::Map::getFloat("Water_Map_Alpha"));
|
||||
geom2->setNodeMask(Mask_SimpleWater);
|
||||
geom2->setName("Simple Water Geometry");
|
||||
mWaterNode->addChild(geom2);
|
||||
|
||||
mSceneRoot->addChild(mWaterNode);
|
||||
|
@ -679,6 +685,7 @@ void Water::createShaderWaterStateSet(osg::Node* node, Reflection* reflection, R
|
|||
const auto rippleDetail = std::clamp(Settings::Manager::getInt("rain ripple detail", "Water"), 0, 2);
|
||||
defineMap["rain_ripple_detail"] = std::to_string(rippleDetail);
|
||||
|
||||
Stereo::Manager::instance().shaderStereoDefines(defineMap);
|
||||
|
||||
Shader::ShaderManager& shaderMgr = mResourceSystem->getSceneManager()->getShaderManager();
|
||||
osg::ref_ptr<osg::Shader> vertexShader(shaderMgr.getShader("water_vertex.glsl", defineMap, osg::Shader::VERTEX));
|
||||
|
@ -694,6 +701,7 @@ void Water::createShaderWaterStateSet(osg::Node* node, Reflection* reflection, R
|
|||
normalMap->setMaxAnisotropy(16);
|
||||
normalMap->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR_MIPMAP_LINEAR);
|
||||
normalMap->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
|
||||
|
||||
|
||||
mRainIntensityUpdater = new RainIntensityUpdater();
|
||||
node->setUpdateCallback(mRainIntensityUpdater);
|
||||
|
|
26
cmake/CheckOsgMultiview.cmake
Normal file
26
cmake/CheckOsgMultiview.cmake
Normal file
|
@ -0,0 +1,26 @@
|
|||
set(TMP_ROOT ${CMAKE_BINARY_DIR}/try-compile)
|
||||
file(MAKE_DIRECTORY ${TMP_ROOT})
|
||||
|
||||
file(WRITE ${TMP_ROOT}/checkmultiview.cpp
|
||||
"
|
||||
#include <osg/Camera>
|
||||
int main(void)
|
||||
{
|
||||
(void)osg::Camera::FACE_CONTROLLED_BY_MULTIVIEW_SHADER;
|
||||
return 0;
|
||||
}
|
||||
")
|
||||
|
||||
message(STATUS "Checking if OSG supports multiview")
|
||||
|
||||
try_compile(RESULT_VAR
|
||||
${TMP_ROOT}/temp
|
||||
${TMP_ROOT}/checkmultiview.cpp
|
||||
CMAKE_FLAGS "-DINCLUDE_DIRECTORIES=${OPENSCENEGRAPH_INCLUDE_DIRS}"
|
||||
)
|
||||
set(HAVE_MULTIVIEW ${RESULT_VAR})
|
||||
if(HAVE_MULTIVIEW)
|
||||
message(STATUS "Osg supports multiview")
|
||||
else(HAVE_MULTIVIEW)
|
||||
message(NOTICE "Osg does not support multiview, disabling use of GL_OVR_multiview")
|
||||
endif(HAVE_MULTIVIEW)
|
|
@ -61,7 +61,7 @@ add_component_dir (sceneutil
|
|||
clone attach visitor util statesetupdater controller skeleton riggeometry morphgeometry lightcontroller
|
||||
lightmanager lightutil positionattitudetransform workqueue pathgridutil waterutil writescene serialize optimizer
|
||||
actorutil detourdebugdraw navmesh agentpath shadow mwshadowtechnique recastmesh shadowsbin osgacontroller rtt
|
||||
screencapture depth
|
||||
screencapture depth color
|
||||
)
|
||||
|
||||
add_component_dir (nif
|
||||
|
@ -196,6 +196,10 @@ add_component_dir (misc
|
|||
compression osguservalues errorMarker color
|
||||
)
|
||||
|
||||
add_component_dir (stereo
|
||||
frustum multiview stereomanager types
|
||||
)
|
||||
|
||||
add_component_dir (debug
|
||||
debugging debuglog gldebug
|
||||
)
|
||||
|
|
157
components/sceneutil/color.cpp
Normal file
157
components/sceneutil/color.cpp
Normal file
|
@ -0,0 +1,157 @@
|
|||
#include "color.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
|
||||
#include <SDL_opengl_glext.h>
|
||||
|
||||
#include <components/debug/debuglog.hpp>
|
||||
#include <components/settings/settings.hpp>
|
||||
|
||||
namespace SceneUtil
|
||||
{
|
||||
|
||||
bool isColorFormat(GLenum format)
|
||||
{
|
||||
static constexpr std::array<GLenum, 42> formats = {
|
||||
GL_RGB,
|
||||
GL_RGB4,
|
||||
GL_RGB5,
|
||||
GL_RGB8,
|
||||
GL_RGB8_SNORM,
|
||||
GL_RGB10,
|
||||
GL_RGB12,
|
||||
GL_RGB16,
|
||||
GL_RGB16_SNORM,
|
||||
GL_SRGB,
|
||||
GL_SRGB8,
|
||||
GL_RGB16F,
|
||||
GL_RGB32F,
|
||||
GL_R11F_G11F_B10F,
|
||||
GL_RGB9_E5,
|
||||
GL_RGB8I,
|
||||
GL_RGB8UI,
|
||||
GL_RGB16I,
|
||||
GL_RGB16UI,
|
||||
GL_RGB32I,
|
||||
GL_RGB32UI,
|
||||
GL_RGBA,
|
||||
GL_RGBA2,
|
||||
GL_RGBA4,
|
||||
GL_RGB5_A1,
|
||||
GL_RGBA8,
|
||||
GL_RGBA8_SNORM,
|
||||
GL_RGB10_A2,
|
||||
GL_RGB10_A2UI,
|
||||
GL_RGBA12,
|
||||
GL_RGBA16,
|
||||
GL_RGBA16_SNORM,
|
||||
GL_SRGB_ALPHA8,
|
||||
GL_SRGB8_ALPHA8,
|
||||
GL_RGBA16F,
|
||||
GL_RGBA32F,
|
||||
GL_RGBA8I,
|
||||
GL_RGBA8UI,
|
||||
GL_RGBA16I,
|
||||
GL_RGBA16UI,
|
||||
GL_RGBA32I,
|
||||
GL_RGBA32UI,
|
||||
};
|
||||
|
||||
return std::find(formats.cbegin(), formats.cend(), format) != formats.cend();
|
||||
}
|
||||
|
||||
bool isFloatingPointColorFormat(GLenum format)
|
||||
{
|
||||
static constexpr std::array<GLenum, 5> formats = {
|
||||
GL_RGB16F,
|
||||
GL_RGB32F,
|
||||
GL_R11F_G11F_B10F,
|
||||
GL_RGBA16F,
|
||||
GL_RGBA32F,
|
||||
};
|
||||
|
||||
return std::find(formats.cbegin(), formats.cend(), format) != formats.cend();
|
||||
}
|
||||
|
||||
int getColorFormatChannelCount(GLenum format)
|
||||
{
|
||||
static constexpr std::array<GLenum, 21> formats = {
|
||||
GL_RGBA,
|
||||
GL_RGBA2,
|
||||
GL_RGBA4,
|
||||
GL_RGB5_A1,
|
||||
GL_RGBA8,
|
||||
GL_RGBA8_SNORM,
|
||||
GL_RGB10_A2,
|
||||
GL_RGB10_A2UI,
|
||||
GL_RGBA12,
|
||||
GL_RGBA16,
|
||||
GL_RGBA16_SNORM,
|
||||
GL_SRGB_ALPHA8,
|
||||
GL_SRGB8_ALPHA8,
|
||||
GL_RGBA16F,
|
||||
GL_RGBA32F,
|
||||
GL_RGBA8I,
|
||||
GL_RGBA8UI,
|
||||
GL_RGBA16I,
|
||||
GL_RGBA16UI,
|
||||
GL_RGBA32I,
|
||||
GL_RGBA32UI,
|
||||
};
|
||||
if (std::find(formats.cbegin(), formats.cend(), format) != formats.cend())
|
||||
return 4;
|
||||
return 3;
|
||||
}
|
||||
|
||||
void getColorFormatSourceFormatAndType(GLenum internalFormat, GLenum& sourceFormat, GLenum& sourceType)
|
||||
{
|
||||
if (getColorFormatChannelCount(internalFormat == 4))
|
||||
sourceFormat = GL_RGBA;
|
||||
else
|
||||
sourceFormat = GL_RGB;
|
||||
|
||||
if (isFloatingPointColorFormat(internalFormat))
|
||||
sourceType = GL_FLOAT;
|
||||
else
|
||||
sourceType = GL_UNSIGNED_BYTE;
|
||||
}
|
||||
|
||||
namespace Color
|
||||
{
|
||||
GLenum sColorInternalFormat;
|
||||
GLenum sColorSourceFormat;
|
||||
GLenum sColorSourceType;
|
||||
|
||||
GLenum colorInternalFormat()
|
||||
{
|
||||
return sColorInternalFormat;
|
||||
}
|
||||
|
||||
GLenum colorSourceFormat()
|
||||
{
|
||||
return sColorSourceFormat;
|
||||
}
|
||||
|
||||
GLenum colorSourceType()
|
||||
{
|
||||
return sColorSourceType;
|
||||
}
|
||||
|
||||
void SelectColorFormatOperation::operator()([[maybe_unused]] osg::GraphicsContext* graphicsContext)
|
||||
{
|
||||
sColorInternalFormat = GL_RGB;
|
||||
|
||||
for (auto supportedFormat : mSupportedFormats)
|
||||
{
|
||||
if (isColorFormat(supportedFormat))
|
||||
{
|
||||
sColorInternalFormat = supportedFormat;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
getColorFormatSourceFormatAndType(sColorInternalFormat, sColorSourceFormat, sColorSourceType);
|
||||
}
|
||||
}
|
||||
}
|
207
components/sceneutil/color.hpp
Normal file
207
components/sceneutil/color.hpp
Normal file
|
@ -0,0 +1,207 @@
|
|||
#ifndef OPENMW_COMPONENTS_SCENEUTIL_COLOR_H
|
||||
#define OPENMW_COMPONENTS_SCENEUTIL_COLOR_H
|
||||
|
||||
#include <osg/GraphicsThread>
|
||||
|
||||
namespace SceneUtil
|
||||
{
|
||||
bool isColorFormat(GLenum format);
|
||||
bool isFloatingPointColorFormat(GLenum format);
|
||||
int getColorFormatChannelCount(GLenum format);
|
||||
void getColorFormatSourceFormatAndType(GLenum internalFormat, GLenum& sourceFormat, GLenum& sourceType);
|
||||
|
||||
namespace Color
|
||||
{
|
||||
GLenum colorSourceFormat();
|
||||
GLenum colorSourceType();
|
||||
GLenum colorInternalFormat();
|
||||
|
||||
class SelectColorFormatOperation final : public osg::GraphicsOperation
|
||||
{
|
||||
public:
|
||||
SelectColorFormatOperation() : GraphicsOperation("SelectColorFormatOperation", false)
|
||||
{}
|
||||
|
||||
void operator()(osg::GraphicsContext* graphicsContext) override;
|
||||
|
||||
void setSupportedFormats(const std::vector<GLenum>& supportedFormats)
|
||||
{
|
||||
mSupportedFormats = supportedFormats;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<GLenum> mSupportedFormats;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef GL_RGB
|
||||
#define GL_RGB 0x1907
|
||||
#endif
|
||||
|
||||
#ifndef GL_RGBA
|
||||
#define GL_RGBA 0x1908
|
||||
#endif
|
||||
|
||||
#ifndef GL_RGB4
|
||||
#define GL_RGB4 0x804F
|
||||
#endif
|
||||
|
||||
#ifndef GL_RGB5
|
||||
#define GL_RGB5 0x8050
|
||||
#endif
|
||||
|
||||
#ifndef GL_RGB8
|
||||
#define GL_RGB8 0x8051
|
||||
#endif
|
||||
|
||||
#ifndef GL_RGB8_SNORM
|
||||
#define GL_RGB8_SNORM 0x8F96
|
||||
#endif
|
||||
|
||||
#ifndef GL_RGB10
|
||||
#define GL_RGB10 0x8052
|
||||
#endif
|
||||
|
||||
#ifndef GL_RGB12
|
||||
#define GL_RGB12 0x8053
|
||||
#endif
|
||||
|
||||
#ifndef GL_RGB16
|
||||
#define GL_RGB16 0x8054
|
||||
#endif
|
||||
|
||||
#ifndef GL_RGB16_SNORM
|
||||
#define GL_RGB16_SNORM 0x8F9A
|
||||
#endif
|
||||
|
||||
#ifndef GL_RGBA2
|
||||
#define GL_RGBA2 0x8055
|
||||
#endif
|
||||
|
||||
#ifndef GL_RGBA4
|
||||
#define GL_RGBA4 0x8056
|
||||
#endif
|
||||
|
||||
#ifndef GL_RGB5_A1
|
||||
#define GL_RGB5_A1 0x8057
|
||||
#endif
|
||||
|
||||
#ifndef GL_RGBA8
|
||||
#define GL_RGBA8 0x8058
|
||||
#endif
|
||||
|
||||
#ifndef GL_RGBA8_SNORM
|
||||
#define GL_RGBA8_SNORM 0x8F97
|
||||
#endif
|
||||
|
||||
#ifndef GL_RGB10_A2
|
||||
#define GL_RGB10_A2 0x906F
|
||||
#endif
|
||||
|
||||
#ifndef GL_RGB10_A2UI
|
||||
#define GL_RGB10_A2UI 0x906F
|
||||
#endif
|
||||
|
||||
#ifndef GL_RGBA12
|
||||
#define GL_RGBA12 0x805A
|
||||
#endif
|
||||
|
||||
#ifndef GL_RGBA16
|
||||
#define GL_RGBA16 0x805B
|
||||
#endif
|
||||
|
||||
#ifndef GL_RGBA16_SNORM
|
||||
#define GL_RGBA16_SNORM 0x8F9B
|
||||
#endif
|
||||
|
||||
#ifndef GL_SRGB
|
||||
#define GL_SRGB 0x8C40
|
||||
#endif
|
||||
|
||||
#ifndef GL_SRGB8
|
||||
#define GL_SRGB8 0x8C41
|
||||
#endif
|
||||
|
||||
#ifndef GL_SRGB_ALPHA8
|
||||
#define GL_SRGB_ALPHA8 0x8C42
|
||||
#endif
|
||||
|
||||
#ifndef GL_SRGB8_ALPHA8
|
||||
#define GL_SRGB8_ALPHA8 0x8C43
|
||||
#endif
|
||||
|
||||
#ifndef GL_RGB16F
|
||||
#define GL_RGB16F 0x881B
|
||||
#endif
|
||||
|
||||
#ifndef GL_RGBA16F
|
||||
#define GL_RGBA16F 0x881A
|
||||
#endif
|
||||
|
||||
#ifndef GL_RGB32F
|
||||
#define GL_RGB32F 0x8815
|
||||
#endif
|
||||
|
||||
#ifndef GL_RGBA32F
|
||||
#define GL_RGBA32F 0x8814
|
||||
#endif
|
||||
|
||||
#ifndef GL_R11F_G11F_B10F
|
||||
#define GL_R11F_G11F_B10F 0x8C3A
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef GL_RGB8I
|
||||
#define GL_RGB8I 0x8D8F
|
||||
#endif
|
||||
|
||||
#ifndef GL_RGB8UI
|
||||
#define GL_RGB8UI 0x8D7D
|
||||
#endif
|
||||
|
||||
#ifndef GL_RGB16I
|
||||
#define GL_RGB16I 0x8D89
|
||||
#endif
|
||||
|
||||
#ifndef GL_RGB16UI
|
||||
#define GL_RGB16UI 0x8D77
|
||||
#endif
|
||||
|
||||
#ifndef GL_RGB32I
|
||||
#define GL_RGB32I 0x8D83
|
||||
#endif
|
||||
|
||||
#ifndef GL_RGB32UI
|
||||
#define GL_RGB32UI 0x8D71
|
||||
#endif
|
||||
|
||||
#ifndef GL_RGBA8I
|
||||
#define GL_RGBA8I 0x8D8E
|
||||
#endif
|
||||
|
||||
#ifndef GL_RGBA8UI
|
||||
#define GL_RGBA8UI 0x8D7C
|
||||
#endif
|
||||
|
||||
#ifndef GL_RGBA16I
|
||||
#define GL_RGBA16I 0x8D88
|
||||
#endif
|
||||
|
||||
#ifndef GL_RGBA16UI
|
||||
#define GL_RGBA16UI 0x8D76
|
||||
#endif
|
||||
|
||||
#ifndef GL_RGBA32I
|
||||
#define GL_RGBA32I 0x8D82
|
||||
#endif
|
||||
|
||||
#ifndef GL_RGBA32UI
|
||||
#define GL_RGBA32UI 0x8D70
|
||||
#endif
|
||||
|
||||
#ifndef GL_RGB9_E5
|
||||
#define GL_RGB9_E5 0x8C3D
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -2,8 +2,7 @@
|
|||
|
||||
#include <algorithm>
|
||||
|
||||
#include <SDL_opengl_glext.h>
|
||||
|
||||
#include <components/debug/debuglog.hpp>
|
||||
#include <components/settings/settings.hpp>
|
||||
|
||||
namespace SceneUtil
|
||||
|
@ -56,4 +55,144 @@ namespace SceneUtil
|
|||
|
||||
return std::find(formats.cbegin(), formats.cend(), format) != formats.cend();
|
||||
}
|
||||
}
|
||||
|
||||
bool isDepthFormat(GLenum format)
|
||||
{
|
||||
constexpr std::array<GLenum, 8> formats = {
|
||||
GL_DEPTH_COMPONENT32F,
|
||||
GL_DEPTH_COMPONENT32F_NV,
|
||||
GL_DEPTH_COMPONENT16,
|
||||
GL_DEPTH_COMPONENT24,
|
||||
GL_DEPTH_COMPONENT32,
|
||||
GL_DEPTH32F_STENCIL8,
|
||||
GL_DEPTH32F_STENCIL8_NV,
|
||||
GL_DEPTH24_STENCIL8,
|
||||
};
|
||||
|
||||
return std::find(formats.cbegin(), formats.cend(), format) != formats.cend();
|
||||
}
|
||||
|
||||
bool isDepthStencilFormat(GLenum format)
|
||||
{
|
||||
constexpr std::array<GLenum, 8> formats = {
|
||||
GL_DEPTH32F_STENCIL8,
|
||||
GL_DEPTH32F_STENCIL8_NV,
|
||||
GL_DEPTH24_STENCIL8,
|
||||
};
|
||||
|
||||
return std::find(formats.cbegin(), formats.cend(), format) != formats.cend();
|
||||
}
|
||||
|
||||
void getDepthFormatSourceFormatAndType(GLenum internalFormat, GLenum& sourceFormat, GLenum& sourceType)
|
||||
{
|
||||
switch (internalFormat)
|
||||
{
|
||||
case GL_DEPTH_COMPONENT16:
|
||||
case GL_DEPTH_COMPONENT24:
|
||||
case GL_DEPTH_COMPONENT32:
|
||||
sourceType = GL_UNSIGNED_INT;
|
||||
sourceFormat = GL_DEPTH_COMPONENT;
|
||||
break;
|
||||
case GL_DEPTH_COMPONENT32F:
|
||||
case GL_DEPTH_COMPONENT32F_NV:
|
||||
sourceType = GL_FLOAT;
|
||||
sourceFormat = GL_DEPTH_COMPONENT;
|
||||
break;
|
||||
case GL_DEPTH24_STENCIL8:
|
||||
sourceType = GL_UNSIGNED_INT_24_8_EXT;
|
||||
sourceFormat = GL_DEPTH_STENCIL_EXT;
|
||||
break;
|
||||
case GL_DEPTH32F_STENCIL8:
|
||||
case GL_DEPTH32F_STENCIL8_NV:
|
||||
sourceType = GL_FLOAT_32_UNSIGNED_INT_24_8_REV;
|
||||
sourceFormat = GL_DEPTH_STENCIL_EXT;
|
||||
break;
|
||||
default:
|
||||
sourceType = GL_UNSIGNED_INT;
|
||||
sourceFormat = GL_DEPTH_COMPONENT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
GLenum getDepthFormatOfDepthStencilFormat(GLenum internalFormat)
|
||||
{
|
||||
switch (internalFormat)
|
||||
{
|
||||
case GL_DEPTH24_STENCIL8:
|
||||
return GL_DEPTH_COMPONENT24;
|
||||
break;
|
||||
case GL_DEPTH32F_STENCIL8:
|
||||
return GL_DEPTH_COMPONENT32F;
|
||||
break;
|
||||
case GL_DEPTH32F_STENCIL8_NV:
|
||||
return GL_DEPTH_COMPONENT32F_NV;
|
||||
break;
|
||||
default:
|
||||
return internalFormat;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void SelectDepthFormatOperation::operator()(osg::GraphicsContext* graphicsContext)
|
||||
{
|
||||
bool enableReverseZ = false;
|
||||
|
||||
if (Settings::Manager::getBool("reverse z", "Camera"))
|
||||
{
|
||||
osg::ref_ptr<osg::GLExtensions> exts = osg::GLExtensions::Get(0, false);
|
||||
if (exts && exts->isClipControlSupported)
|
||||
{
|
||||
enableReverseZ = true;
|
||||
Log(Debug::Info) << "Using reverse-z depth buffer";
|
||||
}
|
||||
else
|
||||
Log(Debug::Warning) << "GL_ARB_clip_control not supported: disabling reverse-z depth buffer";
|
||||
}
|
||||
else
|
||||
Log(Debug::Info) << "Using standard depth buffer";
|
||||
|
||||
SceneUtil::AutoDepth::setReversed(enableReverseZ);
|
||||
|
||||
constexpr char errPreamble[] = "Postprocessing and floating point depth buffers disabled: ";
|
||||
std::vector<GLenum> requestedFormats;
|
||||
unsigned int contextID = graphicsContext->getState()->getContextID();
|
||||
if (SceneUtil::AutoDepth::isReversed())
|
||||
{
|
||||
if (osg::isGLExtensionSupported(contextID, "GL_ARB_depth_buffer_float"))
|
||||
{
|
||||
requestedFormats.push_back(GL_DEPTH32F_STENCIL8);
|
||||
}
|
||||
else if (osg::isGLExtensionSupported(contextID, "GL_NV_depth_buffer_float"))
|
||||
{
|
||||
requestedFormats.push_back(GL_DEPTH32F_STENCIL8_NV);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log(Debug::Warning) << errPreamble << "'GL_ARB_depth_buffer_float' and 'GL_NV_depth_buffer_float' unsupported.";
|
||||
}
|
||||
}
|
||||
|
||||
requestedFormats.push_back(GL_DEPTH24_STENCIL8);
|
||||
if (mSupportedFormats.empty())
|
||||
{
|
||||
SceneUtil::AutoDepth::setDepthFormat(requestedFormats.front());
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto requestedFormat : requestedFormats)
|
||||
{
|
||||
if (std::find(mSupportedFormats.cbegin(), mSupportedFormats.cend(), requestedFormat) != mSupportedFormats.cend())
|
||||
{
|
||||
SceneUtil::AutoDepth::setDepthFormat(requestedFormat);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AutoDepth::setDepthFormat(GLenum format)
|
||||
{
|
||||
sDepthInternalFormat = format;
|
||||
getDepthFormatSourceFormatAndType(sDepthInternalFormat, sDepthSourceFormat, sDepthSourceType);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,26 @@
|
|||
#define GL_DEPTH32F_STENCIL8_NV 0x8DAC
|
||||
#endif
|
||||
|
||||
#ifndef GL_DEPTH32F_STENCIL8
|
||||
#define GL_DEPTH32F_STENCIL8 0x8CAD
|
||||
#endif
|
||||
|
||||
#ifndef GL_FLOAT_32_UNSIGNED_INT_24_8_REV
|
||||
#define GL_FLOAT_32_UNSIGNED_INT_24_8_REV 0x8DAD
|
||||
#endif
|
||||
|
||||
#ifndef GL_DEPTH24_STENCIL8
|
||||
#define GL_DEPTH24_STENCIL8 0x88F0
|
||||
#endif
|
||||
|
||||
#ifndef GL_DEPTH_STENCIL_EXT
|
||||
#define GL_DEPTH_STENCIL_EXT 0x84F9
|
||||
#endif
|
||||
|
||||
#ifndef GL_UNSIGNED_INT_24_8_EXT
|
||||
#define GL_UNSIGNED_INT_24_8_EXT 0x84FA
|
||||
#endif
|
||||
|
||||
namespace SceneUtil
|
||||
{
|
||||
// Sets camera clear depth to 0 if reversed depth buffer is in use, 1 otherwise.
|
||||
|
@ -28,6 +48,18 @@ namespace SceneUtil
|
|||
// Returns true if the GL format is a floating point depth format.
|
||||
bool isFloatingPointDepthFormat(GLenum format);
|
||||
|
||||
// Returns true if the GL format is a depth format
|
||||
bool isDepthFormat(GLenum format);
|
||||
|
||||
// Returns true if the GL format is a depth+stencil format
|
||||
bool isDepthStencilFormat(GLenum format);
|
||||
|
||||
// Returns the corresponding source format and type for the given internal format
|
||||
void getDepthFormatSourceFormatAndType(GLenum internalFormat, GLenum& sourceFormat, GLenum& sourceType);
|
||||
|
||||
// Converts depth-stencil formats to their corresponding depth formats.
|
||||
GLenum getDepthFormatOfDepthStencilFormat(GLenum internalFormat);
|
||||
|
||||
// Brief wrapper around an osg::Depth that applies the reversed depth function when a reversed depth buffer is in use
|
||||
class AutoDepth : public osg::Depth
|
||||
{
|
||||
|
@ -72,9 +104,29 @@ namespace SceneUtil
|
|||
return AutoDepth::sReversed;
|
||||
}
|
||||
|
||||
static void setDepthFormat(GLenum format);
|
||||
|
||||
static GLenum depthInternalFormat()
|
||||
{
|
||||
return AutoDepth::sDepthInternalFormat;
|
||||
}
|
||||
|
||||
static GLenum depthSourceFormat()
|
||||
{
|
||||
return AutoDepth::sDepthSourceFormat;
|
||||
}
|
||||
|
||||
static GLenum depthSourceType()
|
||||
{
|
||||
return AutoDepth::sDepthSourceType;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
static inline bool sReversed = false;
|
||||
static inline GLenum sDepthSourceFormat = GL_DEPTH_COMPONENT;
|
||||
static inline GLenum sDepthInternalFormat = GL_DEPTH_COMPONENT24;
|
||||
static inline GLenum sDepthSourceType = GL_UNSIGNED_INT;
|
||||
|
||||
osg::Depth::Function getReversedDepthFunction() const
|
||||
{
|
||||
|
@ -116,6 +168,23 @@ namespace SceneUtil
|
|||
traverse(node);
|
||||
}
|
||||
};
|
||||
|
||||
class SelectDepthFormatOperation : public osg::GraphicsOperation
|
||||
{
|
||||
public:
|
||||
SelectDepthFormatOperation() : GraphicsOperation("SelectDepthFormatOperation", false)
|
||||
{}
|
||||
|
||||
void operator()(osg::GraphicsContext* graphicsContext) override;
|
||||
|
||||
void setSupportedFormats(const std::vector<GLenum>& supportedFormats)
|
||||
{
|
||||
mSupportedFormats = supportedFormats;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<GLenum> mSupportedFormats;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -633,6 +633,7 @@ void MWShadowTechnique::ShadowData::releaseGLObjects(osg::State* state) const
|
|||
// Frustum
|
||||
//
|
||||
MWShadowTechnique::Frustum::Frustum(osgUtil::CullVisitor* cv, double minZNear, double maxZFar):
|
||||
useCustomClipSpace(false),
|
||||
corners(8),
|
||||
faces(6),
|
||||
edges(12)
|
||||
|
@ -652,19 +653,40 @@ MWShadowTechnique::Frustum::Frustum(osgUtil::CullVisitor* cv, double minZNear, d
|
|||
OSG_INFO<<"zNear = "<<zNear<<", zFar = "<<zFar<<std::endl;
|
||||
OSG_INFO<<"Projection matrix after clamping "<<projectionMatrix<<std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
corners[0].set(-1.0,-1.0,-1.0);
|
||||
corners[1].set(1.0,-1.0,-1.0);
|
||||
corners[2].set(1.0,-1.0,1.0);
|
||||
corners[3].set(-1.0,-1.0,1.0);
|
||||
corners[4].set(-1.0,1.0,-1.0);
|
||||
corners[5].set(1.0,1.0,-1.0);
|
||||
corners[6].set(1.0,1.0,1.0);
|
||||
corners[7].set(-1.0,1.0,1.0);
|
||||
void SceneUtil::MWShadowTechnique::Frustum::setCustomClipSpace(const osg::BoundingBoxd& clipCornersOverride)
|
||||
{
|
||||
useCustomClipSpace = true;
|
||||
customClipSpace = clipCornersOverride;
|
||||
}
|
||||
|
||||
void SceneUtil::MWShadowTechnique::Frustum::init()
|
||||
{
|
||||
osg::Matrixd clipToWorld;
|
||||
clipToWorld.invert(modelViewMatrix * projectionMatrix);
|
||||
|
||||
if (useCustomClipSpace)
|
||||
{
|
||||
corners.clear();
|
||||
// Add corners in the same order OSG expects them
|
||||
for (int i : {0, 1, 5, 4, 2, 3, 7, 6})
|
||||
{
|
||||
corners.push_back(customClipSpace.corner(i));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
corners[0].set(-1.0, -1.0, -1.0);
|
||||
corners[1].set(1.0, -1.0, -1.0);
|
||||
corners[2].set(1.0, -1.0, 1.0);
|
||||
corners[3].set(-1.0, -1.0, 1.0);
|
||||
corners[4].set(-1.0, 1.0, -1.0);
|
||||
corners[5].set(1.0, 1.0, -1.0);
|
||||
corners[6].set(1.0, 1.0, 1.0);
|
||||
corners[7].set(-1.0, 1.0, 1.0);
|
||||
}
|
||||
|
||||
// transform frustum corners from clipspace to world coords, and compute center
|
||||
for(Vertices::iterator itr = corners.begin();
|
||||
itr != corners.end();
|
||||
|
@ -899,7 +921,7 @@ void SceneUtil::MWShadowTechnique::setupCastingShader(Shader::ShaderManager & sh
|
|||
{
|
||||
// This can't be part of the constructor as OSG mandates that there be a trivial constructor available
|
||||
|
||||
osg::ref_ptr<osg::Shader> castingVertexShader = shaderManager.getShader("shadowcasting_vertex.glsl", {}, osg::Shader::VERTEX);
|
||||
osg::ref_ptr<osg::Shader> castingVertexShader = shaderManager.getShader("shadowcasting_vertex.glsl", { }, osg::Shader::VERTEX);
|
||||
osg::ref_ptr<osg::GLExtensions> exts = osg::GLExtensions::Get(0, false);
|
||||
std::string useGPUShader4 = exts && exts->isGpuShader4Supported ? "1" : "0";
|
||||
for (int alphaFunc = GL_NEVER; alphaFunc <= GL_ALWAYS; ++alphaFunc)
|
||||
|
@ -931,6 +953,67 @@ MWShadowTechnique::ViewDependentData* MWShadowTechnique::getViewDependentData(os
|
|||
return vdd.release();
|
||||
}
|
||||
|
||||
void SceneUtil::MWShadowTechnique::copyShadowMap(osgUtil::CullVisitor& cv, ViewDependentData* lhs, ViewDependentData* rhs)
|
||||
{
|
||||
// Prepare for rendering shadows using the shadow map owned by rhs.
|
||||
|
||||
// To achieve this i first copy all data that is not specific to this cv's camera and thus read-only,
|
||||
// trusting openmw and osg won't overwrite that data before this frame is done rendering.
|
||||
// This works due to the double buffering of CullVisitors by osg, but also requires that cull passes are serialized (relative to one another).
|
||||
// Then initialize new copies of the data that will be written with view-specific data
|
||||
// (the stateset and the texgens).
|
||||
|
||||
lhs->_viewDependentShadowMap = rhs->_viewDependentShadowMap;
|
||||
auto* stateset = lhs->getStateSet(cv.getTraversalNumber());
|
||||
stateset->clear();
|
||||
lhs->_lightDataList = rhs->_lightDataList;
|
||||
lhs->_numValidShadows = rhs->_numValidShadows;
|
||||
|
||||
ShadowDataList& sdl = lhs->getShadowDataList();
|
||||
ShadowDataList previous_sdl;
|
||||
previous_sdl.swap(sdl);
|
||||
for (const auto& rhs_sd : rhs->getShadowDataList())
|
||||
{
|
||||
osg::ref_ptr<ShadowData> lhs_sd;
|
||||
|
||||
if (previous_sdl.empty())
|
||||
{
|
||||
OSG_INFO << "Create new ShadowData" << std::endl;
|
||||
lhs_sd = new ShadowData(lhs);
|
||||
}
|
||||
else
|
||||
{
|
||||
OSG_INFO << "Taking ShadowData from from of previous_sdl" << std::endl;
|
||||
lhs_sd = previous_sdl.front();
|
||||
previous_sdl.erase(previous_sdl.begin());
|
||||
}
|
||||
lhs_sd->_camera = rhs_sd->_camera;
|
||||
lhs_sd->_textureUnit = rhs_sd->_textureUnit;
|
||||
lhs_sd->_texture = rhs_sd->_texture;
|
||||
sdl.push_back(lhs_sd);
|
||||
}
|
||||
|
||||
assignTexGenSettings(cv, lhs);
|
||||
|
||||
if (lhs->_numValidShadows > 0)
|
||||
{
|
||||
prepareStateSetForRenderingShadow(*lhs, cv.getTraversalNumber());
|
||||
}
|
||||
}
|
||||
|
||||
void SceneUtil::MWShadowTechnique::setCustomFrustumCallback(CustomFrustumCallback* cfc)
|
||||
{
|
||||
_customFrustumCallback = cfc;
|
||||
}
|
||||
|
||||
void SceneUtil::MWShadowTechnique::assignTexGenSettings(osgUtil::CullVisitor& cv, ViewDependentData* vdd)
|
||||
{
|
||||
for (const auto& sd : vdd->getShadowDataList())
|
||||
{
|
||||
assignTexGenSettings(&cv, sd->_camera, sd->_textureUnit, sd->_texgen);
|
||||
}
|
||||
}
|
||||
|
||||
void MWShadowTechnique::update(osg::NodeVisitor& nv)
|
||||
{
|
||||
OSG_INFO<<"MWShadowTechnique::update(osg::NodeVisitor& "<<&nv<<")"<<std::endl;
|
||||
|
@ -939,6 +1022,7 @@ void MWShadowTechnique::update(osg::NodeVisitor& nv)
|
|||
|
||||
void MWShadowTechnique::cull(osgUtil::CullVisitor& cv)
|
||||
{
|
||||
|
||||
if (!_enableShadows)
|
||||
{
|
||||
if (mSetDummyStateWhenDisabled)
|
||||
|
@ -1036,7 +1120,7 @@ void MWShadowTechnique::cull(osgUtil::CullVisitor& cv)
|
|||
// are all done correctly.
|
||||
cv.computeNearPlane();
|
||||
}
|
||||
|
||||
|
||||
// clamp the minZNear and maxZFar to those provided by ShadowSettings
|
||||
maxZFar = osg::minimum(settings->getMaximumShadowMapDistance(),maxZFar);
|
||||
if (minZNear>maxZFar) minZNear = maxZFar*settings->getMinimumShadowMapNearFarRatio();
|
||||
|
@ -1047,6 +1131,36 @@ void MWShadowTechnique::cull(osgUtil::CullVisitor& cv)
|
|||
cv.setNearFarRatio(minZNear / maxZFar);
|
||||
|
||||
Frustum frustum(&cv, minZNear, maxZFar);
|
||||
if (_customFrustumCallback)
|
||||
{
|
||||
OSG_INFO << "Calling custom frustum callback" << std::endl;
|
||||
osgUtil::CullVisitor* sharedFrustumHint = nullptr;
|
||||
_customClipSpace.init();
|
||||
_customFrustumCallback->operator()(cv, _customClipSpace, sharedFrustumHint);
|
||||
frustum.setCustomClipSpace(_customClipSpace);
|
||||
if (sharedFrustumHint)
|
||||
{
|
||||
// user hinted another view shares its frustum
|
||||
std::lock_guard<std::mutex> lock(_viewDependentDataMapMutex);
|
||||
auto itr = _viewDependentDataMap.find(sharedFrustumHint);
|
||||
if (itr != _viewDependentDataMap.end())
|
||||
{
|
||||
OSG_INFO << "User provided a valid shared frustum hint, re-using previously generated shadow map" << std::endl;
|
||||
|
||||
copyShadowMap(cv, vdd, itr->second);
|
||||
|
||||
// return compute near far mode back to it's original settings
|
||||
cv.setComputeNearFarMode(cachedNearFarMode);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
OSG_INFO << "User provided a shared frustum hint, but it was not valid." << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
frustum.init();
|
||||
if (_debugHud)
|
||||
{
|
||||
osg::ref_ptr<osg::Vec3Array> vertexArray = new osg::Vec3Array();
|
||||
|
@ -1066,7 +1180,7 @@ void MWShadowTechnique::cull(osgUtil::CullVisitor& cv)
|
|||
reducedNear = minZNear;
|
||||
reducedFar = maxZFar;
|
||||
}
|
||||
|
||||
|
||||
// return compute near far mode back to it's original settings
|
||||
cv.setComputeNearFarMode(cachedNearFarMode);
|
||||
|
||||
|
@ -1430,7 +1544,7 @@ void MWShadowTechnique::cull(osgUtil::CullVisitor& cv)
|
|||
vdsmCallback->getProjectionMatrix()->set(camera->getProjectionMatrix());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 4.4 compute main scene graph TexGen + uniform settings + setup state
|
||||
//
|
||||
assignTexGenSettings(&cv, camera.get(), textureUnit, sd->_texgen.get());
|
||||
|
@ -1459,6 +1573,8 @@ void MWShadowTechnique::cull(osgUtil::CullVisitor& cv)
|
|||
}
|
||||
}
|
||||
|
||||
vdd->setNumValidShadows(numValidShadows);
|
||||
|
||||
if (numValidShadows>0)
|
||||
{
|
||||
prepareStateSetForRenderingShadow(*vdd, cv.getTraversalNumber());
|
||||
|
@ -1665,7 +1781,14 @@ osg::Polytope MWShadowTechnique::computeLightViewFrustumPolytope(Frustum& frustu
|
|||
OSG_INFO<<"computeLightViewFrustumPolytope()"<<std::endl;
|
||||
|
||||
osg::Polytope polytope;
|
||||
polytope.setToUnitFrustum();
|
||||
if (frustum.useCustomClipSpace)
|
||||
{
|
||||
polytope.setToBoundingBox(frustum.customClipSpace);
|
||||
}
|
||||
else
|
||||
{
|
||||
polytope.setToUnitFrustum();
|
||||
}
|
||||
|
||||
polytope.transformProvidingInverse( frustum.projectionMatrix );
|
||||
polytope.transformProvidingInverse( frustum.modelViewMatrix );
|
||||
|
|
|
@ -119,10 +119,15 @@ namespace SceneUtil {
|
|||
struct Frustum
|
||||
{
|
||||
Frustum(osgUtil::CullVisitor* cv, double minZNear, double maxZFar);
|
||||
void setCustomClipSpace(const osg::BoundingBoxd& clipCornersOverride);
|
||||
void init();
|
||||
|
||||
osg::Matrixd projectionMatrix;
|
||||
osg::Matrixd modelViewMatrix;
|
||||
|
||||
bool useCustomClipSpace;
|
||||
osg::BoundingBoxd customClipSpace;
|
||||
|
||||
typedef std::vector<osg::Vec3d> Vertices;
|
||||
Vertices corners;
|
||||
|
||||
|
@ -140,6 +145,18 @@ namespace SceneUtil {
|
|||
osg::Vec3d frustumCenterLine;
|
||||
};
|
||||
|
||||
/** Custom frustum callback allowing the application to request shadow maps covering a
|
||||
* different furstum than the camera normally would cover, by customizing the corners of the clip space. */
|
||||
struct CustomFrustumCallback : osg::Referenced
|
||||
{
|
||||
/** The callback operator.
|
||||
* Output the custum frustum to the boundingBox variable.
|
||||
* If sharedFrustumHint is set to a valid cull visitor, the shadow maps of that cull visitor will be re-used instead of recomputing new shadow maps
|
||||
* Note that the customClipSpace bounding box will be uninitialized when this operator is called. If it is not initalized, or a valid shared frustum hint set,
|
||||
* the resulting shadow map may be invalid. */
|
||||
virtual void operator()(osgUtil::CullVisitor& cv, osg::BoundingBoxd& customClipSpace, osgUtil::CullVisitor*& sharedFrustumHint) = 0;
|
||||
};
|
||||
|
||||
// forward declare
|
||||
class ViewDependentData;
|
||||
|
||||
|
@ -197,7 +214,12 @@ namespace SceneUtil {
|
|||
|
||||
virtual void releaseGLObjects(osg::State* = 0) const;
|
||||
|
||||
unsigned int numValidShadows(void) const { return _numValidShadows; }
|
||||
|
||||
void setNumValidShadows(unsigned int numValidShadows) { _numValidShadows = numValidShadows; }
|
||||
|
||||
protected:
|
||||
friend class MWShadowTechnique;
|
||||
virtual ~ViewDependentData() {}
|
||||
|
||||
MWShadowTechnique* _viewDependentShadowMap;
|
||||
|
@ -206,13 +228,19 @@ namespace SceneUtil {
|
|||
|
||||
LightDataList _lightDataList;
|
||||
ShadowDataList _shadowDataList;
|
||||
|
||||
unsigned int _numValidShadows;
|
||||
};
|
||||
|
||||
virtual ViewDependentData* createViewDependentData(osgUtil::CullVisitor* cv);
|
||||
|
||||
ViewDependentData* getViewDependentData(osgUtil::CullVisitor* cv);
|
||||
|
||||
void copyShadowMap(osgUtil::CullVisitor& cv, ViewDependentData* lhs, ViewDependentData* rhs);
|
||||
|
||||
void setCustomFrustumCallback(CustomFrustumCallback* cfc);
|
||||
|
||||
void assignTexGenSettings(osgUtil::CullVisitor& cv, ViewDependentData* vdd);
|
||||
|
||||
virtual void createShaders();
|
||||
|
||||
|
@ -246,6 +274,8 @@ namespace SceneUtil {
|
|||
typedef std::map< osgUtil::CullVisitor*, osg::ref_ptr<ViewDependentData> > ViewDependentDataMap;
|
||||
mutable std::mutex _viewDependentDataMapMutex;
|
||||
ViewDependentDataMap _viewDependentDataMap;
|
||||
osg::ref_ptr<CustomFrustumCallback> _customFrustumCallback;
|
||||
osg::BoundingBoxd _customClipSpace;
|
||||
|
||||
osg::ref_ptr<osg::StateSet> _shadowRecievingPlaceholderStateSet;
|
||||
|
||||
|
|
|
@ -4,11 +4,16 @@
|
|||
#include <osg/Node>
|
||||
#include <osg/NodeVisitor>
|
||||
#include <osg/Texture2D>
|
||||
#include <osg/Texture2DArray>
|
||||
#include <osgUtil/CullVisitor>
|
||||
|
||||
#include <components/sceneutil/nodecallback.hpp>
|
||||
#include <components/settings/settings.hpp>
|
||||
#include <components/sceneutil/depth.hpp>
|
||||
#include <components/sceneutil/color.hpp>
|
||||
#include <components/stereo/multiview.hpp>
|
||||
#include <components/debug/debuglog.hpp>
|
||||
#include <components/stereo/stereomanager.hpp>
|
||||
|
||||
namespace SceneUtil
|
||||
{
|
||||
|
@ -22,11 +27,15 @@ namespace SceneUtil
|
|||
}
|
||||
};
|
||||
|
||||
RTTNode::RTTNode(uint32_t textureWidth, uint32_t textureHeight, int renderOrderNum, bool doPerViewMapping)
|
||||
RTTNode::RTTNode(uint32_t textureWidth, uint32_t textureHeight, uint32_t samples, bool generateMipmaps, int renderOrderNum, StereoAwareness stereoAwareness)
|
||||
: mTextureWidth(textureWidth)
|
||||
, mTextureHeight(textureHeight)
|
||||
, mSamples(samples)
|
||||
, mGenerateMipmaps(generateMipmaps)
|
||||
, mColorBufferInternalFormat(Color::colorInternalFormat())
|
||||
, mDepthBufferInternalFormat(AutoDepth::depthInternalFormat())
|
||||
, mRenderOrderNum(renderOrderNum)
|
||||
, mDoPerViewMapping(doPerViewMapping)
|
||||
, mStereoAwareness(stereoAwareness)
|
||||
{
|
||||
addCullCallback(new CullCallback);
|
||||
setCullingActive(false);
|
||||
|
@ -34,28 +43,139 @@ namespace SceneUtil
|
|||
|
||||
RTTNode::~RTTNode()
|
||||
{
|
||||
for (auto& vdd : mViewDependentDataMap)
|
||||
{
|
||||
auto* camera = vdd.second->mCamera.get();
|
||||
if (camera)
|
||||
{
|
||||
camera->removeChildren(0, camera->getNumChildren());
|
||||
}
|
||||
}
|
||||
mViewDependentDataMap.clear();
|
||||
}
|
||||
|
||||
void RTTNode::cull(osgUtil::CullVisitor* cv)
|
||||
{
|
||||
auto frameNumber = cv->getFrameStamp()->getFrameNumber();
|
||||
auto* vdd = getViewDependentData(cv);
|
||||
apply(vdd->mCamera);
|
||||
vdd->mCamera->accept(*cv);
|
||||
if (frameNumber > vdd->mFrameNumber)
|
||||
{
|
||||
apply(vdd->mCamera);
|
||||
auto& sm = Stereo::Manager::instance();
|
||||
if (sm.getEye(cv) == Stereo::Eye::Left)
|
||||
applyLeft(vdd->mCamera);
|
||||
if (sm.getEye(cv) == Stereo::Eye::Right)
|
||||
applyRight(vdd->mCamera);
|
||||
vdd->mCamera->accept(*cv);
|
||||
}
|
||||
vdd->mFrameNumber = frameNumber;
|
||||
}
|
||||
|
||||
void RTTNode::setColorBufferInternalFormat(GLint internalFormat)
|
||||
{
|
||||
mColorBufferInternalFormat = internalFormat;
|
||||
}
|
||||
|
||||
void RTTNode::setDepthBufferInternalFormat(GLint internalFormat)
|
||||
{
|
||||
mDepthBufferInternalFormat = internalFormat;
|
||||
}
|
||||
|
||||
bool RTTNode::shouldDoPerViewMapping()
|
||||
{
|
||||
if(mStereoAwareness != StereoAwareness::Aware)
|
||||
return false;
|
||||
if (!Stereo::getMultiview())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RTTNode::shouldDoTextureArray()
|
||||
{
|
||||
if (mStereoAwareness == StereoAwareness::Unaware)
|
||||
return false;
|
||||
if (Stereo::getMultiview())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RTTNode::shouldDoTextureView()
|
||||
{
|
||||
if (mStereoAwareness != StereoAwareness::Unaware_MultiViewShaders)
|
||||
return false;
|
||||
if (Stereo::getMultiview())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
osg::Texture2DArray* RTTNode::createTextureArray(GLint internalFormat)
|
||||
{
|
||||
osg::Texture2DArray* textureArray = new osg::Texture2DArray;
|
||||
textureArray->setTextureSize(mTextureWidth, mTextureHeight, 2);
|
||||
textureArray->setInternalFormat(internalFormat);
|
||||
GLenum sourceFormat = 0;
|
||||
GLenum sourceType = 0;
|
||||
if (SceneUtil::isDepthFormat(internalFormat))
|
||||
{
|
||||
SceneUtil::getDepthFormatSourceFormatAndType(internalFormat, sourceFormat, sourceType);
|
||||
}
|
||||
else
|
||||
{
|
||||
SceneUtil::getColorFormatSourceFormatAndType(internalFormat, sourceFormat, sourceType);
|
||||
}
|
||||
textureArray->setSourceFormat(sourceFormat);
|
||||
textureArray->setSourceType(sourceType);
|
||||
textureArray->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
|
||||
textureArray->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
|
||||
textureArray->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
|
||||
textureArray->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
|
||||
textureArray->setWrap(osg::Texture::WRAP_R, osg::Texture::CLAMP_TO_EDGE);
|
||||
return textureArray;
|
||||
}
|
||||
|
||||
osg::Texture2D* RTTNode::createTexture(GLint internalFormat)
|
||||
{
|
||||
osg::Texture2D* texture = new osg::Texture2D;
|
||||
texture->setTextureSize(mTextureWidth, mTextureHeight);
|
||||
texture->setInternalFormat(internalFormat);
|
||||
GLenum sourceFormat = 0;
|
||||
GLenum sourceType = 0;
|
||||
if (SceneUtil::isDepthFormat(internalFormat))
|
||||
{
|
||||
SceneUtil::getDepthFormatSourceFormatAndType(internalFormat, sourceFormat, sourceType);
|
||||
}
|
||||
else
|
||||
{
|
||||
SceneUtil::getColorFormatSourceFormatAndType(internalFormat, sourceFormat, sourceType);
|
||||
}
|
||||
texture->setSourceFormat(sourceFormat);
|
||||
texture->setSourceType(sourceType);
|
||||
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);
|
||||
texture->setWrap(osg::Texture::WRAP_R, osg::Texture::CLAMP_TO_EDGE);
|
||||
return texture;
|
||||
}
|
||||
|
||||
osg::Texture* RTTNode::getColorTexture(osgUtil::CullVisitor* cv)
|
||||
{
|
||||
return getViewDependentData(cv)->mCamera->getBufferAttachmentMap()[osg::Camera::COLOR_BUFFER]._texture;
|
||||
return getViewDependentData(cv)->mColorTexture;
|
||||
}
|
||||
|
||||
osg::Texture* RTTNode::getDepthTexture(osgUtil::CullVisitor* cv)
|
||||
{
|
||||
return getViewDependentData(cv)->mCamera->getBufferAttachmentMap()[osg::Camera::PACKED_DEPTH_STENCIL_BUFFER]._texture;
|
||||
return getViewDependentData(cv)->mDepthTexture;
|
||||
}
|
||||
|
||||
osg::Camera* RTTNode::getCamera(osgUtil::CullVisitor* cv)
|
||||
{
|
||||
return getViewDependentData(cv)->mCamera;
|
||||
}
|
||||
|
||||
RTTNode::ViewDependentData* RTTNode::getViewDependentData(osgUtil::CullVisitor* cv)
|
||||
{
|
||||
if (!mDoPerViewMapping)
|
||||
if (!shouldDoPerViewMapping())
|
||||
// Always setting it to null is an easy way to disable per-view mapping when mDoPerViewMapping is false.
|
||||
// This is safe since the visitor is never dereferenced.
|
||||
cv = nullptr;
|
||||
|
@ -63,7 +183,8 @@ namespace SceneUtil
|
|||
if (mViewDependentDataMap.count(cv) == 0)
|
||||
{
|
||||
auto camera = new osg::Camera();
|
||||
mViewDependentDataMap[cv].reset(new ViewDependentData);
|
||||
auto vdd = std::make_shared<ViewDependentData>();
|
||||
mViewDependentDataMap[cv] = vdd;
|
||||
mViewDependentDataMap[cv]->mCamera = camera;
|
||||
|
||||
camera->setRenderOrder(osg::Camera::PRE_RENDER, mRenderOrderNum);
|
||||
|
@ -72,34 +193,63 @@ namespace SceneUtil
|
|||
camera->setViewport(0, 0, mTextureWidth, mTextureHeight);
|
||||
SceneUtil::setCameraClearDepth(camera);
|
||||
|
||||
setDefaults(mViewDependentDataMap[cv]->mCamera.get());
|
||||
setDefaults(camera);
|
||||
|
||||
// Create any buffer attachments not added in setDefaults
|
||||
if (camera->getBufferAttachmentMap().count(osg::Camera::COLOR_BUFFER) == 0)
|
||||
if (camera->getBufferAttachmentMap().count(osg::Camera::COLOR_BUFFER))
|
||||
vdd->mColorTexture = camera->getBufferAttachmentMap()[osg::Camera::COLOR_BUFFER]._texture;
|
||||
if (camera->getBufferAttachmentMap().count(osg::Camera::PACKED_DEPTH_STENCIL_BUFFER))
|
||||
vdd->mDepthTexture = camera->getBufferAttachmentMap()[osg::Camera::PACKED_DEPTH_STENCIL_BUFFER]._texture;
|
||||
|
||||
#ifdef OSG_HAS_MULTIVIEW
|
||||
if (shouldDoTextureArray())
|
||||
{
|
||||
auto colorBuffer = new osg::Texture2D;
|
||||
colorBuffer->setTextureSize(mTextureWidth, mTextureHeight);
|
||||
colorBuffer->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
|
||||
colorBuffer->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
|
||||
colorBuffer->setInternalFormat(GL_RGB);
|
||||
colorBuffer->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
|
||||
colorBuffer->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
|
||||
camera->attach(osg::Camera::COLOR_BUFFER, colorBuffer);
|
||||
SceneUtil::attachAlphaToCoverageFriendlyFramebufferToCamera(camera, osg::Camera::COLOR_BUFFER, colorBuffer);
|
||||
// Create any buffer attachments not added in setDefaults
|
||||
if (camera->getBufferAttachmentMap().count(osg::Camera::COLOR_BUFFER) == 0)
|
||||
{
|
||||
vdd->mColorTexture = createTextureArray(mColorBufferInternalFormat);
|
||||
camera->attach(osg::Camera::COLOR_BUFFER, vdd->mColorTexture, 0, osg::Camera::FACE_CONTROLLED_BY_MULTIVIEW_SHADER, mGenerateMipmaps, mSamples);
|
||||
SceneUtil::attachAlphaToCoverageFriendlyFramebufferToCamera(camera, osg::Camera::COLOR_BUFFER, vdd->mColorTexture, 0, osg::Camera::FACE_CONTROLLED_BY_MULTIVIEW_SHADER, mGenerateMipmaps);
|
||||
}
|
||||
|
||||
if (camera->getBufferAttachmentMap().count(osg::Camera::PACKED_DEPTH_STENCIL_BUFFER) == 0)
|
||||
{
|
||||
vdd->mDepthTexture = createTextureArray(mDepthBufferInternalFormat);
|
||||
camera->attach(osg::Camera::PACKED_DEPTH_STENCIL_BUFFER, vdd->mDepthTexture, 0, osg::Camera::FACE_CONTROLLED_BY_MULTIVIEW_SHADER, false, mSamples);
|
||||
}
|
||||
|
||||
if (shouldDoTextureView())
|
||||
{
|
||||
// In this case, shaders being set to multiview forces us to render to a multiview framebuffer even though we don't need that.
|
||||
// This forces us to make Texture2DArray. To make this possible to sample as a Texture2D, make a Texture2D view into the texture array.
|
||||
vdd->mColorTexture = Stereo::createTextureView_Texture2DFromTexture2DArray(static_cast<osg::Texture2DArray*>(vdd->mColorTexture.get()), 0);
|
||||
vdd->mDepthTexture = Stereo::createTextureView_Texture2DFromTexture2DArray(static_cast<osg::Texture2DArray*>(vdd->mDepthTexture.get()), 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
// Create any buffer attachments not added in setDefaults
|
||||
if (camera->getBufferAttachmentMap().count(osg::Camera::COLOR_BUFFER) == 0)
|
||||
{
|
||||
vdd->mColorTexture = createTexture(mColorBufferInternalFormat);
|
||||
camera->attach(osg::Camera::COLOR_BUFFER, vdd->mColorTexture, 0, 0, mGenerateMipmaps, mSamples);
|
||||
SceneUtil::attachAlphaToCoverageFriendlyFramebufferToCamera(camera, osg::Camera::COLOR_BUFFER, vdd->mColorTexture, 0, 0, mGenerateMipmaps);
|
||||
}
|
||||
|
||||
if (camera->getBufferAttachmentMap().count(osg::Camera::PACKED_DEPTH_STENCIL_BUFFER) == 0)
|
||||
{
|
||||
vdd->mDepthTexture = createTexture(mDepthBufferInternalFormat);
|
||||
camera->attach(osg::Camera::PACKED_DEPTH_STENCIL_BUFFER, vdd->mDepthTexture, 0, 0, false, mSamples);
|
||||
}
|
||||
}
|
||||
|
||||
if (camera->getBufferAttachmentMap().count(osg::Camera::PACKED_DEPTH_STENCIL_BUFFER) == 0)
|
||||
// OSG appears not to properly initialize this metadata. So when multisampling is enabled, OSG will use incorrect formats for the resolve buffers.
|
||||
if (mSamples > 1)
|
||||
{
|
||||
auto depthBuffer = new osg::Texture2D;
|
||||
depthBuffer->setTextureSize(mTextureWidth, mTextureHeight);
|
||||
depthBuffer->setSourceFormat(GL_DEPTH_STENCIL_EXT);
|
||||
depthBuffer->setInternalFormat(GL_DEPTH24_STENCIL8_EXT);
|
||||
depthBuffer->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
|
||||
depthBuffer->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
|
||||
depthBuffer->setSourceType(GL_UNSIGNED_INT_24_8_EXT);
|
||||
depthBuffer->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
|
||||
depthBuffer->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
|
||||
camera->attach(osg::Camera::PACKED_DEPTH_STENCIL_BUFFER, depthBuffer);
|
||||
camera->getBufferAttachmentMap()[osg::Camera::COLOR_BUFFER]._internalFormat = mColorBufferInternalFormat;
|
||||
camera->getBufferAttachmentMap()[osg::Camera::COLOR_BUFFER]._mipMapGeneration = mGenerateMipmaps;
|
||||
camera->getBufferAttachmentMap()[osg::Camera::PACKED_DEPTH_STENCIL_BUFFER]._internalFormat = mDepthBufferInternalFormat;
|
||||
camera->getBufferAttachmentMap()[osg::Camera::PACKED_DEPTH_STENCIL_BUFFER]._mipMapGeneration = mGenerateMipmaps;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
namespace osg
|
||||
{
|
||||
class Texture2D;
|
||||
class Texture2DArray;
|
||||
class Camera;
|
||||
}
|
||||
|
||||
|
@ -19,6 +20,8 @@ namespace osgUtil
|
|||
|
||||
namespace SceneUtil
|
||||
{
|
||||
class CreateTextureViewsCallback;
|
||||
|
||||
/// @brief Implements per-view RTT operations.
|
||||
/// @par With a naive RTT implementation, subsequent views of multiple views will overwrite the results of the previous views, leading to
|
||||
/// the results of the last view being broadcast to all views. An error in all cases where the RTT result depends on the view.
|
||||
|
@ -32,37 +35,73 @@ namespace SceneUtil
|
|||
class RTTNode : public osg::Node
|
||||
{
|
||||
public:
|
||||
RTTNode(uint32_t textureWidth, uint32_t textureHeight, int renderOrderNum, bool doPerViewMapping);
|
||||
enum class StereoAwareness
|
||||
{
|
||||
Unaware, //! RTT does not vary by view. A single RTT context is created
|
||||
Aware, //! RTT varies by view. One RTT context per view is created. Textures are automatically created as arrays if multiview is enabled.
|
||||
Unaware_MultiViewShaders, //! RTT does not vary by view, but renders with multiview shaders and needs to create texture arrays if multiview is enabled.
|
||||
};
|
||||
|
||||
RTTNode(uint32_t textureWidth, uint32_t textureHeight, uint32_t samples, bool generateMipmaps, int renderOrderNum, StereoAwareness stereoAwareness);
|
||||
~RTTNode();
|
||||
|
||||
osg::Texture* getColorTexture(osgUtil::CullVisitor* cv);
|
||||
|
||||
osg::Texture* getDepthTexture(osgUtil::CullVisitor* cv);
|
||||
|
||||
osg::Camera* getCamera(osgUtil::CullVisitor* cv);
|
||||
|
||||
/// Set default settings - optionally override in derived classes
|
||||
virtual void setDefaults(osg::Camera* camera) {};
|
||||
|
||||
/// Apply state - to override in derived classes
|
||||
/// @note Due to the view mapping approach you *have* to apply all camera settings, even if they have not changed since the last frame.
|
||||
virtual void setDefaults(osg::Camera* camera) {};
|
||||
|
||||
/// Set default settings - optionally override in derived classes
|
||||
virtual void apply(osg::Camera* camera) {};
|
||||
|
||||
/// Apply any state specific to the Left view. Default implementation does nothing. Called after apply()
|
||||
virtual void applyLeft(osg::Camera* camera) {}
|
||||
/// Apply any state specific to the Right view. Default implementation does nothing. Called after apply()
|
||||
virtual void applyRight(osg::Camera* camera) {}
|
||||
|
||||
void cull(osgUtil::CullVisitor* cv);
|
||||
|
||||
uint32_t width() const { return mTextureWidth; }
|
||||
uint32_t height() const { return mTextureHeight; }
|
||||
uint32_t samples() const { return mSamples; }
|
||||
bool generatesMipmaps() const { return mGenerateMipmaps; }
|
||||
|
||||
void setColorBufferInternalFormat(GLint internalFormat);
|
||||
void setDepthBufferInternalFormat(GLint internalFormat);
|
||||
|
||||
protected:
|
||||
bool shouldDoPerViewMapping();
|
||||
bool shouldDoTextureArray();
|
||||
bool shouldDoTextureView();
|
||||
osg::Texture2DArray* createTextureArray(GLint internalFormat);
|
||||
osg::Texture2D* createTexture(GLint internalFormat);
|
||||
|
||||
private:
|
||||
friend class CreateTextureViewsCallback;
|
||||
struct ViewDependentData
|
||||
{
|
||||
osg::ref_ptr<osg::Camera> mCamera;
|
||||
osg::ref_ptr<osg::Texture> mColorTexture;
|
||||
osg::ref_ptr<osg::Texture> mDepthTexture;
|
||||
unsigned int mFrameNumber = 0;
|
||||
};
|
||||
|
||||
ViewDependentData* getViewDependentData(osgUtil::CullVisitor* cv);
|
||||
|
||||
typedef std::map< osgUtil::CullVisitor*, std::unique_ptr<ViewDependentData> > ViewDependentDataMap;
|
||||
typedef std::map< osgUtil::CullVisitor*, std::shared_ptr<ViewDependentData> > ViewDependentDataMap;
|
||||
ViewDependentDataMap mViewDependentDataMap;
|
||||
uint32_t mTextureWidth;
|
||||
uint32_t mTextureHeight;
|
||||
uint32_t mSamples;
|
||||
bool mGenerateMipmaps;
|
||||
GLint mColorBufferInternalFormat;
|
||||
GLint mDepthBufferInternalFormat;
|
||||
int mRenderOrderNum;
|
||||
bool mDoPerViewMapping;
|
||||
StereoAwareness mStereoAwareness;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include <components/misc/stringops.hpp>
|
||||
#include <components/settings/settings.hpp>
|
||||
#include <components/stereo/stereomanager.hpp>
|
||||
|
||||
#include "mwshadowtechnique.hpp"
|
||||
|
||||
|
@ -101,6 +102,7 @@ namespace SceneUtil
|
|||
mIndoorShadowCastingMask(indoorShadowCastingMask)
|
||||
{
|
||||
mShadowedScene->setShadowTechnique(mShadowTechnique);
|
||||
Stereo::Manager::instance().setShadowTechnique(mShadowTechnique);
|
||||
|
||||
mShadowedScene->addChild(sceneRoot);
|
||||
rootNode->addChild(mShadowedScene);
|
||||
|
@ -117,6 +119,7 @@ namespace SceneUtil
|
|||
|
||||
ShadowManager::~ShadowManager()
|
||||
{
|
||||
Stereo::Manager::instance().setShadowTechnique(nullptr);
|
||||
}
|
||||
|
||||
Shader::ShaderManager::DefineMap ShadowManager::getShadowDefines()
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#include "statesetupdater.hpp"
|
||||
|
||||
#include <components/stereo/stereomanager.hpp>
|
||||
|
||||
#include <osg/Node>
|
||||
#include <osg/NodeVisitor>
|
||||
#include <osgUtil/CullVisitor>
|
||||
|
@ -38,6 +40,12 @@ namespace SceneUtil
|
|||
{
|
||||
auto stateset = getCvDependentStateset(cv);
|
||||
apply(stateset, cv);
|
||||
auto& sm = Stereo::Manager::instance();
|
||||
if (sm.getEye(cv) == Stereo::Eye::Left)
|
||||
applyLeft(stateset, cv);
|
||||
if (sm.getEye(cv) == Stereo::Eye::Right)
|
||||
applyRight(stateset, cv);
|
||||
|
||||
cv->pushStateSet(stateset);
|
||||
traverse(node, cv);
|
||||
cv->popStateSet();
|
||||
|
|
|
@ -41,6 +41,11 @@ namespace SceneUtil
|
|||
/// even if it has not changed since the last frame.
|
||||
virtual void apply(osg::StateSet* stateset, osg::NodeVisitor* nv) {}
|
||||
|
||||
/// Apply any state specific to the Left view. Default implementation does nothing. Called after apply() \note requires the updater be a cull callback
|
||||
virtual void applyLeft(osg::StateSet* stateset, osgUtil::CullVisitor* nv) {}
|
||||
/// Apply any state specific to the Right view. Default implementation does nothing. Called after apply() \note requires the updater be a cull callback
|
||||
virtual void applyRight(osg::StateSet* stateset, osgUtil::CullVisitor* nv) {}
|
||||
|
||||
/// Set default state - optionally override in derived classes
|
||||
/// @par May be used e.g. to allocate StateAttributes.
|
||||
virtual void setDefaults(osg::StateSet* stateset) {}
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
#include <components/debug/debuglog.hpp>
|
||||
#include <components/misc/stringops.hpp>
|
||||
#include <components/stereo/stereomanager.hpp>
|
||||
#include <components/resource/imagemanager.hpp>
|
||||
#include <components/vfs/manager.hpp>
|
||||
#include <components/sceneutil/riggeometry.hpp>
|
||||
|
@ -647,6 +648,8 @@ namespace Shader
|
|||
|
||||
defineMap["softParticles"] = reqs.mSoftParticles ? "1" : "0";
|
||||
|
||||
Stereo::Manager::instance().shaderStereoDefines(defineMap);
|
||||
|
||||
std::string shaderPrefix;
|
||||
if (!node.getUserValue("shaderPrefix", shaderPrefix))
|
||||
shaderPrefix = mDefaultShaderPrefix;
|
||||
|
|
162
components/stereo/frustum.cpp
Normal file
162
components/stereo/frustum.cpp
Normal file
|
@ -0,0 +1,162 @@
|
|||
#include "stereomanager.hpp"
|
||||
#include "multiview.hpp"
|
||||
|
||||
#include <osg/io_utils>
|
||||
#include <osg/Texture2D>
|
||||
#include <osg/Texture2DMultisample>
|
||||
#include <osg/Texture2DArray>
|
||||
#include <osg/DisplaySettings>
|
||||
|
||||
#include <osgUtil/CullVisitor>
|
||||
#include <osgUtil/RenderStage>
|
||||
|
||||
#include <osgViewer/Renderer>
|
||||
#include <osgViewer/Viewer>
|
||||
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include <components/debug/debuglog.hpp>
|
||||
|
||||
#include <components/sceneutil/statesetupdater.hpp>
|
||||
#include <components/sceneutil/visitor.hpp>
|
||||
#include <components/sceneutil/util.hpp>
|
||||
#include <components/sceneutil/depth.hpp>
|
||||
#include <components/sceneutil/color.hpp>
|
||||
#include <components/sceneutil/mwshadowtechnique.hpp>
|
||||
|
||||
#include <components/settings/settings.hpp>
|
||||
|
||||
#include <components/misc/stringops.hpp>
|
||||
#include "frustum.hpp"
|
||||
|
||||
namespace Stereo
|
||||
{
|
||||
#ifdef OSG_HAS_MULTIVIEW
|
||||
struct MultiviewFrustumCallback final : public osg::CullSettings::InitialFrustumCallback
|
||||
{
|
||||
MultiviewFrustumCallback(StereoFrustumManager* sfm)
|
||||
: mSfm(sfm)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void setInitialFrustum(osg::CullStack& cullStack, osg::Polytope& frustum) const override
|
||||
{
|
||||
auto cm = cullStack.getCullingMode();
|
||||
bool nearCulling = !!(cm & osg::CullSettings::NEAR_PLANE_CULLING);
|
||||
bool farCulling = !!(cm & osg::CullSettings::FAR_PLANE_CULLING);
|
||||
frustum.setToBoundingBox(mSfm->boundingBox(), nearCulling, farCulling);
|
||||
}
|
||||
|
||||
StereoFrustumManager* mSfm;
|
||||
};
|
||||
#endif
|
||||
|
||||
struct ShadowFrustumCallback final : public SceneUtil::MWShadowTechnique::CustomFrustumCallback
|
||||
{
|
||||
ShadowFrustumCallback(StereoFrustumManager* parent) : mParent(parent)
|
||||
{
|
||||
}
|
||||
|
||||
void operator()(osgUtil::CullVisitor& cv, osg::BoundingBoxd& customClipSpace, osgUtil::CullVisitor*& sharedFrustumHint) override
|
||||
{
|
||||
mParent->customFrustumCallback(cv, customClipSpace, sharedFrustumHint);
|
||||
}
|
||||
|
||||
StereoFrustumManager* mParent;
|
||||
};
|
||||
|
||||
void joinBoundingBoxes(const osg::Matrix& masterProjection, const osg::Matrix& slaveProjection, osg::BoundingBoxd& bb)
|
||||
{
|
||||
static const std::array<osg::Vec3d, 8> clipCorners = {{
|
||||
{-1.0, -1.0, -1.0},
|
||||
{ 1.0, -1.0, -1.0},
|
||||
{ 1.0, -1.0, 1.0},
|
||||
{-1.0, -1.0, 1.0},
|
||||
{-1.0, 1.0, -1.0},
|
||||
{ 1.0, 1.0, -1.0},
|
||||
{ 1.0, 1.0, 1.0},
|
||||
{-1.0, 1.0, 1.0}
|
||||
}};
|
||||
|
||||
osg::Matrix slaveClipToView;
|
||||
slaveClipToView.invert(slaveProjection);
|
||||
|
||||
for (const auto& clipCorner : clipCorners)
|
||||
{
|
||||
auto masterViewVertice = clipCorner * slaveClipToView;
|
||||
auto masterClipVertice = masterViewVertice * masterProjection;
|
||||
bb.expandBy(masterClipVertice);
|
||||
}
|
||||
}
|
||||
|
||||
StereoFrustumManager::StereoFrustumManager(osg::Camera* camera)
|
||||
: mCamera(camera)
|
||||
, mShadowTechnique(nullptr)
|
||||
, mShadowFrustumCallback(nullptr)
|
||||
{
|
||||
if (Stereo::getMultiview())
|
||||
{
|
||||
#ifdef OSG_HAS_MULTIVIEW
|
||||
mMultiviewFrustumCallback = new MultiviewFrustumCallback(this);
|
||||
mCamera->setInitialFrustumCallback(mMultiviewFrustumCallback);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (Settings::Manager::getBool("shared shadow maps", "Stereo"))
|
||||
{
|
||||
mShadowFrustumCallback = new ShadowFrustumCallback(this);
|
||||
auto* renderer = static_cast<osgViewer::Renderer*>(mCamera->getRenderer());
|
||||
for (auto* sceneView : { renderer->getSceneView(0), renderer->getSceneView(1) })
|
||||
{
|
||||
mSharedFrustums[sceneView->getCullVisitorRight()] = sceneView->getCullVisitorLeft();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StereoFrustumManager::~StereoFrustumManager()
|
||||
{
|
||||
if (Stereo::getMultiview())
|
||||
{
|
||||
#ifdef OSG_HAS_MULTIVIEW
|
||||
mCamera->setInitialFrustumCallback(nullptr);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (mShadowTechnique)
|
||||
mShadowTechnique->setCustomFrustumCallback(nullptr);
|
||||
}
|
||||
|
||||
void StereoFrustumManager::setShadowTechnique(
|
||||
SceneUtil::MWShadowTechnique* shadowTechnique)
|
||||
{
|
||||
if (mShadowTechnique)
|
||||
mShadowTechnique->setCustomFrustumCallback(nullptr);
|
||||
mShadowTechnique = shadowTechnique;
|
||||
if (mShadowTechnique)
|
||||
mShadowTechnique->setCustomFrustumCallback(mShadowFrustumCallback);
|
||||
}
|
||||
|
||||
void StereoFrustumManager::customFrustumCallback(
|
||||
osgUtil::CullVisitor& cv,
|
||||
osg::BoundingBoxd& customClipSpace,
|
||||
osgUtil::CullVisitor*& sharedFrustumHint)
|
||||
{
|
||||
auto it = mSharedFrustums.find(&cv);
|
||||
if (it != mSharedFrustums.end())
|
||||
{
|
||||
sharedFrustumHint = it->second;
|
||||
}
|
||||
|
||||
customClipSpace = mBoundingBox;
|
||||
}
|
||||
|
||||
void StereoFrustumManager::update(std::array<osg::Matrix, 2> projections)
|
||||
{
|
||||
mBoundingBox.init();
|
||||
for (auto& projection : projections)
|
||||
joinBoundingBoxes(mCamera->getProjectionMatrix(), projection, mBoundingBox);
|
||||
}
|
||||
}
|
76
components/stereo/frustum.hpp
Normal file
76
components/stereo/frustum.hpp
Normal file
|
@ -0,0 +1,76 @@
|
|||
#ifndef STEREO_FRUSTUM_H
|
||||
#define STEREO_FRUSTUM_H
|
||||
|
||||
#include <osg/Matrix>
|
||||
#include <osg/Vec3>
|
||||
#include <osg/Camera>
|
||||
#include <osg/StateSet>
|
||||
#include <osg/BoundingBox>
|
||||
|
||||
#include <memory>
|
||||
#include <array>
|
||||
#include <map>
|
||||
|
||||
#include <components/stereo/types.hpp>
|
||||
|
||||
namespace osg
|
||||
{
|
||||
class FrameBufferObject;
|
||||
class Texture2D;
|
||||
class Texture2DMultisample;
|
||||
class Texture2DArray;
|
||||
}
|
||||
|
||||
namespace osgViewer
|
||||
{
|
||||
class Viewer;
|
||||
}
|
||||
|
||||
namespace usgUtil
|
||||
{
|
||||
class CullVisitor;
|
||||
}
|
||||
|
||||
namespace SceneUtil
|
||||
{
|
||||
class MWShadowTechnique;
|
||||
}
|
||||
|
||||
namespace Stereo
|
||||
{
|
||||
#ifdef OSG_HAS_MULTIVIEW
|
||||
struct MultiviewFrustumCallback;
|
||||
#endif
|
||||
|
||||
struct ShadowFrustumCallback;
|
||||
|
||||
void joinBoundingBoxes(const osg::Matrix& masterProjection, const osg::Matrix& slaveProjection, osg::BoundingBoxd& bb);
|
||||
|
||||
class StereoFrustumManager
|
||||
{
|
||||
public:
|
||||
StereoFrustumManager(osg::Camera* camera);
|
||||
~StereoFrustumManager();
|
||||
|
||||
void update(std::array<osg::Matrix, 2> projections);
|
||||
|
||||
const osg::BoundingBoxd& boundingBox() const { return mBoundingBox; }
|
||||
|
||||
void setShadowTechnique(SceneUtil::MWShadowTechnique* shadowTechnique);
|
||||
|
||||
void customFrustumCallback(osgUtil::CullVisitor& cv, osg::BoundingBoxd& customClipSpace, osgUtil::CullVisitor*& sharedFrustumHint);
|
||||
|
||||
private:
|
||||
osg::ref_ptr<osg::Camera> mCamera;
|
||||
osg::ref_ptr<SceneUtil::MWShadowTechnique> mShadowTechnique;
|
||||
osg::ref_ptr<ShadowFrustumCallback> mShadowFrustumCallback;
|
||||
std::map< osgUtil::CullVisitor*, osgUtil::CullVisitor*> mSharedFrustums;
|
||||
osg::BoundingBoxd mBoundingBox;
|
||||
|
||||
#ifdef OSG_HAS_MULTIVIEW
|
||||
osg::ref_ptr<MultiviewFrustumCallback> mMultiviewFrustumCallback;
|
||||
#endif
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
484
components/stereo/multiview.cpp
Normal file
484
components/stereo/multiview.cpp
Normal file
|
@ -0,0 +1,484 @@
|
|||
#include "multiview.hpp"
|
||||
|
||||
#include <osg/FrameBufferObject>
|
||||
#include <osg/GLExtensions>
|
||||
#include <osg/Texture2D>
|
||||
#include <osg/Texture2DMultisample>
|
||||
#include <osg/Texture2DArray>
|
||||
#include <osgUtil/RenderStage>
|
||||
#include <osgUtil/CullVisitor>
|
||||
|
||||
#include <components/sceneutil/nodecallback.hpp>
|
||||
#include <components/settings/settings.hpp>
|
||||
#include <components/debug/debuglog.hpp>
|
||||
#include <components/stereo/stereomanager.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace Stereo
|
||||
{
|
||||
namespace
|
||||
{
|
||||
bool getMultiviewSupportedImpl(unsigned int contextID)
|
||||
{
|
||||
#ifdef OSG_HAS_MULTIVIEW
|
||||
if (!osg::isGLExtensionSupported(contextID, "GL_OVR_multiview"))
|
||||
{
|
||||
Log(Debug::Verbose) << "Disabling Multiview (opengl extension \"GL_OVR_multiview\" not supported)";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!osg::isGLExtensionSupported(contextID, "GL_OVR_multiview2"))
|
||||
{
|
||||
Log(Debug::Verbose) << "Disabling Multiview (opengl extension \"GL_OVR_multiview2\" not supported)";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
#else
|
||||
Log(Debug::Verbose) << "Disabling Multiview (OSG does not support multiview)";
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool getMultiviewSupported(unsigned int contextID)
|
||||
{
|
||||
static bool supported = getMultiviewSupportedImpl(contextID);
|
||||
return supported;
|
||||
}
|
||||
|
||||
bool getTextureViewSupportedImpl(unsigned int contextID)
|
||||
{
|
||||
if (!osg::isGLExtensionOrVersionSupported(contextID, "ARB_texture_view", 4.3))
|
||||
{
|
||||
Log(Debug::Verbose) << "Disabling texture views (opengl extension \"ARB_texture_view\" not supported)";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool getTextureViewSupported(unsigned int contextID)
|
||||
{
|
||||
static bool supported = getTextureViewSupportedImpl(contextID);
|
||||
return supported;
|
||||
}
|
||||
|
||||
bool getMultiviewImpl(unsigned int contextID)
|
||||
{
|
||||
if (!Stereo::getStereo())
|
||||
{
|
||||
Log(Debug::Verbose) << "Disabling Multiview (disabled by config)";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Settings::Manager::getBool("multiview", "Stereo"))
|
||||
{
|
||||
Log(Debug::Verbose) << "Disabling Multiview (disabled by config)";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!getMultiviewSupported(contextID))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!getTextureViewSupported(contextID))
|
||||
{
|
||||
Log(Debug::Verbose) << "Disabling Multiview (texture views not supported)";
|
||||
return false;
|
||||
}
|
||||
|
||||
Log(Debug::Verbose) << "Enabling Multiview";
|
||||
return true;
|
||||
}
|
||||
|
||||
bool getMultiview(unsigned int contextID)
|
||||
{
|
||||
static bool multiView = getMultiviewImpl(contextID);
|
||||
return multiView;
|
||||
}
|
||||
}
|
||||
|
||||
bool getTextureViewSupported()
|
||||
{
|
||||
return getTextureViewSupported(0);
|
||||
}
|
||||
|
||||
bool getMultiview()
|
||||
{
|
||||
return getMultiview(0);
|
||||
}
|
||||
|
||||
void configureExtensions(unsigned int contextID)
|
||||
{
|
||||
getTextureViewSupported(contextID);
|
||||
getMultiviewSupported(contextID);
|
||||
getMultiview(contextID);
|
||||
}
|
||||
|
||||
void setVertexBufferHint()
|
||||
{
|
||||
if (getStereo() && Settings::Manager::getBool("multiview", "Stereo"))
|
||||
{
|
||||
auto* ds = osg::DisplaySettings::instance().get();
|
||||
if (!Settings::Manager::getBool("allow display lists for multiview", "Stereo")
|
||||
&& ds->getVertexBufferHint() == osg::DisplaySettings::VertexBufferHint::NO_PREFERENCE)
|
||||
{
|
||||
// Note that this only works if this code is executed before realize() is called on the viewer.
|
||||
// The hint is read by the state object only once, before the user realize operations are run.
|
||||
// Therefore we have to set this hint without access to a graphics context to let us determine
|
||||
// if multiview will actually be supported or not. So if the user has requested multiview, we
|
||||
// will just have to set it regardless.
|
||||
ds->setVertexBufferHint(osg::DisplaySettings::VertexBufferHint::VERTEX_BUFFER_OBJECT);
|
||||
Log(Debug::Verbose) << "Disabling display lists";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Texture2DViewSubloadCallback : public osg::Texture2D::SubloadCallback
|
||||
{
|
||||
public:
|
||||
Texture2DViewSubloadCallback(osg::Texture2DArray* textureArray, int layer);
|
||||
|
||||
void load(const osg::Texture2D& texture, osg::State& state) const override;
|
||||
void subload(const osg::Texture2D& texture, osg::State& state) const override;
|
||||
|
||||
private:
|
||||
osg::ref_ptr<osg::Texture2DArray> mTextureArray;
|
||||
int mLayer;
|
||||
};
|
||||
|
||||
Texture2DViewSubloadCallback::Texture2DViewSubloadCallback(osg::Texture2DArray* textureArray, int layer)
|
||||
: mTextureArray(textureArray)
|
||||
, mLayer(layer)
|
||||
{
|
||||
}
|
||||
|
||||
void Texture2DViewSubloadCallback::load(const osg::Texture2D& texture, osg::State& state) const
|
||||
{
|
||||
state.checkGLErrors("before Texture2DViewSubloadCallback::load()");
|
||||
|
||||
auto contextId = state.getContextID();
|
||||
auto* gl = osg::GLExtensions::Get(contextId, false);
|
||||
mTextureArray->apply(state);
|
||||
|
||||
auto sourceTextureObject = mTextureArray->getTextureObject(contextId);
|
||||
if (!sourceTextureObject)
|
||||
{
|
||||
Log(Debug::Error) << "Texture2DViewSubloadCallback: Texture2DArray did not have a texture object";
|
||||
return;
|
||||
}
|
||||
|
||||
auto targetTextureObject = texture.getTextureObject(contextId);
|
||||
if (!sourceTextureObject)
|
||||
{
|
||||
Log(Debug::Error) << "Texture2DViewSubloadCallback: Texture2D did not have a texture object";
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// OSG already bound this texture ID, giving it a target.
|
||||
// Delete it and make a new texture ID.
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glDeleteTextures(1, &targetTextureObject->_id);
|
||||
glGenTextures(1, &targetTextureObject->_id);
|
||||
|
||||
auto sourceId = sourceTextureObject->_id;
|
||||
auto targetId = targetTextureObject->_id;
|
||||
auto internalFormat = sourceTextureObject->_profile._internalFormat;
|
||||
auto levels = std::max(1, sourceTextureObject->_profile._numMipmapLevels);
|
||||
|
||||
{
|
||||
////// OSG BUG
|
||||
// Texture views require immutable storage.
|
||||
// OSG should always give immutable storage to sized internal formats, but does not do so for depth formats.
|
||||
// Fortunately, we can just call glTexStorage3D here to make it immutable. This probably discards depth info for that frame, but whatever.
|
||||
#ifndef GL_TEXTURE_IMMUTABLE_FORMAT
|
||||
#define GL_TEXTURE_IMMUTABLE_FORMAT 0x912F
|
||||
#endif
|
||||
// Store any current binding and re-apply it after so i don't mess with state.
|
||||
GLint oldBinding = 0;
|
||||
glGetIntegerv(GL_TEXTURE_BINDING_2D_ARRAY, &oldBinding);
|
||||
|
||||
// Bind the source texture and check if it's immutable.
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, sourceId);
|
||||
GLint immutable = 0;
|
||||
glGetTexParameteriv(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_IMMUTABLE_FORMAT, &immutable);
|
||||
if(!immutable)
|
||||
{
|
||||
// It wasn't immutable, so make it immutable.
|
||||
gl->glTexStorage3D(GL_TEXTURE_2D_ARRAY, 1, internalFormat, sourceTextureObject->_profile._width, sourceTextureObject->_profile._height, 2);
|
||||
state.checkGLErrors("after Texture2DViewSubloadCallback::load()::glTexStorage3D");
|
||||
}
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, oldBinding);
|
||||
}
|
||||
|
||||
gl->glTextureView(targetId, GL_TEXTURE_2D, sourceId, internalFormat, 0, levels, mLayer, 1);
|
||||
state.checkGLErrors("after Texture2DViewSubloadCallback::load()::glTextureView");
|
||||
glBindTexture(GL_TEXTURE_2D, targetId);
|
||||
}
|
||||
|
||||
void Texture2DViewSubloadCallback::subload(const osg::Texture2D& texture, osg::State& state) const
|
||||
{
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
osg::ref_ptr<osg::Texture2D> createTextureView_Texture2DFromTexture2DArray(osg::Texture2DArray* textureArray, int layer)
|
||||
{
|
||||
if (!getTextureViewSupported())
|
||||
{
|
||||
Log(Debug::Error) << "createTextureView_Texture2DFromTexture2DArray: Tried to use a texture view but glTextureView is not supported";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
osg::ref_ptr<osg::Texture2D> texture2d = new osg::Texture2D;
|
||||
texture2d->setSubloadCallback(new Texture2DViewSubloadCallback(textureArray, layer));
|
||||
texture2d->setTextureSize(textureArray->getTextureWidth(), textureArray->getTextureHeight());
|
||||
texture2d->setBorderColor(textureArray->getBorderColor());
|
||||
texture2d->setBorderWidth(textureArray->getBorderWidth());
|
||||
texture2d->setLODBias(textureArray->getLODBias());
|
||||
texture2d->setFilter(osg::Texture::FilterParameter::MAG_FILTER, textureArray->getFilter(osg::Texture::FilterParameter::MAG_FILTER));
|
||||
texture2d->setFilter(osg::Texture::FilterParameter::MIN_FILTER, textureArray->getFilter(osg::Texture::FilterParameter::MIN_FILTER));
|
||||
texture2d->setInternalFormat(textureArray->getInternalFormat());
|
||||
texture2d->setNumMipmapLevels(textureArray->getNumMipmapLevels());
|
||||
return texture2d;
|
||||
}
|
||||
|
||||
class UpdateRenderStagesCallback : public SceneUtil::NodeCallback<UpdateRenderStagesCallback, osg::Node*, osgUtil::CullVisitor*>
|
||||
{
|
||||
public:
|
||||
UpdateRenderStagesCallback(Stereo::MultiviewFramebuffer* multiviewFramebuffer)
|
||||
: mMultiviewFramebuffer(multiviewFramebuffer)
|
||||
{
|
||||
mViewport = new osg::Viewport(0, 0, multiviewFramebuffer->width(), multiviewFramebuffer->height());
|
||||
mViewportStateset = new osg::StateSet();
|
||||
mViewportStateset->setAttribute(mViewport.get());
|
||||
}
|
||||
|
||||
void operator()(osg::Node* node, osgUtil::CullVisitor* cv)
|
||||
{
|
||||
osgUtil::RenderStage* renderStage = cv->getCurrentRenderStage();
|
||||
|
||||
bool msaa = mMultiviewFramebuffer->samples() > 1;
|
||||
|
||||
if (!Stereo::getMultiview())
|
||||
{
|
||||
auto eye = static_cast<int>(Stereo::Manager::instance().getEye(cv));
|
||||
|
||||
if (msaa)
|
||||
{
|
||||
renderStage->setFrameBufferObject(mMultiviewFramebuffer->layerMsaaFbo(eye));
|
||||
renderStage->setMultisampleResolveFramebufferObject(mMultiviewFramebuffer->layerFbo(eye));
|
||||
}
|
||||
else
|
||||
{
|
||||
renderStage->setFrameBufferObject(mMultiviewFramebuffer->layerFbo(eye));
|
||||
}
|
||||
}
|
||||
|
||||
// OSG tries to do a horizontal split, but we want to render to separate framebuffers instead.
|
||||
renderStage->setViewport(mViewport);
|
||||
cv->pushStateSet(mViewportStateset.get());
|
||||
traverse(node, cv);
|
||||
cv->popStateSet();
|
||||
}
|
||||
|
||||
private:
|
||||
Stereo::MultiviewFramebuffer* mMultiviewFramebuffer;
|
||||
osg::ref_ptr<osg::Viewport> mViewport;
|
||||
osg::ref_ptr<osg::StateSet> mViewportStateset;
|
||||
};
|
||||
|
||||
MultiviewFramebuffer::MultiviewFramebuffer(int width, int height, int samples)
|
||||
: mWidth(width)
|
||||
, mHeight(height)
|
||||
, mSamples(samples)
|
||||
, mMultiview(getMultiview())
|
||||
, mMultiviewFbo{ new osg::FrameBufferObject }
|
||||
, mLayerFbo{ new osg::FrameBufferObject, new osg::FrameBufferObject }
|
||||
, mLayerMsaaFbo{ new osg::FrameBufferObject, new osg::FrameBufferObject }
|
||||
{
|
||||
}
|
||||
|
||||
MultiviewFramebuffer::~MultiviewFramebuffer()
|
||||
{
|
||||
}
|
||||
|
||||
void MultiviewFramebuffer::attachColorComponent(GLint sourceFormat, GLint sourceType, GLint internalFormat)
|
||||
{
|
||||
if (mMultiview)
|
||||
{
|
||||
#ifdef OSG_HAS_MULTIVIEW
|
||||
mMultiviewColorTexture = createTextureArray(sourceFormat, sourceType, internalFormat);
|
||||
mMultiviewFbo->setAttachment(osg::Camera::COLOR_BUFFER, osg::FrameBufferAttachment(mMultiviewColorTexture, osg::Camera::FACE_CONTROLLED_BY_MULTIVIEW_SHADER, 0));
|
||||
for (unsigned i = 0; i < 2; i++)
|
||||
{
|
||||
mColorTexture[i] = createTextureView_Texture2DFromTexture2DArray(mMultiviewColorTexture.get(), i);
|
||||
mLayerFbo[i]->setAttachment(osg::Camera::COLOR_BUFFER, osg::FrameBufferAttachment(mColorTexture[i]));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
for (unsigned i = 0; i < 2; i++)
|
||||
{
|
||||
if (mSamples > 1)
|
||||
{
|
||||
mMsaaColorTexture[i] = createTextureMsaa(sourceFormat, sourceType, internalFormat);
|
||||
mLayerMsaaFbo[i]->setAttachment(osg::Camera::COLOR_BUFFER, osg::FrameBufferAttachment(mMsaaColorTexture[i]));
|
||||
}
|
||||
mColorTexture[i] = createTexture(sourceFormat, sourceType, internalFormat);
|
||||
mLayerFbo[i]->setAttachment(osg::Camera::COLOR_BUFFER, osg::FrameBufferAttachment(mColorTexture[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MultiviewFramebuffer::attachDepthComponent(GLint sourceFormat, GLint sourceType, GLint internalFormat)
|
||||
{
|
||||
if (mMultiview)
|
||||
{
|
||||
#ifdef OSG_HAS_MULTIVIEW
|
||||
mMultiviewDepthTexture = createTextureArray(sourceFormat, sourceType, internalFormat);
|
||||
mMultiviewFbo->setAttachment(osg::Camera::PACKED_DEPTH_STENCIL_BUFFER, osg::FrameBufferAttachment(mMultiviewDepthTexture, osg::Camera::FACE_CONTROLLED_BY_MULTIVIEW_SHADER, 0));
|
||||
for (unsigned i = 0; i < 2; i++)
|
||||
{
|
||||
mDepthTexture[i] = createTextureView_Texture2DFromTexture2DArray(mMultiviewDepthTexture.get(), i);
|
||||
mLayerFbo[i]->setAttachment(osg::Camera::PACKED_DEPTH_STENCIL_BUFFER, osg::FrameBufferAttachment(mDepthTexture[i]));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
for (unsigned i = 0; i < 2; i++)
|
||||
{
|
||||
if (mSamples > 1)
|
||||
{
|
||||
mMsaaDepthTexture[i] = createTextureMsaa(sourceFormat, sourceType, internalFormat);
|
||||
mLayerMsaaFbo[i]->setAttachment(osg::Camera::PACKED_DEPTH_STENCIL_BUFFER, osg::FrameBufferAttachment(mMsaaDepthTexture[i]));
|
||||
}
|
||||
mDepthTexture[i] = createTexture(sourceFormat, sourceType, internalFormat);
|
||||
mLayerFbo[i]->setAttachment(osg::Camera::PACKED_DEPTH_STENCIL_BUFFER, osg::FrameBufferAttachment(mDepthTexture[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
osg::FrameBufferObject* MultiviewFramebuffer::multiviewFbo()
|
||||
{
|
||||
return mMultiviewFbo;
|
||||
}
|
||||
|
||||
osg::FrameBufferObject* MultiviewFramebuffer::layerFbo(int i)
|
||||
{
|
||||
return mLayerFbo[i];
|
||||
}
|
||||
|
||||
osg::FrameBufferObject* MultiviewFramebuffer::layerMsaaFbo(int i)
|
||||
{
|
||||
return mLayerMsaaFbo[i];
|
||||
}
|
||||
|
||||
osg::Texture2DArray* MultiviewFramebuffer::multiviewColorBuffer()
|
||||
{
|
||||
return mMultiviewColorTexture;
|
||||
}
|
||||
|
||||
osg::Texture2D* MultiviewFramebuffer::layerColorBuffer(int i)
|
||||
{
|
||||
return mColorTexture[i];
|
||||
}
|
||||
|
||||
osg::Texture2D* MultiviewFramebuffer::layerDepthBuffer(int i)
|
||||
{
|
||||
return mDepthTexture[i];
|
||||
}
|
||||
void MultiviewFramebuffer::attachTo(osg::Camera* camera)
|
||||
{
|
||||
#ifdef OSG_HAS_MULTIVIEW
|
||||
if (mMultiview)
|
||||
{
|
||||
if (mMultiviewColorTexture)
|
||||
{
|
||||
camera->attach(osg::Camera::COLOR_BUFFER, mMultiviewColorTexture, 0, osg::Camera::FACE_CONTROLLED_BY_MULTIVIEW_SHADER, false, mSamples);
|
||||
camera->getBufferAttachmentMap()[osg::Camera::COLOR_BUFFER]._internalFormat = mMultiviewColorTexture->getInternalFormat();
|
||||
camera->getBufferAttachmentMap()[osg::Camera::COLOR_BUFFER]._mipMapGeneration = false;
|
||||
}
|
||||
if (mMultiviewDepthTexture)
|
||||
{
|
||||
camera->attach(osg::Camera::PACKED_DEPTH_STENCIL_BUFFER, mMultiviewDepthTexture, 0, osg::Camera::FACE_CONTROLLED_BY_MULTIVIEW_SHADER, false, mSamples);
|
||||
camera->getBufferAttachmentMap()[osg::Camera::PACKED_DEPTH_STENCIL_BUFFER]._internalFormat = mMultiviewDepthTexture->getInternalFormat();
|
||||
camera->getBufferAttachmentMap()[osg::Camera::PACKED_DEPTH_STENCIL_BUFFER]._mipMapGeneration = false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);
|
||||
|
||||
if (!mCullCallback)
|
||||
mCullCallback = new UpdateRenderStagesCallback(this);
|
||||
camera->addCullCallback(mCullCallback);
|
||||
}
|
||||
|
||||
void MultiviewFramebuffer::detachFrom(osg::Camera* camera)
|
||||
{
|
||||
#ifdef OSG_HAS_MULTIVIEW
|
||||
if (mMultiview)
|
||||
{
|
||||
if (mMultiviewColorTexture)
|
||||
{
|
||||
camera->detach(osg::Camera::COLOR_BUFFER);
|
||||
}
|
||||
if (mMultiviewDepthTexture)
|
||||
{
|
||||
camera->detach(osg::Camera::PACKED_DEPTH_STENCIL_BUFFER);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER);
|
||||
if (mCullCallback)
|
||||
camera->removeCullCallback(mCullCallback);
|
||||
}
|
||||
|
||||
osg::Texture2D* MultiviewFramebuffer::createTexture(GLint sourceFormat, GLint sourceType, GLint internalFormat)
|
||||
{
|
||||
osg::Texture2D* texture = new osg::Texture2D;
|
||||
texture->setTextureSize(mWidth, mHeight);
|
||||
texture->setSourceFormat(sourceFormat);
|
||||
texture->setSourceType(sourceType);
|
||||
texture->setInternalFormat(internalFormat);
|
||||
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);
|
||||
texture->setWrap(osg::Texture::WRAP_R, osg::Texture::CLAMP_TO_EDGE);
|
||||
return texture;
|
||||
}
|
||||
|
||||
osg::Texture2DMultisample* MultiviewFramebuffer::createTextureMsaa(GLint sourceFormat, GLint sourceType, GLint internalFormat)
|
||||
{
|
||||
osg::Texture2DMultisample* texture = new osg::Texture2DMultisample;
|
||||
texture->setTextureSize(mWidth, mHeight);
|
||||
texture->setNumSamples(mSamples);
|
||||
texture->setSourceFormat(sourceFormat);
|
||||
texture->setSourceType(sourceType);
|
||||
texture->setInternalFormat(internalFormat);
|
||||
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);
|
||||
texture->setWrap(osg::Texture::WRAP_R, osg::Texture::CLAMP_TO_EDGE);
|
||||
return texture;
|
||||
}
|
||||
|
||||
osg::Texture2DArray* MultiviewFramebuffer::createTextureArray(GLint sourceFormat, GLint sourceType, GLint internalFormat)
|
||||
{
|
||||
osg::Texture2DArray* textureArray = new osg::Texture2DArray;
|
||||
textureArray->setTextureSize(mWidth, mHeight, 2);
|
||||
textureArray->setSourceFormat(sourceFormat);
|
||||
textureArray->setSourceType(sourceType);
|
||||
textureArray->setInternalFormat(internalFormat);
|
||||
textureArray->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
|
||||
textureArray->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
|
||||
textureArray->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
|
||||
textureArray->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
|
||||
textureArray->setWrap(osg::Texture::WRAP_R, osg::Texture::CLAMP_TO_EDGE);
|
||||
return textureArray;
|
||||
}
|
||||
}
|
85
components/stereo/multiview.hpp
Normal file
85
components/stereo/multiview.hpp
Normal file
|
@ -0,0 +1,85 @@
|
|||
#ifndef STEREO_MULTIVIEW_H
|
||||
#define STEREO_MULTIVIEW_H
|
||||
|
||||
#include <osg/ref_ptr>
|
||||
#include <osg/GL>
|
||||
#include <osg/Camera>
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
|
||||
namespace osg
|
||||
{
|
||||
class FrameBufferObject;
|
||||
class Texture;
|
||||
class Texture2D;
|
||||
class Texture2DMultisample;
|
||||
class Texture2DArray;
|
||||
}
|
||||
|
||||
namespace Stereo
|
||||
{
|
||||
class UpdateRenderStagesCallback;
|
||||
|
||||
//! Check if TextureView is supported. Results are undefined if called before configureExtensions().
|
||||
bool getTextureViewSupported();
|
||||
|
||||
//! Check if Multiview should be used. Results are undefined if called before configureExtensions().
|
||||
bool getMultiview();
|
||||
|
||||
//! Use the provided context to check what extensions are supported and configure use of multiview based on extensions and settings.
|
||||
void configureExtensions(unsigned int contextID);
|
||||
|
||||
//! Sets the appropriate vertex buffer hint on OSG's display settings if needed
|
||||
void setVertexBufferHint();
|
||||
|
||||
//! Creates a Texture2D as a texture view into a Texture2DArray
|
||||
osg::ref_ptr<osg::Texture2D> createTextureView_Texture2DFromTexture2DArray(osg::Texture2DArray* textureArray, int layer);
|
||||
|
||||
//! Class that manages the specifics of GL_OVR_Multiview aware framebuffers, separating the layers into separate framebuffers, and disabling
|
||||
class MultiviewFramebuffer
|
||||
{
|
||||
public:
|
||||
MultiviewFramebuffer(int width, int height, int samples);
|
||||
~MultiviewFramebuffer();
|
||||
|
||||
void attachColorComponent(GLint sourceFormat, GLint sourceType, GLint internalFormat);
|
||||
void attachDepthComponent(GLint sourceFormat, GLint sourceType, GLint internalFormat);
|
||||
|
||||
osg::FrameBufferObject* multiviewFbo();
|
||||
osg::FrameBufferObject* layerFbo(int i);
|
||||
osg::FrameBufferObject* layerMsaaFbo(int i);
|
||||
osg::Texture2DArray* multiviewColorBuffer();
|
||||
osg::Texture2D* layerColorBuffer(int i);
|
||||
osg::Texture2D* layerDepthBuffer(int i);
|
||||
|
||||
void attachTo(osg::Camera* camera);
|
||||
void detachFrom(osg::Camera* camera);
|
||||
|
||||
int width() const { return mWidth; }
|
||||
int height() const { return mHeight; }
|
||||
int samples() const { return mSamples; };
|
||||
|
||||
private:
|
||||
osg::Texture2D* createTexture(GLint sourceFormat, GLint sourceType, GLint internalFormat);
|
||||
osg::Texture2DMultisample* createTextureMsaa(GLint sourceFormat, GLint sourceType, GLint internalFormat);
|
||||
osg::Texture2DArray* createTextureArray(GLint sourceFormat, GLint sourceType, GLint internalFormat);
|
||||
|
||||
int mWidth;
|
||||
int mHeight;
|
||||
int mSamples;
|
||||
bool mMultiview;
|
||||
osg::ref_ptr<UpdateRenderStagesCallback> mCullCallback;
|
||||
osg::ref_ptr<osg::FrameBufferObject> mMultiviewFbo;
|
||||
std::array<osg::ref_ptr<osg::FrameBufferObject>, 2> mLayerFbo;
|
||||
std::array<osg::ref_ptr<osg::FrameBufferObject>, 2> mLayerMsaaFbo;
|
||||
osg::ref_ptr<osg::Texture2DArray> mMultiviewColorTexture;
|
||||
osg::ref_ptr<osg::Texture2DArray> mMultiviewDepthTexture;
|
||||
std::array<osg::ref_ptr<osg::Texture2D>, 2> mColorTexture;
|
||||
std::array<osg::ref_ptr<osg::Texture2DMultisample>, 2> mMsaaColorTexture;
|
||||
std::array<osg::ref_ptr<osg::Texture2D>, 2> mDepthTexture;
|
||||
std::array<osg::ref_ptr<osg::Texture2DMultisample>, 2> mMsaaDepthTexture;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
463
components/stereo/stereomanager.cpp
Normal file
463
components/stereo/stereomanager.cpp
Normal file
|
@ -0,0 +1,463 @@
|
|||
#include "stereomanager.hpp"
|
||||
#include "multiview.hpp"
|
||||
#include "frustum.hpp"
|
||||
|
||||
#include <osg/io_utils>
|
||||
#include <osg/Texture2D>
|
||||
#include <osg/Texture2DMultisample>
|
||||
#include <osg/Texture2DArray>
|
||||
#include <osg/DisplaySettings>
|
||||
|
||||
#include <osgUtil/CullVisitor>
|
||||
#include <osgUtil/RenderStage>
|
||||
|
||||
#include <osgViewer/Renderer>
|
||||
#include <osgViewer/Viewer>
|
||||
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include <components/debug/debuglog.hpp>
|
||||
|
||||
#include <components/sceneutil/statesetupdater.hpp>
|
||||
#include <components/sceneutil/visitor.hpp>
|
||||
#include <components/sceneutil/util.hpp>
|
||||
#include <components/sceneutil/depth.hpp>
|
||||
#include <components/sceneutil/color.hpp>
|
||||
|
||||
#include <components/settings/settings.hpp>
|
||||
|
||||
#include <components/misc/stringops.hpp>
|
||||
|
||||
namespace Stereo
|
||||
{
|
||||
// Update stereo view/projection during update
|
||||
class StereoUpdateCallback final : public osg::Callback
|
||||
{
|
||||
public:
|
||||
StereoUpdateCallback(Manager* stereoView) : stereoView(stereoView) {}
|
||||
|
||||
bool run(osg::Object* object, osg::Object* data) override
|
||||
{
|
||||
auto b = traverse(object, data);
|
||||
stereoView->update();
|
||||
return b;
|
||||
}
|
||||
|
||||
Manager* stereoView;
|
||||
};
|
||||
|
||||
// Update states during cull
|
||||
class BruteForceStereoStatesetUpdateCallback final : public SceneUtil::StateSetUpdater
|
||||
{
|
||||
public:
|
||||
BruteForceStereoStatesetUpdateCallback(Manager* manager)
|
||||
: mManager(manager)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void setDefaults(osg::StateSet* stateset) override
|
||||
{
|
||||
stateset->addUniform(new osg::Uniform("projectionMatrix", osg::Matrixf{}));
|
||||
}
|
||||
|
||||
virtual void apply(osg::StateSet* stateset, osg::NodeVisitor* /*nv*/) override
|
||||
{
|
||||
}
|
||||
|
||||
void applyLeft(osg::StateSet* stateset, osgUtil::CullVisitor* nv) override
|
||||
{
|
||||
osg::Matrix dummy;
|
||||
auto* uProjectionMatrix = stateset->getUniform("projectionMatrix");
|
||||
if (uProjectionMatrix)
|
||||
uProjectionMatrix->set(mManager->computeEyeProjection(0, SceneUtil::AutoDepth::isReversed()));
|
||||
}
|
||||
|
||||
void applyRight(osg::StateSet* stateset, osgUtil::CullVisitor* nv) override
|
||||
{
|
||||
osg::Matrix dummy;
|
||||
auto* uProjectionMatrix = stateset->getUniform("projectionMatrix");
|
||||
if (uProjectionMatrix)
|
||||
uProjectionMatrix->set(mManager->computeEyeProjection(1, SceneUtil::AutoDepth::isReversed()));
|
||||
}
|
||||
|
||||
private:
|
||||
Manager* mManager;
|
||||
};
|
||||
|
||||
// Update states during cull
|
||||
class MultiviewStereoStatesetUpdateCallback : public SceneUtil::StateSetUpdater
|
||||
{
|
||||
public:
|
||||
MultiviewStereoStatesetUpdateCallback(Manager* manager)
|
||||
: mManager(manager)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void setDefaults(osg::StateSet* stateset)
|
||||
{
|
||||
stateset->addUniform(new osg::Uniform(osg::Uniform::FLOAT_MAT4, "viewMatrixMultiView", 2));
|
||||
stateset->addUniform(new osg::Uniform(osg::Uniform::FLOAT_MAT4, "projectionMatrixMultiView", 2));
|
||||
}
|
||||
|
||||
virtual void apply(osg::StateSet* stateset, osg::NodeVisitor* /*nv*/)
|
||||
{
|
||||
mManager->updateMultiviewStateset(stateset);
|
||||
}
|
||||
|
||||
private:
|
||||
Manager* mManager;
|
||||
};
|
||||
|
||||
static Manager* sInstance = nullptr;
|
||||
|
||||
Manager& Manager::instance()
|
||||
{
|
||||
return *sInstance;
|
||||
}
|
||||
|
||||
struct CustomViewCallback : public Manager::UpdateViewCallback
|
||||
{
|
||||
public:
|
||||
CustomViewCallback();
|
||||
|
||||
void updateView(View& left, View& right) override;
|
||||
|
||||
private:
|
||||
View mLeft;
|
||||
View mRight;
|
||||
};
|
||||
|
||||
Manager::Manager(osgViewer::Viewer* viewer)
|
||||
: mViewer(viewer)
|
||||
, mMainCamera(mViewer->getCamera())
|
||||
, mUpdateCallback(new StereoUpdateCallback(this))
|
||||
, mMasterProjectionMatrix(osg::Matrix::identity())
|
||||
, mEyeResolutionOverriden(false)
|
||||
, mEyeResolutionOverride(0,0)
|
||||
, mFrustumManager(nullptr)
|
||||
, mUpdateViewCallback(nullptr)
|
||||
{
|
||||
if (sInstance)
|
||||
throw std::logic_error("Double instance of Stereo::Manager");
|
||||
sInstance = this;
|
||||
|
||||
if (Settings::Manager::getBool("use custom view", "Stereo"))
|
||||
mUpdateViewCallback = std::make_shared<CustomViewCallback>();
|
||||
|
||||
if (Settings::Manager::getBool("use custom eye resolution", "Stereo"))
|
||||
{
|
||||
osg::Vec2i eyeResolution = osg::Vec2i();
|
||||
eyeResolution.x() = Settings::Manager::getInt("eye resolution x", "Stereo View");
|
||||
eyeResolution.y() = Settings::Manager::getInt("eye resolution y", "Stereo View");
|
||||
overrideEyeResolution(eyeResolution);
|
||||
}
|
||||
}
|
||||
|
||||
Manager::~Manager()
|
||||
{
|
||||
}
|
||||
|
||||
void Manager::initializeStereo(osg::GraphicsContext* gc)
|
||||
{
|
||||
mMainCamera->addUpdateCallback(mUpdateCallback);
|
||||
mFrustumManager = std::make_unique<StereoFrustumManager>(mViewer->getCamera());
|
||||
|
||||
auto ci = gc->getState()->getContextID();
|
||||
configureExtensions(ci);
|
||||
|
||||
if(getMultiview())
|
||||
setupOVRMultiView2Technique();
|
||||
else
|
||||
setupBruteForceTechnique();
|
||||
|
||||
updateStereoFramebuffer();
|
||||
|
||||
}
|
||||
|
||||
void Manager::shaderStereoDefines(Shader::ShaderManager::DefineMap& defines) const
|
||||
{
|
||||
if (getMultiview())
|
||||
{
|
||||
defines["useOVR_multiview"] = "1";
|
||||
defines["numViews"] = "2";
|
||||
}
|
||||
else
|
||||
{
|
||||
defines["useOVR_multiview"] = "0";
|
||||
defines["numViews"] = "1";
|
||||
}
|
||||
}
|
||||
|
||||
void Manager::overrideEyeResolution(const osg::Vec2i& eyeResolution)
|
||||
{
|
||||
mEyeResolutionOverride = eyeResolution;
|
||||
mEyeResolutionOverriden = true;
|
||||
|
||||
if (mMultiviewFramebuffer)
|
||||
updateStereoFramebuffer();
|
||||
}
|
||||
|
||||
void Manager::screenResolutionChanged()
|
||||
{
|
||||
updateStereoFramebuffer();
|
||||
}
|
||||
|
||||
osg::Vec2i Manager::eyeResolution()
|
||||
{
|
||||
if (mEyeResolutionOverriden)
|
||||
return mEyeResolutionOverride;
|
||||
auto width = mMainCamera->getViewport()->width() / 2;
|
||||
auto height = mMainCamera->getViewport()->height();
|
||||
|
||||
return osg::Vec2i(width, height);
|
||||
}
|
||||
|
||||
void Manager::disableStereoForNode(osg::Node* node)
|
||||
{
|
||||
// Re-apply the main camera's full viewport to return to full screen rendering.
|
||||
node->getOrCreateStateSet()->setAttribute(mMainCamera->getViewport());
|
||||
}
|
||||
|
||||
void Manager::setShadowTechnique(SceneUtil::MWShadowTechnique* shadowTechnique)
|
||||
{
|
||||
if (mFrustumManager)
|
||||
mFrustumManager->setShadowTechnique(shadowTechnique);
|
||||
}
|
||||
|
||||
void Manager::setupBruteForceTechnique()
|
||||
{
|
||||
auto* ds = osg::DisplaySettings::instance().get();
|
||||
ds->setStereo(true);
|
||||
ds->setStereoMode(osg::DisplaySettings::StereoMode::HORIZONTAL_SPLIT);
|
||||
ds->setUseSceneViewForStereoHint(true);
|
||||
|
||||
mMainCamera->addCullCallback(new BruteForceStereoStatesetUpdateCallback(this));
|
||||
|
||||
struct ComputeStereoMatricesCallback : public osgUtil::SceneView::ComputeStereoMatricesCallback
|
||||
{
|
||||
ComputeStereoMatricesCallback(Manager* sv)
|
||||
: mManager(sv)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
osg::Matrixd computeLeftEyeProjection(const osg::Matrixd& projection) const override
|
||||
{
|
||||
(void)projection;
|
||||
return mManager->computeEyeProjection(0, false);
|
||||
}
|
||||
|
||||
osg::Matrixd computeLeftEyeView(const osg::Matrixd& view) const override
|
||||
{
|
||||
(void)view;
|
||||
return mManager->computeEyeView(0);
|
||||
}
|
||||
|
||||
osg::Matrixd computeRightEyeProjection(const osg::Matrixd& projection) const override
|
||||
{
|
||||
(void)projection;
|
||||
return mManager->computeEyeProjection(1, false);
|
||||
}
|
||||
|
||||
osg::Matrixd computeRightEyeView(const osg::Matrixd& view) const override
|
||||
{
|
||||
(void)view;
|
||||
return mManager->computeEyeView(1);
|
||||
}
|
||||
|
||||
Manager* mManager;
|
||||
};
|
||||
|
||||
auto* renderer = static_cast<osgViewer::Renderer*>(mMainCamera->getRenderer());
|
||||
for (auto* sceneView : { renderer->getSceneView(0), renderer->getSceneView(1) })
|
||||
{
|
||||
sceneView->setComputeStereoMatricesCallback(new ComputeStereoMatricesCallback(this));
|
||||
|
||||
auto* cvMain = sceneView->getCullVisitor();
|
||||
auto* cvLeft = sceneView->getCullVisitorLeft();
|
||||
auto* cvRight = sceneView->getCullVisitorRight();
|
||||
if (!cvMain)
|
||||
sceneView->setCullVisitor(cvMain = new osgUtil::CullVisitor());
|
||||
if (!cvLeft)
|
||||
sceneView->setCullVisitor(cvLeft = cvMain->clone());
|
||||
if (!cvRight)
|
||||
sceneView->setCullVisitor(cvRight = cvMain->clone());
|
||||
|
||||
// Osg by default gives cullVisitorLeft and cullVisitor the same identifier.
|
||||
// So we make our own to avoid confusion
|
||||
cvMain->setIdentifier(mIdentifierMain);
|
||||
cvLeft->setIdentifier(mIdentifierLeft);
|
||||
cvRight->setIdentifier(mIdentifierRight);
|
||||
}
|
||||
}
|
||||
|
||||
void Manager::setupOVRMultiView2Technique()
|
||||
{
|
||||
auto* ds = osg::DisplaySettings::instance().get();
|
||||
ds->setStereo(false);
|
||||
|
||||
mMainCamera->addCullCallback(new MultiviewStereoStatesetUpdateCallback(this));
|
||||
}
|
||||
|
||||
void Manager::updateStereoFramebuffer()
|
||||
{
|
||||
auto samples = Settings::Manager::getInt("antialiasing", "Video");
|
||||
auto eyeRes = eyeResolution();
|
||||
|
||||
if (mMultiviewFramebuffer)
|
||||
mMultiviewFramebuffer->detachFrom(mMainCamera);
|
||||
mMultiviewFramebuffer = std::make_shared<MultiviewFramebuffer>(static_cast<int>(eyeRes.x()), static_cast<int>(eyeRes.y()), samples);
|
||||
mMultiviewFramebuffer->attachColorComponent(SceneUtil::Color::colorSourceFormat(), SceneUtil::Color::colorSourceType(), SceneUtil::Color::colorInternalFormat());
|
||||
mMultiviewFramebuffer->attachDepthComponent(SceneUtil::AutoDepth::depthSourceFormat(), SceneUtil::AutoDepth::depthSourceType(), SceneUtil::AutoDepth::depthInternalFormat());
|
||||
mMultiviewFramebuffer->attachTo(mMainCamera);
|
||||
}
|
||||
|
||||
void Manager::update()
|
||||
{
|
||||
double near_ = 1.f;
|
||||
double far_ = 10000.f;
|
||||
|
||||
near_ = Settings::Manager::getFloat("near clip", "Camera");
|
||||
far_ = Settings::Manager::getFloat("viewing distance", "Camera");
|
||||
auto projectionMatrix = mMainCamera->getProjectionMatrix();
|
||||
|
||||
if (mUpdateViewCallback)
|
||||
{
|
||||
mUpdateViewCallback->updateView(mView[0], mView[1]);
|
||||
auto viewMatrix = mMainCamera->getViewMatrix();
|
||||
mViewOffsetMatrix[0] = mView[0].viewMatrix(true);
|
||||
mViewOffsetMatrix[1] = mView[1].viewMatrix(true);
|
||||
mViewMatrix[0] = viewMatrix * mViewOffsetMatrix[0];
|
||||
mViewMatrix[1] = viewMatrix * mViewOffsetMatrix[1];
|
||||
mProjectionMatrix[0] = mView[0].perspectiveMatrix(near_, far_, false);
|
||||
mProjectionMatrix[1] = mView[1].perspectiveMatrix(near_, far_, false);
|
||||
if (SceneUtil::AutoDepth::isReversed())
|
||||
{
|
||||
mProjectionMatrixReverseZ[0] = mView[0].perspectiveMatrix(near_, far_, true);
|
||||
mProjectionMatrixReverseZ[1] = mView[1].perspectiveMatrix(near_, far_, true);
|
||||
}
|
||||
|
||||
View masterView;
|
||||
masterView.fov.angleDown = std::min(mView[0].fov.angleDown, mView[1].fov.angleDown);
|
||||
masterView.fov.angleUp = std::max(mView[0].fov.angleUp, mView[1].fov.angleUp);
|
||||
masterView.fov.angleLeft = std::min(mView[0].fov.angleLeft, mView[1].fov.angleLeft);
|
||||
masterView.fov.angleRight = std::max(mView[0].fov.angleRight, mView[1].fov.angleRight);
|
||||
projectionMatrix = masterView.perspectiveMatrix(near_, far_, false);
|
||||
mMainCamera->setProjectionMatrix(projectionMatrix);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto* ds = osg::DisplaySettings::instance().get();
|
||||
auto viewMatrix = mMainCamera->getViewMatrix();
|
||||
mViewMatrix[0] = ds->computeLeftEyeViewImplementation(viewMatrix);
|
||||
mViewMatrix[1] = ds->computeRightEyeViewImplementation(viewMatrix);
|
||||
mViewOffsetMatrix[0] = osg::Matrix::inverse(viewMatrix) * mViewMatrix[0];
|
||||
mViewOffsetMatrix[1] = osg::Matrix::inverse(viewMatrix) * mViewMatrix[1];
|
||||
mProjectionMatrix[0] = ds->computeLeftEyeProjectionImplementation(projectionMatrix);
|
||||
mProjectionMatrix[1] = ds->computeRightEyeProjectionImplementation(projectionMatrix);
|
||||
if (SceneUtil::AutoDepth::isReversed())
|
||||
{
|
||||
mProjectionMatrixReverseZ[0] = ds->computeLeftEyeProjectionImplementation(mMasterProjectionMatrix);
|
||||
mProjectionMatrixReverseZ[1] = ds->computeRightEyeProjectionImplementation(mMasterProjectionMatrix);
|
||||
}
|
||||
}
|
||||
|
||||
mFrustumManager->update(
|
||||
{
|
||||
mViewOffsetMatrix[0] * mProjectionMatrix[0],
|
||||
mViewOffsetMatrix[1] * mProjectionMatrix[1]
|
||||
});
|
||||
}
|
||||
|
||||
void Manager::updateMultiviewStateset(osg::StateSet* stateset)
|
||||
{
|
||||
// Update stereo uniforms
|
||||
auto * viewMatrixMultiViewUniform = stateset->getUniform("viewMatrixMultiView");
|
||||
auto * projectionMatrixMultiViewUniform = stateset->getUniform("projectionMatrixMultiView");
|
||||
|
||||
for (int view : {0, 1})
|
||||
{
|
||||
viewMatrixMultiViewUniform->setElement(view, mViewOffsetMatrix[view]);
|
||||
projectionMatrixMultiViewUniform->setElement(view, computeEyeProjection(view, SceneUtil::AutoDepth::isReversed()));
|
||||
}
|
||||
}
|
||||
|
||||
void Manager::setUpdateViewCallback(std::shared_ptr<UpdateViewCallback> cb)
|
||||
{
|
||||
mUpdateViewCallback = cb;
|
||||
}
|
||||
|
||||
void Manager::setCullCallback(osg::ref_ptr<osg::NodeCallback> cb)
|
||||
{
|
||||
mMainCamera->setCullCallback(cb);
|
||||
}
|
||||
|
||||
osg::Matrixd Manager::computeEyeProjection(int view, bool reverseZ) const
|
||||
{
|
||||
return reverseZ ? mProjectionMatrixReverseZ[view] : mProjectionMatrix[view];
|
||||
}
|
||||
|
||||
osg::Matrixd Manager::computeEyeView(int view) const
|
||||
{
|
||||
return mViewMatrix[view];
|
||||
}
|
||||
|
||||
osg::Matrixd Manager::computeEyeViewOffset(int view) const
|
||||
{
|
||||
return mViewOffsetMatrix[view];
|
||||
}
|
||||
|
||||
Eye Manager::getEye(const osgUtil::CullVisitor* cv) const
|
||||
{
|
||||
if (cv->getIdentifier() == mIdentifierMain)
|
||||
return Eye::Center;
|
||||
if (cv->getIdentifier() == mIdentifierLeft)
|
||||
return Eye::Left;
|
||||
if (cv->getIdentifier() == mIdentifierRight)
|
||||
return Eye::Right;
|
||||
return Eye::Center;
|
||||
}
|
||||
|
||||
bool getStereo()
|
||||
{
|
||||
static bool stereo = Settings::Manager::getBool("stereo enabled", "Stereo") || osg::DisplaySettings::instance().get()->getStereo();
|
||||
return stereo;
|
||||
}
|
||||
|
||||
CustomViewCallback::CustomViewCallback()
|
||||
{
|
||||
mLeft.pose.position.x() = Settings::Manager::getDouble("left eye offset x", "Stereo View");
|
||||
mLeft.pose.position.y() = Settings::Manager::getDouble("left eye offset y", "Stereo View");
|
||||
mLeft.pose.position.z() = Settings::Manager::getDouble("left eye offset z", "Stereo View");
|
||||
mLeft.pose.orientation.x() = Settings::Manager::getDouble("left eye orientation x", "Stereo View");
|
||||
mLeft.pose.orientation.y() = Settings::Manager::getDouble("left eye orientation y", "Stereo View");
|
||||
mLeft.pose.orientation.z() = Settings::Manager::getDouble("left eye orientation z", "Stereo View");
|
||||
mLeft.pose.orientation.w() = Settings::Manager::getDouble("left eye orientation w", "Stereo View");
|
||||
mLeft.fov.angleLeft = Settings::Manager::getDouble("left eye fov left", "Stereo View");
|
||||
mLeft.fov.angleRight = Settings::Manager::getDouble("left eye fov right", "Stereo View");
|
||||
mLeft.fov.angleUp = Settings::Manager::getDouble("left eye fov up", "Stereo View");
|
||||
mLeft.fov.angleDown = Settings::Manager::getDouble("left eye fov down", "Stereo View");
|
||||
|
||||
mRight.pose.position.x() = Settings::Manager::getDouble("right eye offset x", "Stereo View");
|
||||
mRight.pose.position.y() = Settings::Manager::getDouble("right eye offset y", "Stereo View");
|
||||
mRight.pose.position.z() = Settings::Manager::getDouble("right eye offset z", "Stereo View");
|
||||
mRight.pose.orientation.x() = Settings::Manager::getDouble("right eye orientation x", "Stereo View");
|
||||
mRight.pose.orientation.y() = Settings::Manager::getDouble("right eye orientation y", "Stereo View");
|
||||
mRight.pose.orientation.z() = Settings::Manager::getDouble("right eye orientation z", "Stereo View");
|
||||
mRight.pose.orientation.w() = Settings::Manager::getDouble("right eye orientation w", "Stereo View");
|
||||
mRight.fov.angleLeft = Settings::Manager::getDouble("right eye fov left", "Stereo View");
|
||||
mRight.fov.angleRight = Settings::Manager::getDouble("right eye fov right", "Stereo View");
|
||||
mRight.fov.angleUp = Settings::Manager::getDouble("right eye fov up", "Stereo View");
|
||||
mRight.fov.angleDown = Settings::Manager::getDouble("right eye fov down", "Stereo View");
|
||||
}
|
||||
|
||||
void CustomViewCallback::updateView(View& left, View& right)
|
||||
{
|
||||
left = mLeft;
|
||||
right = mRight;
|
||||
}
|
||||
}
|
135
components/stereo/stereomanager.hpp
Normal file
135
components/stereo/stereomanager.hpp
Normal file
|
@ -0,0 +1,135 @@
|
|||
#ifndef STEREO_MANAGER_H
|
||||
#define STEREO_MANAGER_H
|
||||
|
||||
#include <osg/Matrix>
|
||||
#include <osg/Vec3>
|
||||
#include <osg/Camera>
|
||||
#include <osg/StateSet>
|
||||
#include <osgUtil/CullVisitor>
|
||||
|
||||
#include <memory>
|
||||
#include <array>
|
||||
|
||||
#include <components/stereo/types.hpp>
|
||||
#include <components/shader/shadermanager.hpp>
|
||||
|
||||
namespace osg
|
||||
{
|
||||
class FrameBufferObject;
|
||||
class Texture2D;
|
||||
class Texture2DMultisample;
|
||||
class Texture2DArray;
|
||||
}
|
||||
|
||||
namespace osgViewer
|
||||
{
|
||||
class Viewer;
|
||||
}
|
||||
|
||||
namespace SceneUtil
|
||||
{
|
||||
class MWShadowTechnique;
|
||||
}
|
||||
|
||||
namespace Stereo
|
||||
{
|
||||
class MultiviewFramebuffer;
|
||||
class StereoFrustumManager;
|
||||
class MultiviewStereoStatesetUpdateCallback;
|
||||
|
||||
bool getStereo();
|
||||
|
||||
//! Class that provides tools for managing stereo mode
|
||||
class Manager
|
||||
{
|
||||
public:
|
||||
struct UpdateViewCallback
|
||||
{
|
||||
virtual ~UpdateViewCallback() = default;
|
||||
|
||||
//! Called during the update traversal of every frame to update stereo views.
|
||||
virtual void updateView(View& left, View& right) = 0;
|
||||
};
|
||||
|
||||
//! Gets the singleton instance
|
||||
static Manager& instance();
|
||||
|
||||
Manager(osgViewer::Viewer* viewer);
|
||||
~Manager();
|
||||
|
||||
//! Called during update traversal
|
||||
void update();
|
||||
|
||||
void initializeStereo(osg::GraphicsContext* gc);
|
||||
|
||||
//! Callback that updates stereo configuration during the update pass
|
||||
void setUpdateViewCallback(std::shared_ptr<UpdateViewCallback> cb);
|
||||
|
||||
//! Set the cull callback on the appropriate camera object
|
||||
void setCullCallback(osg::ref_ptr<osg::NodeCallback> cb);
|
||||
|
||||
osg::Matrixd computeEyeProjection(int view, bool reverseZ) const;
|
||||
osg::Matrixd computeEyeView(int view) const;
|
||||
osg::Matrixd computeEyeViewOffset(int view) const;
|
||||
|
||||
//! Sets up any definitions necessary for stereo rendering
|
||||
void shaderStereoDefines(Shader::ShaderManager::DefineMap& defines) const;
|
||||
|
||||
const std::shared_ptr<MultiviewFramebuffer>& multiviewFramebuffer() { return mMultiviewFramebuffer; };
|
||||
|
||||
//! Sets rendering resolution of each eye to eyeResolution.
|
||||
//! Once set, there will no longer be any connection between rendering resolution and screen/window resolution.
|
||||
void overrideEyeResolution(const osg::Vec2i& eyeResolution);
|
||||
|
||||
//! Notify stereo manager that the screen/window resolution has changed.
|
||||
void screenResolutionChanged();
|
||||
|
||||
//! Get current eye resolution
|
||||
osg::Vec2i eyeResolution();
|
||||
|
||||
//! The projection intended for rendering. When reverse Z is enabled, this is not the same as the camera's projection matrix,
|
||||
//! and therefore must be provided to the manager explicitly.
|
||||
void setMasterProjectionMatrix(const osg::Matrix& projectionMatrix) { mMasterProjectionMatrix = projectionMatrix; }
|
||||
|
||||
//! Causes the subgraph represented by the node to draw to the full viewport.
|
||||
//! This has no effect if stereo is not enabled
|
||||
void disableStereoForNode(osg::Node* node);
|
||||
|
||||
void setShadowTechnique(SceneUtil::MWShadowTechnique* shadowTechnique);
|
||||
|
||||
/// Determine which view the cull visitor belongs to
|
||||
Eye getEye(const osgUtil::CullVisitor* cv) const;
|
||||
|
||||
private:
|
||||
friend class MultiviewStereoStatesetUpdateCallback;
|
||||
void updateMultiviewStateset(osg::StateSet* stateset);
|
||||
void updateStereoFramebuffer();
|
||||
void setupBruteForceTechnique();
|
||||
void setupOVRMultiView2Technique();
|
||||
|
||||
osg::ref_ptr<osgViewer::Viewer> mViewer;
|
||||
osg::ref_ptr<osg::Camera> mMainCamera;
|
||||
osg::ref_ptr<osg::Callback> mUpdateCallback;
|
||||
std::string mError;
|
||||
osg::Matrix mMasterProjectionMatrix;
|
||||
std::shared_ptr<MultiviewFramebuffer> mMultiviewFramebuffer;
|
||||
bool mEyeResolutionOverriden;
|
||||
osg::Vec2i mEyeResolutionOverride;
|
||||
|
||||
std::array<View, 2> mView;
|
||||
std::array<osg::Matrix, 2> mViewMatrix;
|
||||
std::array<osg::Matrix, 2> mViewOffsetMatrix;
|
||||
std::array<osg::Matrix, 2> mProjectionMatrix;
|
||||
std::array<osg::Matrix, 2> mProjectionMatrixReverseZ;
|
||||
|
||||
std::unique_ptr<StereoFrustumManager> mFrustumManager;
|
||||
std::shared_ptr<UpdateViewCallback> mUpdateViewCallback;
|
||||
|
||||
using Identifier = osgUtil::CullVisitor::Identifier;
|
||||
osg::ref_ptr<Identifier> mIdentifierMain = new Identifier();
|
||||
osg::ref_ptr<Identifier> mIdentifierLeft = new Identifier();
|
||||
osg::ref_ptr<Identifier> mIdentifierRight = new Identifier();
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
168
components/stereo/types.cpp
Normal file
168
components/stereo/types.cpp
Normal file
|
@ -0,0 +1,168 @@
|
|||
#include "types.hpp"
|
||||
|
||||
#include <osg/io_utils>
|
||||
|
||||
namespace Stereo
|
||||
{
|
||||
|
||||
Pose Pose::operator+(const Pose& rhs)
|
||||
{
|
||||
Pose pose = *this;
|
||||
pose.position += this->orientation * rhs.position;
|
||||
pose.orientation = rhs.orientation * this->orientation;
|
||||
return pose;
|
||||
}
|
||||
|
||||
const Pose& Pose::operator+=(const Pose& rhs)
|
||||
{
|
||||
*this = *this + rhs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Pose Pose::operator*(float scalar)
|
||||
{
|
||||
Pose pose = *this;
|
||||
pose.position *= scalar;
|
||||
return pose;
|
||||
}
|
||||
|
||||
const Pose& Pose::operator*=(float scalar)
|
||||
{
|
||||
*this = *this * scalar;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Pose Pose::operator/(float scalar)
|
||||
{
|
||||
Pose pose = *this;
|
||||
pose.position /= scalar;
|
||||
return pose;
|
||||
}
|
||||
const Pose& Pose::operator/=(float scalar)
|
||||
{
|
||||
*this = *this / scalar;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool Pose::operator==(const Pose& rhs) const
|
||||
{
|
||||
return position == rhs.position && orientation == rhs.orientation;
|
||||
}
|
||||
|
||||
osg::Matrix View::viewMatrix(bool useGLConventions)
|
||||
{
|
||||
auto position = pose.position;
|
||||
auto orientation = pose.orientation;
|
||||
|
||||
if (useGLConventions)
|
||||
{
|
||||
// When applied as an offset to an existing view matrix,
|
||||
// that view matrix will already convert points to a camera space
|
||||
// with opengl conventions. So we need to convert offsets to opengl
|
||||
// conventions.
|
||||
float y = position.y();
|
||||
float z = position.z();
|
||||
position.y() = z;
|
||||
position.z() = -y;
|
||||
|
||||
y = orientation.y();
|
||||
z = orientation.z();
|
||||
orientation.y() = z;
|
||||
orientation.z() = -y;
|
||||
|
||||
osg::Matrix viewMatrix;
|
||||
viewMatrix.setTrans(-position);
|
||||
viewMatrix.postMultRotate(orientation.conj());
|
||||
return viewMatrix;
|
||||
}
|
||||
else
|
||||
{
|
||||
osg::Vec3d forward = orientation * osg::Vec3d(0, 1, 0);
|
||||
osg::Vec3d up = orientation * osg::Vec3d(0, 0, 1);
|
||||
osg::Matrix viewMatrix;
|
||||
viewMatrix.makeLookAt(position, position + forward, up);
|
||||
|
||||
return viewMatrix;
|
||||
}
|
||||
}
|
||||
|
||||
osg::Matrix View::perspectiveMatrix(float near, float far, bool reverseZ)
|
||||
{
|
||||
const float tanLeft = tanf(fov.angleLeft);
|
||||
const float tanRight = tanf(fov.angleRight);
|
||||
const float tanDown = tanf(fov.angleDown);
|
||||
const float tanUp = tanf(fov.angleUp);
|
||||
|
||||
const float tanWidth = tanRight - tanLeft;
|
||||
const float tanHeight = tanUp - tanDown;
|
||||
|
||||
float matrix[16] = {};
|
||||
|
||||
matrix[0] = 2 / tanWidth;
|
||||
matrix[4] = 0;
|
||||
matrix[8] = (tanRight + tanLeft) / tanWidth;
|
||||
matrix[12] = 0;
|
||||
|
||||
matrix[1] = 0;
|
||||
matrix[5] = 2 / tanHeight;
|
||||
matrix[9] = (tanUp + tanDown) / tanHeight;
|
||||
matrix[13] = 0;
|
||||
|
||||
if (reverseZ) {
|
||||
matrix[2] = 0;
|
||||
matrix[6] = 0;
|
||||
matrix[10] = (2.f * near) / (far - near);
|
||||
matrix[14] = ((2.f * near) * far) / (far - near);
|
||||
}
|
||||
else {
|
||||
matrix[2] = 0;
|
||||
matrix[6] = 0;
|
||||
matrix[10] = -(far + near) / (far - near);
|
||||
matrix[14] = -(far * (2.f * near)) / (far - near);
|
||||
}
|
||||
|
||||
matrix[3] = 0;
|
||||
matrix[7] = 0;
|
||||
matrix[11] = -1;
|
||||
matrix[15] = 0;
|
||||
|
||||
return osg::Matrix(matrix);
|
||||
}
|
||||
|
||||
bool FieldOfView::operator==(const FieldOfView& rhs) const
|
||||
{
|
||||
return angleDown == rhs.angleDown
|
||||
&& angleUp == rhs.angleUp
|
||||
&& angleLeft == rhs.angleLeft
|
||||
&& angleRight == rhs.angleRight;
|
||||
}
|
||||
|
||||
bool View::operator==(const View& rhs) const
|
||||
{
|
||||
return pose == rhs.pose && fov == rhs.fov;
|
||||
}
|
||||
|
||||
std::ostream& operator <<(
|
||||
std::ostream& os,
|
||||
const Pose& pose)
|
||||
{
|
||||
os << "position=" << pose.position << ", orientation=" << pose.orientation;
|
||||
return os;
|
||||
}
|
||||
|
||||
std::ostream& operator <<(
|
||||
std::ostream& os,
|
||||
const FieldOfView& fov)
|
||||
{
|
||||
os << "left=" << fov.angleLeft << ", right=" << fov.angleRight << ", down=" << fov.angleDown << ", up=" << fov.angleUp;
|
||||
return os;
|
||||
}
|
||||
|
||||
std::ostream& operator <<(
|
||||
std::ostream& os,
|
||||
const View& view)
|
||||
{
|
||||
os << "pose=< " << view.pose << " >, fov=< " << view.fov << " >";
|
||||
return os;
|
||||
}
|
||||
}
|
61
components/stereo/types.hpp
Normal file
61
components/stereo/types.hpp
Normal file
|
@ -0,0 +1,61 @@
|
|||
#ifndef STEREO_TYPES_H
|
||||
#define STEREO_TYPES_H
|
||||
|
||||
#include <osg/Matrix>
|
||||
#include <osg/Vec3>
|
||||
|
||||
|
||||
namespace Stereo
|
||||
{
|
||||
enum class Eye
|
||||
{
|
||||
Left = 0,
|
||||
Right = 1,
|
||||
Center = 2
|
||||
};
|
||||
|
||||
struct Pose
|
||||
{
|
||||
//! Position in space
|
||||
osg::Vec3 position{ 0,0,0 };
|
||||
//! Orientation in space.
|
||||
osg::Quat orientation{ 0,0,0,1 };
|
||||
|
||||
//! Add one pose to another
|
||||
Pose operator+(const Pose& rhs);
|
||||
const Pose& operator+=(const Pose& rhs);
|
||||
|
||||
//! Scale a pose (does not affect orientation)
|
||||
Pose operator*(float scalar);
|
||||
const Pose& operator*=(float scalar);
|
||||
Pose operator/(float scalar);
|
||||
const Pose& operator/=(float scalar);
|
||||
|
||||
bool operator==(const Pose& rhs) const;
|
||||
};
|
||||
|
||||
struct FieldOfView {
|
||||
float angleLeft{ 0.f };
|
||||
float angleRight{ 0.f };
|
||||
float angleUp{ 0.f };
|
||||
float angleDown{ 0.f };
|
||||
|
||||
bool operator==(const FieldOfView& rhs) const;
|
||||
};
|
||||
|
||||
struct View
|
||||
{
|
||||
Pose pose;
|
||||
FieldOfView fov;
|
||||
bool operator==(const View& rhs) const;
|
||||
|
||||
osg::Matrix viewMatrix(bool useGLConventions);
|
||||
osg::Matrix perspectiveMatrix(float near, float far, bool reverseZ);
|
||||
};
|
||||
|
||||
std::ostream& operator <<(std::ostream& os, const Pose& pose);
|
||||
std::ostream& operator <<(std::ostream& os, const FieldOfView& fov);
|
||||
std::ostream& operator <<(std::ostream& os, const View& view);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -7,6 +7,7 @@
|
|||
#include <osg/TexMat>
|
||||
#include <osg/BlendFunc>
|
||||
|
||||
#include <components/stereo/stereomanager.hpp>
|
||||
#include <components/shader/shadermanager.hpp>
|
||||
#include <components/sceneutil/depth.hpp>
|
||||
|
||||
|
@ -251,6 +252,8 @@ namespace Terrain
|
|||
defineMap["specularMap"] = it->mSpecular ? "1" : "0";
|
||||
defineMap["parallax"] = (it->mNormalMap && it->mParallax) ? "1" : "0";
|
||||
|
||||
Stereo::Manager::instance().shaderStereoDefines(defineMap);
|
||||
|
||||
osg::ref_ptr<osg::Shader> vertexShader = shaderManager->getShader("terrain_vertex.glsl", defineMap, osg::Shader::VERTEX);
|
||||
osg::ref_ptr<osg::Shader> fragmentShader = shaderManager->getShader("terrain_fragment.glsl", defineMap, osg::Shader::FRAGMENT);
|
||||
if (!vertexShader || !fragmentShader)
|
||||
|
|
|
@ -1140,3 +1140,60 @@ lua debug = false
|
|||
# Set the maximum number of threads used for Lua scripts.
|
||||
# If zero, Lua scripts are processed in the main thread.
|
||||
lua num threads = 1
|
||||
|
||||
[Stereo]
|
||||
# Enable/disable stereo view. This setting is ignored in VR.
|
||||
stereo enabled = false
|
||||
|
||||
# If enabled, OpenMW will use the GL_OVR_MultiView and GL_OVR_MultiView2 extensions where possible.
|
||||
multiview = false
|
||||
|
||||
# May accelerate the BruteForce method when shadows are enabled
|
||||
shared shadow maps = true
|
||||
|
||||
# If false, OpenMW-VR will disable display lists when using multiview. Necessary on some buggy drivers, but may incur a slight performance penalty.
|
||||
allow display lists for multiview = false
|
||||
|
||||
# If false, the default OSG horizontal split will be used for stereo
|
||||
# If true, the config defined in the [Stereo View] settings category will be used
|
||||
# Note: This option is ignored in VR, and exists primarily for debugging purposes
|
||||
use custom view = false
|
||||
|
||||
# If true, overrides rendering resolution for each eye.
|
||||
# Note: This option is ignored in VR, and exists primarily for debugging purposes
|
||||
use custom eye resolution = false
|
||||
|
||||
[Stereo View]
|
||||
# The default values are based on an HP Reverb G2 HMD
|
||||
eye resolution x = 3128
|
||||
eye resolution y = 3060
|
||||
|
||||
# Left eye offset from center, expressed in MW units (1 meter = ~70)
|
||||
left eye offset x = -2.35
|
||||
left eye offset y = 0.0
|
||||
left eye offset z = 0.0
|
||||
# Left eye orientation, expressed as a quaternion
|
||||
left eye orientation x = 0.0
|
||||
left eye orientation y = 0.0
|
||||
left eye orientation z = 0.0
|
||||
left eye orientation w = 1.0
|
||||
# Left eye field of view, expressed in radians
|
||||
left eye fov left = -0.86
|
||||
left eye fov right = 0.78
|
||||
left eye fov up = 0.8
|
||||
left eye fov down = -0.8
|
||||
|
||||
# Left eye offset from center, expressed in MW units (1 meter = ~70)
|
||||
right eye offset x = 2.35
|
||||
right eye offset y = 0.0
|
||||
right eye offset z = 0.0
|
||||
# Left eye orientation, expressed as a quaternion
|
||||
right eye orientation x = 0.0
|
||||
right eye orientation y = 0.0
|
||||
right eye orientation z = 0.0
|
||||
right eye orientation w = 1.0
|
||||
# Left eye field of view
|
||||
right eye fov left = -0.78
|
||||
right eye fov right = 0.86
|
||||
right eye fov up = 0.8
|
||||
right eye fov down = -0.8
|
|
@ -18,8 +18,10 @@ set(SHADER_FILES
|
|||
objects_fragment.glsl
|
||||
openmw_fragment.glsl
|
||||
openmw_fragment.h.glsl
|
||||
openmw_fragment_multiview.glsl
|
||||
openmw_vertex.glsl
|
||||
openmw_vertex.h.glsl
|
||||
openmw_vertex_multiview.glsl
|
||||
terrain_vertex.glsl
|
||||
terrain_fragment.glsl
|
||||
lighting.glsl
|
||||
|
|
|
@ -144,7 +144,7 @@ void main(void)
|
|||
if (length(gl_ModelViewMatrix * vec4(position, 1.0)) > @groundcoverFadeEnd)
|
||||
gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
|
||||
else
|
||||
gl_Position = mw_viewToClip(mw_viewStereoAdjust(viewPos));
|
||||
gl_Position = mw_viewToClip(viewPos);
|
||||
|
||||
linearDepth = getLinearDepth(gl_Position.z, viewPos.z);
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
#endif
|
||||
|
||||
#include "openmw_vertex.h.glsl"
|
||||
|
||||
#if @diffuseMap
|
||||
varying vec2 diffuseMapUV;
|
||||
#endif
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
#endif
|
||||
|
||||
#include "openmw_vertex.h.glsl"
|
||||
|
||||
#if @diffuseMap
|
||||
varying vec2 diffuseMapUV;
|
||||
#endif
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
@link "openmw_fragment.glsl"
|
||||
@link "openmw_fragment.glsl" if !@useOVR_multiview
|
||||
@link "openmw_fragment_multiview.glsl" if @useOVR_multiview
|
||||
|
||||
vec4 mw_sampleReflectionMap(vec2 uv);
|
||||
|
||||
|
|
31
files/shaders/openmw_fragment_multiview.glsl
Normal file
31
files/shaders/openmw_fragment_multiview.glsl
Normal file
|
@ -0,0 +1,31 @@
|
|||
#version 330 compatibility
|
||||
|
||||
#extension GL_OVR_multiview : require
|
||||
#extension GL_OVR_multiview2 : require
|
||||
#extension GL_EXT_texture_array : require
|
||||
|
||||
#include "openmw_fragment.h.glsl"
|
||||
|
||||
uniform sampler2DArray reflectionMap;
|
||||
|
||||
vec4 mw_sampleReflectionMap(vec2 uv)
|
||||
{
|
||||
return texture2DArray(reflectionMap, vec3((uv), gl_ViewID_OVR));
|
||||
}
|
||||
|
||||
#if @refraction_enabled
|
||||
|
||||
uniform sampler2DArray refractionMap;
|
||||
uniform sampler2DArray refractionDepthMap;
|
||||
|
||||
vec4 mw_sampleRefractionMap(vec2 uv)
|
||||
{
|
||||
return texture2DArray(refractionMap, vec3((uv), gl_ViewID_OVR));
|
||||
}
|
||||
|
||||
float mw_sampleRefractionDepthMap(vec2 uv)
|
||||
{
|
||||
return texture2DArray(refractionDepthMap, vec3((uv), gl_ViewID_OVR)).x;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -23,13 +23,3 @@ vec4 mw_viewStereoAdjust(vec4 pos)
|
|||
{
|
||||
return pos;
|
||||
}
|
||||
|
||||
mat4 mw_viewMatrix()
|
||||
{
|
||||
return gl_ModelViewMatrix;
|
||||
}
|
||||
|
||||
mat4 mw_projectionMatrix()
|
||||
{
|
||||
return projectionMatrix;
|
||||
}
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
@link "openmw_vertex.glsl"
|
||||
@link "openmw_vertex.glsl" if !@useOVR_multiview
|
||||
@link "openmw_vertex_multiview.glsl" if @useOVR_multiview
|
||||
|
||||
vec4 mw_modelToClip(vec4 pos);
|
||||
vec4 mw_modelToView(vec4 pos);
|
||||
vec4 mw_viewToClip(vec4 pos);
|
||||
vec4 mw_viewStereoAdjust(vec4 pos);
|
||||
mat4 mw_viewMatrix();
|
||||
mat4 mw_projectionMatrix();
|
||||
vec4 mw_viewStereoAdjust(vec4 pos);
|
31
files/shaders/openmw_vertex_multiview.glsl
Normal file
31
files/shaders/openmw_vertex_multiview.glsl
Normal file
|
@ -0,0 +1,31 @@
|
|||
#version 330 compatibility
|
||||
|
||||
#extension GL_OVR_multiview : require
|
||||
#extension GL_OVR_multiview2 : require
|
||||
|
||||
layout(num_views = @numViews) in;
|
||||
|
||||
#include "openmw_vertex.h.glsl"
|
||||
|
||||
uniform mat4 projectionMatrixMultiView[@numViews];
|
||||
uniform mat4 viewMatrixMultiView[@numViews];
|
||||
|
||||
vec4 mw_modelToClip(vec4 pos)
|
||||
{
|
||||
return projectionMatrixMultiView[gl_ViewID_OVR] * mw_modelToView(pos);
|
||||
}
|
||||
|
||||
vec4 mw_modelToView(vec4 pos)
|
||||
{
|
||||
return viewMatrixMultiView[gl_ViewID_OVR] * gl_ModelViewMatrix * pos;
|
||||
}
|
||||
|
||||
vec4 mw_viewToClip(vec4 pos)
|
||||
{
|
||||
return projectionMatrixMultiView[gl_ViewID_OVR] * viewMatrixMultiView[gl_ViewID_OVR] * pos;
|
||||
}
|
||||
|
||||
vec4 mw_viewStereoAdjust(vec4 pos)
|
||||
{
|
||||
return viewMatrixMultiView[gl_ViewID_OVR] * pos;
|
||||
}
|
|
@ -9,7 +9,6 @@
|
|||
#endif
|
||||
|
||||
#include "openmw_vertex.h.glsl"
|
||||
|
||||
varying vec2 uv;
|
||||
varying float euclideanDepth;
|
||||
varying float linearDepth;
|
||||
|
|
Loading…
Reference in a new issue