mirror of
https://github.com/OpenMW/openmw.git
synced 2025-04-02 22: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_BASEDIR = Get-Location
|
||||||
- $env:CCACHE_DIR = "$(Get-Location)\ccache"
|
- $env:CCACHE_DIR = "$(Get-Location)\ccache"
|
||||||
- New-Item -Type Directory -Force -Path $env:CCACHE_DIR
|
- 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
|
- cd MSVC2019_64_Ninja
|
||||||
- .\ActivateMSVC.ps1
|
- .\ActivateMSVC.ps1
|
||||||
- cmake --build . --config $config --target ($targets.Split(','))
|
- cmake --build . --config $config --target ($targets.Split(','))
|
||||||
|
@ -424,6 +424,14 @@ Windows_Ninja_Engine_Release:
|
||||||
<<: *engine-targets
|
<<: *engine-targets
|
||||||
config: "Release"
|
config: "Release"
|
||||||
|
|
||||||
|
Windows_Ninja_Engine_Release_MultiView:
|
||||||
|
extends:
|
||||||
|
- .Windows_Ninja_Base
|
||||||
|
variables:
|
||||||
|
<<: *engine-targets
|
||||||
|
multiview: "-M"
|
||||||
|
config: "Release"
|
||||||
|
|
||||||
Windows_Ninja_Engine_Debug:
|
Windows_Ninja_Engine_Debug:
|
||||||
extends:
|
extends:
|
||||||
- .Windows_Ninja_Base
|
- .Windows_Ninja_Base
|
||||||
|
@ -506,7 +514,7 @@ Windows_Ninja_Tests_RelWithDebInfo:
|
||||||
- $env:CCACHE_BASEDIR = Get-Location
|
- $env:CCACHE_BASEDIR = Get-Location
|
||||||
- $env:CCACHE_DIR = "$(Get-Location)\ccache"
|
- $env:CCACHE_DIR = "$(Get-Location)\ccache"
|
||||||
- New-Item -Type Directory -Force -Path $env:CCACHE_DIR
|
- 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
|
- cd MSVC2019_64
|
||||||
- cmake --build . --config $config --target ($targets.Split(','))
|
- cmake --build . --config $config --target ($targets.Split(','))
|
||||||
- ccache --show-stats
|
- ccache --show-stats
|
||||||
|
|
|
@ -75,6 +75,7 @@ TEST_FRAMEWORK=""
|
||||||
GOOGLE_INSTALL_ROOT=""
|
GOOGLE_INSTALL_ROOT=""
|
||||||
INSTALL_PREFIX="."
|
INSTALL_PREFIX="."
|
||||||
BUILD_BENCHMARKS=""
|
BUILD_BENCHMARKS=""
|
||||||
|
OSG_MULTIVIEW_BUILD=""
|
||||||
|
|
||||||
ACTIVATE_MSVC=""
|
ACTIVATE_MSVC=""
|
||||||
SINGLE_CONFIG=""
|
SINGLE_CONFIG=""
|
||||||
|
@ -139,7 +140,10 @@ while [ $# -gt 0 ]; do
|
||||||
|
|
||||||
b )
|
b )
|
||||||
BUILD_BENCHMARKS=true ;;
|
BUILD_BENCHMARKS=true ;;
|
||||||
|
|
||||||
|
M )
|
||||||
|
OSG_MULTIVIEW_BUILD=true ;;
|
||||||
|
|
||||||
h )
|
h )
|
||||||
cat <<EOF
|
cat <<EOF
|
||||||
Usage: $0 [-cdehkpuvVi]
|
Usage: $0 [-cdehkpuvVi]
|
||||||
|
@ -178,6 +182,8 @@ Options:
|
||||||
CMake install prefix
|
CMake install prefix
|
||||||
-b
|
-b
|
||||||
Build benchmarks
|
Build benchmarks
|
||||||
|
-M
|
||||||
|
Use a multiview build of OSG
|
||||||
EOF
|
EOF
|
||||||
wrappedExit 0
|
wrappedExit 0
|
||||||
;;
|
;;
|
||||||
|
@ -514,6 +520,16 @@ fi
|
||||||
|
|
||||||
ICU_VER="70_1"
|
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 "==================================="
|
echo "==================================="
|
||||||
echo "Starting prebuild on MSVC${MSVC_DISPLAY_YEAR} WIN${BITS}"
|
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" \
|
"https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/OpenAL-Soft-1.20.1.zip" \
|
||||||
"OpenAL-Soft-1.20.1.zip"
|
"OpenAL-Soft-1.20.1.zip"
|
||||||
|
|
||||||
# OSGoS
|
# 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 "OSGoS 3.6.5" \
|
download "${OSG_ARCHIVE_NAME}" \
|
||||||
"https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/OSGoS-3.6.5-b02abe2-msvc${MSVC_REAL_YEAR}-win${BITS}.7z" \
|
"${OSG_ARCHIVE_REPO_URL}/windows/${OSG_ARCHIVE}.7z" \
|
||||||
"OSGoS-3.6.5-b02abe2-msvc${MSVC_REAL_YEAR}-win${BITS}.7z"
|
"${OSG_ARCHIVE}.7z"
|
||||||
|
|
||||||
if [ -n "$PDBS" ]; then
|
if [ -n "$PDBS" ]; then
|
||||||
download "OSGoS symbols" \
|
download "${OSG_ARCHIVE_NAME} symbols" \
|
||||||
"https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/OSGoS-3.6.5-b02abe2-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z" \
|
"${OSG_ARCHIVE_REPO_URL}/windows/${OSG_ARCHIVE}-sym.7z" \
|
||||||
"OSGoS-3.6.5-b02abe2-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z"
|
"${OSG_ARCHIVE}-sym.7z"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# SDL2
|
# SDL2
|
||||||
|
@ -781,7 +797,7 @@ printf "OpenAL-Soft 1.20.1... "
|
||||||
cd $DEPS
|
cd $DEPS
|
||||||
echo
|
echo
|
||||||
# OSGoS
|
# OSGoS
|
||||||
printf "OSGoS 3.6.5... "
|
printf "${OSG_ARCHIVE_NAME}... "
|
||||||
{
|
{
|
||||||
cd $DEPS_INSTALL
|
cd $DEPS_INSTALL
|
||||||
if [ -d OSG ] && \
|
if [ -d OSG ] && \
|
||||||
|
@ -792,9 +808,9 @@ printf "OSGoS 3.6.5... "
|
||||||
printf "Exists. "
|
printf "Exists. "
|
||||||
elif [ -z $SKIP_EXTRACT ]; then
|
elif [ -z $SKIP_EXTRACT ]; then
|
||||||
rm -rf OSG
|
rm -rf OSG
|
||||||
eval 7z x -y "${DEPS}/OSGoS-3.6.5-b02abe2-msvc${MSVC_REAL_YEAR}-win${BITS}.7z" $STRIP
|
eval 7z x -y "${DEPS}/${OSG_ARCHIVE}.7z" $STRIP
|
||||||
[ -n "$PDBS" ] && eval 7z x -y "${DEPS}/OSGoS-3.6.5-b02abe2-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z" $STRIP
|
[ -n "$PDBS" ] && eval 7z x -y "${DEPS}/${OSG_ARCHIVE}-sym.7z" $STRIP
|
||||||
mv "OSGoS-3.6.5-b02abe2-msvc${MSVC_REAL_YEAR}-win${BITS}" OSG
|
mv "${OSG_ARCHIVE}" OSG
|
||||||
fi
|
fi
|
||||||
OSG_SDK="$(real_pwd)/OSG"
|
OSG_SDK="$(real_pwd)/OSG"
|
||||||
add_cmake_opts -DOSG_DIR="$OSG_SDK"
|
add_cmake_opts -DOSG_DIR="$OSG_SDK"
|
||||||
|
@ -804,8 +820,13 @@ printf "OSGoS 3.6.5... "
|
||||||
else
|
else
|
||||||
SUFFIX=""
|
SUFFIX=""
|
||||||
fi
|
fi
|
||||||
add_runtime_dlls $CONFIGURATION "$(pwd)/OSG/bin/"{OpenThreads,zlib,libpng}${SUFFIX}.dll \
|
if ! [ -z $OSG_MULTIVIEW_BUILD ]; then
|
||||||
"$(pwd)/OSG/bin/osg"{,Animation,DB,FX,GA,Particle,Text,Util,Viewer,Shadow}${SUFFIX}.dll
|
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_"{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
|
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
|
done
|
||||||
|
|
|
@ -410,6 +410,11 @@ if(OSG_STATIC)
|
||||||
add_definitions(-DOSG_LIBRARY_STATIC)
|
add_definitions(-DOSG_LIBRARY_STATIC)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
include(cmake/CheckOsgMultiview.cmake)
|
||||||
|
if(HAVE_MULTIVIEW)
|
||||||
|
add_definitions(-DOSG_HAS_MULTIVIEW)
|
||||||
|
endif(HAVE_MULTIVIEW)
|
||||||
|
|
||||||
set(BOOST_COMPONENTS system filesystem program_options iostreams)
|
set(BOOST_COMPONENTS system filesystem program_options iostreams)
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
set(BOOST_COMPONENTS ${BOOST_COMPONENTS} locale)
|
set(BOOST_COMPONENTS ${BOOST_COMPONENTS} locale)
|
||||||
|
|
|
@ -30,6 +30,9 @@
|
||||||
|
|
||||||
#include <components/compiler/extensions0.hpp>
|
#include <components/compiler/extensions0.hpp>
|
||||||
|
|
||||||
|
#include <components/stereo/stereomanager.hpp>
|
||||||
|
#include <components/stereo/multiview.hpp>
|
||||||
|
|
||||||
#include <components/sceneutil/workqueue.hpp>
|
#include <components/sceneutil/workqueue.hpp>
|
||||||
|
|
||||||
#include <components/files/configurationmanager.hpp>
|
#include <components/files/configurationmanager.hpp>
|
||||||
|
@ -40,6 +43,7 @@
|
||||||
|
|
||||||
#include <components/sceneutil/screencapture.hpp>
|
#include <components/sceneutil/screencapture.hpp>
|
||||||
#include <components/sceneutil/depth.hpp>
|
#include <components/sceneutil/depth.hpp>
|
||||||
|
#include <components/sceneutil/color.hpp>
|
||||||
#include <components/sceneutil/util.hpp>
|
#include <components/sceneutil/util.hpp>
|
||||||
|
|
||||||
#include "mwinput/inputmanagerimp.hpp"
|
#include "mwinput/inputmanagerimp.hpp"
|
||||||
|
@ -251,6 +255,18 @@ namespace
|
||||||
Log(Debug::Info) << "OpenGL Version: " << glGetString(GL_VERSION);
|
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()
|
void OMW::Engine::executeLocalScripts()
|
||||||
|
@ -413,6 +429,9 @@ OMW::Engine::Engine(Files::ConfigurationManager& configurationManager)
|
||||||
, mEncoding(ToUTF8::WINDOWS_1252)
|
, mEncoding(ToUTF8::WINDOWS_1252)
|
||||||
, mEncoder(nullptr)
|
, mEncoder(nullptr)
|
||||||
, mScreenCaptureOperation(nullptr)
|
, mScreenCaptureOperation(nullptr)
|
||||||
|
, mSelectDepthFormatOperation(new SceneUtil::SelectDepthFormatOperation())
|
||||||
|
, mSelectColorFormatOperation(new SceneUtil::Color::SelectColorFormatOperation())
|
||||||
|
, mStereoManager(nullptr)
|
||||||
, mSkipMenu (false)
|
, mSkipMenu (false)
|
||||||
, mUseSound (true)
|
, mUseSound (true)
|
||||||
, mCompileAll (false)
|
, mCompileAll (false)
|
||||||
|
@ -448,6 +467,8 @@ OMW::Engine::~Engine()
|
||||||
if (mScreenCaptureOperation != nullptr)
|
if (mScreenCaptureOperation != nullptr)
|
||||||
mScreenCaptureOperation->stop();
|
mScreenCaptureOperation->stop();
|
||||||
|
|
||||||
|
mStereoManager = nullptr;
|
||||||
|
|
||||||
mEnvironment.cleanup();
|
mEnvironment.cleanup();
|
||||||
|
|
||||||
delete mScriptContext;
|
delete mScriptContext;
|
||||||
|
@ -640,6 +661,15 @@ void OMW::Engine::createWindow(Settings::Manager& settings)
|
||||||
if (Debug::shouldDebugOpenGL())
|
if (Debug::shouldDebugOpenGL())
|
||||||
realizeOperations->add(new Debug::EnableGLDebugOperation());
|
realizeOperations->add(new Debug::EnableGLDebugOperation());
|
||||||
|
|
||||||
|
realizeOperations->add(mSelectDepthFormatOperation);
|
||||||
|
realizeOperations->add(mSelectColorFormatOperation);
|
||||||
|
|
||||||
|
if (Stereo::getStereo())
|
||||||
|
{
|
||||||
|
realizeOperations->add(new InitializeStereoOperation());
|
||||||
|
Stereo::setVertexBufferHint();
|
||||||
|
}
|
||||||
|
|
||||||
mViewer->realize();
|
mViewer->realize();
|
||||||
|
|
||||||
mViewer->getEventQueue()->getCurrentEventState()->setWindowRectangle(0, 0, graphicsWindow->getTraits()->width, graphicsWindow->getTraits()->height);
|
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 (
|
mEnvironment.setStateManager (
|
||||||
std::make_unique<MWState::StateManager> (mCfgMgr.getUserDataPath() / "saves", mContentFiles));
|
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);
|
mViewer->setSceneData(rootNode);
|
||||||
|
|
||||||
|
createWindow(settings);
|
||||||
|
|
||||||
mVFS = std::make_unique<VFS::Manager>(mFSStrict);
|
mVFS = std::make_unique<VFS::Manager>(mFSStrict);
|
||||||
|
|
||||||
VFS::registerArchives(mVFS.get(), mFileCollections, mArchives, true);
|
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);
|
osg::ref_ptr<osg::GLExtensions> exts = osg::GLExtensions::Get(0, false);
|
||||||
bool shadersSupported = exts && (exts->glslLanguageVersion >= 1.2f);
|
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)
|
#if OSG_VERSION_LESS_THAN(3, 6, 6)
|
||||||
// hack fix for https://github.com/openscenegraph/OpenSceneGraph/issues/1028
|
// 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;
|
osg::ref_ptr<osg::Group> guiRoot = new osg::Group;
|
||||||
guiRoot->setName("GUI Root");
|
guiRoot->setName("GUI Root");
|
||||||
guiRoot->setNodeMask(MWRender::Mask_GUI);
|
guiRoot->setNodeMask(MWRender::Mask_GUI);
|
||||||
|
mStereoManager->disableStereoForNode(guiRoot);
|
||||||
rootNode->addChild(guiRoot);
|
rootNode->addChild(guiRoot);
|
||||||
|
|
||||||
auto windowMgr = std::make_unique<MWGui::WindowManager>(mWindow, mViewer, guiRoot, mResourceSystem.get(), mWorkQueue.get(),
|
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));
|
mEnvironment.setWindowManager (std::move(windowMgr));
|
||||||
|
|
||||||
auto inputMgr = std::make_unique<MWInput::InputManager>(mWindow, mViewer, mScreenCaptureHandler, mScreenCaptureOperation, keybinderUser, keybinderUserExists, userGameControllerdb, gameControllerdb, mGrab);
|
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
|
// Create sound system
|
||||||
mEnvironment.setSoundManager (std::make_unique<MWSound::SoundManager>(mVFS.get(), mUseSound));
|
mEnvironment.setSoundManager (std::make_unique<MWSound::SoundManager>(mVFS.get(), mUseSound));
|
||||||
|
|
|
@ -39,6 +39,11 @@ namespace MWLua
|
||||||
class LuaManager;
|
class LuaManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace Stereo
|
||||||
|
{
|
||||||
|
class Manager;
|
||||||
|
}
|
||||||
|
|
||||||
namespace Files
|
namespace Files
|
||||||
{
|
{
|
||||||
struct ConfigurationManager;
|
struct ConfigurationManager;
|
||||||
|
@ -49,6 +54,16 @@ namespace osgViewer
|
||||||
class ScreenCaptureHandler;
|
class ScreenCaptureHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace SceneUtil
|
||||||
|
{
|
||||||
|
class SelectDepthFormatOperation;
|
||||||
|
|
||||||
|
namespace Color
|
||||||
|
{
|
||||||
|
class SelectColorFormatOperation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct SDL_Window;
|
struct SDL_Window;
|
||||||
|
|
||||||
namespace OMW
|
namespace OMW
|
||||||
|
@ -69,9 +84,14 @@ namespace OMW
|
||||||
osg::ref_ptr<osgViewer::Viewer> mViewer;
|
osg::ref_ptr<osgViewer::Viewer> mViewer;
|
||||||
osg::ref_ptr<osgViewer::ScreenCaptureHandler> mScreenCaptureHandler;
|
osg::ref_ptr<osgViewer::ScreenCaptureHandler> mScreenCaptureHandler;
|
||||||
osg::ref_ptr<SceneUtil::AsyncScreenCaptureOperation> mScreenCaptureOperation;
|
osg::ref_ptr<SceneUtil::AsyncScreenCaptureOperation> mScreenCaptureOperation;
|
||||||
|
osg::ref_ptr<SceneUtil::SelectDepthFormatOperation> mSelectDepthFormatOperation;
|
||||||
|
osg::ref_ptr<SceneUtil::Color::SelectColorFormatOperation> mSelectColorFormatOperation;
|
||||||
std::string mCellName;
|
std::string mCellName;
|
||||||
std::vector<std::string> mContentFiles;
|
std::vector<std::string> mContentFiles;
|
||||||
std::vector<std::string> mGroundcoverFiles;
|
std::vector<std::string> mGroundcoverFiles;
|
||||||
|
|
||||||
|
std::unique_ptr<Stereo::Manager> mStereoManager;
|
||||||
|
|
||||||
bool mSkipMenu;
|
bool mSkipMenu;
|
||||||
bool mUseSound;
|
bool mUseSound;
|
||||||
bool mCompileAll;
|
bool mCompileAll;
|
||||||
|
|
|
@ -367,6 +367,12 @@ namespace MWBase
|
||||||
virtual void forceLootMode(const MWWorld::Ptr& ptr) = 0;
|
virtual void forceLootMode(const MWWorld::Ptr& ptr) = 0;
|
||||||
|
|
||||||
virtual void asyncPrepareSaveMap() = 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)
|
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")
|
: WindowPinnableBase("openmw_map_window.layout")
|
||||||
|
#endif
|
||||||
, LocalMapBase(customMarkers, localMapRender)
|
, LocalMapBase(customMarkers, localMapRender)
|
||||||
, NoDrop(drag, mMainWidget)
|
, NoDrop(drag, mMainWidget)
|
||||||
, mGlobalMap(nullptr)
|
, mGlobalMap(nullptr)
|
||||||
|
|
|
@ -573,17 +573,17 @@ namespace MWGui
|
||||||
void WindowManager::enableScene(bool enable)
|
void WindowManager::enableScene(bool enable)
|
||||||
{
|
{
|
||||||
unsigned int disablemask = MWRender::Mask_GUI|MWRender::Mask_PreCompile;
|
unsigned int disablemask = MWRender::Mask_GUI|MWRender::Mask_PreCompile;
|
||||||
if (!enable && mViewer->getCamera()->getCullMask() != disablemask)
|
if (!enable && getCullMask() != disablemask)
|
||||||
{
|
{
|
||||||
mOldUpdateMask = mViewer->getUpdateVisitor()->getTraversalMask();
|
mOldUpdateMask = mViewer->getUpdateVisitor()->getTraversalMask();
|
||||||
mOldCullMask = mViewer->getCamera()->getCullMask();
|
mOldCullMask = getCullMask();
|
||||||
mViewer->getUpdateVisitor()->setTraversalMask(disablemask);
|
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->getUpdateVisitor()->setTraversalMask(mOldUpdateMask);
|
||||||
mViewer->getCamera()->setCullMask(mOldCullMask);
|
setCullMask(mOldCullMask);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1227,6 +1227,21 @@ namespace MWGui
|
||||||
updateVisible();
|
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)
|
void WindowManager::popGuiMode(bool noSound)
|
||||||
{
|
{
|
||||||
if (mDragAndDrop && mDragAndDrop->mIsOnDragAndDrop)
|
if (mDragAndDrop && mDragAndDrop->mIsOnDragAndDrop)
|
||||||
|
|
|
@ -582,6 +582,9 @@ namespace MWGui
|
||||||
void handleScheduledMessageBoxes();
|
void handleScheduledMessageBoxes();
|
||||||
|
|
||||||
void pushGuiMode(GuiMode mode, const MWWorld::Ptr& arg, bool force);
|
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/resource/resourcesystem.hpp>
|
||||||
#include <components/sceneutil/lightmanager.hpp>
|
#include <components/sceneutil/lightmanager.hpp>
|
||||||
#include <components/sceneutil/shadow.hpp>
|
#include <components/sceneutil/shadow.hpp>
|
||||||
|
#include <components/sceneutil/rtt.hpp>
|
||||||
#include <components/settings/settings.hpp>
|
#include <components/settings/settings.hpp>
|
||||||
#include <components/sceneutil/nodecallback.hpp>
|
#include <components/sceneutil/nodecallback.hpp>
|
||||||
#include <components/sceneutil/depth.hpp>
|
#include <components/sceneutil/depth.hpp>
|
||||||
|
@ -41,9 +42,10 @@ namespace MWRender
|
||||||
class DrawOnceCallback : public SceneUtil::NodeCallback<DrawOnceCallback>
|
class DrawOnceCallback : public SceneUtil::NodeCallback<DrawOnceCallback>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
DrawOnceCallback ()
|
DrawOnceCallback(osg::Node* subgraph)
|
||||||
: mRendered(false)
|
: mRendered(false)
|
||||||
, mLastRenderedFrame(0)
|
, mLastRenderedFrame(0)
|
||||||
|
, mSubgraph(subgraph)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,6 +63,9 @@ namespace MWRender
|
||||||
|
|
||||||
nv->setFrameStamp(fs);
|
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);
|
traverse(node, nv);
|
||||||
|
|
||||||
nv->setFrameStamp(previousFramestamp);
|
nv->setFrameStamp(previousFramestamp);
|
||||||
|
@ -84,6 +89,7 @@ namespace MWRender
|
||||||
private:
|
private:
|
||||||
bool mRendered;
|
bool mRendered;
|
||||||
unsigned int mLastRenderedFrame;
|
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,
|
CharacterPreview::CharacterPreview(osg::Group* parent, Resource::ResourceSystem* resourceSystem,
|
||||||
const MWWorld::Ptr& character, int sizeX, int sizeY, const osg::Vec3f& position, const osg::Vec3f& lookAt)
|
const MWWorld::Ptr& character, int sizeX, int sizeY, const osg::Vec3f& position, const osg::Vec3f& lookAt)
|
||||||
: mParent(parent)
|
: mParent(parent)
|
||||||
|
@ -149,31 +245,11 @@ namespace MWRender
|
||||||
, mSizeX(sizeX)
|
, mSizeX(sizeX)
|
||||||
, mSizeY(sizeY)
|
, 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 = new osg::StateSet;
|
||||||
mTextureStateSet->setAttribute(new osg::BlendFunc(osg::BlendFunc::ONE, osg::BlendFunc::ONE_MINUS_SRC_ALPHA));
|
mTextureStateSet->setAttribute(new osg::BlendFunc(osg::BlendFunc::ONE, osg::BlendFunc::ONE_MINUS_SRC_ALPHA));
|
||||||
|
|
||||||
mCamera = new osg::Camera;
|
mRTTNode = new CharacterPreviewRTTNode(sizeX, sizeY);
|
||||||
// hints that the camera is not relative to the master camera
|
mRTTNode->setNodeMask(Mask_RenderToTexture);
|
||||||
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);
|
|
||||||
|
|
||||||
bool ffp = mResourceSystem->getSceneManager()->getLightingMethod() == SceneUtil::LightingMethod::FFP;
|
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));
|
defaultMat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 0.f));
|
||||||
stateset->setAttribute(defaultMat);
|
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);
|
SceneUtil::ShadowManager::disableShadowsForStateSet(stateset);
|
||||||
|
|
||||||
// assign large value to effectively turn off fog
|
// assign large value to effectively turn off fog
|
||||||
|
@ -266,23 +334,22 @@ namespace MWRender
|
||||||
|
|
||||||
lightManager->addChild(lightSource);
|
lightManager->addChild(lightSource);
|
||||||
|
|
||||||
mCamera->addChild(lightManager);
|
mRTTNode->addChild(lightManager);
|
||||||
|
|
||||||
mNode = new osg::PositionAttitudeTransform;
|
mNode = new osg::PositionAttitudeTransform;
|
||||||
lightManager->addChild(mNode);
|
lightManager->addChild(mNode);
|
||||||
|
|
||||||
mDrawOnceCallback = new DrawOnceCallback;
|
mDrawOnceCallback = new DrawOnceCallback(mRTTNode->mGroup);
|
||||||
mCamera->addUpdateCallback(mDrawOnceCallback);
|
mRTTNode->addUpdateCallback(mDrawOnceCallback);
|
||||||
|
|
||||||
mParent->addChild(mCamera);
|
mParent->addChild(mRTTNode);
|
||||||
|
|
||||||
mCharacter.mCell = nullptr;
|
mCharacter.mCell = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
CharacterPreview::~CharacterPreview ()
|
CharacterPreview::~CharacterPreview ()
|
||||||
{
|
{
|
||||||
mCamera->removeChildren(0, mCamera->getNumChildren());
|
mParent->removeChild(mRTTNode);
|
||||||
mParent->removeChild(mCamera);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int CharacterPreview::getTextureWidth() const
|
int CharacterPreview::getTextureWidth() const
|
||||||
|
@ -308,7 +375,7 @@ namespace MWRender
|
||||||
|
|
||||||
osg::ref_ptr<osg::Texture2D> CharacterPreview::getTexture()
|
osg::ref_ptr<osg::Texture2D> CharacterPreview::getTexture()
|
||||||
{
|
{
|
||||||
return mTexture;
|
return static_cast<osg::Texture2D*>(mRTTNode->getColorTexture(nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CharacterPreview::rebuild()
|
void CharacterPreview::rebuild()
|
||||||
|
@ -325,7 +392,7 @@ namespace MWRender
|
||||||
|
|
||||||
void CharacterPreview::redraw()
|
void CharacterPreview::redraw()
|
||||||
{
|
{
|
||||||
mCamera->setNodeMask(Mask_RenderToTexture);
|
mRTTNode->setNodeMask(Mask_RenderToTexture);
|
||||||
mDrawOnceCallback->redrawNextFrame();
|
mDrawOnceCallback->redrawNextFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -346,7 +413,7 @@ namespace MWRender
|
||||||
osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet;
|
osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet;
|
||||||
mViewport = new osg::Viewport(0, mSizeY-sizeY, std::min(mSizeX, sizeX), std::min(mSizeY, sizeY));
|
mViewport = new osg::Viewport(0, mSizeY-sizeY, std::min(mSizeX, sizeX), std::min(mSizeY, sizeY));
|
||||||
stateset->setAttributeAndModes(mViewport);
|
stateset->setAttributeAndModes(mViewport);
|
||||||
mCamera->setStateSet(stateset);
|
mRTTNode->setCameraStateset(stateset);
|
||||||
|
|
||||||
redraw();
|
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
|
// Set the traversal number from the last draw, so that the frame switch used for RigGeometry double buffering works correctly
|
||||||
visitor.setTraversalNumber(mDrawOnceCallback->getLastRenderedFrame());
|
visitor.setTraversalNumber(mDrawOnceCallback->getLastRenderedFrame());
|
||||||
|
|
||||||
osg::Node::NodeMask nodeMask = mCamera->getNodeMask();
|
auto* camera = mRTTNode->getCamera(nullptr);
|
||||||
mCamera->setNodeMask(~0u);
|
osg::Node::NodeMask nodeMask = camera->getNodeMask();
|
||||||
mCamera->accept(visitor);
|
camera->setNodeMask(~0u);
|
||||||
mCamera->setNodeMask(nodeMask);
|
camera->accept(visitor);
|
||||||
|
camera->setNodeMask(nodeMask);
|
||||||
|
|
||||||
if (intersector->containsIntersections())
|
if (intersector->containsIntersections())
|
||||||
{
|
{
|
||||||
|
@ -459,7 +527,8 @@ namespace MWRender
|
||||||
|
|
||||||
mNode->setScale(scale);
|
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();
|
rebuild();
|
||||||
}
|
}
|
||||||
|
|
||||||
class UpdateCameraCallback : public SceneUtil::NodeCallback<UpdateCameraCallback, osg::Camera*>
|
class UpdateCameraCallback : public SceneUtil::NodeCallback<UpdateCameraCallback, CharacterPreviewRTTNode*>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
UpdateCameraCallback(osg::ref_ptr<const osg::Node> nodeToFollow, const osg::Vec3& posOffset, const osg::Vec3& lookAtOffset)
|
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...
|
// Update keyframe controllers in the scene graph first...
|
||||||
traverse(cam, nv);
|
traverse(node, nv);
|
||||||
|
|
||||||
// Now update camera utilizing the updated head position
|
// Now update camera utilizing the updated head position
|
||||||
osg::NodePathList nodepaths = mNodeToFollow->getParentalNodePaths();
|
osg::NodePathList nodepaths = mNodeToFollow->getParentalNodePaths();
|
||||||
|
@ -514,7 +583,8 @@ namespace MWRender
|
||||||
osg::Matrix worldMat = osg::computeLocalToWorld(nodepaths[0]);
|
osg::Matrix worldMat = osg::computeLocalToWorld(nodepaths[0]);
|
||||||
osg::Vec3 headOffset = worldMat.getTrans();
|
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:
|
private:
|
||||||
|
@ -531,13 +601,13 @@ namespace MWRender
|
||||||
|
|
||||||
// attach camera to follow the head node
|
// attach camera to follow the head node
|
||||||
if (mUpdateCameraCallback)
|
if (mUpdateCameraCallback)
|
||||||
mCamera->removeUpdateCallback(mUpdateCameraCallback);
|
mRTTNode->removeUpdateCallback(mUpdateCameraCallback);
|
||||||
|
|
||||||
const osg::Node* head = mAnimation->getNode("Bip01 Head");
|
const osg::Node* head = mAnimation->getNode("Bip01 Head");
|
||||||
if (head)
|
if (head)
|
||||||
{
|
{
|
||||||
mUpdateCameraCallback = new UpdateCameraCallback(head, mPosition, mLookAt);
|
mUpdateCameraCallback = new UpdateCameraCallback(head, mPosition, mLookAt);
|
||||||
mCamera->addUpdateCallback(mUpdateCameraCallback);
|
mRTTNode->addUpdateCallback(mUpdateCameraCallback);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
Log(Debug::Error) << "Error: Bip01 Head node not found";
|
Log(Debug::Error) << "Error: Bip01 Head node not found";
|
||||||
|
|
|
@ -26,6 +26,7 @@ namespace MWRender
|
||||||
|
|
||||||
class NpcAnimation;
|
class NpcAnimation;
|
||||||
class DrawOnceCallback;
|
class DrawOnceCallback;
|
||||||
|
class CharacterPreviewRTTNode;
|
||||||
|
|
||||||
class CharacterPreview
|
class CharacterPreview
|
||||||
{
|
{
|
||||||
|
@ -56,10 +57,9 @@ namespace MWRender
|
||||||
|
|
||||||
osg::ref_ptr<osg::Group> mParent;
|
osg::ref_ptr<osg::Group> mParent;
|
||||||
Resource::ResourceSystem* mResourceSystem;
|
Resource::ResourceSystem* mResourceSystem;
|
||||||
osg::ref_ptr<osg::Texture2D> mTexture;
|
|
||||||
osg::ref_ptr<osg::StateSet> mTextureStateSet;
|
osg::ref_ptr<osg::StateSet> mTextureStateSet;
|
||||||
osg::ref_ptr<osg::Camera> mCamera;
|
|
||||||
osg::ref_ptr<DrawOnceCallback> mDrawOnceCallback;
|
osg::ref_ptr<DrawOnceCallback> mDrawOnceCallback;
|
||||||
|
osg::ref_ptr<CharacterPreviewRTTNode> mRTTNode;
|
||||||
|
|
||||||
osg::Vec3f mPosition;
|
osg::Vec3f mPosition;
|
||||||
osg::Vec3f mLookAt;
|
osg::Vec3f mLookAt;
|
||||||
|
|
|
@ -15,12 +15,14 @@
|
||||||
#include <components/esm3/fogstate.hpp>
|
#include <components/esm3/fogstate.hpp>
|
||||||
#include <components/esm3/loadcell.hpp>
|
#include <components/esm3/loadcell.hpp>
|
||||||
#include <components/misc/constants.hpp>
|
#include <components/misc/constants.hpp>
|
||||||
|
#include <components/stereo/multiview.hpp>
|
||||||
#include <components/settings/settings.hpp>
|
#include <components/settings/settings.hpp>
|
||||||
#include <components/sceneutil/visitor.hpp>
|
#include <components/sceneutil/visitor.hpp>
|
||||||
#include <components/sceneutil/shadow.hpp>
|
#include <components/sceneutil/shadow.hpp>
|
||||||
#include <components/sceneutil/depth.hpp>
|
#include <components/sceneutil/depth.hpp>
|
||||||
#include <components/sceneutil/lightmanager.hpp>
|
#include <components/sceneutil/lightmanager.hpp>
|
||||||
#include <components/sceneutil/nodecallback.hpp>
|
#include <components/sceneutil/nodecallback.hpp>
|
||||||
|
#include <components/sceneutil/rtt.hpp>
|
||||||
#include <components/files/memorystream.hpp>
|
#include <components/files/memorystream.hpp>
|
||||||
#include <components/resource/scenemanager.hpp>
|
#include <components/resource/scenemanager.hpp>
|
||||||
|
|
||||||
|
@ -34,36 +36,6 @@
|
||||||
|
|
||||||
namespace
|
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)
|
float square(float val)
|
||||||
{
|
{
|
||||||
return val*val;
|
return val*val;
|
||||||
|
@ -82,6 +54,28 @@ namespace
|
||||||
|
|
||||||
namespace MWRender
|
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)
|
LocalMap::LocalMap(osg::Group* root)
|
||||||
: mRoot(root)
|
: mRoot(root)
|
||||||
|
@ -104,10 +98,8 @@ LocalMap::LocalMap(osg::Group* root)
|
||||||
|
|
||||||
LocalMap::~LocalMap()
|
LocalMap::~LocalMap()
|
||||||
{
|
{
|
||||||
for (auto& camera : mActiveCameras)
|
for (auto& rtt : mLocalMapRTTs)
|
||||||
removeCamera(camera);
|
mRoot->removeChild(rtt);
|
||||||
for (auto& camera : mCamerasPendingRemoval)
|
|
||||||
removeCamera(camera);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const osg::Vec2f LocalMap::rotatePoint(const osg::Vec2f& point, const osg::Vec2f& center, const float angle)
|
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())
|
mRoot->addChild(mLocalMapRTTs.back());
|
||||||
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);
|
|
||||||
|
|
||||||
camera->setComputeNearFarMode(osg::Camera::DO_NOT_COMPUTE_NEAR_FAR);
|
MapSegment& segment = mInterior? mInteriorSegments[std::make_pair(segment_x, segment_y)] : mExteriorSegments[std::make_pair(segment_x, segment_y)];
|
||||||
camera->setViewMatrixAsLookAt(osg::Vec3d(x, y, zmax + 5), osg::Vec3d(x, y, zmin), upVector);
|
segment.mMapTexture = static_cast<osg::Texture2D*>(mLocalMapRTTs.back()->getColorTexture(nullptr));
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LocalMap::requestMap(const MWWorld::CellStore* cell)
|
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;
|
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()
|
void LocalMap::cleanupCameras()
|
||||||
{
|
{
|
||||||
if (mCamerasPendingRemoval.empty())
|
auto it = mLocalMapRTTs.begin();
|
||||||
return;
|
while (it != mLocalMapRTTs.end())
|
||||||
|
{
|
||||||
for (auto& camera : mCamerasPendingRemoval)
|
if (!(*it)->isActive())
|
||||||
removeCamera(camera);
|
{
|
||||||
|
mRoot->removeChild(*it);
|
||||||
mCamerasPendingRemoval.clear();
|
it = mLocalMapRTTs.erase(it);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
it++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LocalMap::requestExteriorMap(const MWWorld::CellStore* cell)
|
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 zmin = bound.center().z() - bound.radius();
|
||||||
float zmax = 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,
|
setupRenderToTexture(cell->getCell()->getGridX(), cell->getCell()->getGridY(),
|
||||||
osg::Vec3d(0,1,0), zmin, zmax);
|
x * mMapWorldSize + mMapWorldSize / 2.f, y * mMapWorldSize + mMapWorldSize / 2.f,
|
||||||
setupRenderToTexture(camera, cell->getCell()->getGridX(), cell->getCell()->getGridY());
|
osg::Vec3d(0, 1, 0), zmin, zmax);
|
||||||
|
|
||||||
MapSegment& segment = mExteriorSegments[std::make_pair(cell->getCell()->getGridX(), cell->getCell()->getGridY())];
|
MapSegment& segment = mExteriorSegments[std::make_pair(cell->getCell()->getGridX(), cell->getCell()->getGridY())];
|
||||||
if (!segment.mFogOfWarImage)
|
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::Vec2f pos = osg::Vec2f(rotatedCenter.x(), rotatedCenter.y()) + center;
|
||||||
|
|
||||||
osg::ref_ptr<osg::Camera> camera = createOrthographicCamera(pos.x(), pos.y(),
|
setupRenderToTexture(x, y, pos.x(), pos.y(),
|
||||||
mMapWorldSize, mMapWorldSize,
|
osg::Vec3f(north.x(), north.y(), 0.f), zMin, zMax);
|
||||||
osg::Vec3f(north.x(), north.y(), 0.f), zMin, zMax);
|
|
||||||
|
|
||||||
setupRenderToTexture(camera, x, y);
|
|
||||||
|
|
||||||
auto coords = std::make_pair(x,y);
|
auto coords = std::make_pair(x,y);
|
||||||
MapSegment& segment = mInteriorSegments[coords];
|
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_S, osg::Texture::CLAMP_TO_EDGE);
|
||||||
mFogOfWarTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
|
mFogOfWarTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
|
||||||
mFogOfWarTexture->setUnRefImageDataAfterApply(false);
|
mFogOfWarTexture->setUnRefImageDataAfterApply(false);
|
||||||
|
mFogOfWarTexture->setImage(mFogOfWarImage);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LocalMap::MapSegment::initFogOfWar()
|
void LocalMap::MapSegment::initFogOfWar()
|
||||||
|
@ -697,7 +594,6 @@ void LocalMap::MapSegment::initFogOfWar()
|
||||||
memcpy(mFogOfWarImage->data(), &data[0], data.size()*4);
|
memcpy(mFogOfWarImage->data(), &data[0], data.size()*4);
|
||||||
|
|
||||||
createFogOfWarTexture();
|
createFogOfWarTexture();
|
||||||
mFogOfWarTexture->setImage(mFogOfWarImage);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LocalMap::MapSegment::loadFogOfWar(const ESM::FogTexture &esm)
|
void LocalMap::MapSegment::loadFogOfWar(const ESM::FogTexture &esm)
|
||||||
|
@ -730,7 +626,6 @@ void LocalMap::MapSegment::loadFogOfWar(const ESM::FogTexture &esm)
|
||||||
mFogOfWarImage->dirty();
|
mFogOfWarImage->dirty();
|
||||||
|
|
||||||
createFogOfWarTexture();
|
createFogOfWarTexture();
|
||||||
mFogOfWarTexture->setImage(mFogOfWarImage);
|
|
||||||
mHasFogState = true;
|
mHasFogState = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -762,4 +657,107 @@ void LocalMap::MapSegment::saveFogOfWar(ESM::FogTexture &fog) const
|
||||||
fog.mImageData = std::vector<char>(data.begin(), data.end());
|
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
|
namespace MWRender
|
||||||
{
|
{
|
||||||
|
class LocalMapRenderToTexture;
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \brief Local map rendering
|
/// \brief Local map rendering
|
||||||
///
|
///
|
||||||
|
@ -58,13 +60,6 @@ namespace MWRender
|
||||||
|
|
||||||
osg::ref_ptr<osg::Texture2D> getFogOfWarTexture (int x, int y);
|
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
|
* 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
|
* 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::Group> mRoot;
|
||||||
osg::ref_ptr<osg::Node> mSceneRoot;
|
osg::ref_ptr<osg::Node> mSceneRoot;
|
||||||
|
|
||||||
typedef std::vector< osg::ref_ptr<osg::Camera> > CameraVector;
|
typedef std::vector< osg::ref_ptr<LocalMapRenderToTexture> > RTTVector;
|
||||||
|
RTTVector mLocalMapRTTs;
|
||||||
CameraVector mActiveCameras;
|
|
||||||
|
|
||||||
CameraVector mCamerasPendingRemoval;
|
|
||||||
|
|
||||||
typedef std::set<std::pair<int, int> > Grid;
|
typedef std::set<std::pair<int, int> > Grid;
|
||||||
Grid mCurrentGrid;
|
Grid mCurrentGrid;
|
||||||
|
@ -152,8 +144,7 @@ namespace MWRender
|
||||||
void requestExteriorMap(const MWWorld::CellStore* cell);
|
void requestExteriorMap(const MWWorld::CellStore* cell);
|
||||||
void requestInteriorMap(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(int segment_x, int segment_y, float left, float top, const osg::Vec3d& upVector, float zmin, float zmax);
|
||||||
void setupRenderToTexture(osg::ref_ptr<osg::Camera> camera, int x, int y);
|
|
||||||
|
|
||||||
bool mInterior;
|
bool mInterior;
|
||||||
osg::BoundingBox mBounds;
|
osg::BoundingBox mBounds;
|
||||||
|
|
|
@ -6,15 +6,20 @@
|
||||||
#include <osg/Camera>
|
#include <osg/Camera>
|
||||||
#include <osg/Callback>
|
#include <osg/Callback>
|
||||||
#include <osg/Texture2D>
|
#include <osg/Texture2D>
|
||||||
|
#include <osg/Texture2DArray>
|
||||||
#include <osg/FrameBufferObject>
|
#include <osg/FrameBufferObject>
|
||||||
|
|
||||||
#include <osgViewer/Viewer>
|
#include <osgViewer/Viewer>
|
||||||
|
|
||||||
#include <components/settings/settings.hpp>
|
#include <components/settings/settings.hpp>
|
||||||
#include <components/sceneutil/depth.hpp>
|
#include <components/sceneutil/depth.hpp>
|
||||||
|
#include <components/sceneutil/color.hpp>
|
||||||
#include <components/sceneutil/nodecallback.hpp>
|
#include <components/sceneutil/nodecallback.hpp>
|
||||||
#include <components/debug/debuglog.hpp>
|
#include <components/debug/debuglog.hpp>
|
||||||
|
|
||||||
|
#include <components/stereo/multiview.hpp>
|
||||||
|
#include <components/stereo/stereomanager.hpp>
|
||||||
|
|
||||||
#include "vismask.hpp"
|
#include "vismask.hpp"
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
|
@ -38,8 +43,8 @@ namespace
|
||||||
class CullCallback : public SceneUtil::NodeCallback<CullCallback, osg::Node*, osgUtil::CullVisitor*>
|
class CullCallback : public SceneUtil::NodeCallback<CullCallback, osg::Node*, osgUtil::CullVisitor*>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CullCallback()
|
CullCallback(MWRender::PostProcessor* pp)
|
||||||
: mLastFrameNumber(0)
|
: mPostProcessor(pp)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,36 +52,21 @@ namespace
|
||||||
{
|
{
|
||||||
osgUtil::RenderStage* renderStage = cv->getCurrentRenderStage();
|
osgUtil::RenderStage* renderStage = cv->getCurrentRenderStage();
|
||||||
|
|
||||||
unsigned int frame = cv->getTraversalNumber();
|
if (!mPostProcessor->getMsaaFbo())
|
||||||
if (frame != mLastFrameNumber)
|
|
||||||
{
|
{
|
||||||
mLastFrameNumber = frame;
|
renderStage->setFrameBufferObject(mPostProcessor->getFbo());
|
||||||
|
}
|
||||||
MWRender::PostProcessor* postProcessor = dynamic_cast<MWRender::PostProcessor*>(cv->getCurrentCamera()->getUserData());
|
else
|
||||||
|
{
|
||||||
if (!postProcessor)
|
renderStage->setMultisampleResolveFramebufferObject(mPostProcessor->getFbo());
|
||||||
{
|
renderStage->setFrameBufferObject(mPostProcessor->getMsaaFbo());
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
traverse(node, cv);
|
traverse(node, cv);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
unsigned int mLastFrameNumber;
|
MWRender::PostProcessor* mPostProcessor;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ResizedCallback : osg::GraphicsContext::ResizedCallback
|
struct ResizedCallback : osg::GraphicsContext::ResizedCallback
|
||||||
|
@ -157,15 +147,13 @@ namespace MWRender
|
||||||
PostProcessor::PostProcessor(osgViewer::Viewer* viewer, osg::Group* rootNode)
|
PostProcessor::PostProcessor(osgViewer::Viewer* viewer, osg::Group* rootNode)
|
||||||
: mViewer(viewer)
|
: mViewer(viewer)
|
||||||
, mRootNode(new osg::Group)
|
, mRootNode(new osg::Group)
|
||||||
, mDepthFormat(GL_DEPTH24_STENCIL8_EXT)
|
|
||||||
{
|
{
|
||||||
bool softParticles = Settings::Manager::getBool("soft particles", "Shaders");
|
bool softParticles = Settings::Manager::getBool("soft particles", "Shaders");
|
||||||
|
|
||||||
if (!SceneUtil::AutoDepth::isReversed() && !softParticles)
|
if (!SceneUtil::AutoDepth::isReversed() && !softParticles && !Stereo::getStereo())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
osg::GraphicsContext* gc = viewer->getCamera()->getGraphicsContext();
|
osg::GraphicsContext* gc = viewer->getCamera()->getGraphicsContext();
|
||||||
unsigned int contextID = gc->getState()->getContextID();
|
|
||||||
osg::GLExtensions* ext = gc->getState()->get<osg::GLExtensions>();
|
osg::GLExtensions* ext = gc->getState()->get<osg::GLExtensions>();
|
||||||
|
|
||||||
constexpr char errPreamble[] = "Postprocessing and floating point depth buffers disabled: ";
|
constexpr char errPreamble[] = "Postprocessing and floating point depth buffers disabled: ";
|
||||||
|
@ -184,24 +172,19 @@ namespace MWRender
|
||||||
|
|
||||||
if (SceneUtil::AutoDepth::isReversed())
|
if (SceneUtil::AutoDepth::isReversed())
|
||||||
{
|
{
|
||||||
if (osg::isGLExtensionSupported(contextID, "GL_ARB_depth_buffer_float"))
|
if(SceneUtil::AutoDepth::depthSourceType() != GL_FLOAT_32_UNSIGNED_INT_24_8_REV)
|
||||||
mDepthFormat = GL_DEPTH32F_STENCIL8;
|
|
||||||
else if (osg::isGLExtensionSupported(contextID, "GL_NV_depth_buffer_float"))
|
|
||||||
mDepthFormat = GL_DEPTH32F_STENCIL8_NV;
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
// TODO: Once we have post-processing implemented we want to skip this return and continue with setup.
|
// 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
|
// 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.
|
// 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 && !Stereo::getStereo())
|
||||||
|
|
||||||
if (!softParticles)
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int width = viewer->getCamera()->getViewport()->width();
|
auto* traits = gc->getTraits();
|
||||||
int height = viewer->getCamera()->getViewport()->height();
|
int width = traits->width;
|
||||||
|
int height = traits->height;
|
||||||
|
|
||||||
createTexturesAndCamera(width, height);
|
createTexturesAndCamera(width, height);
|
||||||
resize(width, height);
|
resize(width, height);
|
||||||
|
@ -210,15 +193,17 @@ namespace MWRender
|
||||||
mRootNode->addChild(rootNode);
|
mRootNode->addChild(rootNode);
|
||||||
mViewer->setSceneData(mRootNode);
|
mViewer->setSceneData(mRootNode);
|
||||||
|
|
||||||
// We need to manually set the FBO and resolve FBO during the cull callback. If we were using a separate
|
if (!Stereo::getStereo())
|
||||||
// RTT camera this would not be needed.
|
{
|
||||||
mViewer->getCamera()->addCullCallback(new CullCallback);
|
// We need to manually set the FBO and resolve FBO during the cull callback. If we were using a separate
|
||||||
mViewer->getCamera()->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);
|
// RTT camera this would not be needed.
|
||||||
mViewer->getCamera()->attach(osg::Camera::COLOR_BUFFER0, mSceneTex);
|
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()->attach(osg::Camera::PACKED_DEPTH_STENCIL_BUFFER, mDepthTex);
|
||||||
|
}
|
||||||
|
|
||||||
mViewer->getCamera()->getGraphicsContext()->setResizedCallback(new ResizedCallback(this));
|
mViewer->getCamera()->getGraphicsContext()->setResizedCallback(new ResizedCallback(this));
|
||||||
mViewer->getCamera()->setUserData(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PostProcessor::resize(int width, int height)
|
void PostProcessor::resize(int width, int height)
|
||||||
|
@ -260,15 +245,89 @@ namespace MWRender
|
||||||
|
|
||||||
mViewer->getCamera()->resize(width, height);
|
mViewer->getCamera()->resize(width, height);
|
||||||
mHUDCamera->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)
|
void PostProcessor::createTexturesAndCamera(int width, int height)
|
||||||
{
|
{
|
||||||
mDepthTex = new osg::Texture2D;
|
mDepthTex = new osg::Texture2D;
|
||||||
mDepthTex->setTextureSize(width, height);
|
mDepthTex->setTextureSize(width, height);
|
||||||
mDepthTex->setSourceFormat(GL_DEPTH_STENCIL_EXT);
|
mDepthTex->setSourceFormat(SceneUtil::AutoDepth::depthSourceFormat());
|
||||||
mDepthTex->setSourceType(SceneUtil::isFloatingPointDepthFormat(getDepthFormat()) ? GL_FLOAT_32_UNSIGNED_INT_24_8_REV : GL_UNSIGNED_INT_24_8_EXT);
|
mDepthTex->setSourceType(SceneUtil::AutoDepth::depthSourceType());
|
||||||
mDepthTex->setInternalFormat(mDepthFormat);
|
mDepthTex->setInternalFormat(SceneUtil::AutoDepth::depthInternalFormat());
|
||||||
mDepthTex->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::NEAREST);
|
mDepthTex->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::NEAREST);
|
||||||
mDepthTex->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::NEAREST);
|
mDepthTex->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::NEAREST);
|
||||||
mDepthTex->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
|
mDepthTex->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
|
||||||
|
@ -283,9 +342,9 @@ namespace MWRender
|
||||||
|
|
||||||
mSceneTex = new osg::Texture2D;
|
mSceneTex = new osg::Texture2D;
|
||||||
mSceneTex->setTextureSize(width, height);
|
mSceneTex->setTextureSize(width, height);
|
||||||
mSceneTex->setSourceFormat(GL_RGB);
|
mSceneTex->setSourceFormat(SceneUtil::Color::colorSourceFormat());
|
||||||
mSceneTex->setSourceType(GL_UNSIGNED_BYTE);
|
mSceneTex->setSourceType(SceneUtil::Color::colorSourceType());
|
||||||
mSceneTex->setInternalFormat(GL_RGB);
|
mSceneTex->setInternalFormat(SceneUtil::Color::colorInternalFormat());
|
||||||
mSceneTex->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::NEAREST);
|
mSceneTex->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::NEAREST);
|
||||||
mSceneTex->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::NEAREST);
|
mSceneTex->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::NEAREST);
|
||||||
mSceneTex->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
|
mSceneTex->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
|
||||||
|
@ -296,6 +355,7 @@ namespace MWRender
|
||||||
mHUDCamera->setReferenceFrame(osg::Camera::ABSOLUTE_RF);
|
mHUDCamera->setReferenceFrame(osg::Camera::ABSOLUTE_RF);
|
||||||
mHUDCamera->setRenderOrder(osg::Camera::POST_RENDER);
|
mHUDCamera->setRenderOrder(osg::Camera::POST_RENDER);
|
||||||
mHUDCamera->setClearColor(osg::Vec4(0.45, 0.45, 0.14, 1.0));
|
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->setProjectionMatrix(osg::Matrix::ortho2D(0, 1, 0, 1));
|
||||||
mHUDCamera->setAllowEventFocus(false);
|
mHUDCamera->setAllowEventFocus(false);
|
||||||
mHUDCamera->setViewport(0, 0, width, height);
|
mHUDCamera->setViewport(0, 0, width, height);
|
||||||
|
@ -325,8 +385,28 @@ namespace MWRender
|
||||||
}
|
}
|
||||||
)GLSL";
|
)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> 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;
|
osg::ref_ptr<osg::Program> program = new osg::Program;
|
||||||
program->addShader(vertShader);
|
program->addShader(vertShader);
|
||||||
|
@ -334,13 +414,8 @@ namespace MWRender
|
||||||
|
|
||||||
mHUDCamera->addChild(createFullScreenTri());
|
mHUDCamera->addChild(createFullScreenTri());
|
||||||
mHUDCamera->setNodeMask(Mask_RenderToTexture);
|
mHUDCamera->setNodeMask(Mask_RenderToTexture);
|
||||||
|
mHUDCamera->setCullCallback(new HUDCameraStatesetUpdater(mHUDCamera, program, mSceneTex));
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,11 +7,18 @@
|
||||||
#include <osg/Camera>
|
#include <osg/Camera>
|
||||||
#include <osg/ref_ptr>
|
#include <osg/ref_ptr>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
namespace osgViewer
|
namespace osgViewer
|
||||||
{
|
{
|
||||||
class Viewer;
|
class Viewer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace Stereo
|
||||||
|
{
|
||||||
|
class MultiviewFramebuffer;
|
||||||
|
}
|
||||||
|
|
||||||
namespace MWRender
|
namespace MWRender
|
||||||
{
|
{
|
||||||
class PostProcessor : public osg::Referenced
|
class PostProcessor : public osg::Referenced
|
||||||
|
@ -23,7 +30,6 @@ namespace MWRender
|
||||||
auto getFbo() { return mFbo; }
|
auto getFbo() { return mFbo; }
|
||||||
auto getFirstPersonRBProxy() { return mFirstPersonDepthRBProxy; }
|
auto getFirstPersonRBProxy() { return mFirstPersonDepthRBProxy; }
|
||||||
|
|
||||||
int getDepthFormat() { return mDepthFormat; }
|
|
||||||
osg::ref_ptr<osg::Texture2D> getOpaqueDepthTex() { return mOpaqueDepthTex; }
|
osg::ref_ptr<osg::Texture2D> getOpaqueDepthTex() { return mOpaqueDepthTex; }
|
||||||
|
|
||||||
void resize(int width, int height);
|
void resize(int width, int height);
|
||||||
|
@ -35,6 +41,7 @@ namespace MWRender
|
||||||
osg::ref_ptr<osg::Group> mRootNode;
|
osg::ref_ptr<osg::Group> mRootNode;
|
||||||
osg::ref_ptr<osg::Camera> mHUDCamera;
|
osg::ref_ptr<osg::Camera> mHUDCamera;
|
||||||
|
|
||||||
|
std::shared_ptr<Stereo::MultiviewFramebuffer> mMultiviewFbo;
|
||||||
osg::ref_ptr<osg::FrameBufferObject> mMsaaFbo;
|
osg::ref_ptr<osg::FrameBufferObject> mMsaaFbo;
|
||||||
osg::ref_ptr<osg::FrameBufferObject> mFbo;
|
osg::ref_ptr<osg::FrameBufferObject> mFbo;
|
||||||
osg::ref_ptr<osg::RenderBuffer> mFirstPersonDepthRBProxy;
|
osg::ref_ptr<osg::RenderBuffer> mFirstPersonDepthRBProxy;
|
||||||
|
@ -42,9 +49,8 @@ namespace MWRender
|
||||||
osg::ref_ptr<osg::Texture2D> mSceneTex;
|
osg::ref_ptr<osg::Texture2D> mSceneTex;
|
||||||
osg::ref_ptr<osg::Texture2D> mDepthTex;
|
osg::ref_ptr<osg::Texture2D> mDepthTex;
|
||||||
osg::ref_ptr<osg::Texture2D> mOpaqueDepthTex;
|
osg::ref_ptr<osg::Texture2D> mOpaqueDepthTex;
|
||||||
|
|
||||||
int mDepthFormat;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,9 @@
|
||||||
|
|
||||||
#include <components/debug/debuglog.hpp>
|
#include <components/debug/debuglog.hpp>
|
||||||
|
|
||||||
|
#include <components/stereo/stereomanager.hpp>
|
||||||
|
#include <components/stereo/multiview.hpp>
|
||||||
|
|
||||||
#include <components/resource/resourcesystem.hpp>
|
#include <components/resource/resourcesystem.hpp>
|
||||||
#include <components/resource/imagemanager.hpp>
|
#include <components/resource/imagemanager.hpp>
|
||||||
#include <components/resource/keyframemanager.hpp>
|
#include <components/resource/keyframemanager.hpp>
|
||||||
|
@ -31,6 +34,7 @@
|
||||||
#include <components/settings/settings.hpp>
|
#include <components/settings/settings.hpp>
|
||||||
|
|
||||||
#include <components/sceneutil/depth.hpp>
|
#include <components/sceneutil/depth.hpp>
|
||||||
|
#include <components/sceneutil/visitor.hpp>
|
||||||
#include <components/sceneutil/lightmanager.hpp>
|
#include <components/sceneutil/lightmanager.hpp>
|
||||||
#include <components/sceneutil/statesetupdater.hpp>
|
#include <components/sceneutil/statesetupdater.hpp>
|
||||||
#include <components/sceneutil/positionattitudetransform.hpp>
|
#include <components/sceneutil/positionattitudetransform.hpp>
|
||||||
|
@ -45,6 +49,7 @@
|
||||||
|
|
||||||
#include <components/detournavigator/navigator.hpp>
|
#include <components/detournavigator/navigator.hpp>
|
||||||
|
|
||||||
|
#include "../mwbase/windowmanager.hpp"
|
||||||
#include "../mwworld/cellstore.hpp"
|
#include "../mwworld/cellstore.hpp"
|
||||||
#include "../mwworld/class.hpp"
|
#include "../mwworld/class.hpp"
|
||||||
#include "../mwworld/groundcoverstore.hpp"
|
#include "../mwworld/groundcoverstore.hpp"
|
||||||
|
@ -70,6 +75,54 @@
|
||||||
|
|
||||||
namespace MWRender
|
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
|
class SharedUniformStateUpdater : public SceneUtil::StateSetUpdater
|
||||||
{
|
{
|
||||||
public:
|
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("linearFac", 0.f));
|
||||||
stateset->addUniform(new osg::Uniform("near", 0.f));
|
stateset->addUniform(new osg::Uniform("near", 0.f));
|
||||||
stateset->addUniform(new osg::Uniform("far", 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
|
void apply(osg::StateSet* stateset, osg::NodeVisitor* nv) override
|
||||||
{
|
{
|
||||||
auto* uProjectionMatrix = stateset->getUniform("projectionMatrix");
|
|
||||||
if (uProjectionMatrix)
|
|
||||||
uProjectionMatrix->set(mProjectionMatrix);
|
|
||||||
|
|
||||||
auto* uLinearFac = stateset->getUniform("linearFac");
|
auto* uLinearFac = stateset->getUniform("linearFac");
|
||||||
if (uLinearFac)
|
if (uLinearFac)
|
||||||
uLinearFac->set(mLinearFac);
|
uLinearFac->set(mLinearFac);
|
||||||
|
@ -130,11 +178,6 @@ namespace MWRender
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void setProjectionMatrix(const osg::Matrixf& projectionMatrix)
|
|
||||||
{
|
|
||||||
mProjectionMatrix = projectionMatrix;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setLinearFac(float linearFac)
|
void setLinearFac(float linearFac)
|
||||||
{
|
{
|
||||||
mLinearFac = linearFac;
|
mLinearFac = linearFac;
|
||||||
|
@ -167,7 +210,6 @@ namespace MWRender
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
osg::Matrixf mProjectionMatrix;
|
|
||||||
float mLinearFac;
|
float mLinearFac;
|
||||||
float mNear;
|
float mNear;
|
||||||
float mFar;
|
float mFar;
|
||||||
|
@ -312,14 +354,16 @@ namespace MWRender
|
||||||
auto lightingMethod = SceneUtil::LightManager::getLightingMethodFromString(Settings::Manager::getString("lighting method", "Shaders"));
|
auto lightingMethod = SceneUtil::LightManager::getLightingMethodFromString(Settings::Manager::getString("lighting method", "Shaders"));
|
||||||
|
|
||||||
resourceSystem->getSceneManager()->setParticleSystemMask(MWRender::Mask_ParticleSystem);
|
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")
|
bool forceShaders = Settings::Manager::getBool("radial fog", "Shaders")
|
||||||
|| Settings::Manager::getBool("soft particles", "Shaders")
|
|| Settings::Manager::getBool("soft particles", "Shaders")
|
||||||
|| Settings::Manager::getBool("force shaders", "Shaders")
|
|| Settings::Manager::getBool("force shaders", "Shaders")
|
||||||
|| Settings::Manager::getBool("enable shadows", "Shadows")
|
|| Settings::Manager::getBool("enable shadows", "Shadows")
|
||||||
|| lightingMethod != SceneUtil::LightingMethod::FFP
|
|| lightingMethod != SceneUtil::LightingMethod::FFP
|
||||||
|| reverseZ;
|
|| reverseZ
|
||||||
|
|| Stereo::getMultiview();
|
||||||
resourceSystem->getSceneManager()->setForceShaders(forceShaders);
|
resourceSystem->getSceneManager()->setForceShaders(forceShaders);
|
||||||
|
|
||||||
// FIXME: calling dummy method because terrain needs to know whether lighting is clamped
|
// FIXME: calling dummy method because terrain needs to know whether lighting is clamped
|
||||||
resourceSystem->getSceneManager()->setClampLighting(Settings::Manager::getBool("clamp lighting", "Shaders"));
|
resourceSystem->getSceneManager()->setClampLighting(Settings::Manager::getBool("clamp lighting", "Shaders"));
|
||||||
resourceSystem->getSceneManager()->setAutoUseNormalMaps(Settings::Manager::getBool("auto use object normal maps", "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["preLightEnv"] = Settings::Manager::getBool("apply lighting to environment maps", "Shaders") ? "1" : "0";
|
||||||
globalDefines["radialFog"] = Settings::Manager::getBool("radial fog", "Shaders") ? "1" : "0";
|
globalDefines["radialFog"] = Settings::Manager::getBool("radial fog", "Shaders") ? "1" : "0";
|
||||||
globalDefines["useGPUShader4"] = "0";
|
globalDefines["useGPUShader4"] = "0";
|
||||||
|
globalDefines["useOVR_multiview"] = "0";
|
||||||
|
globalDefines["numViews"] = "1";
|
||||||
|
|
||||||
for (auto itr = lightDefines.begin(); itr != lightDefines.end(); itr++)
|
for (auto itr = lightDefines.begin(); itr != lightDefines.end(); itr++)
|
||||||
globalDefines[itr->first] = itr->second;
|
globalDefines[itr->first] = itr->second;
|
||||||
|
@ -453,16 +499,19 @@ namespace MWRender
|
||||||
mSharedUniformStateUpdater = new SharedUniformStateUpdater(groundcover);
|
mSharedUniformStateUpdater = new SharedUniformStateUpdater(groundcover);
|
||||||
rootNode->addUpdateCallback(mSharedUniformStateUpdater);
|
rootNode->addUpdateCallback(mSharedUniformStateUpdater);
|
||||||
|
|
||||||
|
mPerViewUniformStateUpdater = new PerViewUniformStateUpdater();
|
||||||
|
rootNode->addCullCallback(mPerViewUniformStateUpdater);
|
||||||
|
|
||||||
mPostProcessor = new PostProcessor(viewer, mRootNode);
|
mPostProcessor = new PostProcessor(viewer, mRootNode);
|
||||||
resourceSystem->getSceneManager()->setDepthFormat(mPostProcessor->getDepthFormat());
|
resourceSystem->getSceneManager()->setDepthFormat(SceneUtil::AutoDepth::depthInternalFormat());
|
||||||
resourceSystem->getSceneManager()->setOpaqueDepthTex(mPostProcessor->getOpaqueDepthTex());
|
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.";
|
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
|
// water goes after terrain for correct waterculling order
|
||||||
mWater.reset(new Water(sceneRoot->getParent(0), sceneRoot, mResourceSystem, mViewer->getIncrementalCompileOperation(), resourcePath));
|
mWater.reset(new Water(sceneRoot->getParent(0), sceneRoot, mResourceSystem, mViewer->getIncrementalCompileOperation(), resourcePath));
|
||||||
|
|
||||||
mCamera.reset(new Camera(mViewer->getCamera()));
|
mCamera.reset(new Camera(mViewer->getCamera()));
|
||||||
|
|
||||||
mScreenshotManager.reset(new ScreenshotManager(viewer, mRootNode, sceneRoot, mResourceSystem, mWater.get()));
|
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()->setComputeNearFarMode(osg::Camera::DO_NOT_COMPUTE_NEAR_FAR);
|
||||||
mViewer->getCamera()->setCullingMode(cullingMode);
|
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::setHiddenNodeMask(Mask_UpdateVisitor);
|
||||||
NifOsg::Loader::setIntersectionDisabledNodeMask(Mask_Effect);
|
NifOsg::Loader::setIntersectionDisabledNodeMask(Mask_Effect);
|
||||||
Nif::NIFFile::setLoadUnsupportedFiles(Settings::Manager::getBool("load unsupported nif files", "Models"));
|
Nif::NIFFile::setLoadUnsupportedFiles(Settings::Manager::getBool("load unsupported nif files", "Models"));
|
||||||
|
@ -537,6 +587,7 @@ namespace MWRender
|
||||||
|
|
||||||
SceneUtil::setCameraClearDepth(mViewer->getCamera());
|
SceneUtil::setCameraClearDepth(mViewer->getCamera());
|
||||||
|
|
||||||
|
|
||||||
updateProjectionMatrix();
|
updateProjectionMatrix();
|
||||||
|
|
||||||
mViewer->getCamera()->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
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)
|
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);
|
bool enabled = !(mask&sToggleWorldMask);
|
||||||
if (enabled)
|
if (enabled)
|
||||||
mask |= sToggleWorldMask;
|
mask |= sToggleWorldMask;
|
||||||
else
|
else
|
||||||
mask &= ~sToggleWorldMask;
|
mask &= ~sToggleWorldMask;
|
||||||
mWater->showWorld(enabled);
|
mWater->showWorld(enabled);
|
||||||
mViewer->getCamera()->setCullMask(mask);
|
wm->setCullMask(mask);
|
||||||
return enabled;
|
return enabled;
|
||||||
}
|
}
|
||||||
else if (mode == Render_NavMesh)
|
else if (mode == Render_NavMesh)
|
||||||
|
@ -1155,14 +1207,23 @@ namespace MWRender
|
||||||
if (SceneUtil::AutoDepth::isReversed())
|
if (SceneUtil::AutoDepth::isReversed())
|
||||||
{
|
{
|
||||||
mSharedUniformStateUpdater->setLinearFac(-mNearClip / (mViewDistance - mNearClip) - 1.f);
|
mSharedUniformStateUpdater->setLinearFac(-mNearClip / (mViewDistance - mNearClip) - 1.f);
|
||||||
mSharedUniformStateUpdater->setProjectionMatrix(SceneUtil::getReversedZProjectionMatrixAsPerspective(fov, aspect, mNearClip, mViewDistance));
|
mPerViewUniformStateUpdater->setProjectionMatrix(SceneUtil::getReversedZProjectionMatrixAsPerspective(fov, aspect, mNearClip, mViewDistance));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
mSharedUniformStateUpdater->setProjectionMatrix(mViewer->getCamera()->getProjectionMatrix());
|
mPerViewUniformStateUpdater->setProjectionMatrix(mViewer->getCamera()->getProjectionMatrix());
|
||||||
|
|
||||||
mSharedUniformStateUpdater->setNear(mNearClip);
|
mSharedUniformStateUpdater->setNear(mNearClip);
|
||||||
mSharedUniformStateUpdater->setFar(mViewDistance);
|
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.
|
// 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.
|
// Limit FOV here just for sure, otherwise viewing distance can be too high.
|
||||||
|
|
|
@ -76,6 +76,7 @@ namespace MWRender
|
||||||
{
|
{
|
||||||
class StateUpdater;
|
class StateUpdater;
|
||||||
class SharedUniformStateUpdater;
|
class SharedUniformStateUpdater;
|
||||||
|
class PerViewUniformStateUpdater;
|
||||||
|
|
||||||
class EffectManager;
|
class EffectManager;
|
||||||
class ScreenshotManager;
|
class ScreenshotManager;
|
||||||
|
@ -97,7 +98,7 @@ namespace MWRender
|
||||||
class RenderingManager : public MWRender::RenderingInterface
|
class RenderingManager : public MWRender::RenderingInterface
|
||||||
{
|
{
|
||||||
public:
|
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,
|
Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue,
|
||||||
const std::string& resourcePath, DetourNavigator::Navigator& navigator, const MWWorld::GroundcoverStore& groundcoverStore);
|
const std::string& resourcePath, DetourNavigator::Navigator& navigator, const MWWorld::GroundcoverStore& groundcoverStore);
|
||||||
~RenderingManager();
|
~RenderingManager();
|
||||||
|
@ -255,6 +256,7 @@ namespace MWRender
|
||||||
|
|
||||||
void updateRecastMesh();
|
void updateRecastMesh();
|
||||||
|
|
||||||
|
|
||||||
osg::ref_ptr<osgUtil::IntersectionVisitor> getIntersectionVisitor(osgUtil::Intersector* intersector, bool ignorePlayer, bool ignoreActors);
|
osg::ref_ptr<osgUtil::IntersectionVisitor> getIntersectionVisitor(osgUtil::Intersector* intersector, bool ignorePlayer, bool ignoreActors);
|
||||||
|
|
||||||
osg::ref_ptr<osgUtil::IntersectionVisitor> mIntersectionVisitor;
|
osg::ref_ptr<osgUtil::IntersectionVisitor> mIntersectionVisitor;
|
||||||
|
@ -292,6 +294,7 @@ namespace MWRender
|
||||||
|
|
||||||
osg::ref_ptr<StateUpdater> mStateUpdater;
|
osg::ref_ptr<StateUpdater> mStateUpdater;
|
||||||
osg::ref_ptr<SharedUniformStateUpdater> mSharedUniformStateUpdater;
|
osg::ref_ptr<SharedUniformStateUpdater> mSharedUniformStateUpdater;
|
||||||
|
osg::ref_ptr<PerViewUniformStateUpdater> mPerViewUniformStateUpdater;
|
||||||
|
|
||||||
osg::Vec4f mAmbientColor;
|
osg::Vec4f mAmbientColor;
|
||||||
float mMinimumAmbientLuminance;
|
float mMinimumAmbientLuminance;
|
||||||
|
|
|
@ -13,6 +13,8 @@
|
||||||
#include <components/resource/scenemanager.hpp>
|
#include <components/resource/scenemanager.hpp>
|
||||||
#include <components/shader/shadermanager.hpp>
|
#include <components/shader/shadermanager.hpp>
|
||||||
#include <components/sceneutil/depth.hpp>
|
#include <components/sceneutil/depth.hpp>
|
||||||
|
#include <components/stereo/stereomanager.hpp>
|
||||||
|
#include <components/stereo/multiview.hpp>
|
||||||
|
|
||||||
#include <components/settings/settings.hpp>
|
#include <components/settings/settings.hpp>
|
||||||
|
|
||||||
|
@ -85,6 +87,12 @@ namespace MWRender
|
||||||
{
|
{
|
||||||
int screenW = renderInfo.getCurrentCamera()->getViewport()->width();
|
int screenW = renderInfo.getCurrentCamera()->getViewport()->width();
|
||||||
int screenH = renderInfo.getCurrentCamera()->getViewport()->height();
|
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;
|
double imageaspect = (double)mWidth/(double)mHeight;
|
||||||
int leftPadding = std::max(0, static_cast<int>(screenW - screenH * imageaspect) / 2);
|
int leftPadding = std::max(0, static_cast<int>(screenW - screenH * imageaspect) / 2);
|
||||||
int topPadding = std::max(0, static_cast<int>(screenH - screenW / imageaspect) / 2);
|
int 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.
|
// 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
|
// glReadPixel() cannot read from multisampled targets
|
||||||
PostProcessor* postProcessor = dynamic_cast<PostProcessor*>(renderInfo.getCurrentCamera()->getUserData());
|
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);
|
osg::FrameBufferObject* fbo = nullptr;
|
||||||
if (ext)
|
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);
|
renderInfo.getState()->glReadBuffer(GL_COLOR_ATTACHMENT0_EXT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -346,7 +360,7 @@ namespace MWRender
|
||||||
rttCamera->addChild(mWater->getReflectionNode());
|
rttCamera->addChild(mWater->getReflectionNode());
|
||||||
rttCamera->addChild(mWater->getRefractionNode());
|
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);
|
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/rng.hpp>
|
||||||
#include <components/misc/resourcehelpers.hpp>
|
#include <components/misc/resourcehelpers.hpp>
|
||||||
|
#include <components/stereo/stereomanager.hpp>
|
||||||
|
|
||||||
#include <components/nifosg/particle.hpp>
|
#include <components/nifosg/particle.hpp>
|
||||||
|
|
||||||
|
@ -332,8 +333,10 @@ namespace MWRender
|
||||||
|
|
||||||
if (mSceneManager->getForceShaders())
|
if (mSceneManager->getForceShaders())
|
||||||
{
|
{
|
||||||
auto vertex = mSceneManager->getShaderManager().getShader("sky_vertex.glsl", {}, osg::Shader::VERTEX);
|
Shader::ShaderManager::DefineMap defines = {};
|
||||||
auto fragment = mSceneManager->getShaderManager().getShader("sky_fragment.glsl", {}, osg::Shader::FRAGMENT);
|
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);
|
auto program = mSceneManager->getShaderManager().getProgram(vertex, fragment);
|
||||||
mEarlyRenderBinRoot->getOrCreateStateSet()->addUniform(new osg::Uniform("pass", -1));
|
mEarlyRenderBinRoot->getOrCreateStateSet()->addUniform(new osg::Uniform("pass", -1));
|
||||||
mEarlyRenderBinRoot->getOrCreateStateSet()->setAttributeAndModes(program, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
|
mEarlyRenderBinRoot->getOrCreateStateSet()->setAttributeAndModes(program, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include <osgParticle/Particle>
|
#include <osgParticle/Particle>
|
||||||
|
|
||||||
#include <components/misc/rng.hpp>
|
#include <components/misc/rng.hpp>
|
||||||
|
#include <components/stereo/stereomanager.hpp>
|
||||||
|
|
||||||
#include <components/resource/scenemanager.hpp>
|
#include <components/resource/scenemanager.hpp>
|
||||||
#include <components/resource/imagemanager.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()
|
CameraRelativeTransform::CameraRelativeTransform()
|
||||||
{
|
{
|
||||||
// Culling works in node-local space, not in camera space, so we can't cull this node correctly
|
// 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);
|
setCullingActive(false);
|
||||||
|
|
||||||
addCullCallback(new CameraRelativeTransformCullCallback);
|
addCullCallback(new CameraRelativeTransformCullCallback);
|
||||||
|
addCullCallback(new SkyMultiviewStatesetUpdater);
|
||||||
}
|
}
|
||||||
|
|
||||||
CameraRelativeTransform::CameraRelativeTransform(const CameraRelativeTransform& copy, const osg::CopyOp& copyop)
|
CameraRelativeTransform::CameraRelativeTransform(const CameraRelativeTransform& copy, const osg::CopyOp& copyop)
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include <osg/PositionAttitudeTransform>
|
#include <osg/PositionAttitudeTransform>
|
||||||
#include <osg/ClipNode>
|
#include <osg/ClipNode>
|
||||||
#include <osg/FrontFace>
|
#include <osg/FrontFace>
|
||||||
|
#include <osg/ViewportIndexed>
|
||||||
|
|
||||||
#include <osgDB/ReadFile>
|
#include <osgDB/ReadFile>
|
||||||
|
|
||||||
|
@ -33,6 +34,7 @@
|
||||||
|
|
||||||
#include <components/misc/constants.hpp>
|
#include <components/misc/constants.hpp>
|
||||||
#include <components/misc/stringops.hpp>
|
#include <components/misc/stringops.hpp>
|
||||||
|
#include <components/stereo/stereomanager.hpp>
|
||||||
|
|
||||||
#include <components/nifosg/controller.hpp>
|
#include <components/nifosg/controller.hpp>
|
||||||
|
|
||||||
|
@ -44,6 +46,8 @@
|
||||||
|
|
||||||
#include "../mwworld/cellstore.hpp"
|
#include "../mwworld/cellstore.hpp"
|
||||||
|
|
||||||
|
#include "../mwbase/environment.hpp"
|
||||||
|
|
||||||
#include "vismask.hpp"
|
#include "vismask.hpp"
|
||||||
#include "ripplesimulation.hpp"
|
#include "ripplesimulation.hpp"
|
||||||
#include "renderbin.hpp"
|
#include "renderbin.hpp"
|
||||||
|
@ -166,7 +170,7 @@ private:
|
||||||
class InheritViewPointCallback : public SceneUtil::NodeCallback<InheritViewPointCallback, osg::Node*, osgUtil::CullVisitor*>
|
class InheritViewPointCallback : public SceneUtil::NodeCallback<InheritViewPointCallback, osg::Node*, osgUtil::CullVisitor*>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
InheritViewPointCallback() {}
|
InheritViewPointCallback() {}
|
||||||
|
|
||||||
void operator()(osg::Node* node, osgUtil::CullVisitor* cv)
|
void operator()(osg::Node* node, osgUtil::CullVisitor* cv)
|
||||||
{
|
{
|
||||||
|
@ -260,7 +264,7 @@ class Refraction : public SceneUtil::RTTNode
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Refraction(uint32_t rttSize)
|
Refraction(uint32_t rttSize)
|
||||||
: RTTNode(rttSize, rttSize, 1, false)
|
: RTTNode(rttSize, rttSize, 0, false, 1, StereoAwareness::Aware)
|
||||||
, mNodeMask(Refraction::sDefaultCullMask)
|
, mNodeMask(Refraction::sDefaultCullMask)
|
||||||
{
|
{
|
||||||
mClipCullNode = new ClipCullNode;
|
mClipCullNode = new ClipCullNode;
|
||||||
|
@ -335,7 +339,7 @@ class Reflection : public SceneUtil::RTTNode
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Reflection(uint32_t rttSize, bool isInterior)
|
Reflection(uint32_t rttSize, bool isInterior)
|
||||||
: RTTNode(rttSize, rttSize, 0, false)
|
: RTTNode(rttSize, rttSize, 0, false, 0, StereoAwareness::Aware)
|
||||||
{
|
{
|
||||||
setInterior(isInterior);
|
setInterior(isInterior);
|
||||||
mClipCullNode = new ClipCullNode;
|
mClipCullNode = new ClipCullNode;
|
||||||
|
@ -458,6 +462,7 @@ Water::Water(osg::Group *parent, osg::Group* sceneRoot, Resource::ResourceSystem
|
||||||
mWaterGeom->setDrawCallback(new DepthClampCallback);
|
mWaterGeom->setDrawCallback(new DepthClampCallback);
|
||||||
mWaterGeom->setNodeMask(Mask_Water);
|
mWaterGeom->setNodeMask(Mask_Water);
|
||||||
mWaterGeom->setDataVariance(osg::Object::STATIC);
|
mWaterGeom->setDataVariance(osg::Object::STATIC);
|
||||||
|
mWaterGeom->setName("Water Geometry");
|
||||||
|
|
||||||
mWaterNode = new osg::PositionAttitudeTransform;
|
mWaterNode = new osg::PositionAttitudeTransform;
|
||||||
mWaterNode->setName("Water Root");
|
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));
|
osg::ref_ptr<osg::Geometry> geom2 (osg::clone(mWaterGeom.get(), osg::CopyOp::DEEP_COPY_NODES));
|
||||||
createSimpleWaterStateSet(geom2, Fallback::Map::getFloat("Water_Map_Alpha"));
|
createSimpleWaterStateSet(geom2, Fallback::Map::getFloat("Water_Map_Alpha"));
|
||||||
geom2->setNodeMask(Mask_SimpleWater);
|
geom2->setNodeMask(Mask_SimpleWater);
|
||||||
|
geom2->setName("Simple Water Geometry");
|
||||||
mWaterNode->addChild(geom2);
|
mWaterNode->addChild(geom2);
|
||||||
|
|
||||||
mSceneRoot->addChild(mWaterNode);
|
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);
|
const auto rippleDetail = std::clamp(Settings::Manager::getInt("rain ripple detail", "Water"), 0, 2);
|
||||||
defineMap["rain_ripple_detail"] = std::to_string(rippleDetail);
|
defineMap["rain_ripple_detail"] = std::to_string(rippleDetail);
|
||||||
|
|
||||||
|
Stereo::Manager::instance().shaderStereoDefines(defineMap);
|
||||||
|
|
||||||
Shader::ShaderManager& shaderMgr = mResourceSystem->getSceneManager()->getShaderManager();
|
Shader::ShaderManager& shaderMgr = mResourceSystem->getSceneManager()->getShaderManager();
|
||||||
osg::ref_ptr<osg::Shader> vertexShader(shaderMgr.getShader("water_vertex.glsl", defineMap, osg::Shader::VERTEX));
|
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->setMaxAnisotropy(16);
|
||||||
normalMap->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR_MIPMAP_LINEAR);
|
normalMap->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR_MIPMAP_LINEAR);
|
||||||
normalMap->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
|
normalMap->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
|
||||||
|
|
||||||
|
|
||||||
mRainIntensityUpdater = new RainIntensityUpdater();
|
mRainIntensityUpdater = new RainIntensityUpdater();
|
||||||
node->setUpdateCallback(mRainIntensityUpdater);
|
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
|
clone attach visitor util statesetupdater controller skeleton riggeometry morphgeometry lightcontroller
|
||||||
lightmanager lightutil positionattitudetransform workqueue pathgridutil waterutil writescene serialize optimizer
|
lightmanager lightutil positionattitudetransform workqueue pathgridutil waterutil writescene serialize optimizer
|
||||||
actorutil detourdebugdraw navmesh agentpath shadow mwshadowtechnique recastmesh shadowsbin osgacontroller rtt
|
actorutil detourdebugdraw navmesh agentpath shadow mwshadowtechnique recastmesh shadowsbin osgacontroller rtt
|
||||||
screencapture depth
|
screencapture depth color
|
||||||
)
|
)
|
||||||
|
|
||||||
add_component_dir (nif
|
add_component_dir (nif
|
||||||
|
@ -196,6 +196,10 @@ add_component_dir (misc
|
||||||
compression osguservalues errorMarker color
|
compression osguservalues errorMarker color
|
||||||
)
|
)
|
||||||
|
|
||||||
|
add_component_dir (stereo
|
||||||
|
frustum multiview stereomanager types
|
||||||
|
)
|
||||||
|
|
||||||
add_component_dir (debug
|
add_component_dir (debug
|
||||||
debugging debuglog gldebug
|
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 <algorithm>
|
||||||
|
|
||||||
#include <SDL_opengl_glext.h>
|
#include <components/debug/debuglog.hpp>
|
||||||
|
|
||||||
#include <components/settings/settings.hpp>
|
#include <components/settings/settings.hpp>
|
||||||
|
|
||||||
namespace SceneUtil
|
namespace SceneUtil
|
||||||
|
@ -56,4 +55,144 @@ namespace SceneUtil
|
||||||
|
|
||||||
return std::find(formats.cbegin(), formats.cend(), format) != formats.cend();
|
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
|
#define GL_DEPTH32F_STENCIL8_NV 0x8DAC
|
||||||
#endif
|
#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
|
namespace SceneUtil
|
||||||
{
|
{
|
||||||
// Sets camera clear depth to 0 if reversed depth buffer is in use, 1 otherwise.
|
// 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.
|
// Returns true if the GL format is a floating point depth format.
|
||||||
bool isFloatingPointDepthFormat(GLenum 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
|
// 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
|
class AutoDepth : public osg::Depth
|
||||||
{
|
{
|
||||||
|
@ -72,9 +104,29 @@ namespace SceneUtil
|
||||||
return AutoDepth::sReversed;
|
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:
|
private:
|
||||||
|
|
||||||
static inline bool sReversed = false;
|
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
|
osg::Depth::Function getReversedDepthFunction() const
|
||||||
{
|
{
|
||||||
|
@ -116,6 +168,23 @@ namespace SceneUtil
|
||||||
traverse(node);
|
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
|
#endif
|
||||||
|
|
|
@ -633,6 +633,7 @@ void MWShadowTechnique::ShadowData::releaseGLObjects(osg::State* state) const
|
||||||
// Frustum
|
// Frustum
|
||||||
//
|
//
|
||||||
MWShadowTechnique::Frustum::Frustum(osgUtil::CullVisitor* cv, double minZNear, double maxZFar):
|
MWShadowTechnique::Frustum::Frustum(osgUtil::CullVisitor* cv, double minZNear, double maxZFar):
|
||||||
|
useCustomClipSpace(false),
|
||||||
corners(8),
|
corners(8),
|
||||||
faces(6),
|
faces(6),
|
||||||
edges(12)
|
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<<"zNear = "<<zNear<<", zFar = "<<zFar<<std::endl;
|
||||||
OSG_INFO<<"Projection matrix after clamping "<<projectionMatrix<<std::endl;
|
OSG_INFO<<"Projection matrix after clamping "<<projectionMatrix<<std::endl;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
corners[0].set(-1.0,-1.0,-1.0);
|
void SceneUtil::MWShadowTechnique::Frustum::setCustomClipSpace(const osg::BoundingBoxd& clipCornersOverride)
|
||||||
corners[1].set(1.0,-1.0,-1.0);
|
{
|
||||||
corners[2].set(1.0,-1.0,1.0);
|
useCustomClipSpace = true;
|
||||||
corners[3].set(-1.0,-1.0,1.0);
|
customClipSpace = clipCornersOverride;
|
||||||
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::init()
|
||||||
|
{
|
||||||
osg::Matrixd clipToWorld;
|
osg::Matrixd clipToWorld;
|
||||||
clipToWorld.invert(modelViewMatrix * projectionMatrix);
|
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
|
// transform frustum corners from clipspace to world coords, and compute center
|
||||||
for(Vertices::iterator itr = corners.begin();
|
for(Vertices::iterator itr = corners.begin();
|
||||||
itr != corners.end();
|
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
|
// 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);
|
osg::ref_ptr<osg::GLExtensions> exts = osg::GLExtensions::Get(0, false);
|
||||||
std::string useGPUShader4 = exts && exts->isGpuShader4Supported ? "1" : "0";
|
std::string useGPUShader4 = exts && exts->isGpuShader4Supported ? "1" : "0";
|
||||||
for (int alphaFunc = GL_NEVER; alphaFunc <= GL_ALWAYS; ++alphaFunc)
|
for (int alphaFunc = GL_NEVER; alphaFunc <= GL_ALWAYS; ++alphaFunc)
|
||||||
|
@ -931,6 +953,67 @@ MWShadowTechnique::ViewDependentData* MWShadowTechnique::getViewDependentData(os
|
||||||
return vdd.release();
|
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)
|
void MWShadowTechnique::update(osg::NodeVisitor& nv)
|
||||||
{
|
{
|
||||||
OSG_INFO<<"MWShadowTechnique::update(osg::NodeVisitor& "<<&nv<<")"<<std::endl;
|
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)
|
void MWShadowTechnique::cull(osgUtil::CullVisitor& cv)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (!_enableShadows)
|
if (!_enableShadows)
|
||||||
{
|
{
|
||||||
if (mSetDummyStateWhenDisabled)
|
if (mSetDummyStateWhenDisabled)
|
||||||
|
@ -1036,7 +1120,7 @@ void MWShadowTechnique::cull(osgUtil::CullVisitor& cv)
|
||||||
// are all done correctly.
|
// are all done correctly.
|
||||||
cv.computeNearPlane();
|
cv.computeNearPlane();
|
||||||
}
|
}
|
||||||
|
|
||||||
// clamp the minZNear and maxZFar to those provided by ShadowSettings
|
// clamp the minZNear and maxZFar to those provided by ShadowSettings
|
||||||
maxZFar = osg::minimum(settings->getMaximumShadowMapDistance(),maxZFar);
|
maxZFar = osg::minimum(settings->getMaximumShadowMapDistance(),maxZFar);
|
||||||
if (minZNear>maxZFar) minZNear = maxZFar*settings->getMinimumShadowMapNearFarRatio();
|
if (minZNear>maxZFar) minZNear = maxZFar*settings->getMinimumShadowMapNearFarRatio();
|
||||||
|
@ -1047,6 +1131,36 @@ void MWShadowTechnique::cull(osgUtil::CullVisitor& cv)
|
||||||
cv.setNearFarRatio(minZNear / maxZFar);
|
cv.setNearFarRatio(minZNear / maxZFar);
|
||||||
|
|
||||||
Frustum frustum(&cv, 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)
|
if (_debugHud)
|
||||||
{
|
{
|
||||||
osg::ref_ptr<osg::Vec3Array> vertexArray = new osg::Vec3Array();
|
osg::ref_ptr<osg::Vec3Array> vertexArray = new osg::Vec3Array();
|
||||||
|
@ -1066,7 +1180,7 @@ void MWShadowTechnique::cull(osgUtil::CullVisitor& cv)
|
||||||
reducedNear = minZNear;
|
reducedNear = minZNear;
|
||||||
reducedFar = maxZFar;
|
reducedFar = maxZFar;
|
||||||
}
|
}
|
||||||
|
|
||||||
// return compute near far mode back to it's original settings
|
// return compute near far mode back to it's original settings
|
||||||
cv.setComputeNearFarMode(cachedNearFarMode);
|
cv.setComputeNearFarMode(cachedNearFarMode);
|
||||||
|
|
||||||
|
@ -1430,7 +1544,7 @@ void MWShadowTechnique::cull(osgUtil::CullVisitor& cv)
|
||||||
vdsmCallback->getProjectionMatrix()->set(camera->getProjectionMatrix());
|
vdsmCallback->getProjectionMatrix()->set(camera->getProjectionMatrix());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4.4 compute main scene graph TexGen + uniform settings + setup state
|
// 4.4 compute main scene graph TexGen + uniform settings + setup state
|
||||||
//
|
//
|
||||||
assignTexGenSettings(&cv, camera.get(), textureUnit, sd->_texgen.get());
|
assignTexGenSettings(&cv, camera.get(), textureUnit, sd->_texgen.get());
|
||||||
|
@ -1459,6 +1573,8 @@ void MWShadowTechnique::cull(osgUtil::CullVisitor& cv)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vdd->setNumValidShadows(numValidShadows);
|
||||||
|
|
||||||
if (numValidShadows>0)
|
if (numValidShadows>0)
|
||||||
{
|
{
|
||||||
prepareStateSetForRenderingShadow(*vdd, cv.getTraversalNumber());
|
prepareStateSetForRenderingShadow(*vdd, cv.getTraversalNumber());
|
||||||
|
@ -1665,7 +1781,14 @@ osg::Polytope MWShadowTechnique::computeLightViewFrustumPolytope(Frustum& frustu
|
||||||
OSG_INFO<<"computeLightViewFrustumPolytope()"<<std::endl;
|
OSG_INFO<<"computeLightViewFrustumPolytope()"<<std::endl;
|
||||||
|
|
||||||
osg::Polytope polytope;
|
osg::Polytope polytope;
|
||||||
polytope.setToUnitFrustum();
|
if (frustum.useCustomClipSpace)
|
||||||
|
{
|
||||||
|
polytope.setToBoundingBox(frustum.customClipSpace);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
polytope.setToUnitFrustum();
|
||||||
|
}
|
||||||
|
|
||||||
polytope.transformProvidingInverse( frustum.projectionMatrix );
|
polytope.transformProvidingInverse( frustum.projectionMatrix );
|
||||||
polytope.transformProvidingInverse( frustum.modelViewMatrix );
|
polytope.transformProvidingInverse( frustum.modelViewMatrix );
|
||||||
|
|
|
@ -119,10 +119,15 @@ namespace SceneUtil {
|
||||||
struct Frustum
|
struct Frustum
|
||||||
{
|
{
|
||||||
Frustum(osgUtil::CullVisitor* cv, double minZNear, double maxZFar);
|
Frustum(osgUtil::CullVisitor* cv, double minZNear, double maxZFar);
|
||||||
|
void setCustomClipSpace(const osg::BoundingBoxd& clipCornersOverride);
|
||||||
|
void init();
|
||||||
|
|
||||||
osg::Matrixd projectionMatrix;
|
osg::Matrixd projectionMatrix;
|
||||||
osg::Matrixd modelViewMatrix;
|
osg::Matrixd modelViewMatrix;
|
||||||
|
|
||||||
|
bool useCustomClipSpace;
|
||||||
|
osg::BoundingBoxd customClipSpace;
|
||||||
|
|
||||||
typedef std::vector<osg::Vec3d> Vertices;
|
typedef std::vector<osg::Vec3d> Vertices;
|
||||||
Vertices corners;
|
Vertices corners;
|
||||||
|
|
||||||
|
@ -140,6 +145,18 @@ namespace SceneUtil {
|
||||||
osg::Vec3d frustumCenterLine;
|
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
|
// forward declare
|
||||||
class ViewDependentData;
|
class ViewDependentData;
|
||||||
|
|
||||||
|
@ -197,7 +214,12 @@ namespace SceneUtil {
|
||||||
|
|
||||||
virtual void releaseGLObjects(osg::State* = 0) const;
|
virtual void releaseGLObjects(osg::State* = 0) const;
|
||||||
|
|
||||||
|
unsigned int numValidShadows(void) const { return _numValidShadows; }
|
||||||
|
|
||||||
|
void setNumValidShadows(unsigned int numValidShadows) { _numValidShadows = numValidShadows; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
friend class MWShadowTechnique;
|
||||||
virtual ~ViewDependentData() {}
|
virtual ~ViewDependentData() {}
|
||||||
|
|
||||||
MWShadowTechnique* _viewDependentShadowMap;
|
MWShadowTechnique* _viewDependentShadowMap;
|
||||||
|
@ -206,13 +228,19 @@ namespace SceneUtil {
|
||||||
|
|
||||||
LightDataList _lightDataList;
|
LightDataList _lightDataList;
|
||||||
ShadowDataList _shadowDataList;
|
ShadowDataList _shadowDataList;
|
||||||
|
|
||||||
|
unsigned int _numValidShadows;
|
||||||
};
|
};
|
||||||
|
|
||||||
virtual ViewDependentData* createViewDependentData(osgUtil::CullVisitor* cv);
|
virtual ViewDependentData* createViewDependentData(osgUtil::CullVisitor* cv);
|
||||||
|
|
||||||
ViewDependentData* getViewDependentData(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();
|
virtual void createShaders();
|
||||||
|
|
||||||
|
@ -246,6 +274,8 @@ namespace SceneUtil {
|
||||||
typedef std::map< osgUtil::CullVisitor*, osg::ref_ptr<ViewDependentData> > ViewDependentDataMap;
|
typedef std::map< osgUtil::CullVisitor*, osg::ref_ptr<ViewDependentData> > ViewDependentDataMap;
|
||||||
mutable std::mutex _viewDependentDataMapMutex;
|
mutable std::mutex _viewDependentDataMapMutex;
|
||||||
ViewDependentDataMap _viewDependentDataMap;
|
ViewDependentDataMap _viewDependentDataMap;
|
||||||
|
osg::ref_ptr<CustomFrustumCallback> _customFrustumCallback;
|
||||||
|
osg::BoundingBoxd _customClipSpace;
|
||||||
|
|
||||||
osg::ref_ptr<osg::StateSet> _shadowRecievingPlaceholderStateSet;
|
osg::ref_ptr<osg::StateSet> _shadowRecievingPlaceholderStateSet;
|
||||||
|
|
||||||
|
|
|
@ -4,11 +4,16 @@
|
||||||
#include <osg/Node>
|
#include <osg/Node>
|
||||||
#include <osg/NodeVisitor>
|
#include <osg/NodeVisitor>
|
||||||
#include <osg/Texture2D>
|
#include <osg/Texture2D>
|
||||||
|
#include <osg/Texture2DArray>
|
||||||
#include <osgUtil/CullVisitor>
|
#include <osgUtil/CullVisitor>
|
||||||
|
|
||||||
#include <components/sceneutil/nodecallback.hpp>
|
#include <components/sceneutil/nodecallback.hpp>
|
||||||
#include <components/settings/settings.hpp>
|
#include <components/settings/settings.hpp>
|
||||||
#include <components/sceneutil/depth.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
|
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)
|
: mTextureWidth(textureWidth)
|
||||||
, mTextureHeight(textureHeight)
|
, mTextureHeight(textureHeight)
|
||||||
|
, mSamples(samples)
|
||||||
|
, mGenerateMipmaps(generateMipmaps)
|
||||||
|
, mColorBufferInternalFormat(Color::colorInternalFormat())
|
||||||
|
, mDepthBufferInternalFormat(AutoDepth::depthInternalFormat())
|
||||||
, mRenderOrderNum(renderOrderNum)
|
, mRenderOrderNum(renderOrderNum)
|
||||||
, mDoPerViewMapping(doPerViewMapping)
|
, mStereoAwareness(stereoAwareness)
|
||||||
{
|
{
|
||||||
addCullCallback(new CullCallback);
|
addCullCallback(new CullCallback);
|
||||||
setCullingActive(false);
|
setCullingActive(false);
|
||||||
|
@ -34,28 +43,139 @@ namespace SceneUtil
|
||||||
|
|
||||||
RTTNode::~RTTNode()
|
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)
|
void RTTNode::cull(osgUtil::CullVisitor* cv)
|
||||||
{
|
{
|
||||||
|
auto frameNumber = cv->getFrameStamp()->getFrameNumber();
|
||||||
auto* vdd = getViewDependentData(cv);
|
auto* vdd = getViewDependentData(cv);
|
||||||
apply(vdd->mCamera);
|
if (frameNumber > vdd->mFrameNumber)
|
||||||
vdd->mCamera->accept(*cv);
|
{
|
||||||
|
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)
|
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)
|
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)
|
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.
|
// 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.
|
// This is safe since the visitor is never dereferenced.
|
||||||
cv = nullptr;
|
cv = nullptr;
|
||||||
|
@ -63,7 +183,8 @@ namespace SceneUtil
|
||||||
if (mViewDependentDataMap.count(cv) == 0)
|
if (mViewDependentDataMap.count(cv) == 0)
|
||||||
{
|
{
|
||||||
auto camera = new osg::Camera();
|
auto camera = new osg::Camera();
|
||||||
mViewDependentDataMap[cv].reset(new ViewDependentData);
|
auto vdd = std::make_shared<ViewDependentData>();
|
||||||
|
mViewDependentDataMap[cv] = vdd;
|
||||||
mViewDependentDataMap[cv]->mCamera = camera;
|
mViewDependentDataMap[cv]->mCamera = camera;
|
||||||
|
|
||||||
camera->setRenderOrder(osg::Camera::PRE_RENDER, mRenderOrderNum);
|
camera->setRenderOrder(osg::Camera::PRE_RENDER, mRenderOrderNum);
|
||||||
|
@ -72,34 +193,63 @@ namespace SceneUtil
|
||||||
camera->setViewport(0, 0, mTextureWidth, mTextureHeight);
|
camera->setViewport(0, 0, mTextureWidth, mTextureHeight);
|
||||||
SceneUtil::setCameraClearDepth(camera);
|
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))
|
||||||
if (camera->getBufferAttachmentMap().count(osg::Camera::COLOR_BUFFER) == 0)
|
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;
|
// Create any buffer attachments not added in setDefaults
|
||||||
colorBuffer->setTextureSize(mTextureWidth, mTextureHeight);
|
if (camera->getBufferAttachmentMap().count(osg::Camera::COLOR_BUFFER) == 0)
|
||||||
colorBuffer->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
|
{
|
||||||
colorBuffer->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
|
vdd->mColorTexture = createTextureArray(mColorBufferInternalFormat);
|
||||||
colorBuffer->setInternalFormat(GL_RGB);
|
camera->attach(osg::Camera::COLOR_BUFFER, vdd->mColorTexture, 0, osg::Camera::FACE_CONTROLLED_BY_MULTIVIEW_SHADER, mGenerateMipmaps, mSamples);
|
||||||
colorBuffer->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
|
SceneUtil::attachAlphaToCoverageFriendlyFramebufferToCamera(camera, osg::Camera::COLOR_BUFFER, vdd->mColorTexture, 0, osg::Camera::FACE_CONTROLLED_BY_MULTIVIEW_SHADER, mGenerateMipmaps);
|
||||||
colorBuffer->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
|
}
|
||||||
camera->attach(osg::Camera::COLOR_BUFFER, colorBuffer);
|
|
||||||
SceneUtil::attachAlphaToCoverageFriendlyFramebufferToCamera(camera, osg::Camera::COLOR_BUFFER, colorBuffer);
|
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;
|
camera->getBufferAttachmentMap()[osg::Camera::COLOR_BUFFER]._internalFormat = mColorBufferInternalFormat;
|
||||||
depthBuffer->setTextureSize(mTextureWidth, mTextureHeight);
|
camera->getBufferAttachmentMap()[osg::Camera::COLOR_BUFFER]._mipMapGeneration = mGenerateMipmaps;
|
||||||
depthBuffer->setSourceFormat(GL_DEPTH_STENCIL_EXT);
|
camera->getBufferAttachmentMap()[osg::Camera::PACKED_DEPTH_STENCIL_BUFFER]._internalFormat = mDepthBufferInternalFormat;
|
||||||
depthBuffer->setInternalFormat(GL_DEPTH24_STENCIL8_EXT);
|
camera->getBufferAttachmentMap()[osg::Camera::PACKED_DEPTH_STENCIL_BUFFER]._mipMapGeneration = mGenerateMipmaps;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
namespace osg
|
namespace osg
|
||||||
{
|
{
|
||||||
class Texture2D;
|
class Texture2D;
|
||||||
|
class Texture2DArray;
|
||||||
class Camera;
|
class Camera;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,6 +20,8 @@ namespace osgUtil
|
||||||
|
|
||||||
namespace SceneUtil
|
namespace SceneUtil
|
||||||
{
|
{
|
||||||
|
class CreateTextureViewsCallback;
|
||||||
|
|
||||||
/// @brief Implements per-view RTT operations.
|
/// @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
|
/// @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.
|
/// 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
|
class RTTNode : public osg::Node
|
||||||
{
|
{
|
||||||
public:
|
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();
|
~RTTNode();
|
||||||
|
|
||||||
osg::Texture* getColorTexture(osgUtil::CullVisitor* cv);
|
osg::Texture* getColorTexture(osgUtil::CullVisitor* cv);
|
||||||
|
|
||||||
osg::Texture* getDepthTexture(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
|
/// 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.
|
/// @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) {};
|
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);
|
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:
|
private:
|
||||||
|
friend class CreateTextureViewsCallback;
|
||||||
struct ViewDependentData
|
struct ViewDependentData
|
||||||
{
|
{
|
||||||
osg::ref_ptr<osg::Camera> mCamera;
|
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);
|
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;
|
ViewDependentDataMap mViewDependentDataMap;
|
||||||
uint32_t mTextureWidth;
|
uint32_t mTextureWidth;
|
||||||
uint32_t mTextureHeight;
|
uint32_t mTextureHeight;
|
||||||
|
uint32_t mSamples;
|
||||||
|
bool mGenerateMipmaps;
|
||||||
|
GLint mColorBufferInternalFormat;
|
||||||
|
GLint mDepthBufferInternalFormat;
|
||||||
int mRenderOrderNum;
|
int mRenderOrderNum;
|
||||||
bool mDoPerViewMapping;
|
StereoAwareness mStereoAwareness;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
#include <components/misc/stringops.hpp>
|
#include <components/misc/stringops.hpp>
|
||||||
#include <components/settings/settings.hpp>
|
#include <components/settings/settings.hpp>
|
||||||
|
#include <components/stereo/stereomanager.hpp>
|
||||||
|
|
||||||
#include "mwshadowtechnique.hpp"
|
#include "mwshadowtechnique.hpp"
|
||||||
|
|
||||||
|
@ -101,6 +102,7 @@ namespace SceneUtil
|
||||||
mIndoorShadowCastingMask(indoorShadowCastingMask)
|
mIndoorShadowCastingMask(indoorShadowCastingMask)
|
||||||
{
|
{
|
||||||
mShadowedScene->setShadowTechnique(mShadowTechnique);
|
mShadowedScene->setShadowTechnique(mShadowTechnique);
|
||||||
|
Stereo::Manager::instance().setShadowTechnique(mShadowTechnique);
|
||||||
|
|
||||||
mShadowedScene->addChild(sceneRoot);
|
mShadowedScene->addChild(sceneRoot);
|
||||||
rootNode->addChild(mShadowedScene);
|
rootNode->addChild(mShadowedScene);
|
||||||
|
@ -117,6 +119,7 @@ namespace SceneUtil
|
||||||
|
|
||||||
ShadowManager::~ShadowManager()
|
ShadowManager::~ShadowManager()
|
||||||
{
|
{
|
||||||
|
Stereo::Manager::instance().setShadowTechnique(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
Shader::ShaderManager::DefineMap ShadowManager::getShadowDefines()
|
Shader::ShaderManager::DefineMap ShadowManager::getShadowDefines()
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
#include "statesetupdater.hpp"
|
#include "statesetupdater.hpp"
|
||||||
|
|
||||||
|
#include <components/stereo/stereomanager.hpp>
|
||||||
|
|
||||||
#include <osg/Node>
|
#include <osg/Node>
|
||||||
#include <osg/NodeVisitor>
|
#include <osg/NodeVisitor>
|
||||||
#include <osgUtil/CullVisitor>
|
#include <osgUtil/CullVisitor>
|
||||||
|
@ -38,6 +40,12 @@ namespace SceneUtil
|
||||||
{
|
{
|
||||||
auto stateset = getCvDependentStateset(cv);
|
auto stateset = getCvDependentStateset(cv);
|
||||||
apply(stateset, 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);
|
cv->pushStateSet(stateset);
|
||||||
traverse(node, cv);
|
traverse(node, cv);
|
||||||
cv->popStateSet();
|
cv->popStateSet();
|
||||||
|
|
|
@ -41,6 +41,11 @@ namespace SceneUtil
|
||||||
/// even if it has not changed since the last frame.
|
/// even if it has not changed since the last frame.
|
||||||
virtual void apply(osg::StateSet* stateset, osg::NodeVisitor* nv) {}
|
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
|
/// Set default state - optionally override in derived classes
|
||||||
/// @par May be used e.g. to allocate StateAttributes.
|
/// @par May be used e.g. to allocate StateAttributes.
|
||||||
virtual void setDefaults(osg::StateSet* stateset) {}
|
virtual void setDefaults(osg::StateSet* stateset) {}
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
|
|
||||||
#include <components/debug/debuglog.hpp>
|
#include <components/debug/debuglog.hpp>
|
||||||
#include <components/misc/stringops.hpp>
|
#include <components/misc/stringops.hpp>
|
||||||
|
#include <components/stereo/stereomanager.hpp>
|
||||||
#include <components/resource/imagemanager.hpp>
|
#include <components/resource/imagemanager.hpp>
|
||||||
#include <components/vfs/manager.hpp>
|
#include <components/vfs/manager.hpp>
|
||||||
#include <components/sceneutil/riggeometry.hpp>
|
#include <components/sceneutil/riggeometry.hpp>
|
||||||
|
@ -647,6 +648,8 @@ namespace Shader
|
||||||
|
|
||||||
defineMap["softParticles"] = reqs.mSoftParticles ? "1" : "0";
|
defineMap["softParticles"] = reqs.mSoftParticles ? "1" : "0";
|
||||||
|
|
||||||
|
Stereo::Manager::instance().shaderStereoDefines(defineMap);
|
||||||
|
|
||||||
std::string shaderPrefix;
|
std::string shaderPrefix;
|
||||||
if (!node.getUserValue("shaderPrefix", shaderPrefix))
|
if (!node.getUserValue("shaderPrefix", shaderPrefix))
|
||||||
shaderPrefix = mDefaultShaderPrefix;
|
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/TexMat>
|
||||||
#include <osg/BlendFunc>
|
#include <osg/BlendFunc>
|
||||||
|
|
||||||
|
#include <components/stereo/stereomanager.hpp>
|
||||||
#include <components/shader/shadermanager.hpp>
|
#include <components/shader/shadermanager.hpp>
|
||||||
#include <components/sceneutil/depth.hpp>
|
#include <components/sceneutil/depth.hpp>
|
||||||
|
|
||||||
|
@ -251,6 +252,8 @@ namespace Terrain
|
||||||
defineMap["specularMap"] = it->mSpecular ? "1" : "0";
|
defineMap["specularMap"] = it->mSpecular ? "1" : "0";
|
||||||
defineMap["parallax"] = (it->mNormalMap && it->mParallax) ? "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> 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);
|
osg::ref_ptr<osg::Shader> fragmentShader = shaderManager->getShader("terrain_fragment.glsl", defineMap, osg::Shader::FRAGMENT);
|
||||||
if (!vertexShader || !fragmentShader)
|
if (!vertexShader || !fragmentShader)
|
||||||
|
|
|
@ -1140,3 +1140,60 @@ lua debug = false
|
||||||
# Set the maximum number of threads used for Lua scripts.
|
# Set the maximum number of threads used for Lua scripts.
|
||||||
# If zero, Lua scripts are processed in the main thread.
|
# If zero, Lua scripts are processed in the main thread.
|
||||||
lua num threads = 1
|
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
|
objects_fragment.glsl
|
||||||
openmw_fragment.glsl
|
openmw_fragment.glsl
|
||||||
openmw_fragment.h.glsl
|
openmw_fragment.h.glsl
|
||||||
|
openmw_fragment_multiview.glsl
|
||||||
openmw_vertex.glsl
|
openmw_vertex.glsl
|
||||||
openmw_vertex.h.glsl
|
openmw_vertex.h.glsl
|
||||||
|
openmw_vertex_multiview.glsl
|
||||||
terrain_vertex.glsl
|
terrain_vertex.glsl
|
||||||
terrain_fragment.glsl
|
terrain_fragment.glsl
|
||||||
lighting.glsl
|
lighting.glsl
|
||||||
|
|
|
@ -144,7 +144,7 @@ void main(void)
|
||||||
if (length(gl_ModelViewMatrix * vec4(position, 1.0)) > @groundcoverFadeEnd)
|
if (length(gl_ModelViewMatrix * vec4(position, 1.0)) > @groundcoverFadeEnd)
|
||||||
gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
|
gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
|
||||||
else
|
else
|
||||||
gl_Position = mw_viewToClip(mw_viewStereoAdjust(viewPos));
|
gl_Position = mw_viewToClip(viewPos);
|
||||||
|
|
||||||
linearDepth = getLinearDepth(gl_Position.z, viewPos.z);
|
linearDepth = getLinearDepth(gl_Position.z, viewPos.z);
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "openmw_vertex.h.glsl"
|
#include "openmw_vertex.h.glsl"
|
||||||
|
|
||||||
#if @diffuseMap
|
#if @diffuseMap
|
||||||
varying vec2 diffuseMapUV;
|
varying vec2 diffuseMapUV;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "openmw_vertex.h.glsl"
|
#include "openmw_vertex.h.glsl"
|
||||||
|
|
||||||
#if @diffuseMap
|
#if @diffuseMap
|
||||||
varying vec2 diffuseMapUV;
|
varying vec2 diffuseMapUV;
|
||||||
#endif
|
#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);
|
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;
|
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_modelToClip(vec4 pos);
|
||||||
vec4 mw_modelToView(vec4 pos);
|
vec4 mw_modelToView(vec4 pos);
|
||||||
vec4 mw_viewToClip(vec4 pos);
|
vec4 mw_viewToClip(vec4 pos);
|
||||||
vec4 mw_viewStereoAdjust(vec4 pos);
|
vec4 mw_viewStereoAdjust(vec4 pos);
|
||||||
mat4 mw_viewMatrix();
|
|
||||||
mat4 mw_projectionMatrix();
|
|
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
|
#endif
|
||||||
|
|
||||||
#include "openmw_vertex.h.glsl"
|
#include "openmw_vertex.h.glsl"
|
||||||
|
|
||||||
varying vec2 uv;
|
varying vec2 uv;
|
||||||
varying float euclideanDepth;
|
varying float euclideanDepth;
|
||||||
varying float linearDepth;
|
varying float linearDepth;
|
||||||
|
|
Loading…
Reference in a new issue