mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-07-03 13:21:34 +00:00
Add OpenMW VR commits up to 4 Jul 2021
# Conflicts: # CMakeLists.txt # LICENSE # README.md # apps/openmw/CMakeLists.txt # apps/openmw/engine.cpp # apps/openmw/mwclass/creature.cpp # apps/openmw/mwclass/npc.cpp # apps/openmw/mwclass/npc.hpp # apps/openmw/mwinput/bindingsmanager.cpp # apps/openmw/mwmechanics/combat.cpp
This commit is contained in:
commit
339a196579
222 changed files with 19245 additions and 753 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -11,6 +11,7 @@ prebuilt
|
||||||
##windows build process
|
##windows build process
|
||||||
/deps
|
/deps
|
||||||
/MSVC*
|
/MSVC*
|
||||||
|
.vs
|
||||||
|
|
||||||
## doxygen
|
## doxygen
|
||||||
Doxygen
|
Doxygen
|
||||||
|
|
|
@ -163,7 +163,7 @@ macOS10.15_Xcode11:
|
||||||
CCACHE_SIZE: 3G
|
CCACHE_SIZE: 3G
|
||||||
|
|
||||||
variables: &engine-targets
|
variables: &engine-targets
|
||||||
targets: "openmw,openmw-essimporter,openmw-iniimporter,openmw-launcher,openmw-wizard"
|
targets: "openmw_vr,openmw-essimporter,openmw-iniimporter,openmw-launcher,openmw-wizard"
|
||||||
package: "Engine"
|
package: "Engine"
|
||||||
|
|
||||||
variables: &cs-targets
|
variables: &cs-targets
|
||||||
|
@ -378,37 +378,37 @@ Windows_MSBuild_Tests_RelWithDebInfo:
|
||||||
# Gitlab can't successfully execute following binaries due to unknown reason
|
# Gitlab can't successfully execute following binaries due to unknown reason
|
||||||
# executables: "openmw_test_suite.exe,openmw_detournavigator_navmeshtilescache_benchmark.exe"
|
# executables: "openmw_test_suite.exe,openmw_detournavigator_navmeshtilescache_benchmark.exe"
|
||||||
|
|
||||||
Debian_AndroidNDK_arm64-v8a:
|
#Debian_AndroidNDK_arm64-v8a:
|
||||||
tags:
|
# tags:
|
||||||
- linux
|
# - linux
|
||||||
image: debian:bullseye
|
# image: debian:bullseye
|
||||||
variables:
|
# variables:
|
||||||
CCACHE_SIZE: 3G
|
# CCACHE_SIZE: 3G
|
||||||
cache:
|
# cache:
|
||||||
key: Debian_AndroidNDK_arm64-v8a.v3
|
# key: Debian_AndroidNDK_arm64-v8a.v3
|
||||||
paths:
|
# paths:
|
||||||
- apt-cache/
|
# - apt-cache/
|
||||||
- ccache/
|
# - ccache/
|
||||||
- build/extern/fetched/
|
# - build/extern/fetched/
|
||||||
before_script:
|
# before_script:
|
||||||
- export APT_CACHE_DIR=`pwd`/apt-cache && mkdir -pv $APT_CACHE_DIR
|
# - export APT_CACHE_DIR=`pwd`/apt-cache && mkdir -pv $APT_CACHE_DIR
|
||||||
- echo "deb http://deb.debian.org/debian unstable main contrib" > /etc/apt/sources.list
|
# - echo "deb http://deb.debian.org/debian unstable main contrib" > /etc/apt/sources.list
|
||||||
- echo "google-android-ndk-installer google-android-installers/mirror select https://dl.google.com" | debconf-set-selections
|
# - echo "google-android-ndk-installer google-android-installers/mirror select https://dl.google.com" | debconf-set-selections
|
||||||
- apt-get update -yq
|
# - apt-get update -yq
|
||||||
- apt-get -q -o dir::cache::archives="$APT_CACHE_DIR" install -y cmake ccache curl unzip git build-essential google-android-ndk-installer
|
# - apt-get -q -o dir::cache::archives="$APT_CACHE_DIR" install -y cmake ccache curl unzip git build-essential google-android-ndk-installer
|
||||||
stage: build
|
# stage: build
|
||||||
script:
|
# script:
|
||||||
- export CCACHE_BASEDIR="`pwd`"
|
# - export CCACHE_BASEDIR="`pwd`"
|
||||||
- export CCACHE_DIR="`pwd`/ccache" && mkdir -pv "$CCACHE_DIR"
|
# - export CCACHE_DIR="`pwd`/ccache" && mkdir -pv "$CCACHE_DIR"
|
||||||
- ccache -z -M "${CCACHE_SIZE}"
|
# - ccache -z -M "${CCACHE_SIZE}"
|
||||||
- CI/before_install.android.sh
|
# - CI/before_install.android.sh
|
||||||
- CI/before_script.android.sh
|
# - CI/before_script.android.sh
|
||||||
- cd build
|
# - cd build
|
||||||
- cmake --build . -- -j $(nproc)
|
# - cmake --build . -- -j $(nproc)
|
||||||
- cmake --install .
|
# - cmake --install .
|
||||||
- ccache -s
|
# - ccache -s
|
||||||
artifacts:
|
# artifacts:
|
||||||
paths:
|
# paths:
|
||||||
- build/install/
|
# - build/install/
|
||||||
# When CCache doesn't exist (e.g. first build on a fork), build takes more than 1h, which is the default for forks.
|
# # When CCache doesn't exist (e.g. first build on a fork), build takes more than 1h, which is the default for forks.
|
||||||
timeout: 1h30m
|
# timeout: 1h30m
|
||||||
|
|
43
CI/before_script.msvc.sh
Normal file → Executable file
43
CI/before_script.msvc.sh
Normal file → Executable file
|
@ -94,6 +94,9 @@ while [ $# -gt 0 ]; do
|
||||||
V )
|
V )
|
||||||
VERBOSE=true ;;
|
VERBOSE=true ;;
|
||||||
|
|
||||||
|
nVR )
|
||||||
|
SKIP_VR=true ;;
|
||||||
|
|
||||||
d )
|
d )
|
||||||
SKIP_DOWNLOAD=true ;;
|
SKIP_DOWNLOAD=true ;;
|
||||||
|
|
||||||
|
@ -555,14 +558,14 @@ if [ -z $SKIP_DOWNLOAD ]; then
|
||||||
"OpenAL-Soft-1.20.1.zip"
|
"OpenAL-Soft-1.20.1.zip"
|
||||||
|
|
||||||
# OSG
|
# OSG
|
||||||
download "OpenSceneGraph 3.6.5" \
|
download "OpenSceneGraph 3.6.x" \
|
||||||
"https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/OSG-3.6.5-msvc${MSVC_REAL_YEAR}-win${BITS}.7z" \
|
"https://gitlab.com/madsbuvi/openmw-deps/-/raw/openmw-vr/windows/OSG-3.6.x-msvc${MSVC_REAL_YEAR}-win${BITS}.7z" \
|
||||||
"OSG-3.6.5-msvc${MSVC_REAL_YEAR}-win${BITS}.7z"
|
"OSG-3.6.x-msvc${MSVC_REAL_YEAR}-win${BITS}.7z"
|
||||||
|
|
||||||
if [ -n "$PDBS" ]; then
|
if [ -n "$PDBS" ]; then
|
||||||
download "OpenSceneGraph symbols" \
|
download "OpenSceneGraph symbols" \
|
||||||
"https://gitlab.com/OpenMW/openmw-deps/-/raw/main/windows/OSG-3.6.5-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z" \
|
"https://gitlab.com/madsbuvi/openmw-deps/-/raw/openmw-vr/windows/OSG-3.6.x-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z" \
|
||||||
"OSG-3.6.5-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z"
|
"OSG-3.6.x-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# SDL2
|
# SDL2
|
||||||
|
@ -760,7 +763,7 @@ printf "OpenAL-Soft 1.20.1... "
|
||||||
cd $DEPS
|
cd $DEPS
|
||||||
echo
|
echo
|
||||||
# OSG
|
# OSG
|
||||||
printf "OSG 3.6.5... "
|
printf "OSG 3.6.x... "
|
||||||
{
|
{
|
||||||
cd $DEPS_INSTALL
|
cd $DEPS_INSTALL
|
||||||
if [ -d OSG ] && \
|
if [ -d OSG ] && \
|
||||||
|
@ -771,9 +774,9 @@ printf "OSG 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}/OSG-3.6.5-msvc${MSVC_REAL_YEAR}-win${BITS}.7z" $STRIP
|
eval 7z x -y "${DEPS}/OSG-3.6.x-msvc${MSVC_REAL_YEAR}-win${BITS}.7z" $STRIP
|
||||||
[ -n "$PDBS" ] && eval 7z x -y "${DEPS}/OSG-3.6.5-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z" $STRIP
|
[ -n "$PDBS" ] && eval 7z x -y "${DEPS}/OSG-3.6.x-msvc${MSVC_REAL_YEAR}-win${BITS}-sym.7z" $STRIP
|
||||||
mv "OSG-3.6.5-msvc${MSVC_REAL_YEAR}-win${BITS}" OSG
|
mv "OSG-3.6.x-msvc${MSVC_REAL_YEAR}-win${BITS}" 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"
|
||||||
|
@ -783,7 +786,7 @@ printf "OSG 3.6.5... "
|
||||||
else
|
else
|
||||||
SUFFIX=""
|
SUFFIX=""
|
||||||
fi
|
fi
|
||||||
add_runtime_dlls $CONFIGURATION "$(pwd)/OSG/bin/"{OpenThreads,zlib,libpng}${SUFFIX}.dll \
|
add_runtime_dlls $CONFIGURATION "$(pwd)/OSG/bin/"{OpenThreads,zlib,libpng16}${SUFFIX}.dll \
|
||||||
"$(pwd)/OSG/bin/osg"{,Animation,DB,FX,GA,Particle,Text,Util,Viewer,Shadow}${SUFFIX}.dll
|
"$(pwd)/OSG/bin/osg"{,Animation,DB,FX,GA,Particle,Text,Util,Viewer,Shadow}${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_"{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
|
||||||
|
@ -934,6 +937,7 @@ printf "LZ4 1.9.2... "
|
||||||
}
|
}
|
||||||
cd $DEPS
|
cd $DEPS
|
||||||
echo
|
echo
|
||||||
|
|
||||||
# Google Test and Google Mock
|
# Google Test and Google Mock
|
||||||
if [ ! -z $TEST_FRAMEWORK ]; then
|
if [ ! -z $TEST_FRAMEWORK ]; then
|
||||||
printf "Google test 1.10.0 ..."
|
printf "Google test 1.10.0 ..."
|
||||||
|
@ -988,6 +992,11 @@ if [ ! -z $TEST_FRAMEWORK ]; then
|
||||||
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# VR build
|
||||||
|
if [ ! -Z $SKIP_VR ]; then
|
||||||
|
add_cmake_opts -DBUILD_VR_OPENXR=no
|
||||||
|
fi
|
||||||
|
|
||||||
echo
|
echo
|
||||||
cd $DEPS_INSTALL/..
|
cd $DEPS_INSTALL/..
|
||||||
echo
|
echo
|
||||||
|
@ -1003,6 +1012,7 @@ if [ ! -z $CI ]; then
|
||||||
-DBUILD_MWINIIMPORTER=no \
|
-DBUILD_MWINIIMPORTER=no \
|
||||||
-DBUILD_OPENCS=no \
|
-DBUILD_OPENCS=no \
|
||||||
-DBUILD_OPENMW=no \
|
-DBUILD_OPENMW=no \
|
||||||
|
-DBUILD_OPENMW_VR=no \
|
||||||
-DBUILD_WIZARD=no
|
-DBUILD_WIZARD=no
|
||||||
;;
|
;;
|
||||||
openmw )
|
openmw )
|
||||||
|
@ -1011,20 +1021,31 @@ if [ ! -z $CI ]; then
|
||||||
-DBUILD_LAUNCHER=no \
|
-DBUILD_LAUNCHER=no \
|
||||||
-DBUILD_MWINIIMPORTER=no \
|
-DBUILD_MWINIIMPORTER=no \
|
||||||
-DBUILD_OPENCS=no \
|
-DBUILD_OPENCS=no \
|
||||||
|
-DBUILD_OPENMW_VR=no \
|
||||||
-DBUILD_WIZARD=no
|
-DBUILD_WIZARD=no
|
||||||
;;
|
;;
|
||||||
|
vr )
|
||||||
|
echo " Building subproject: OpenMW-VR."
|
||||||
|
add_cmake_opts -DBUILD_ESSIMPORTER=no \
|
||||||
|
-DBUILD_OPENCS=no \
|
||||||
|
-DBUILD_BSATOOL=no \
|
||||||
|
-DBUILD_OPENMW=no \
|
||||||
|
-DBUILD_ESMTOOL=no
|
||||||
|
;;
|
||||||
opencs )
|
opencs )
|
||||||
echo " Building subproject: OpenCS."
|
echo " Building subproject: OpenCS."
|
||||||
add_cmake_opts -DBUILD_ESSIMPORTER=no \
|
add_cmake_opts -DBUILD_ESSIMPORTER=no \
|
||||||
-DBUILD_LAUNCHER=no \
|
-DBUILD_LAUNCHER=no \
|
||||||
-DBUILD_MWINIIMPORTER=no \
|
-DBUILD_MWINIIMPORTER=no \
|
||||||
-DBUILD_OPENMW=no \
|
-DBUILD_OPENMW=no \
|
||||||
|
-DBUILD_OPENMW_VR=no \
|
||||||
-DBUILD_WIZARD=no
|
-DBUILD_WIZARD=no
|
||||||
;;
|
;;
|
||||||
misc )
|
misc )
|
||||||
echo " Building subprojects: Misc."
|
echo " Building subprojects: Misc."
|
||||||
add_cmake_opts -DBUILD_OPENCS=no \
|
add_cmake_opts -DBUILD_OPENCS=no \
|
||||||
-DBUILD_OPENMW=no
|
-DBUILD_OPENMW=no \
|
||||||
|
-DBUILD_OPENMW_VR=no
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
|
@ -29,6 +29,7 @@ option(BUILD_BSATOOL "Build BSA extractor" ON)
|
||||||
option(BUILD_ESMTOOL "Build ESM inspector" ON)
|
option(BUILD_ESMTOOL "Build ESM inspector" ON)
|
||||||
option(BUILD_NIFTEST "Build nif file tester" ON)
|
option(BUILD_NIFTEST "Build nif file tester" ON)
|
||||||
option(BUILD_DOCS "Build documentation." OFF )
|
option(BUILD_DOCS "Build documentation." OFF )
|
||||||
|
option(BUILD_OPENMW_VR "Build VR support using OpenXR" ON)
|
||||||
option(BUILD_WITH_CODE_COVERAGE "Enable code coverage with gconv" OFF)
|
option(BUILD_WITH_CODE_COVERAGE "Enable code coverage with gconv" OFF)
|
||||||
option(BUILD_UNITTESTS "Enable Unittests with Google C++ Unittest" OFF)
|
option(BUILD_UNITTESTS "Enable Unittests with Google C++ Unittest" OFF)
|
||||||
option(BUILD_BENCHMARKS "Build benchmarks with Google Benchmark" OFF)
|
option(BUILD_BENCHMARKS "Build benchmarks with Google Benchmark" OFF)
|
||||||
|
@ -215,7 +216,7 @@ endif()
|
||||||
# Start of tes3mp addition
|
# Start of tes3mp addition
|
||||||
#
|
#
|
||||||
# Don't require certain dependencies for the server
|
# Don't require certain dependencies for the server
|
||||||
IF(BUILD_OPENMW OR BUILD_OPENCS)
|
IF(BUILD_OPENMW OR BUILD_OPENCS OR BUILD_OPENMW_VR)
|
||||||
# End of tes3mp addition
|
# End of tes3mp addition
|
||||||
set(USED_OSG_COMPONENTS
|
set(USED_OSG_COMPONENTS
|
||||||
osgDB
|
osgDB
|
||||||
|
@ -239,7 +240,7 @@ set(USED_OSG_PLUGINS
|
||||||
# Start of tes3mp addition
|
# Start of tes3mp addition
|
||||||
#
|
#
|
||||||
# Don't require certain dependencies for the server
|
# Don't require certain dependencies for the server
|
||||||
ENDIF(BUILD_OPENMW OR BUILD_OPENCS)
|
ENDIF(BUILD_OPENMW OR BUILD_OPENCS OR BUILD_OPENMW_VR)
|
||||||
# End of tes3mp addition
|
# End of tes3mp addition
|
||||||
|
|
||||||
add_subdirectory(extern)
|
add_subdirectory(extern)
|
||||||
|
@ -249,7 +250,7 @@ add_subdirectory(extern)
|
||||||
# Start of tes3mp addition
|
# Start of tes3mp addition
|
||||||
#
|
#
|
||||||
# Don't require certain dependencies for the server
|
# Don't require certain dependencies for the server
|
||||||
IF(BUILD_OPENMW OR BUILD_OPENCS)
|
IF(BUILD_OPENMW OR BUILD_OPENCS OR BUILD_OPENMW_VR)
|
||||||
# End of tes3mp addition
|
# End of tes3mp addition
|
||||||
# Require at least ffmpeg 3.2 for now
|
# Require at least ffmpeg 3.2 for now
|
||||||
SET(FFVER_OK FALSE)
|
SET(FFVER_OK FALSE)
|
||||||
|
@ -312,7 +313,7 @@ endif()
|
||||||
# Start of tes3mp addition
|
# Start of tes3mp addition
|
||||||
#
|
#
|
||||||
# Don't require certain dependencies for the server
|
# Don't require certain dependencies for the server
|
||||||
ENDIF(BUILD_OPENMW OR BUILD_OPENCS)
|
ENDIF(BUILD_OPENMW OR BUILD_OPENCS OR BUILD_OPENMW_VR)
|
||||||
# End of tes3mp addition
|
# End of tes3mp addition
|
||||||
|
|
||||||
# Platform specific
|
# Platform specific
|
||||||
|
@ -332,7 +333,7 @@ endif()
|
||||||
# Start of tes3mp addition
|
# Start of tes3mp addition
|
||||||
#
|
#
|
||||||
# Don't require certain dependencies for the server
|
# Don't require certain dependencies for the server
|
||||||
IF(BUILD_OPENMW OR BUILD_OPENCS)
|
IF(BUILD_OPENMW OR BUILD_OPENCS OR BUILD_OPENMW_VR)
|
||||||
# End of tes3mp addition
|
# End of tes3mp addition
|
||||||
if(OPENMW_USE_SYSTEM_BULLET)
|
if(OPENMW_USE_SYSTEM_BULLET)
|
||||||
set(REQUIRED_BULLET_VERSION 286) # Bullet 286 required due to runtime bugfixes for btCapsuleShape
|
set(REQUIRED_BULLET_VERSION 286) # Bullet 286 required due to runtime bugfixes for btCapsuleShape
|
||||||
|
@ -369,7 +370,7 @@ endif()
|
||||||
# Start of tes3mp addition
|
# Start of tes3mp addition
|
||||||
#
|
#
|
||||||
# Don't require certain dependencies for the server
|
# Don't require certain dependencies for the server
|
||||||
ENDIF(BUILD_OPENMW OR BUILD_OPENCS)
|
ENDIF(BUILD_OPENMW OR BUILD_OPENCS OR BUILD_OPENMW_VR)
|
||||||
# End of tes3mp addition
|
# End of tes3mp addition
|
||||||
|
|
||||||
if (NOT WIN32 AND BUILD_WIZARD) # windows users can just run the morrowind installer
|
if (NOT WIN32 AND BUILD_WIZARD) # windows users can just run the morrowind installer
|
||||||
|
@ -394,10 +395,12 @@ endif()
|
||||||
#
|
#
|
||||||
# Don't require certain dependencies for the server
|
# Don't require certain dependencies for the server
|
||||||
# but keep OSG's headers (HACK)
|
# but keep OSG's headers (HACK)
|
||||||
IF(BUILD_OPENMW OR BUILD_OPENCS)
|
IF(BUILD_OPENMW OR BUILD_OPENCS OR BUILD_OPENMW_VR)
|
||||||
# End of tes3mp addition
|
# End of tes3mp addition
|
||||||
|
|
||||||
if(OPENMW_USE_SYSTEM_OSG)
|
if(OPENMW_USE_SYSTEM_OSG)
|
||||||
find_package(OpenSceneGraph 3.4.0 REQUIRED ${USED_OSG_COMPONENTS})
|
find_package(OpenSceneGraph ${OSG_VERSION_REQUIRED} REQUIRED ${USED_OSG_COMPONENTS})
|
||||||
|
|
||||||
if (${OPENSCENEGRAPH_VERSION} VERSION_GREATER 3.6.2 AND ${OPENSCENEGRAPH_VERSION} VERSION_LESS 3.6.5)
|
if (${OPENSCENEGRAPH_VERSION} VERSION_GREATER 3.6.2 AND ${OPENSCENEGRAPH_VERSION} VERSION_LESS 3.6.5)
|
||||||
message(FATAL_ERROR "OpenSceneGraph version ${OPENSCENEGRAPH_VERSION} has critical regressions which cause crashes. Please upgrade to 3.6.5 or later. We strongly recommend using the tip of the official 'OpenSceneGraph-3.6' branch or the tip of '3.6' OpenMW/osg (OSGoS).")
|
message(FATAL_ERROR "OpenSceneGraph version ${OPENSCENEGRAPH_VERSION} has critical regressions which cause crashes. Please upgrade to 3.6.5 or later. We strongly recommend using the tip of the official 'OpenSceneGraph-3.6' branch or the tip of '3.6' OpenMW/osg (OSGoS).")
|
||||||
endif()
|
endif()
|
||||||
|
@ -416,9 +419,9 @@ endif()
|
||||||
#
|
#
|
||||||
# Don't require certain dependencies for the server
|
# Don't require certain dependencies for the server
|
||||||
# but keep OSG's headers (HACK)
|
# but keep OSG's headers (HACK)
|
||||||
ELSE(BUILD_OPENMW OR BUILD_OPENCS)
|
ELSE(BUILD_OPENMW OR BUILD_OPENCS OR BUILD_OPENMW_VR)
|
||||||
include_directories(${OPENSCENEGRAPH_INCLUDE_DIRS})
|
include_directories(${OPENSCENEGRAPH_INCLUDE_DIRS})
|
||||||
ENDIF(BUILD_OPENMW OR BUILD_OPENCS)
|
ENDIF(BUILD_OPENMW OR BUILD_OPENCS OR BUILD_OPENMW_VR)
|
||||||
# End of tes3mp addition
|
# End of tes3mp addition
|
||||||
|
|
||||||
set(BOOST_COMPONENTS system filesystem program_options iostreams)
|
set(BOOST_COMPONENTS system filesystem program_options iostreams)
|
||||||
|
@ -441,7 +444,7 @@ find_package(Boost 1.6.2 REQUIRED COMPONENTS ${BOOST_COMPONENTS} OPTIONAL_COMPON
|
||||||
# Start of tes3mp addition
|
# Start of tes3mp addition
|
||||||
#
|
#
|
||||||
# Don't require certain dependencies for the server
|
# Don't require certain dependencies for the server
|
||||||
IF(BUILD_OPENMW OR BUILD_OPENCS)
|
IF(BUILD_OPENMW OR BUILD_OPENCS OR BUILD_OPENMW_VR)
|
||||||
# End of tes3mp addition
|
# End of tes3mp addition
|
||||||
if(OPENMW_USE_SYSTEM_MYGUI)
|
if(OPENMW_USE_SYSTEM_MYGUI)
|
||||||
find_package(MyGUI 3.2.2 REQUIRED)
|
find_package(MyGUI 3.2.2 REQUIRED)
|
||||||
|
@ -449,7 +452,7 @@ endif()
|
||||||
# End of tes3mp addition
|
# End of tes3mp addition
|
||||||
#
|
#
|
||||||
# Don't require certain dependencies for the server
|
# Don't require certain dependencies for the server
|
||||||
ENDIF(BUILD_OPENMW OR BUILD_OPENCS)
|
ENDIF(BUILD_OPENMW OR BUILD_OPENCS OR BUILD_OPENMW_VR)
|
||||||
# End of tes3mp addition
|
# End of tes3mp addition
|
||||||
find_package(SDL2 2.0.9 REQUIRED)
|
find_package(SDL2 2.0.9 REQUIRED)
|
||||||
# Start of tes3mp addition
|
# Start of tes3mp addition
|
||||||
|
@ -521,6 +524,9 @@ configure_resource_file(${OpenMW_SOURCE_DIR}/files/tes3mp/tes3mp-server-default.
|
||||||
pack_resource_file(${OpenMW_SOURCE_DIR}/files/settings-default.cfg
|
pack_resource_file(${OpenMW_SOURCE_DIR}/files/settings-default.cfg
|
||||||
"${OpenMW_BINARY_DIR}" "defaults.bin")
|
"${OpenMW_BINARY_DIR}" "defaults.bin")
|
||||||
|
|
||||||
|
configure_resource_file(${OpenMW_SOURCE_DIR}/files/settings-overrides-vr.cfg
|
||||||
|
"${OpenMW_BINARY_DIR}" "settings-overrides-vr.cfg")
|
||||||
|
|
||||||
configure_resource_file(${OpenMW_SOURCE_DIR}/files/openmw.appdata.xml
|
configure_resource_file(${OpenMW_SOURCE_DIR}/files/openmw.appdata.xml
|
||||||
"${OpenMW_BINARY_DIR}" "openmw.appdata.xml")
|
"${OpenMW_BINARY_DIR}" "openmw.appdata.xml")
|
||||||
|
|
||||||
|
@ -598,20 +604,20 @@ endif (CMAKE_CXX_COMPILER_ID STREQUAL GNU OR CMAKE_CXX_COMPILER_ID STREQUAL Clan
|
||||||
# Start of tes3mp addition
|
# Start of tes3mp addition
|
||||||
#
|
#
|
||||||
# Don't require certain dependencies for the server
|
# Don't require certain dependencies for the server
|
||||||
IF(BUILD_OPENMW OR BUILD_OPENCS)
|
IF(BUILD_OPENMW OR BUILD_OPENCS OR BUILD_OPENMW_VR)
|
||||||
# End of tes3mp addition
|
# End of tes3mp addition
|
||||||
add_subdirectory (extern/osg-ffmpeg-videoplayer)
|
add_subdirectory (extern/osg-ffmpeg-videoplayer)
|
||||||
add_subdirectory (extern/oics)
|
add_subdirectory (extern/oics)
|
||||||
# Start of tes3mp addition
|
# Start of tes3mp addition
|
||||||
#
|
#
|
||||||
# Don't require certain dependencies for the server
|
# Don't require certain dependencies for the server
|
||||||
ENDIF(BUILD_OPENMW OR BUILD_OPENCS)
|
ENDIF(BUILD_OPENMW OR BUILD_OPENCS OR BUILD_OPENMW_VR)
|
||||||
# End of tes3mp addition
|
# End of tes3mp addition
|
||||||
add_subdirectory (extern/Base64)
|
add_subdirectory (extern/Base64)
|
||||||
# Start of tes3mp addition
|
# Start of tes3mp addition
|
||||||
#
|
#
|
||||||
# Don't require certain dependencies for the server
|
# Don't require certain dependencies for the server
|
||||||
IF(BUILD_OPENMW OR BUILD_OPENCS)
|
IF(BUILD_OPENMW OR BUILD_OPENCS OR BUILD_OPENMW_VR)
|
||||||
# End of tes3mp addition
|
# End of tes3mp addition
|
||||||
if (BUILD_OPENCS)
|
if (BUILD_OPENCS)
|
||||||
add_subdirectory (extern/osgQt)
|
add_subdirectory (extern/osgQt)
|
||||||
|
@ -619,7 +625,7 @@ endif()
|
||||||
# Start of tes3mp addition
|
# Start of tes3mp addition
|
||||||
#
|
#
|
||||||
# Don't require certain dependencies for the server
|
# Don't require certain dependencies for the server
|
||||||
ENDIF(BUILD_OPENMW OR BUILD_OPENCS)
|
ENDIF(BUILD_OPENMW OR BUILD_OPENCS OR BUILD_OPENMW_VR)
|
||||||
# End of tes3mp addition
|
# End of tes3mp addition
|
||||||
|
|
||||||
# Components
|
# Components
|
||||||
|
@ -635,7 +641,7 @@ if (BUILD_MASTER)
|
||||||
add_subdirectory( apps/master )
|
add_subdirectory( apps/master )
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (BUILD_OPENMW)
|
if (BUILD_OPENMW OR BUILD_OPENMW_VR)
|
||||||
add_subdirectory( apps/openmw )
|
add_subdirectory( apps/openmw )
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
@ -712,6 +718,23 @@ if (WIN32)
|
||||||
set_target_properties(tes3mp PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:WINDOWS")
|
set_target_properties(tes3mp PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:WINDOWS")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# TODO: properties and link targets should be copied from openmw to openmw_vr instead of duplicating every line
|
||||||
|
if (USE_DEBUG_CONSOLE AND BUILD_OPENMW_VR)
|
||||||
|
set_target_properties(openmw_vr PROPERTIES LINK_FLAGS_DEBUG "/SUBSYSTEM:CONSOLE")
|
||||||
|
set_target_properties(openmw_vr PROPERTIES LINK_FLAGS_RELWITHDEBINFO "/SUBSYSTEM:CONSOLE")
|
||||||
|
set_target_properties(openmw_vr PROPERTIES COMPILE_DEFINITIONS $<$<CONFIG:Debug>:_CONSOLE>)
|
||||||
|
elseif (BUILD_OPENMW_VR)
|
||||||
|
# Turn off debug console, debug output will be written to visual studio output instead
|
||||||
|
set_target_properties(openmw_vr PROPERTIES LINK_FLAGS_DEBUG "/SUBSYSTEM:WINDOWS")
|
||||||
|
set_target_properties(openmw_vr PROPERTIES LINK_FLAGS_RELWITHDEBINFO "/SUBSYSTEM:WINDOWS")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (BUILD_OPENMW_VR)
|
||||||
|
# Release builds don't use the debug console
|
||||||
|
set_target_properties(openmw_vr PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS")
|
||||||
|
set_target_properties(openmw_vr PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:WINDOWS")
|
||||||
|
endif()
|
||||||
|
|
||||||
# Play a bit with the warning levels
|
# Play a bit with the warning levels
|
||||||
|
|
||||||
set(WARNINGS "/W4")
|
set(WARNINGS "/W4")
|
||||||
|
@ -784,6 +807,14 @@ if (WIN32)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if (BUILD_OPENMW_VR)
|
||||||
|
if (OPENMW_UNITY_BUILD)
|
||||||
|
set_target_properties(openmw_vr PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD} /bigobj")
|
||||||
|
else()
|
||||||
|
set_target_properties(openmw_vr PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
if (BUILD_WIZARD)
|
if (BUILD_WIZARD)
|
||||||
set_target_properties(openmw-wizard PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
|
set_target_properties(openmw-wizard PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
|
||||||
endif()
|
endif()
|
||||||
|
@ -936,6 +967,7 @@ elseif(NOT APPLE)
|
||||||
INSTALL(FILES "${INSTALL_SOURCE}/tes3mp-server-default.cfg" DESTINATION ".")
|
INSTALL(FILES "${INSTALL_SOURCE}/tes3mp-server-default.cfg" DESTINATION ".")
|
||||||
# End of tes3mp addition
|
# End of tes3mp addition
|
||||||
INSTALL(FILES "${INSTALL_SOURCE}/gamecontrollerdb.txt" DESTINATION ".")
|
INSTALL(FILES "${INSTALL_SOURCE}/gamecontrollerdb.txt" DESTINATION ".")
|
||||||
|
INSTALL(FILES "${INSTALL_SOURCE}/xrcontrollersuggestions.xml" DESTINATION ".")
|
||||||
|
|
||||||
INSTALL(DIRECTORY "${INSTALL_SOURCE}/resources" DESTINATION ".")
|
INSTALL(DIRECTORY "${INSTALL_SOURCE}/resources" DESTINATION ".")
|
||||||
|
|
||||||
|
@ -1076,6 +1108,7 @@ elseif(NOT APPLE)
|
||||||
INSTALL(FILES "${INSTALL_SOURCE}/openmw.cfg.install" DESTINATION "${SYSCONFDIR}" RENAME "openmw.cfg" COMPONENT "openmw")
|
INSTALL(FILES "${INSTALL_SOURCE}/openmw.cfg.install" DESTINATION "${SYSCONFDIR}" RENAME "openmw.cfg" COMPONENT "openmw")
|
||||||
INSTALL(FILES "${INSTALL_SOURCE}/resources/version" DESTINATION "${SYSCONFDIR}" COMPONENT "openmw")
|
INSTALL(FILES "${INSTALL_SOURCE}/resources/version" DESTINATION "${SYSCONFDIR}" COMPONENT "openmw")
|
||||||
INSTALL(FILES "${INSTALL_SOURCE}/gamecontrollerdb.txt" DESTINATION "${SYSCONFDIR}" COMPONENT "openmw")
|
INSTALL(FILES "${INSTALL_SOURCE}/gamecontrollerdb.txt" DESTINATION "${SYSCONFDIR}" COMPONENT "openmw")
|
||||||
|
INSTALL(FILES "${INSTALL_SOURCE}/xrcontrollersuggestions.xml" DESTINATION "${SYSCONFDIR}" COMPONENT "openmw")
|
||||||
|
|
||||||
# Start of tes3mp addition
|
# Start of tes3mp addition
|
||||||
INSTALL(FILES "${INSTALL_SOURCE}/tes3mp-client-default.cfg" DESTINATION "${SYSCONFDIR}" COMPONENT "openmw")
|
INSTALL(FILES "${INSTALL_SOURCE}/tes3mp-client-default.cfg" DESTINATION "${SYSCONFDIR}" COMPONENT "openmw")
|
||||||
|
|
6
LICENSE
6
LICENSE
|
@ -679,3 +679,9 @@ AND AUTHORS OF THAT COVERED WORK FOR ANY DAMAEGS, DEMANDS, CLAIMS, LOSSES,
|
||||||
CAUSES OF ACTION, LAWSUITS, JUDGMENTS EXPENSES (INCLUDING WITHOUT LIMITATION
|
CAUSES OF ACTION, LAWSUITS, JUDGMENTS EXPENSES (INCLUDING WITHOUT LIMITATION
|
||||||
REASONABLE ATTORNEYS' FEES AND EXPENSES) OR ANY OTHER LIABLITY ARISING FROM,
|
REASONABLE ATTORNEYS' FEES AND EXPENSES) OR ANY OTHER LIABLITY ARISING FROM,
|
||||||
RELATED TO OR IN CONNECTION WITH YOUR ASSUMPTIONS OF LIABILITY.
|
RELATED TO OR IN CONNECTION WITH YOUR ASSUMPTIONS OF LIABILITY.
|
||||||
|
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
On Windows releases, this project bundles the OpenXR loader binary, which is
|
||||||
|
licensed under the Apache 2.0 license. For license details, see:
|
||||||
|
https://github.com/KhronosGroup/OpenXR-SDK/blob/master/LICENSE
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
TES3MP
|
TES3MP VR
|
||||||
======
|
=========
|
||||||
|
|
||||||
Copyright (c) 2008-2015, OpenMW Team
|
Copyright (c) 2008-2015, OpenMW Team
|
||||||
Copyright (c) 2016-2022, David Cernat & Stanislav Zhukov
|
Copyright (c) 2016-2022, David Cernat & Stanislav Zhukov
|
||||||
|
|
||||||
TES3MP is a project adding multiplayer functionality to [OpenMW](https://github.com/OpenMW/openmw), an open-source game engine that supports playing "The Elder Scrolls III: Morrowind" by Bethesda Softworks.
|
TES3MP is a project adding multiplayer functionality to [OpenMW](https://github.com/OpenMW/openmw), an open-source game engine that supports playing "The Elder Scrolls III: Morrowind" by Bethesda Softworks.
|
||||||
|
|
||||||
|
When it's combined with Mads Buvik Sandvei's [fork adding VR support to OpenMW](https://gitlab.com/madsbuvi/openmw), the result is TES3MP VR.
|
||||||
|
|
||||||
* TES3MP version: 0.8.0
|
* TES3MP version: 0.8.0
|
||||||
* OpenMW version: 0.47.0
|
* OpenMW version: 0.47.0
|
||||||
* License: GPLv3 (see [LICENSE](https://github.com/TES3MP/TES3MP/blob/master/LICENSE) for more information)
|
* License: GPLv3 (see [LICENSE](https://github.com/TES3MP/TES3MP/blob/master/LICENSE) for more information)
|
||||||
|
|
|
@ -230,6 +230,26 @@ bool Launcher::AdvancedPage::loadSettings()
|
||||||
startDefaultCharacterAtField->setText(mGameSettings.value("start"));
|
startDefaultCharacterAtField->setText(mGameSettings.value("start"));
|
||||||
runScriptAfterStartupField->setText(mGameSettings.value("script-run"));
|
runScriptAfterStartupField->setText(mGameSettings.value("script-run"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// VR
|
||||||
|
{
|
||||||
|
std::string stereoMethod = Settings::Manager::getString("stereo method", "Stereo");
|
||||||
|
useGeometryShaders->setChecked(stereoMethod == "GeometryShader");
|
||||||
|
|
||||||
|
loadSettingBool(useSharedShadowMaps, "shared shadow maps", "Stereo");
|
||||||
|
loadSettingBool(preferDirectXSwapchains, "Prefer DirectX swapchains", "VR");
|
||||||
|
loadSettingBool(preferSRGBSwapchains, "Prefer sRGB swapchains", "VR");
|
||||||
|
loadSettingBool(useXrDebug, "enable XR_EXT_debug_utils", "VR Debug");
|
||||||
|
loadSettingBool(logAllXrCalls, "log all openxr calls", "VR Debug");
|
||||||
|
loadSettingBool(ignoreXrErrors, "continue on errors", "VR Debug");
|
||||||
|
|
||||||
|
double minimumSwingSpeed = Settings::Manager::getDouble("realistic combat minimum swing velocity", "VR");
|
||||||
|
realisticCombatMinimumSwingSpeedSpinBox->setValue(minimumSwingSpeed);
|
||||||
|
double maximumSwingSpeed = Settings::Manager::getDouble("realistic combat maximum swing velocity", "VR");
|
||||||
|
realisticCombatMaximumSwingSpeedSpinBox->setValue(maximumSwingSpeed);
|
||||||
|
double realHeightValue = Settings::Manager::getDouble("real height", "VR");
|
||||||
|
realHeightSpinBox->setValue(realHeightValue);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -395,6 +415,34 @@ void Launcher::AdvancedPage::saveSettings()
|
||||||
if (scriptRun != mGameSettings.value("script-run"))
|
if (scriptRun != mGameSettings.value("script-run"))
|
||||||
mGameSettings.setValue("script-run", scriptRun);
|
mGameSettings.setValue("script-run", scriptRun);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// VR
|
||||||
|
{
|
||||||
|
std::string stereoMethod = "BruteForce";
|
||||||
|
if (useGeometryShaders->isChecked())
|
||||||
|
stereoMethod = "GeometryShader";
|
||||||
|
if (stereoMethod != Settings::Manager::getString("stereo method", "Stereo"))
|
||||||
|
Settings::Manager::setString("stereo method", "Stereo", stereoMethod);
|
||||||
|
|
||||||
|
saveSettingBool(useSharedShadowMaps, "shared shadow maps", "Stereo");
|
||||||
|
saveSettingBool(preferDirectXSwapchains, "Prefer sRGB swapchains", "VR");
|
||||||
|
saveSettingBool(preferSRGBSwapchains, "Prefer DirectX swapchains", "VR");
|
||||||
|
saveSettingBool(useXrDebug, "enable XR_EXT_debug_utils", "VR Debug");
|
||||||
|
saveSettingBool(logAllXrCalls, "log all openxr calls", "VR Debug");
|
||||||
|
saveSettingBool(ignoreXrErrors, "continue on errors", "VR Debug");
|
||||||
|
|
||||||
|
double minimumSwingSpeed = realisticCombatMinimumSwingSpeedSpinBox->value();
|
||||||
|
if (minimumSwingSpeed != Settings::Manager::getFloat("realistic combat minimum swing velocity", "VR"))
|
||||||
|
Settings::Manager::setFloat("realistic combat minimum swing velocity", "VR", minimumSwingSpeed);
|
||||||
|
|
||||||
|
double maximumSwingSpeed = realisticCombatMaximumSwingSpeedSpinBox->value();
|
||||||
|
if (maximumSwingSpeed != Settings::Manager::getFloat("realistic combat maximum swing velocity", "VR"))
|
||||||
|
Settings::Manager::setFloat("realistic combat maximum swing velocity", "VR", maximumSwingSpeed);
|
||||||
|
|
||||||
|
double realHeightValue = realHeightSpinBox->value();
|
||||||
|
if (realHeightValue != Settings::Manager::getFloat("real height", "VR"))
|
||||||
|
Settings::Manager::setFloat("real height", "VR", realHeightValue);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Launcher::AdvancedPage::loadSettingBool(QCheckBox *checkbox, const std::string &setting, const std::string &group)
|
void Launcher::AdvancedPage::loadSettingBool(QCheckBox *checkbox, const std::string &setting, const std::string &group)
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include <components/resource/scenemanager.hpp>
|
#include <components/resource/scenemanager.hpp>
|
||||||
#include <components/resource/resourcesystem.hpp>
|
#include <components/resource/resourcesystem.hpp>
|
||||||
#include <components/sceneutil/lightmanager.hpp>
|
#include <components/sceneutil/lightmanager.hpp>
|
||||||
|
#include <components/sdlutil/sdlgraphicswindow.hpp>
|
||||||
|
|
||||||
#include "../widget/scenetoolmode.hpp"
|
#include "../widget/scenetoolmode.hpp"
|
||||||
|
|
||||||
|
@ -104,7 +105,7 @@ RenderWidget::~RenderWidget()
|
||||||
// before OSG 3.6.4, the default font was a static object, and if it wasn't attached to the scene when a graphics context was destroyed, it's program wouldn't be released.
|
// before OSG 3.6.4, the default font was a static object, and if it wasn't attached to the scene when a graphics context was destroyed, it's program wouldn't be released.
|
||||||
// 3.6.4 moved it into the object cache, which meant it usually got released, but not here.
|
// 3.6.4 moved it into the object cache, which meant it usually got released, but not here.
|
||||||
// 3.6.5 improved cleanup with osgViewer::CompositeViewer::removeView so it more reliably released associated state for objects in the object cache.
|
// 3.6.5 improved cleanup with osgViewer::CompositeViewer::removeView so it more reliably released associated state for objects in the object cache.
|
||||||
osg::ref_ptr<osg::GraphicsContext> graphicsContext = mView->getCamera()->getGraphicsContext();
|
osg::ref_ptr<osg::GraphicsContext> graphicsContext = SDLUtil::GraphicsWindowSDL2::findContext(*mView);
|
||||||
osgText::Font::getDefaultFont()->releaseGLObjects(graphicsContext->getState());
|
osgText::Font::getDefaultFont()->releaseGLObjects(graphicsContext->getState());
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -132,7 +133,7 @@ osg::Camera *RenderWidget::getCamera()
|
||||||
void RenderWidget::toggleRenderStats()
|
void RenderWidget::toggleRenderStats()
|
||||||
{
|
{
|
||||||
osgViewer::GraphicsWindow* window =
|
osgViewer::GraphicsWindow* window =
|
||||||
static_cast<osgViewer::GraphicsWindow*>(mView->getCamera()->getGraphicsContext());
|
static_cast<osgViewer::GraphicsWindow*>(SDLUtil::GraphicsWindowSDL2::findContext(*mView));
|
||||||
|
|
||||||
window->getEventQueue()->keyPress(osgGA::GUIEventAdapter::KEY_S);
|
window->getEventQueue()->keyPress(osgGA::GUIEventAdapter::KEY_S);
|
||||||
window->getEventQueue()->keyRelease(osgGA::GUIEventAdapter::KEY_S);
|
window->getEventQueue()->keyRelease(osgGA::GUIEventAdapter::KEY_S);
|
||||||
|
@ -263,7 +264,7 @@ SceneWidget::SceneWidget(std::shared_ptr<Resource::ResourceSystem> resourceSyste
|
||||||
SceneWidget::~SceneWidget()
|
SceneWidget::~SceneWidget()
|
||||||
{
|
{
|
||||||
// Since we're holding on to the resources past the existence of this graphics context, we'll need to manually release the created objects
|
// Since we're holding on to the resources past the existence of this graphics context, we'll need to manually release the created objects
|
||||||
mResourceSystem->releaseGLObjects(mView->getCamera()->getGraphicsContext()->getState());
|
mResourceSystem->releaseGLObjects(SDLUtil::GraphicsWindowSDL2::findContext(*mView)->getState());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -170,7 +170,7 @@ include_directories(
|
||||||
${FFmpeg_INCLUDE_DIRS}
|
${FFmpeg_INCLUDE_DIRS}
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(tes3mp
|
set(OPENMW_LINK_TARGETS
|
||||||
# CMake's built-in OSG finder does not use pkgconfig, so we have to
|
# CMake's built-in OSG finder does not use pkgconfig, so we have to
|
||||||
# manually ensure the order is correct for inter-library dependencies.
|
# manually ensure the order is correct for inter-library dependencies.
|
||||||
# This only makes a difference with `-DOPENMW_USE_SYSTEM_OSG=ON -DOSG_STATIC=ON`.
|
# This only makes a difference with `-DOPENMW_USE_SYSTEM_OSG=ON -DOSG_STATIC=ON`.
|
||||||
|
@ -198,6 +198,16 @@ target_link_libraries(tes3mp
|
||||||
${RakNet_LIBRARY}
|
${RakNet_LIBRARY}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (USE_SYSTEM_TINYXML)
|
||||||
|
set(OPENMW_LINK_TARGETS ${OPENMW_LINK_TARGETS}
|
||||||
|
${TinyXML_LIBRARIES})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (NOT UNIX)
|
||||||
|
set(OPENMW_LINK_TARGETS ${OPENMW_LINK_TARGETS}
|
||||||
|
${SDL2MAIN_LIBRARY})
|
||||||
|
endif()
|
||||||
|
|
||||||
if(OSG_STATIC)
|
if(OSG_STATIC)
|
||||||
unset(_osg_plugins_static_files)
|
unset(_osg_plugins_static_files)
|
||||||
add_library(openmw_osg_plugins INTERFACE)
|
add_library(openmw_osg_plugins INTERFACE)
|
||||||
|
@ -214,30 +224,36 @@ if(OSG_STATIC)
|
||||||
# We use --whole-archive because OSG plugins use registration.
|
# We use --whole-archive because OSG plugins use registration.
|
||||||
get_whole_archive_options(_opts ${_osg_plugins_static_files})
|
get_whole_archive_options(_opts ${_osg_plugins_static_files})
|
||||||
target_link_options(openmw_osg_plugins INTERFACE ${_opts})
|
target_link_options(openmw_osg_plugins INTERFACE ${_opts})
|
||||||
target_link_libraries(tes3mp openmw_osg_plugins)
|
set(OPENMW_LINK_TARGETS ${OPENMW_LINK_TARGETS}
|
||||||
|
openmw_osg_plugins)
|
||||||
|
|
||||||
if(OPENMW_USE_SYSTEM_OSG)
|
if(OPENMW_USE_SYSTEM_OSG)
|
||||||
# OSG plugin pkgconfig files are missing these dependencies.
|
# OSG plugin pkgconfig files are missing these dependencies.
|
||||||
# https://github.com/openscenegraph/OpenSceneGraph/issues/1052
|
# https://github.com/openscenegraph/OpenSceneGraph/issues/1052
|
||||||
target_link_libraries(tes3mp freetype jpeg png)
|
set(OPENMW_LINK_TARGETS ${OPENMW_LINK_TARGETS}
|
||||||
|
freetype jpeg png)
|
||||||
endif()
|
endif()
|
||||||
endif(OSG_STATIC)
|
endif(OSG_STATIC)
|
||||||
|
|
||||||
if (ANDROID)
|
if (ANDROID)
|
||||||
target_link_libraries(tes3mp EGL android log z)
|
set(OPENMW_LINK_TARGETS ${OPENMW_LINK_TARGETS}
|
||||||
|
EGL android log z)
|
||||||
endif (ANDROID)
|
endif (ANDROID)
|
||||||
|
|
||||||
if (USE_SYSTEM_TINYXML)
|
if (USE_SYSTEM_TINYXML)
|
||||||
target_link_libraries(tes3mp ${TinyXML_LIBRARIES})
|
set(OPENMW_LINK_TARGETS ${OPENMW_LINK_TARGETS}
|
||||||
|
${TinyXML_LIBRARIES})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (NOT UNIX)
|
if (NOT UNIX)
|
||||||
target_link_libraries(tes3mp ${SDL2MAIN_LIBRARY})
|
set(OPENMW_LINK_TARGETS ${OPENMW_LINK_TARGETS}
|
||||||
|
${SDL2MAIN_LIBRARY})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Fix for not visible pthreads functions for linker with glibc 2.15
|
# Fix for not visible pthreads functions for linker with glibc 2.15
|
||||||
if (UNIX AND NOT APPLE)
|
if (UNIX AND NOT APPLE)
|
||||||
target_link_libraries(tes3mp ${CMAKE_THREAD_LIBS_INIT})
|
set(OPENMW_LINK_TARGETS ${OPENMW_LINK_TARGETS}
|
||||||
|
${CMAKE_THREAD_LIBS_INIT})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
|
@ -252,24 +268,26 @@ if(APPLE)
|
||||||
configure_file("${OpenMW_BINARY_DIR}/openmw.cfg" ${BUNDLE_RESOURCES_DIR} COPYONLY)
|
configure_file("${OpenMW_BINARY_DIR}/openmw.cfg" ${BUNDLE_RESOURCES_DIR} COPYONLY)
|
||||||
configure_file("${OpenMW_BINARY_DIR}/gamecontrollerdb.txt" ${BUNDLE_RESOURCES_DIR} COPYONLY)
|
configure_file("${OpenMW_BINARY_DIR}/gamecontrollerdb.txt" ${BUNDLE_RESOURCES_DIR} COPYONLY)
|
||||||
|
|
||||||
add_custom_command(TARGET openmw
|
|
||||||
POST_BUILD
|
|
||||||
COMMAND cp "${OpenMW_BINARY_DIR}/resources/version" "${BUNDLE_RESOURCES_DIR}/resources")
|
|
||||||
|
|
||||||
find_library(COCOA_FRAMEWORK Cocoa)
|
find_library(COCOA_FRAMEWORK Cocoa)
|
||||||
find_library(IOKIT_FRAMEWORK IOKit)
|
find_library(IOKIT_FRAMEWORK IOKit)
|
||||||
target_link_libraries(tes3mp ${COCOA_FRAMEWORK} ${IOKIT_FRAMEWORK})
|
set(OPENMW_LINK_TARGETS ${OPENMW_LINK_TARGETS}
|
||||||
|
${COCOA_FRAMEWORK}
|
||||||
|
${IOKIT_FRAMEWORK})
|
||||||
|
|
||||||
if (FFmpeg_FOUND)
|
if (FFmpeg_FOUND)
|
||||||
find_library(COREVIDEO_FRAMEWORK CoreVideo)
|
find_library(COREVIDEO_FRAMEWORK CoreVideo)
|
||||||
find_library(VDA_FRAMEWORK VideoDecodeAcceleration)
|
find_library(VDA_FRAMEWORK VideoDecodeAcceleration)
|
||||||
target_link_libraries(tes3mp z ${COREVIDEO_FRAMEWORK} ${VDA_FRAMEWORK})
|
set(OPENMW_LINK_TARGETS ${OPENMW_LINK_TARGETS}
|
||||||
|
z
|
||||||
|
${COREVIDEO_FRAMEWORK}
|
||||||
|
${VDA_FRAMEWORK})
|
||||||
endif()
|
endif()
|
||||||
endif(APPLE)
|
endif(APPLE)
|
||||||
|
|
||||||
if (BUILD_WITH_CODE_COVERAGE)
|
if (BUILD_WITH_CODE_COVERAGE)
|
||||||
add_definitions (--coverage)
|
add_definitions (--coverage)
|
||||||
target_link_libraries(tes3mp gcov)
|
set(OPENMW_LINK_TARGETS ${OPENMW_LINK_TARGETS}
|
||||||
|
gcov)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (MSVC)
|
if (MSVC)
|
||||||
|
@ -279,6 +297,95 @@ if (MSVC)
|
||||||
endif (CMAKE_CL_64)
|
endif (CMAKE_CL_64)
|
||||||
endif (MSVC)
|
endif (MSVC)
|
||||||
|
|
||||||
|
if(BUILD_OPENMW)
|
||||||
|
if (NOT ANDROID)
|
||||||
|
openmw_add_executable(openmw
|
||||||
|
${OPENMW_FILES}
|
||||||
|
${GAME} ${GAME_HEADER}
|
||||||
|
${APPLE_BUNDLE_RESOURCES}
|
||||||
|
)
|
||||||
|
else ()
|
||||||
|
add_library(openmw
|
||||||
|
SHARED
|
||||||
|
${OPENMW_FILES}
|
||||||
|
${GAME} ${GAME_HEADER}
|
||||||
|
)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
target_link_libraries(tes3mp ${OPENMW_LINK_TARGETS})
|
||||||
|
|
||||||
|
if(APPLE)
|
||||||
|
add_custom_command(TARGET tes3mp
|
||||||
|
POST_BUILD
|
||||||
|
COMMAND cp "${OpenMW_BINARY_DIR}/resources/version" "${BUNDLE_RESOURCES_DIR}/resources")
|
||||||
|
endif(APPLE)
|
||||||
|
|
||||||
|
if (WIN32)
|
||||||
|
INSTALL(TARGETS openmw RUNTIME DESTINATION ".")
|
||||||
|
endif (WIN32)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(BUILD_OPENMW_VR)
|
||||||
|
# Use of FetchContent to include the OpenXR SDK requires CMake 3.11
|
||||||
|
if(${CMAKE_VERSION} VERSION_LESS "3.11")
|
||||||
|
message(FATAL_ERROR "Building openmw_vr requires CMake version 3.11 or later.")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# TODO: Openmw and openmw_vr should preferrably share game code as a static or shared library
|
||||||
|
# instead of being compiled separately, though for now that's not possible as i depend on
|
||||||
|
# USE_OPENXR preprocessor switches.
|
||||||
|
set(OPENMW_VR_FILES
|
||||||
|
vrengine.cpp
|
||||||
|
)
|
||||||
|
add_openmw_dir (mwvr
|
||||||
|
openxraction openxractionset openxrdebug openxrinput openxrmanager openxrmanagerimpl openxrplatform openxrswapchain openxrswapchainimage openxrswapchainimpl openxrtracker openxrtypeconversions
|
||||||
|
realisticcombat
|
||||||
|
vranimation vrcamera vrenvironment vrframebuffer vrgui vrinputmanager vrinput vrlistbox vrmetamenu vrpointer vrsession vrtracking vrtypes vrutil vrviewer vrvirtualkeyboard
|
||||||
|
)
|
||||||
|
|
||||||
|
openmw_add_executable(openmw_vr
|
||||||
|
${OPENMW_FILES}
|
||||||
|
${OPENMW_VR_FILES}
|
||||||
|
${GAME} ${GAME_HEADER}
|
||||||
|
${APPLE_BUNDLE_RESOURCES}
|
||||||
|
)
|
||||||
|
|
||||||
|
configure_resource_file(${OpenMW_SOURCE_DIR}/files/xrcontrollersuggestions.xml
|
||||||
|
"${OpenMW_BINARY_DIR}" "xrcontrollersuggestions.xml")
|
||||||
|
|
||||||
|
########### Import the OpenXR SDK
|
||||||
|
# Force the openxr-sdk to use its bundled jsoncpp to avoid problems from system jsoncpp if present
|
||||||
|
set(BUILD_WITH_SYSTEM_JSONCPP off)
|
||||||
|
include(FetchContent)
|
||||||
|
FetchContent_Declare(
|
||||||
|
OpenXR
|
||||||
|
GIT_REPOSITORY https://github.com/KhronosGroup/OpenXR-SDK.git
|
||||||
|
GIT_TAG release-1.0.15
|
||||||
|
)
|
||||||
|
FetchContent_MakeAvailable(OpenXR)
|
||||||
|
|
||||||
|
target_link_libraries(openmw_vr openxr_loader)
|
||||||
|
|
||||||
|
# Preprocessor variable used to control code paths to vr code
|
||||||
|
if (WIN32)
|
||||||
|
target_compile_options(openmw_vr PUBLIC -DUSE_OPENXR -DXR_USE_GRAPHICS_API_OPENGL -DXR_USE_GRAPHICS_API_D3D11 -DXR_USE_PLATFORM_WIN32)
|
||||||
|
elseif(UNIX)
|
||||||
|
target_compile_options(openmw_vr PUBLIC -DUSE_OPENXR -DXR_USE_GRAPHICS_API_OPENGL -DXR_USE_PLATFORM_XLIB)
|
||||||
|
find_package(X11 REQUIRED)
|
||||||
|
target_link_libraries(openmw_vr ${X11_LIBRARIES})
|
||||||
|
endif()
|
||||||
|
target_link_libraries(openmw_vr ${OPENMW_LINK_TARGETS})
|
||||||
|
if(APPLE)
|
||||||
|
add_custom_command(TARGET openmw_vr
|
||||||
|
POST_BUILD
|
||||||
|
COMMAND cp "${OpenMW_BINARY_DIR}/resources/version" "${BUNDLE_RESOURCES_DIR}/resources")
|
||||||
|
endif(APPLE)
|
||||||
|
|
||||||
|
if (WIN32)
|
||||||
|
INSTALL(TARGETS openmw_vr RUNTIME DESTINATION ".")
|
||||||
|
endif (WIN32)
|
||||||
|
endif()
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
INSTALL(TARGETS tes3mp RUNTIME DESTINATION ".")
|
INSTALL(TARGETS tes3mp RUNTIME DESTINATION ".")
|
||||||
endif (WIN32)
|
endif (WIN32)
|
||||||
|
|
|
@ -24,12 +24,17 @@
|
||||||
#include <components/sdlutil/sdlgraphicswindow.hpp>
|
#include <components/sdlutil/sdlgraphicswindow.hpp>
|
||||||
#include <components/sdlutil/imagetosurface.hpp>
|
#include <components/sdlutil/imagetosurface.hpp>
|
||||||
|
|
||||||
|
#include <components/shader/shadermanager.hpp>
|
||||||
|
|
||||||
#include <components/resource/resourcesystem.hpp>
|
#include <components/resource/resourcesystem.hpp>
|
||||||
#include <components/resource/scenemanager.hpp>
|
#include <components/resource/scenemanager.hpp>
|
||||||
#include <components/resource/stats.hpp>
|
#include <components/resource/stats.hpp>
|
||||||
|
|
||||||
#include <components/compiler/extensions0.hpp>
|
#include <components/compiler/extensions0.hpp>
|
||||||
|
|
||||||
|
#include <components/misc/stereo.hpp>
|
||||||
|
#include <components/misc/callbackmanager.hpp>
|
||||||
|
|
||||||
#include <components/sceneutil/workqueue.hpp>
|
#include <components/sceneutil/workqueue.hpp>
|
||||||
|
|
||||||
#include <components/files/configurationmanager.hpp>
|
#include <components/files/configurationmanager.hpp>
|
||||||
|
@ -66,6 +71,7 @@
|
||||||
#include "mwworld/worldimp.hpp"
|
#include "mwworld/worldimp.hpp"
|
||||||
|
|
||||||
#include "mwrender/vismask.hpp"
|
#include "mwrender/vismask.hpp"
|
||||||
|
#include "mwrender/camera.hpp"
|
||||||
|
|
||||||
#include "mwclass/classes.hpp"
|
#include "mwclass/classes.hpp"
|
||||||
|
|
||||||
|
@ -77,6 +83,13 @@
|
||||||
|
|
||||||
#include "mwstate/statemanagerimp.hpp"
|
#include "mwstate/statemanagerimp.hpp"
|
||||||
|
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
#include "mwvr/vrinputmanager.hpp"
|
||||||
|
#include "mwvr/vrviewer.hpp"
|
||||||
|
#include "mwvr/vrgui.hpp"
|
||||||
|
#include "mwvr/vrcamera.hpp"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
void checkSDLError(int ret)
|
void checkSDLError(int ret)
|
||||||
|
@ -465,6 +478,9 @@ OMW::Engine::Engine(Files::ConfigurationManager& configurationManager)
|
||||||
, mEncoding(ToUTF8::WINDOWS_1252)
|
, mEncoding(ToUTF8::WINDOWS_1252)
|
||||||
, mEncoder(nullptr)
|
, mEncoder(nullptr)
|
||||||
, mScreenCaptureOperation(nullptr)
|
, mScreenCaptureOperation(nullptr)
|
||||||
|
, mStereoEnabled(false)
|
||||||
|
, mStereoOverride(false)
|
||||||
|
, mStereoView(nullptr)
|
||||||
, mSkipMenu (false)
|
, mSkipMenu (false)
|
||||||
, mUseSound (true)
|
, mUseSound (true)
|
||||||
, mCompileAll (false)
|
, mCompileAll (false)
|
||||||
|
@ -508,6 +524,8 @@ OMW::Engine::~Engine()
|
||||||
End of tes3mp addition
|
End of tes3mp addition
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
mStereoView = nullptr;
|
||||||
|
|
||||||
mEnvironment.cleanup();
|
mEnvironment.cleanup();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -617,6 +635,18 @@ std::string OMW::Engine::loadSettings (Settings::Manager & settings)
|
||||||
if (boost::filesystem::exists(settingspath))
|
if (boost::filesystem::exists(settingspath))
|
||||||
settings.loadUser(settingspath);
|
settings.loadUser(settingspath);
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
const std::string localoverrides = (mCfgMgr.getLocalPath() / "settings-overrides-vr.cfg").string();
|
||||||
|
const std::string globaloverrides = (mCfgMgr.getGlobalPath() / "settings-overrides-vr.cfg").string();
|
||||||
|
if (boost::filesystem::exists(localoverrides))
|
||||||
|
settings.loadOverrides(localoverrides);
|
||||||
|
else if (boost::filesystem::exists(globaloverrides))
|
||||||
|
settings.loadOverrides(globaloverrides);
|
||||||
|
else
|
||||||
|
throw std::runtime_error("No settings overrides file found! Make sure the file \"settings-overrides-vr.cfg\" was properly installed.");
|
||||||
|
#endif
|
||||||
|
|
||||||
return settingspath;
|
return settingspath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -734,6 +764,10 @@ void OMW::Engine::createWindow(Settings::Manager& settings)
|
||||||
if (Debug::shouldDebugOpenGL())
|
if (Debug::shouldDebugOpenGL())
|
||||||
mViewer->setRealizeOperation(new Debug::EnableGLDebugOperation());
|
mViewer->setRealizeOperation(new Debug::EnableGLDebugOperation());
|
||||||
|
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
initVr();
|
||||||
|
#endif
|
||||||
|
|
||||||
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);
|
||||||
|
@ -773,6 +807,8 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
|
||||||
osg::ref_ptr<osg::Group> rootNode (new osg::Group);
|
osg::ref_ptr<osg::Group> rootNode (new osg::Group);
|
||||||
mViewer->setSceneData(rootNode);
|
mViewer->setSceneData(rootNode);
|
||||||
|
|
||||||
|
mCallbackManager.reset(new Misc::CallbackManager(mViewer));
|
||||||
|
|
||||||
mVFS.reset(new VFS::Manager(mFSStrict));
|
mVFS.reset(new VFS::Manager(mFSStrict));
|
||||||
|
|
||||||
VFS::registerArchives(mVFS.get(), mFileCollections, mArchives, true);
|
VFS::registerArchives(mVFS.get(), mFileCollections, mArchives, true);
|
||||||
|
@ -785,6 +821,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
|
||||||
Settings::Manager::getString("texture mipmap", "General"),
|
Settings::Manager::getString("texture mipmap", "General"),
|
||||||
Settings::Manager::getInt("anisotropy", "General")
|
Settings::Manager::getInt("anisotropy", "General")
|
||||||
);
|
);
|
||||||
|
mEnvironment.setResourceSystem(mResourceSystem.get());
|
||||||
|
|
||||||
int numThreads = Settings::Manager::getInt("preload num threads", "Cells");
|
int numThreads = Settings::Manager::getInt("preload num threads", "Cells");
|
||||||
if (numThreads <= 0)
|
if (numThreads <= 0)
|
||||||
|
@ -838,25 +875,89 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
|
||||||
Version::getOpenmwVersionDescription(mResDir.string()), mCfgMgr.getUserConfigPath().string());
|
Version::getOpenmwVersionDescription(mResDir.string()), mCfgMgr.getUserConfigPath().string());
|
||||||
mEnvironment.setWindowManager (window);
|
mEnvironment.setWindowManager (window);
|
||||||
|
|
||||||
MWInput::InputManager* input = new MWInput::InputManager (mWindow, mViewer, mScreenCaptureHandler, mScreenCaptureOperation, keybinderUser, keybinderUserExists, userGameControllerdb, gameControllerdb, mGrab);
|
#ifdef USE_OPENXR
|
||||||
|
const std::string xrinputuserdefault = mCfgMgr.getUserConfigPath().string() + "/xrcontrollersuggestions.xml";
|
||||||
|
const std::string xrinputlocaldefault = mCfgMgr.getLocalPath().string() + "/xrcontrollersuggestions.xml";
|
||||||
|
const std::string xrinputglobaldefault = mCfgMgr.getGlobalPath().string() + "/xrcontrollersuggestions.xml";
|
||||||
|
|
||||||
|
std::string xrControllerSuggestions;
|
||||||
|
if (boost::filesystem::exists(xrinputuserdefault))
|
||||||
|
xrControllerSuggestions = xrinputuserdefault;
|
||||||
|
else if (boost::filesystem::exists(xrinputlocaldefault))
|
||||||
|
xrControllerSuggestions = xrinputlocaldefault;
|
||||||
|
else if (boost::filesystem::exists(xrinputglobaldefault))
|
||||||
|
xrControllerSuggestions = xrinputglobaldefault;
|
||||||
|
else
|
||||||
|
xrControllerSuggestions = ""; //if it doesn't exist, pass in an empty string
|
||||||
|
|
||||||
|
Log(Debug::Verbose) << "xrinputuserdefault: " << xrinputuserdefault;
|
||||||
|
Log(Debug::Verbose) << "xrinputlocaldefault: " << xrinputlocaldefault;
|
||||||
|
Log(Debug::Verbose) << "xrinputglobaldefault: " << xrinputglobaldefault;
|
||||||
|
|
||||||
|
MWInput::InputManager* input =
|
||||||
|
new MWVR::VRInputManager(mWindow, mViewer, mScreenCaptureHandler, mScreenCaptureOperation, keybinderUser, keybinderUserExists, userGameControllerdb, gameControllerdb, mGrab, xrControllerSuggestions);
|
||||||
|
#else
|
||||||
|
MWInput::InputManager* input =
|
||||||
|
new MWInput::InputManager (mWindow, mViewer, mScreenCaptureHandler, mScreenCaptureOperation, keybinderUser, keybinderUserExists, userGameControllerdb, gameControllerdb, mGrab);
|
||||||
|
#endif
|
||||||
mEnvironment.setInputManager (input);
|
mEnvironment.setInputManager (input);
|
||||||
|
|
||||||
// Create sound system
|
// Create sound system
|
||||||
mEnvironment.setSoundManager (new MWSound::SoundManager(mVFS.get(), mUseSound));
|
mEnvironment.setSoundManager (new MWSound::SoundManager(mVFS.get(), mUseSound));
|
||||||
|
|
||||||
|
|
||||||
|
if (mStereoEnabled)
|
||||||
|
{
|
||||||
|
// Set up stereo
|
||||||
|
// Stereo setup is split in two because the GeometryShader approach cannot be used before the RenderingManager has been created.
|
||||||
|
// To be able to see the logo and initial loading screen the BruteForce technique must be set up here.
|
||||||
|
mStereoView->initializeStereo(mViewer, Misc::StereoView::Technique::BruteForce);
|
||||||
|
mResourceSystem->getSceneManager()->getShaderManager().setStereoGeometryShaderEnabled(Misc::getStereoTechnique() == Misc::StereoView::Technique::GeometryShader_IndexedViewports);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
mXrEnvironment.setGUIManager(new MWVR::VRGUIManager(mViewer, mResourceSystem.get(), rootNode));
|
||||||
|
mXrEnvironment.getViewer()->configureCallbacks();
|
||||||
|
mStereoView->setCullMask(mStereoView->getCullMask() & ~MWRender::VisMask::Mask_GUI);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
MWVR::VRCamera* camera = new MWVR::VRCamera(mViewer->getCamera());
|
||||||
|
#else
|
||||||
|
MWRender::Camera* camera = new MWRender::Camera(mViewer->getCamera());
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
if (!mSkipMenu)
|
if (!mSkipMenu)
|
||||||
{
|
{
|
||||||
const std::string& logo = Fallback::Map::getString("Movies_Company_Logo");
|
const std::string& logo = Fallback::Map::getString("Movies_Company_Logo");
|
||||||
if (!logo.empty())
|
if (!logo.empty())
|
||||||
window->playVideo(logo, true);
|
mEnvironment.getWindowManager()->playVideo(logo, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the world
|
// Create the world
|
||||||
mEnvironment.setWorld( new MWWorld::World (mViewer, rootNode, mResourceSystem.get(), mWorkQueue.get(),
|
mEnvironment.setWorld( new MWWorld::World (mViewer, rootNode, std::unique_ptr<MWRender::Camera>(camera), mResourceSystem.get(), mWorkQueue.get(),
|
||||||
mFileCollections, mContentFiles, mGroundcoverFiles, mEncoder, mActivationDistanceOverride, mCellName,
|
mFileCollections, mContentFiles, mGroundcoverFiles, mEncoder, mActivationDistanceOverride, mCellName,
|
||||||
mStartupScript, mResDir.string(), mCfgMgr.getUserDataPath().string()));
|
mStartupScript, mResDir.string(), mCfgMgr.getUserDataPath().string()));
|
||||||
mEnvironment.getWorld()->setupPlayer();
|
mEnvironment.getWorld()->setupPlayer();
|
||||||
|
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
// TODO: Workaround. Needed to stop camera from querying the world object before it is created.
|
||||||
|
// This will be prettier when i clean up the tracking logic.
|
||||||
|
camera->setShouldTrackPlayerCharacter(true);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (mStereoEnabled)
|
||||||
|
{
|
||||||
|
// Stereo shader technique can be set up now.
|
||||||
|
mStereoView->setStereoTechnique(Misc::getStereoTechnique());
|
||||||
|
mStereoView->initializeScene();
|
||||||
|
|
||||||
|
if (mEnvironment.getVrMode())
|
||||||
|
mStereoView->setCullMask(mStereoView->getCullMask() & ~MWRender::VisMask::Mask_GUI);
|
||||||
|
}
|
||||||
|
|
||||||
window->setStore(mEnvironment.getWorld()->getStore());
|
window->setStore(mEnvironment.getWorld()->getStore());
|
||||||
window->initUI();
|
window->initUI();
|
||||||
|
|
||||||
|
@ -881,7 +982,6 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
|
||||||
// Create dialog system
|
// Create dialog system
|
||||||
mEnvironment.setJournal (new MWDialogue::Journal);
|
mEnvironment.setJournal (new MWDialogue::Journal);
|
||||||
mEnvironment.setDialogueManager (new MWDialogue::DialogueManager (mExtensions, mTranslationDataStorage));
|
mEnvironment.setDialogueManager (new MWDialogue::DialogueManager (mExtensions, mTranslationDataStorage));
|
||||||
mEnvironment.setResourceSystem(mResourceSystem.get());
|
|
||||||
|
|
||||||
// scripts
|
// scripts
|
||||||
if (mCompileAll)
|
if (mCompileAll)
|
||||||
|
@ -985,6 +1085,22 @@ void OMW::Engine::go()
|
||||||
// Create encoder
|
// Create encoder
|
||||||
mEncoder = new ToUTF8::Utf8Encoder(mEncoding);
|
mEncoder = new ToUTF8::Utf8Encoder(mEncoding);
|
||||||
|
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
mEnvironment.setVrMode(true);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// geometry shader must be enabled before the RenderingManager sets up any shaders
|
||||||
|
// therefore this part is separate from the rest of stereo setup.
|
||||||
|
mStereoEnabled = mEnvironment.getVrMode() || Settings::Manager::getBool("stereo enabled", "Stereo");
|
||||||
|
if (mStereoEnabled)
|
||||||
|
{
|
||||||
|
// Mask in everything that does not currently use shaders.
|
||||||
|
// Remove that altogether when the sky finally uses them.
|
||||||
|
auto noShaderMask = MWRender::VisMask::Mask_Sky | MWRender::VisMask::Mask_Sun | MWRender::VisMask::Mask_WeatherParticles;
|
||||||
|
// Since shaders are not yet created, we need to use the brute force technique initially
|
||||||
|
mStereoView.reset(new Misc::StereoView(noShaderMask, MWRender::VisMask::Mask_Scene));
|
||||||
|
}
|
||||||
|
|
||||||
// Setup viewer
|
// Setup viewer
|
||||||
mViewer = new osgViewer::Viewer;
|
mViewer = new osgViewer::Viewer;
|
||||||
mViewer->setReleaseContextAtEndOfFrameHint(false);
|
mViewer->setReleaseContextAtEndOfFrameHint(false);
|
||||||
|
@ -1093,12 +1209,7 @@ void OMW::Engine::go()
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mViewer->eventTraversal();
|
mEnvironment.getWindowManager()->viewerTraversals(true);
|
||||||
mViewer->updateTraversal();
|
|
||||||
|
|
||||||
mEnvironment.getWorld()->updateWindowManager();
|
|
||||||
|
|
||||||
mViewer->renderingTraversals();
|
|
||||||
|
|
||||||
bool guiActive = mEnvironment.getWindowManager()->isGuiMode();
|
bool guiActive = mEnvironment.getWindowManager()->isGuiMode();
|
||||||
|
|
||||||
|
|
883
apps/openmw/engine.cpp.orig
Normal file
883
apps/openmw/engine.cpp.orig
Normal file
|
@ -0,0 +1,883 @@
|
||||||
|
#include "engine.hpp"
|
||||||
|
|
||||||
|
#include <iomanip>
|
||||||
|
|
||||||
|
#include <boost/filesystem/fstream.hpp>
|
||||||
|
|
||||||
|
#include <osgViewer/ViewerEventHandlers>
|
||||||
|
#include <osgDB/ReadFile>
|
||||||
|
#include <osgDB/WriteFile>
|
||||||
|
|
||||||
|
#include <SDL.h>
|
||||||
|
|
||||||
|
#include <components/debug/debuglog.hpp>
|
||||||
|
|
||||||
|
#include <components/misc/rng.hpp>
|
||||||
|
|
||||||
|
#include <components/vfs/manager.hpp>
|
||||||
|
#include <components/vfs/registerarchives.hpp>
|
||||||
|
|
||||||
|
#include <components/sdlutil/sdlgraphicswindow.hpp>
|
||||||
|
#include <components/sdlutil/imagetosurface.hpp>
|
||||||
|
|
||||||
|
#include <components/resource/resourcesystem.hpp>
|
||||||
|
#include <components/resource/scenemanager.hpp>
|
||||||
|
#include <components/resource/stats.hpp>
|
||||||
|
|
||||||
|
#include <components/compiler/extensions0.hpp>
|
||||||
|
|
||||||
|
#include <components/sceneutil/workqueue.hpp>
|
||||||
|
|
||||||
|
#include <components/files/configurationmanager.hpp>
|
||||||
|
|
||||||
|
#include <components/version/version.hpp>
|
||||||
|
|
||||||
|
#include <components/detournavigator/navigator.hpp>
|
||||||
|
|
||||||
|
#include "mwinput/inputmanagerimp.hpp"
|
||||||
|
|
||||||
|
#include "mwgui/windowmanagerimp.hpp"
|
||||||
|
|
||||||
|
#include "mwscript/scriptmanagerimp.hpp"
|
||||||
|
#include "mwscript/interpretercontext.hpp"
|
||||||
|
|
||||||
|
#include "mwsound/soundmanagerimp.hpp"
|
||||||
|
|
||||||
|
#include "mwworld/class.hpp"
|
||||||
|
#include "mwworld/player.hpp"
|
||||||
|
#include "mwworld/worldimp.hpp"
|
||||||
|
|
||||||
|
#include "mwrender/vismask.hpp"
|
||||||
|
|
||||||
|
#include "mwclass/classes.hpp"
|
||||||
|
|
||||||
|
#include "mwdialogue/dialoguemanagerimp.hpp"
|
||||||
|
#include "mwdialogue/journalimp.hpp"
|
||||||
|
#include "mwdialogue/scripttest.hpp"
|
||||||
|
|
||||||
|
#include "mwmechanics/mechanicsmanagerimp.hpp"
|
||||||
|
|
||||||
|
#include "mwstate/statemanagerimp.hpp"
|
||||||
|
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
#include "mwvr/openxrinputmanager.hpp"
|
||||||
|
#include "mwvr/openxrviewer.hpp"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
void checkSDLError(int ret)
|
||||||
|
{
|
||||||
|
if (ret != 0)
|
||||||
|
Log(Debug::Error) << "SDL error: " << SDL_GetError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OMW::Engine::executeLocalScripts()
|
||||||
|
{
|
||||||
|
MWWorld::LocalScripts& localScripts = mEnvironment.getWorld()->getLocalScripts();
|
||||||
|
|
||||||
|
localScripts.startIteration();
|
||||||
|
std::pair<std::string, MWWorld::Ptr> script;
|
||||||
|
while (localScripts.getNext(script))
|
||||||
|
{
|
||||||
|
MWScript::InterpreterContext interpreterContext (
|
||||||
|
&script.second.getRefData().getLocals(), script.second);
|
||||||
|
mEnvironment.getScriptManager()->run (script.first, interpreterContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OMW::Engine::frame(float frametime)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
mStartTick = mViewer->getStartTick();
|
||||||
|
|
||||||
|
mEnvironment.setFrameDuration(frametime);
|
||||||
|
|
||||||
|
// update input
|
||||||
|
mEnvironment.getInputManager()->update(frametime, false);
|
||||||
|
|
||||||
|
// When the window is minimized, pause the game. Currently this *has* to be here to work around a MyGUI bug.
|
||||||
|
// If we are not currently rendering, then RenderItems will not be reused resulting in a memory leak upon changing widget textures (fixed in MyGUI 3.3.2),
|
||||||
|
// and destroyed widgets will not be deleted (not fixed yet, https://github.com/MyGUI/mygui/issues/21)
|
||||||
|
if (!mEnvironment.getWindowManager()->isWindowVisible())
|
||||||
|
{
|
||||||
|
mEnvironment.getSoundManager()->pausePlayback();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
mEnvironment.getSoundManager()->resumePlayback();
|
||||||
|
|
||||||
|
// sound
|
||||||
|
if (mUseSound)
|
||||||
|
mEnvironment.getSoundManager()->update(frametime);
|
||||||
|
|
||||||
|
// Main menu opened? Then scripts are also paused.
|
||||||
|
bool paused = mEnvironment.getWindowManager()->containsMode(MWGui::GM_MainMenu);
|
||||||
|
|
||||||
|
// update game state
|
||||||
|
mEnvironment.getStateManager()->update (frametime);
|
||||||
|
|
||||||
|
bool guiActive = mEnvironment.getWindowManager()->isGuiMode();
|
||||||
|
|
||||||
|
osg::Timer_t beforeScriptTick = osg::Timer::instance()->tick();
|
||||||
|
if (mEnvironment.getStateManager()->getState()!=
|
||||||
|
MWBase::StateManager::State_NoGame)
|
||||||
|
{
|
||||||
|
if (!paused)
|
||||||
|
{
|
||||||
|
if (mEnvironment.getWorld()->getScriptsEnabled())
|
||||||
|
{
|
||||||
|
// local scripts
|
||||||
|
executeLocalScripts();
|
||||||
|
|
||||||
|
// global scripts
|
||||||
|
mEnvironment.getScriptManager()->getGlobalScripts().run();
|
||||||
|
}
|
||||||
|
|
||||||
|
mEnvironment.getWorld()->markCellAsUnchanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!guiActive)
|
||||||
|
{
|
||||||
|
double hours = (frametime * mEnvironment.getWorld()->getTimeScaleFactor()) / 3600.0;
|
||||||
|
mEnvironment.getWorld()->advanceTime(hours, true);
|
||||||
|
mEnvironment.getWorld()->rechargeItems(frametime, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
osg::Timer_t afterScriptTick = osg::Timer::instance()->tick();
|
||||||
|
|
||||||
|
// update actors
|
||||||
|
osg::Timer_t beforeMechanicsTick = osg::Timer::instance()->tick();
|
||||||
|
if (mEnvironment.getStateManager()->getState()!=
|
||||||
|
MWBase::StateManager::State_NoGame)
|
||||||
|
{
|
||||||
|
mEnvironment.getMechanicsManager()->update(frametime,
|
||||||
|
guiActive);
|
||||||
|
}
|
||||||
|
osg::Timer_t afterMechanicsTick = osg::Timer::instance()->tick();
|
||||||
|
|
||||||
|
if (mEnvironment.getStateManager()->getState()==
|
||||||
|
MWBase::StateManager::State_Running)
|
||||||
|
{
|
||||||
|
MWWorld::Ptr player = mEnvironment.getWorld()->getPlayerPtr();
|
||||||
|
if(!guiActive && player.getClass().getCreatureStats(player).isDead())
|
||||||
|
mEnvironment.getStateManager()->endGame();
|
||||||
|
}
|
||||||
|
|
||||||
|
// update physics
|
||||||
|
osg::Timer_t beforePhysicsTick = osg::Timer::instance()->tick();
|
||||||
|
if (mEnvironment.getStateManager()->getState()!=
|
||||||
|
MWBase::StateManager::State_NoGame)
|
||||||
|
{
|
||||||
|
mEnvironment.getWorld()->updatePhysics(frametime, guiActive);
|
||||||
|
}
|
||||||
|
osg::Timer_t afterPhysicsTick = osg::Timer::instance()->tick();
|
||||||
|
|
||||||
|
// update world
|
||||||
|
osg::Timer_t beforeWorldTick = osg::Timer::instance()->tick();
|
||||||
|
if (mEnvironment.getStateManager()->getState()!=
|
||||||
|
MWBase::StateManager::State_NoGame)
|
||||||
|
{
|
||||||
|
mEnvironment.getWorld()->update(frametime, guiActive);
|
||||||
|
}
|
||||||
|
osg::Timer_t afterWorldTick = osg::Timer::instance()->tick();
|
||||||
|
|
||||||
|
// update GUI
|
||||||
|
mEnvironment.getWindowManager()->onFrame(frametime);
|
||||||
|
|
||||||
|
unsigned int frameNumber = mViewer->getFrameStamp()->getFrameNumber();
|
||||||
|
osg::Stats* stats = mViewer->getViewerStats();
|
||||||
|
stats->setAttribute(frameNumber, "script_time_begin", osg::Timer::instance()->delta_s(mStartTick, beforeScriptTick));
|
||||||
|
stats->setAttribute(frameNumber, "script_time_taken", osg::Timer::instance()->delta_s(beforeScriptTick, afterScriptTick));
|
||||||
|
stats->setAttribute(frameNumber, "script_time_end", osg::Timer::instance()->delta_s(mStartTick, afterScriptTick));
|
||||||
|
|
||||||
|
stats->setAttribute(frameNumber, "mechanics_time_begin", osg::Timer::instance()->delta_s(mStartTick, beforeMechanicsTick));
|
||||||
|
stats->setAttribute(frameNumber, "mechanics_time_taken", osg::Timer::instance()->delta_s(beforeMechanicsTick, afterMechanicsTick));
|
||||||
|
stats->setAttribute(frameNumber, "mechanics_time_end", osg::Timer::instance()->delta_s(mStartTick, afterMechanicsTick));
|
||||||
|
|
||||||
|
stats->setAttribute(frameNumber, "physics_time_begin", osg::Timer::instance()->delta_s(mStartTick, beforePhysicsTick));
|
||||||
|
stats->setAttribute(frameNumber, "physics_time_taken", osg::Timer::instance()->delta_s(beforePhysicsTick, afterPhysicsTick));
|
||||||
|
stats->setAttribute(frameNumber, "physics_time_end", osg::Timer::instance()->delta_s(mStartTick, afterPhysicsTick));
|
||||||
|
|
||||||
|
stats->setAttribute(frameNumber, "world_time_begin", osg::Timer::instance()->delta_s(mStartTick, beforeWorldTick));
|
||||||
|
stats->setAttribute(frameNumber, "world_time_taken", osg::Timer::instance()->delta_s(beforeWorldTick, afterWorldTick));
|
||||||
|
stats->setAttribute(frameNumber, "world_time_end", osg::Timer::instance()->delta_s(mStartTick, afterWorldTick));
|
||||||
|
|
||||||
|
if (stats->collectStats("resource"))
|
||||||
|
{
|
||||||
|
mResourceSystem->reportStats(frameNumber, stats);
|
||||||
|
|
||||||
|
stats->setAttribute(frameNumber, "WorkQueue", mWorkQueue->getNumItems());
|
||||||
|
stats->setAttribute(frameNumber, "WorkThread", mWorkQueue->getNumActiveThreads());
|
||||||
|
|
||||||
|
mEnvironment.getWorld()->getNavigator()->reportStats(frameNumber, *stats);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (const std::exception& e)
|
||||||
|
{
|
||||||
|
Log(Debug::Error) << "Error in frame: " << e.what();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
OMW::Engine::Engine(Files::ConfigurationManager& configurationManager)
|
||||||
|
: mWindow(nullptr)
|
||||||
|
, mEncoding(ToUTF8::WINDOWS_1252)
|
||||||
|
, mEncoder(nullptr)
|
||||||
|
, mScreenCaptureOperation(nullptr)
|
||||||
|
, mSkipMenu (false)
|
||||||
|
, mUseSound (true)
|
||||||
|
, mCompileAll (false)
|
||||||
|
, mCompileAllDialogue (false)
|
||||||
|
, mWarningsMode (1)
|
||||||
|
, mScriptConsoleMode (false)
|
||||||
|
, mActivationDistanceOverride(-1)
|
||||||
|
, mGrab(true)
|
||||||
|
, mExportFonts(false)
|
||||||
|
, mRandomSeed(0)
|
||||||
|
, mScriptContext (0)
|
||||||
|
, mFSStrict (false)
|
||||||
|
, mScriptBlacklistUse (true)
|
||||||
|
, mNewGame (false)
|
||||||
|
, mCfgMgr(configurationManager)
|
||||||
|
{
|
||||||
|
MWClass::registerClasses();
|
||||||
|
|
||||||
|
SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0"); // We use only gamepads
|
||||||
|
|
||||||
|
Uint32 flags = SDL_INIT_VIDEO|SDL_INIT_NOPARACHUTE|SDL_INIT_GAMECONTROLLER|SDL_INIT_JOYSTICK|SDL_INIT_SENSOR;
|
||||||
|
if(SDL_WasInit(flags) == 0)
|
||||||
|
{
|
||||||
|
SDL_SetMainReady();
|
||||||
|
if(SDL_Init(flags) != 0)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Could not initialize SDL! " + std::string(SDL_GetError()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mStartTick = osg::Timer::instance()->tick();
|
||||||
|
}
|
||||||
|
|
||||||
|
OMW::Engine::~Engine()
|
||||||
|
{
|
||||||
|
mEnvironment.cleanup();
|
||||||
|
|
||||||
|
delete mScriptContext;
|
||||||
|
mScriptContext = nullptr;
|
||||||
|
|
||||||
|
mWorkQueue = nullptr;
|
||||||
|
|
||||||
|
mViewer = nullptr;
|
||||||
|
|
||||||
|
mResourceSystem.reset();
|
||||||
|
|
||||||
|
delete mEncoder;
|
||||||
|
mEncoder = nullptr;
|
||||||
|
|
||||||
|
if (mWindow)
|
||||||
|
{
|
||||||
|
SDL_DestroyWindow(mWindow);
|
||||||
|
mWindow = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_Quit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OMW::Engine::enableFSStrict(bool fsStrict)
|
||||||
|
{
|
||||||
|
mFSStrict = fsStrict;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set data dir
|
||||||
|
|
||||||
|
void OMW::Engine::setDataDirs (const Files::PathContainer& dataDirs)
|
||||||
|
{
|
||||||
|
mDataDirs = dataDirs;
|
||||||
|
mDataDirs.insert(mDataDirs.begin(), (mResDir / "vfs"));
|
||||||
|
mFileCollections = Files::Collections (mDataDirs, !mFSStrict);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add BSA archive
|
||||||
|
void OMW::Engine::addArchive (const std::string& archive) {
|
||||||
|
mArchives.push_back(archive);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set resource dir
|
||||||
|
void OMW::Engine::setResourceDir (const boost::filesystem::path& parResDir)
|
||||||
|
{
|
||||||
|
mResDir = parResDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set start cell name
|
||||||
|
void OMW::Engine::setCell (const std::string& cellName)
|
||||||
|
{
|
||||||
|
mCellName = cellName;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OMW::Engine::addContentFile(const std::string& file)
|
||||||
|
{
|
||||||
|
mContentFiles.push_back(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OMW::Engine::setSkipMenu (bool skipMenu, bool newGame)
|
||||||
|
{
|
||||||
|
mSkipMenu = skipMenu;
|
||||||
|
mNewGame = newGame;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string OMW::Engine::loadSettings (Settings::Manager & settings)
|
||||||
|
{
|
||||||
|
// Create the settings manager and load default settings file
|
||||||
|
const std::string localdefault = (mCfgMgr.getLocalPath() / "settings-default.cfg").string();
|
||||||
|
const std::string globaldefault = (mCfgMgr.getGlobalPath() / "settings-default.cfg").string();
|
||||||
|
|
||||||
|
// prefer local
|
||||||
|
if (boost::filesystem::exists(localdefault))
|
||||||
|
settings.loadDefault(localdefault);
|
||||||
|
else if (boost::filesystem::exists(globaldefault))
|
||||||
|
settings.loadDefault(globaldefault);
|
||||||
|
else
|
||||||
|
throw std::runtime_error ("No default settings file found! Make sure the file \"settings-default.cfg\" was properly installed.");
|
||||||
|
|
||||||
|
// load user settings if they exist
|
||||||
|
const std::string settingspath = (mCfgMgr.getUserConfigPath() / "settings.cfg").string();
|
||||||
|
if (boost::filesystem::exists(settingspath))
|
||||||
|
settings.loadUser(settingspath);
|
||||||
|
|
||||||
|
return settingspath;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OMW::Engine::createWindow(Settings::Manager& settings)
|
||||||
|
{
|
||||||
|
int screen = settings.getInt("screen", "Video");
|
||||||
|
int width = settings.getInt("resolution x", "Video");
|
||||||
|
int height = settings.getInt("resolution y", "Video");
|
||||||
|
bool fullscreen = settings.getBool("fullscreen", "Video");
|
||||||
|
bool windowBorder = settings.getBool("window border", "Video");
|
||||||
|
bool vsync = settings.getBool("vsync", "Video");
|
||||||
|
int antialiasing = settings.getInt("antialiasing", "Video");
|
||||||
|
|
||||||
|
int pos_x = SDL_WINDOWPOS_CENTERED_DISPLAY(screen),
|
||||||
|
pos_y = SDL_WINDOWPOS_CENTERED_DISPLAY(screen);
|
||||||
|
|
||||||
|
if(fullscreen)
|
||||||
|
{
|
||||||
|
pos_x = SDL_WINDOWPOS_UNDEFINED_DISPLAY(screen);
|
||||||
|
pos_y = SDL_WINDOWPOS_UNDEFINED_DISPLAY(screen);
|
||||||
|
}
|
||||||
|
|
||||||
|
Uint32 flags = SDL_WINDOW_OPENGL|SDL_WINDOW_SHOWN|SDL_WINDOW_RESIZABLE;
|
||||||
|
if(fullscreen)
|
||||||
|
flags |= SDL_WINDOW_FULLSCREEN;
|
||||||
|
|
||||||
|
if (!windowBorder)
|
||||||
|
flags |= SDL_WINDOW_BORDERLESS;
|
||||||
|
|
||||||
|
SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS,
|
||||||
|
settings.getBool("minimize on focus loss", "Video") ? "1" : "0");
|
||||||
|
|
||||||
|
checkSDLError(SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8));
|
||||||
|
checkSDLError(SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8));
|
||||||
|
checkSDLError(SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8));
|
||||||
|
checkSDLError(SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0));
|
||||||
|
checkSDLError(SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24));
|
||||||
|
|
||||||
|
if (antialiasing > 0)
|
||||||
|
{
|
||||||
|
checkSDLError(SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1));
|
||||||
|
checkSDLError(SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, antialiasing));
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!mWindow)
|
||||||
|
{
|
||||||
|
mWindow = SDL_CreateWindow("OpenMW", pos_x, pos_y, width, height, flags);
|
||||||
|
if (!mWindow)
|
||||||
|
{
|
||||||
|
// Try with a lower AA
|
||||||
|
if (antialiasing > 0)
|
||||||
|
{
|
||||||
|
Log(Debug::Warning) << "Warning: " << antialiasing << "x antialiasing not supported, trying " << antialiasing/2;
|
||||||
|
antialiasing /= 2;
|
||||||
|
Settings::Manager::setInt("antialiasing", "Video", antialiasing);
|
||||||
|
checkSDLError(SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, antialiasing));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::stringstream error;
|
||||||
|
error << "Failed to create SDL window: " << SDL_GetError();
|
||||||
|
throw std::runtime_error(error.str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setWindowIcon();
|
||||||
|
|
||||||
|
osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;
|
||||||
|
SDL_GetWindowPosition(mWindow, &traits->x, &traits->y);
|
||||||
|
SDL_GetWindowSize(mWindow, &traits->width, &traits->height);
|
||||||
|
traits->windowName = SDL_GetWindowTitle(mWindow);
|
||||||
|
traits->windowDecoration = !(SDL_GetWindowFlags(mWindow)&SDL_WINDOW_BORDERLESS);
|
||||||
|
traits->screenNum = SDL_GetWindowDisplayIndex(mWindow);
|
||||||
|
// We tried to get rid of the hardcoding but failed: https://github.com/OpenMW/openmw/pull/1771
|
||||||
|
// Here goes kcat's quote:
|
||||||
|
// It's ultimately a chicken and egg problem, and the reason why the code is like it was in the first place.
|
||||||
|
// It needs a context to get the current attributes, but it needs the attributes to set up the context.
|
||||||
|
// So it just specifies the same values that were given to SDL in the hopes that it's good enough to what the window eventually gets.
|
||||||
|
traits->red = 8;
|
||||||
|
traits->green = 8;
|
||||||
|
traits->blue = 8;
|
||||||
|
traits->alpha = 0; // set to 0 to stop ScreenCaptureHandler reading the alpha channel
|
||||||
|
traits->depth = 24;
|
||||||
|
traits->stencil = 8;
|
||||||
|
traits->vsync = vsync;
|
||||||
|
traits->doubleBuffer = true;
|
||||||
|
traits->inheritedWindowData = new SDLUtil::GraphicsWindowSDL2::WindowData(mWindow);
|
||||||
|
|
||||||
|
osg::ref_ptr<SDLUtil::GraphicsWindowSDL2> graphicsWindow = new SDLUtil::GraphicsWindowSDL2(traits);
|
||||||
|
if(!graphicsWindow->valid()) throw std::runtime_error("Failed to create GraphicsContext");
|
||||||
|
|
||||||
|
osg::ref_ptr<osg::Camera> camera = mViewer->getCamera();
|
||||||
|
camera->setGraphicsContext(graphicsWindow);
|
||||||
|
camera->setViewport(0, 0, traits->width, traits->height);
|
||||||
|
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
initVr();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
mViewer->realize();
|
||||||
|
|
||||||
|
mViewer->getEventQueue()->getCurrentEventState()->setWindowRectangle(0, 0, traits->width, traits->height);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OMW::Engine::setWindowIcon()
|
||||||
|
{
|
||||||
|
boost::filesystem::ifstream windowIconStream;
|
||||||
|
std::string windowIcon = (mResDir / "mygui" / "openmw.png").string();
|
||||||
|
windowIconStream.open(windowIcon, std::ios_base::in | std::ios_base::binary);
|
||||||
|
if (windowIconStream.fail())
|
||||||
|
Log(Debug::Error) << "Error: Failed to open " << windowIcon;
|
||||||
|
osgDB::ReaderWriter* reader = osgDB::Registry::instance()->getReaderWriterForExtension("png");
|
||||||
|
if (!reader)
|
||||||
|
{
|
||||||
|
Log(Debug::Error) << "Error: Failed to read window icon, no png readerwriter found";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
osgDB::ReaderWriter::ReadResult result = reader->readImage(windowIconStream);
|
||||||
|
if (!result.success())
|
||||||
|
Log(Debug::Error) << "Error: Failed to read " << windowIcon << ": " << result.message() << " code " << result.status();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
osg::ref_ptr<osg::Image> image = result.getImage();
|
||||||
|
auto surface = SDLUtil::imageToSurface(image, true);
|
||||||
|
SDL_SetWindowIcon(mWindow, surface.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OMW::Engine::prepareEngine (Settings::Manager & settings)
|
||||||
|
{
|
||||||
|
mEnvironment.setStateManager (
|
||||||
|
new MWState::StateManager (mCfgMgr.getUserDataPath() / "saves", mContentFiles.at (0)));
|
||||||
|
|
||||||
|
createWindow(settings);
|
||||||
|
|
||||||
|
osg::ref_ptr<osg::Group> rootNode (new osg::Group);
|
||||||
|
|
||||||
|
mViewer->setSceneData(rootNode);
|
||||||
|
|
||||||
|
mVFS.reset(new VFS::Manager(mFSStrict));
|
||||||
|
|
||||||
|
VFS::registerArchives(mVFS.get(), mFileCollections, mArchives, true);
|
||||||
|
|
||||||
|
mResourceSystem.reset(new Resource::ResourceSystem(mVFS.get()));
|
||||||
|
mResourceSystem->getSceneManager()->setUnRefImageDataAfterApply(false); // keep to Off for now to allow better state sharing
|
||||||
|
mResourceSystem->getSceneManager()->setFilterSettings(
|
||||||
|
Settings::Manager::getString("texture mag filter", "General"),
|
||||||
|
Settings::Manager::getString("texture min filter", "General"),
|
||||||
|
Settings::Manager::getString("texture mipmap", "General"),
|
||||||
|
Settings::Manager::getInt("anisotropy", "General")
|
||||||
|
);
|
||||||
|
|
||||||
|
int numThreads = Settings::Manager::getInt("preload num threads", "Cells");
|
||||||
|
if (numThreads <= 0)
|
||||||
|
throw std::runtime_error("Invalid setting: 'preload num threads' must be >0");
|
||||||
|
mWorkQueue = new SceneUtil::WorkQueue(numThreads);
|
||||||
|
|
||||||
|
// Create input and UI first to set up a bootstrapping environment for
|
||||||
|
// showing a loading screen and keeping the window responsive while doing so
|
||||||
|
|
||||||
|
std::string keybinderUser = (mCfgMgr.getUserConfigPath() / "input_v3.xml").string();
|
||||||
|
bool keybinderUserExists = boost::filesystem::exists(keybinderUser);
|
||||||
|
if(!keybinderUserExists)
|
||||||
|
{
|
||||||
|
std::string input2 = (mCfgMgr.getUserConfigPath() / "input_v2.xml").string();
|
||||||
|
if(boost::filesystem::exists(input2)) {
|
||||||
|
boost::filesystem::copy_file(input2, keybinderUser);
|
||||||
|
keybinderUserExists = boost::filesystem::exists(keybinderUser);
|
||||||
|
Log(Debug::Info) << "Loading keybindings file: " << keybinderUser;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Log(Debug::Info) << "Loading keybindings file: " << keybinderUser;
|
||||||
|
|
||||||
|
const std::string userdefault = mCfgMgr.getUserConfigPath().string() + "/gamecontrollerdb.txt";
|
||||||
|
const std::string localdefault = mCfgMgr.getLocalPath().string() + "/gamecontrollerdb.txt";
|
||||||
|
const std::string globaldefault = mCfgMgr.getGlobalPath().string() + "/gamecontrollerdb.txt";
|
||||||
|
|
||||||
|
std::string userGameControllerdb;
|
||||||
|
if (boost::filesystem::exists(userdefault)){
|
||||||
|
userGameControllerdb = userdefault;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
userGameControllerdb = "";
|
||||||
|
|
||||||
|
std::string gameControllerdb;
|
||||||
|
if (boost::filesystem::exists(localdefault))
|
||||||
|
gameControllerdb = localdefault;
|
||||||
|
else if (boost::filesystem::exists(globaldefault))
|
||||||
|
gameControllerdb = globaldefault;
|
||||||
|
else
|
||||||
|
gameControllerdb = ""; //if it doesn't exist, pass in an empty string
|
||||||
|
<<<<<<< HEAD
|
||||||
|
MWInput::InputManager* input =
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
new MWVR::OpenXRInputManager(mWindow, mViewer, mScreenCaptureHandler, mScreenCaptureOperation, keybinderUser, keybinderUserExists, userGameControllerdb, gameControllerdb, mGrab);
|
||||||
|
#else
|
||||||
|
new MWInput::InputManager (mWindow, mViewer, mScreenCaptureHandler, mScreenCaptureOperation, keybinderUser, keybinderUserExists, userGameControllerdb, gameControllerdb, mGrab);
|
||||||
|
#endif
|
||||||
|
mEnvironment.setInputManager (input);
|
||||||
|
=======
|
||||||
|
>>>>>>> 6b44b7f245e12566c26b6bdd92448aeb6dd90a85
|
||||||
|
|
||||||
|
std::string myguiResources = (mResDir / "mygui").string();
|
||||||
|
osg::ref_ptr<osg::Group> guiRoot = new osg::Group;
|
||||||
|
guiRoot->setName("GUI Root");
|
||||||
|
guiRoot->setNodeMask(MWRender::Mask_GUI);
|
||||||
|
rootNode->addChild(guiRoot);
|
||||||
|
MWGui::WindowManager* window = new MWGui::WindowManager(mWindow, mViewer, guiRoot, mResourceSystem.get(), mWorkQueue.get(),
|
||||||
|
mCfgMgr.getLogPath().string() + std::string("/"), myguiResources,
|
||||||
|
mScriptConsoleMode, mTranslationDataStorage, mEncoding, mExportFonts,
|
||||||
|
Version::getOpenmwVersionDescription(mResDir.string()), mCfgMgr.getUserConfigPath().string());
|
||||||
|
mEnvironment.setWindowManager (window);
|
||||||
|
|
||||||
|
MWInput::InputManager* input = new MWInput::InputManager (mWindow, mViewer, mScreenCaptureHandler, mScreenCaptureOperation, keybinderUser, keybinderUserExists, userGameControllerdb, gameControllerdb, mGrab);
|
||||||
|
mEnvironment.setInputManager (input);
|
||||||
|
|
||||||
|
// Create sound system
|
||||||
|
mEnvironment.setSoundManager (new MWSound::SoundManager(mVFS.get(), mUseSound));
|
||||||
|
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
mXrEnvironment.setGUIManager(new MWVR::VRGUIManager(mViewer));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!mSkipMenu)
|
||||||
|
{
|
||||||
|
const std::string& logo = Fallback::Map::getString("Movies_Company_Logo");
|
||||||
|
if (!logo.empty())
|
||||||
|
window->playVideo(logo, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the world
|
||||||
|
mEnvironment.setWorld( new MWWorld::World (mViewer, rootNode, mResourceSystem.get(), mWorkQueue.get(),
|
||||||
|
mFileCollections, mContentFiles, mEncoder, mActivationDistanceOverride, mCellName,
|
||||||
|
mStartupScript, mResDir.string(), mCfgMgr.getUserDataPath().string()));
|
||||||
|
mEnvironment.getWorld()->setupPlayer();
|
||||||
|
|
||||||
|
window->setStore(mEnvironment.getWorld()->getStore());
|
||||||
|
window->initUI();
|
||||||
|
|
||||||
|
//Load translation data
|
||||||
|
mTranslationDataStorage.setEncoder(mEncoder);
|
||||||
|
for (size_t i = 0; i < mContentFiles.size(); i++)
|
||||||
|
mTranslationDataStorage.loadTranslationData(mFileCollections, mContentFiles[i]);
|
||||||
|
|
||||||
|
Compiler::registerExtensions (mExtensions);
|
||||||
|
|
||||||
|
// Create script system
|
||||||
|
mScriptContext = new MWScript::CompilerContext (MWScript::CompilerContext::Type_Full);
|
||||||
|
mScriptContext->setExtensions (&mExtensions);
|
||||||
|
|
||||||
|
mEnvironment.setScriptManager (new MWScript::ScriptManager (mEnvironment.getWorld()->getStore(), *mScriptContext, mWarningsMode,
|
||||||
|
mScriptBlacklistUse ? mScriptBlacklist : std::vector<std::string>()));
|
||||||
|
|
||||||
|
// Create game mechanics system
|
||||||
|
MWMechanics::MechanicsManager* mechanics = new MWMechanics::MechanicsManager;
|
||||||
|
mEnvironment.setMechanicsManager (mechanics);
|
||||||
|
|
||||||
|
// Create dialog system
|
||||||
|
mEnvironment.setJournal (new MWDialogue::Journal);
|
||||||
|
mEnvironment.setDialogueManager (new MWDialogue::DialogueManager (mExtensions, mTranslationDataStorage));
|
||||||
|
|
||||||
|
// scripts
|
||||||
|
if (mCompileAll)
|
||||||
|
{
|
||||||
|
std::pair<int, int> result = mEnvironment.getScriptManager()->compileAll();
|
||||||
|
if (result.first)
|
||||||
|
Log(Debug::Info)
|
||||||
|
<< "compiled " << result.second << " of " << result.first << " scripts ("
|
||||||
|
<< 100*static_cast<double> (result.second)/result.first
|
||||||
|
<< "%)";
|
||||||
|
}
|
||||||
|
if (mCompileAllDialogue)
|
||||||
|
{
|
||||||
|
std::pair<int, int> result = MWDialogue::ScriptTest::compileAll(&mExtensions, mWarningsMode);
|
||||||
|
if (result.first)
|
||||||
|
Log(Debug::Info)
|
||||||
|
<< "compiled " << result.second << " of " << result.first << " dialogue script/actor combinations a("
|
||||||
|
<< 100*static_cast<double> (result.second)/result.first
|
||||||
|
<< "%)";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class WriteScreenshotToFileOperation : public osgViewer::ScreenCaptureHandler::CaptureOperation
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
WriteScreenshotToFileOperation(const std::string& screenshotPath, const std::string& screenshotFormat)
|
||||||
|
: mScreenshotPath(screenshotPath)
|
||||||
|
, mScreenshotFormat(screenshotFormat)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void operator()(const osg::Image& image, const unsigned int context_id)
|
||||||
|
{
|
||||||
|
// Count screenshots.
|
||||||
|
int shotCount = 0;
|
||||||
|
|
||||||
|
// Find the first unused filename with a do-while
|
||||||
|
std::ostringstream stream;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
// Reset the stream
|
||||||
|
stream.str("");
|
||||||
|
stream.clear();
|
||||||
|
|
||||||
|
stream << mScreenshotPath << "/screenshot" << std::setw(3) << std::setfill('0') << shotCount++ << "." << mScreenshotFormat;
|
||||||
|
|
||||||
|
} while (boost::filesystem::exists(stream.str()));
|
||||||
|
|
||||||
|
boost::filesystem::ofstream outStream;
|
||||||
|
outStream.open(boost::filesystem::path(stream.str()), std::ios::binary);
|
||||||
|
|
||||||
|
osgDB::ReaderWriter* readerwriter = osgDB::Registry::instance()->getReaderWriterForExtension(mScreenshotFormat);
|
||||||
|
if (!readerwriter)
|
||||||
|
{
|
||||||
|
Log(Debug::Error) << "Error: Can't write screenshot, no '" << mScreenshotFormat << "' readerwriter found";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
osgDB::ReaderWriter::WriteResult result = readerwriter->writeImage(image, outStream);
|
||||||
|
if (!result.success())
|
||||||
|
{
|
||||||
|
Log(Debug::Error) << "Error: Can't write screenshot: " << result.message() << " code " << result.status();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string mScreenshotPath;
|
||||||
|
std::string mScreenshotFormat;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Initialise and enter main loop.
|
||||||
|
void OMW::Engine::go()
|
||||||
|
{
|
||||||
|
assert (!mContentFiles.empty());
|
||||||
|
|
||||||
|
Log(Debug::Info) << "OSG version: " << osgGetVersion();
|
||||||
|
SDL_version sdlVersion;
|
||||||
|
SDL_GetVersion(&sdlVersion);
|
||||||
|
Log(Debug::Info) << "SDL version: " << (int)sdlVersion.major << "." << (int)sdlVersion.minor << "." << (int)sdlVersion.patch;
|
||||||
|
|
||||||
|
Misc::Rng::init(mRandomSeed);
|
||||||
|
|
||||||
|
// Load settings
|
||||||
|
Settings::Manager settings;
|
||||||
|
std::string settingspath;
|
||||||
|
settingspath = loadSettings (settings);
|
||||||
|
|
||||||
|
// Create encoder
|
||||||
|
mEncoder = new ToUTF8::Utf8Encoder(mEncoding);
|
||||||
|
|
||||||
|
// Setup viewer
|
||||||
|
mViewer = new osgViewer::Viewer;
|
||||||
|
mViewer->setReleaseContextAtEndOfFrameHint(false);
|
||||||
|
|
||||||
|
#if OSG_VERSION_GREATER_OR_EQUAL(3,5,5)
|
||||||
|
// Do not try to outsmart the OS thread scheduler (see bug #4785).
|
||||||
|
mViewer->setUseConfigureAffinity(false);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
mScreenCaptureOperation = new WriteScreenshotToFileOperation(
|
||||||
|
mCfgMgr.getScreenshotPath().string(),
|
||||||
|
Settings::Manager::getString("screenshot format", "General"));
|
||||||
|
|
||||||
|
mScreenCaptureHandler = new osgViewer::ScreenCaptureHandler(mScreenCaptureOperation);
|
||||||
|
|
||||||
|
mViewer->addEventHandler(mScreenCaptureHandler);
|
||||||
|
|
||||||
|
mEnvironment.setFrameRateLimit(Settings::Manager::getFloat("framerate limit", "Video"));
|
||||||
|
|
||||||
|
prepareEngine (settings);
|
||||||
|
|
||||||
|
// Setup profiler
|
||||||
|
osg::ref_ptr<Resource::Profiler> statshandler = new Resource::Profiler;
|
||||||
|
|
||||||
|
statshandler->addUserStatsLine("Script", osg::Vec4f(1.f, 1.f, 1.f, 1.f), osg::Vec4f(1.f, 1.f, 1.f, 1.f),
|
||||||
|
"script_time_taken", 1000.0, true, false, "script_time_begin", "script_time_end", 10000);
|
||||||
|
statshandler->addUserStatsLine("Mech", osg::Vec4f(1.f, 1.f, 1.f, 1.f), osg::Vec4f(1.f, 1.f, 1.f, 1.f),
|
||||||
|
"mechanics_time_taken", 1000.0, true, false, "mechanics_time_begin", "mechanics_time_end", 10000);
|
||||||
|
statshandler->addUserStatsLine("Phys", osg::Vec4f(1.f, 1.f, 1.f, 1.f), osg::Vec4f(1.f, 1.f, 1.f, 1.f),
|
||||||
|
"physics_time_taken", 1000.0, true, false, "physics_time_begin", "physics_time_end", 10000);
|
||||||
|
statshandler->addUserStatsLine("World", osg::Vec4f(1.f, 1.f, 1.f, 1.f), osg::Vec4f(1.f, 1.f, 1.f, 1.f),
|
||||||
|
"world_time_taken", 1000.0, true, false, "world_time_begin", "world_time_end", 10000);
|
||||||
|
|
||||||
|
mViewer->addEventHandler(statshandler);
|
||||||
|
|
||||||
|
osg::ref_ptr<Resource::StatsHandler> resourceshandler = new Resource::StatsHandler;
|
||||||
|
mViewer->addEventHandler(resourceshandler);
|
||||||
|
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
auto* root = mViewer->getSceneData();
|
||||||
|
auto* xrViewer = MWVR::Environment::get().getViewer();
|
||||||
|
xrViewer->addChild(root);
|
||||||
|
mViewer->setSceneData(xrViewer);
|
||||||
|
mXrEnvironment.setGUIManager(new MWVR::VRGUIManager(mViewer));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Start the game
|
||||||
|
if (!mSaveGameFile.empty())
|
||||||
|
{
|
||||||
|
mEnvironment.getStateManager()->loadGame(mSaveGameFile);
|
||||||
|
}
|
||||||
|
else if (!mSkipMenu)
|
||||||
|
{
|
||||||
|
// start in main menu
|
||||||
|
mEnvironment.getWindowManager()->pushGuiMode (MWGui::GM_MainMenu);
|
||||||
|
mEnvironment.getSoundManager()->playTitleMusic();
|
||||||
|
const std::string& logo = Fallback::Map::getString("Movies_Morrowind_Logo");
|
||||||
|
if (!logo.empty())
|
||||||
|
mEnvironment.getWindowManager()->playVideo(logo, true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mEnvironment.getStateManager()->newGame (!mNewGame);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mStartupScript.empty() && mEnvironment.getStateManager()->getState() == MWState::StateManager::State_Running)
|
||||||
|
{
|
||||||
|
mEnvironment.getWindowManager()->executeInConsole(mStartupScript);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Start the main rendering loop
|
||||||
|
osg::Timer frameTimer;
|
||||||
|
double simulationTime = 0.0;
|
||||||
|
while (!mViewer->done() && !mEnvironment.getStateManager()->hasQuitRequest())
|
||||||
|
{
|
||||||
|
double dt = frameTimer.time_s();
|
||||||
|
frameTimer.setStartTick();
|
||||||
|
dt = std::min(dt, 0.2);
|
||||||
|
|
||||||
|
mViewer->advance(simulationTime);
|
||||||
|
|
||||||
|
if (!frame(dt))
|
||||||
|
{
|
||||||
|
OpenThreads::Thread::microSleep(5000);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
mViewer->eventTraversal();
|
||||||
|
|
||||||
|
mEnvironment.getWorld()->updateWindowManager();
|
||||||
|
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
xrViewer->traversals();
|
||||||
|
#else
|
||||||
|
mViewer->updateTraversal();
|
||||||
|
|
||||||
|
mViewer->renderingTraversals();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool guiActive = mEnvironment.getWindowManager()->isGuiMode();
|
||||||
|
if (!guiActive)
|
||||||
|
simulationTime += dt;
|
||||||
|
}
|
||||||
|
|
||||||
|
mEnvironment.limitFrameRate(frameTimer.time_s());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save user settings
|
||||||
|
settings.saveUser(settingspath);
|
||||||
|
|
||||||
|
Log(Debug::Info) << "Quitting peacefully.";
|
||||||
|
}
|
||||||
|
|
||||||
|
void OMW::Engine::setCompileAll (bool all)
|
||||||
|
{
|
||||||
|
mCompileAll = all;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OMW::Engine::setCompileAllDialogue (bool all)
|
||||||
|
{
|
||||||
|
mCompileAllDialogue = all;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OMW::Engine::setSoundUsage(bool soundUsage)
|
||||||
|
{
|
||||||
|
mUseSound = soundUsage;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OMW::Engine::setEncoding(const ToUTF8::FromType& encoding)
|
||||||
|
{
|
||||||
|
mEncoding = encoding;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OMW::Engine::setScriptConsoleMode (bool enabled)
|
||||||
|
{
|
||||||
|
mScriptConsoleMode = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OMW::Engine::setStartupScript (const std::string& path)
|
||||||
|
{
|
||||||
|
mStartupScript = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OMW::Engine::setActivationDistanceOverride (int distance)
|
||||||
|
{
|
||||||
|
mActivationDistanceOverride = distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OMW::Engine::setWarningsMode (int mode)
|
||||||
|
{
|
||||||
|
mWarningsMode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OMW::Engine::setScriptBlacklist (const std::vector<std::string>& list)
|
||||||
|
{
|
||||||
|
mScriptBlacklist = list;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OMW::Engine::setScriptBlacklistUse (bool use)
|
||||||
|
{
|
||||||
|
mScriptBlacklistUse = use;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OMW::Engine::enableFontExport(bool exportFonts)
|
||||||
|
{
|
||||||
|
mExportFonts = exportFonts;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OMW::Engine::setSaveGameFile(const std::string &savegame)
|
||||||
|
{
|
||||||
|
mSaveGameFile = savegame;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OMW::Engine::setRandomSeed(unsigned int seed)
|
||||||
|
{
|
||||||
|
mRandomSeed = seed;
|
||||||
|
}
|
|
@ -13,6 +13,10 @@
|
||||||
|
|
||||||
#include "mwworld/ptr.hpp"
|
#include "mwworld/ptr.hpp"
|
||||||
|
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
#include "mwvr/vrenvironment.hpp"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace Resource
|
namespace Resource
|
||||||
{
|
{
|
||||||
class ResourceSystem;
|
class ResourceSystem;
|
||||||
|
@ -33,6 +37,12 @@ namespace Compiler
|
||||||
class Context;
|
class Context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace Misc
|
||||||
|
{
|
||||||
|
class StereoView;
|
||||||
|
class CallbackManager;
|
||||||
|
}
|
||||||
|
|
||||||
namespace Files
|
namespace Files
|
||||||
{
|
{
|
||||||
struct ConfigurationManager;
|
struct ConfigurationManager;
|
||||||
|
@ -66,6 +76,13 @@ namespace OMW
|
||||||
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;
|
||||||
|
|
||||||
|
bool mStereoEnabled;
|
||||||
|
bool mStereoOverride;
|
||||||
|
std::unique_ptr<Misc::StereoView> mStereoView;
|
||||||
|
|
||||||
|
std::unique_ptr<Misc::CallbackManager> mCallbackManager;
|
||||||
|
|
||||||
bool mSkipMenu;
|
bool mSkipMenu;
|
||||||
bool mUseSound;
|
bool mUseSound;
|
||||||
bool mCompileAll;
|
bool mCompileAll;
|
||||||
|
@ -185,6 +202,12 @@ namespace OMW
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Files::ConfigurationManager& mCfgMgr;
|
Files::ConfigurationManager& mCfgMgr;
|
||||||
|
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
MWVR::Environment mXrEnvironment;
|
||||||
|
|
||||||
|
void initVr();
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ MWBase::Environment *MWBase::Environment::sThis = nullptr;
|
||||||
MWBase::Environment::Environment()
|
MWBase::Environment::Environment()
|
||||||
: mWorld (nullptr), mSoundManager (nullptr), mScriptManager (nullptr), mWindowManager (nullptr),
|
: mWorld (nullptr), mSoundManager (nullptr), mScriptManager (nullptr), mWindowManager (nullptr),
|
||||||
mMechanicsManager (nullptr), mDialogueManager (nullptr), mJournal (nullptr), mInputManager (nullptr),
|
mMechanicsManager (nullptr), mDialogueManager (nullptr), mJournal (nullptr), mInputManager (nullptr),
|
||||||
mStateManager (nullptr), mResourceSystem (nullptr), mFrameDuration (0), mFrameRateLimit(0.f)
|
mStateManager (nullptr), mResourceSystem (nullptr), mFrameDuration (0), mFrameRateLimit(0.f), mVrMode(false)
|
||||||
{
|
{
|
||||||
assert (!sThis);
|
assert (!sThis);
|
||||||
sThis = this;
|
sThis = this;
|
||||||
|
@ -96,6 +96,16 @@ float MWBase::Environment::getFrameRateLimit() const
|
||||||
return mFrameRateLimit;
|
return mFrameRateLimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MWBase::Environment::setVrMode(bool vrMode)
|
||||||
|
{
|
||||||
|
mVrMode = vrMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MWBase::Environment::getVrMode(void) const
|
||||||
|
{
|
||||||
|
return mVrMode;
|
||||||
|
}
|
||||||
|
|
||||||
MWBase::World *MWBase::Environment::getWorld() const
|
MWBase::World *MWBase::Environment::getWorld() const
|
||||||
{
|
{
|
||||||
assert (mWorld);
|
assert (mWorld);
|
||||||
|
|
|
@ -45,6 +45,7 @@ namespace MWBase
|
||||||
Resource::ResourceSystem *mResourceSystem;
|
Resource::ResourceSystem *mResourceSystem;
|
||||||
float mFrameDuration;
|
float mFrameDuration;
|
||||||
float mFrameRateLimit;
|
float mFrameRateLimit;
|
||||||
|
bool mVrMode;
|
||||||
|
|
||||||
Environment (const Environment&);
|
Environment (const Environment&);
|
||||||
///< not implemented
|
///< not implemented
|
||||||
|
@ -84,6 +85,9 @@ namespace MWBase
|
||||||
void setFrameRateLimit(float frameRateLimit);
|
void setFrameRateLimit(float frameRateLimit);
|
||||||
float getFrameRateLimit() const;
|
float getFrameRateLimit() const;
|
||||||
|
|
||||||
|
void setVrMode(bool vrMode);
|
||||||
|
bool getVrMode(void) const;
|
||||||
|
|
||||||
World *getWorld() const;
|
World *getWorld() const;
|
||||||
|
|
||||||
SoundManager *getSoundManager() const;
|
SoundManager *getSoundManager() const;
|
||||||
|
|
|
@ -63,6 +63,8 @@ namespace MWBase
|
||||||
virtual void enableDetectingBindingMode (int action, bool keyboard) = 0;
|
virtual void enableDetectingBindingMode (int action, bool keyboard) = 0;
|
||||||
virtual void resetToDefaultKeyBindings() = 0;
|
virtual void resetToDefaultKeyBindings() = 0;
|
||||||
virtual void resetToDefaultControllerBindings() = 0;
|
virtual void resetToDefaultControllerBindings() = 0;
|
||||||
|
virtual void applyHapticsLeftHand(float intensity) = 0;
|
||||||
|
virtual void applyHapticsRightHand(float intensity) = 0;
|
||||||
|
|
||||||
/// Returns if the last used input device was a joystick or a keyboard
|
/// Returns if the last used input device was a joystick or a keyboard
|
||||||
/// @return true if joystick, false otherwise
|
/// @return true if joystick, false otherwise
|
||||||
|
|
|
@ -54,7 +54,7 @@ namespace MWWorld
|
||||||
namespace MWGui
|
namespace MWGui
|
||||||
{
|
{
|
||||||
class Layout;
|
class Layout;
|
||||||
|
class DragAndDrop;
|
||||||
class Console;
|
class Console;
|
||||||
class SpellWindow;
|
class SpellWindow;
|
||||||
class TradeWindow;
|
class TradeWindow;
|
||||||
|
@ -106,6 +106,7 @@ namespace MWBase
|
||||||
/// @note This method will block until the video finishes playing
|
/// @note This method will block until the video finishes playing
|
||||||
/// (and will continually update the window while doing so)
|
/// (and will continually update the window while doing so)
|
||||||
virtual void playVideo(const std::string& name, bool allowSkipping) = 0;
|
virtual void playVideo(const std::string& name, bool allowSkipping) = 0;
|
||||||
|
virtual bool isPlayingVideo(void) const = 0;
|
||||||
|
|
||||||
virtual void setNewGame(bool newgame) = 0;
|
virtual void setNewGame(bool newgame) = 0;
|
||||||
|
|
||||||
|
@ -289,6 +290,7 @@ namespace MWBase
|
||||||
virtual void showCrosshair(bool show) = 0;
|
virtual void showCrosshair(bool show) = 0;
|
||||||
virtual bool getSubtitlesEnabled() = 0;
|
virtual bool getSubtitlesEnabled() = 0;
|
||||||
virtual bool toggleHud() = 0;
|
virtual bool toggleHud() = 0;
|
||||||
|
virtual MWGui::DragAndDrop& getDragAndDrop(void) = 0;
|
||||||
|
|
||||||
virtual void disallowMouse() = 0;
|
virtual void disallowMouse() = 0;
|
||||||
virtual void allowMouse() = 0;
|
virtual void allowMouse() = 0;
|
||||||
|
@ -444,6 +446,8 @@ namespace MWBase
|
||||||
|
|
||||||
virtual void watchActor(const MWWorld::Ptr& ptr) = 0;
|
virtual void watchActor(const MWWorld::Ptr& ptr) = 0;
|
||||||
virtual MWWorld::Ptr getWatchedActor() const = 0;
|
virtual MWWorld::Ptr getWatchedActor() const = 0;
|
||||||
|
|
||||||
|
virtual void viewerTraversals(bool updateWindowManager) = 0;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
#include <components/esm/cellid.hpp>
|
#include <components/esm/cellid.hpp>
|
||||||
|
|
||||||
|
@ -33,7 +34,9 @@ namespace osg
|
||||||
class Matrixf;
|
class Matrixf;
|
||||||
class Quat;
|
class Quat;
|
||||||
class Image;
|
class Image;
|
||||||
|
class Node;
|
||||||
class Stats;
|
class Stats;
|
||||||
|
class Transform;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Loading
|
namespace Loading
|
||||||
|
@ -72,6 +75,8 @@ namespace MWPhysics
|
||||||
namespace MWRender
|
namespace MWRender
|
||||||
{
|
{
|
||||||
class Animation;
|
class Animation;
|
||||||
|
class RenderingManager;
|
||||||
|
struct RayResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace MWMechanics
|
namespace MWMechanics
|
||||||
|
@ -96,6 +101,11 @@ namespace MWWorld
|
||||||
typedef std::vector<std::pair<MWWorld::Ptr,MWMechanics::Movement> > PtrMovementList;
|
typedef std::vector<std::pair<MWWorld::Ptr,MWMechanics::Movement> > PtrMovementList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace MWVR
|
||||||
|
{
|
||||||
|
class UserPointer;
|
||||||
|
}
|
||||||
|
|
||||||
namespace MWBase
|
namespace MWBase
|
||||||
{
|
{
|
||||||
/// \brief Interface for the World (implemented in MWWorld)
|
/// \brief Interface for the World (implemented in MWWorld)
|
||||||
|
@ -156,6 +166,8 @@ namespace MWBase
|
||||||
virtual MWWorld::Ptr getPlayerPtr() = 0;
|
virtual MWWorld::Ptr getPlayerPtr() = 0;
|
||||||
virtual MWWorld::ConstPtr getPlayerConstPtr() const = 0;
|
virtual MWWorld::ConstPtr getPlayerConstPtr() const = 0;
|
||||||
|
|
||||||
|
virtual MWRender::RenderingManager& getRenderingManager() = 0;
|
||||||
|
|
||||||
virtual const MWWorld::ESMStore& getStore() const = 0;
|
virtual const MWWorld::ESMStore& getStore() const = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -385,6 +397,8 @@ namespace MWBase
|
||||||
|
|
||||||
virtual float getMaxActivationDistance() = 0;
|
virtual float getMaxActivationDistance() = 0;
|
||||||
|
|
||||||
|
virtual float getActivationDistancePlusTelekinesis() = 0;
|
||||||
|
|
||||||
/// Returns a pointer to the object the provided object would hit (if within the
|
/// Returns a pointer to the object the provided object would hit (if within the
|
||||||
/// specified distance), and the point where the hit occurs. This will attempt to
|
/// specified distance), and the point where the hit occurs. This will attempt to
|
||||||
/// use the "Head" node, or alternatively the "Bip01 Head" node as a basis.
|
/// use the "Head" node, or alternatively the "Bip01 Head" node as a basis.
|
||||||
|
@ -563,6 +577,12 @@ namespace MWBase
|
||||||
/// @param cursor Y (relative 0-1)
|
/// @param cursor Y (relative 0-1)
|
||||||
/// @param number of objects to place
|
/// @param number of objects to place
|
||||||
|
|
||||||
|
virtual MWWorld::Ptr placeObject (const MWWorld::ConstPtr& object, const MWRender::RayResult& ray, int amount) = 0;
|
||||||
|
///< copy and place an object into the gameworld based on the given intersection
|
||||||
|
/// @param object
|
||||||
|
/// @param world position to place object
|
||||||
|
/// @param number of objects to place
|
||||||
|
|
||||||
virtual MWWorld::Ptr dropObjectOnGround (const MWWorld::Ptr& actor, const MWWorld::ConstPtr& object, int amount) = 0;
|
virtual MWWorld::Ptr dropObjectOnGround (const MWWorld::Ptr& actor, const MWWorld::ConstPtr& object, int amount) = 0;
|
||||||
///< copy and place an object into the gameworld at the given actor's position
|
///< copy and place an object into the gameworld at the given actor's position
|
||||||
/// @param actor giving the dropped object position
|
/// @param actor giving the dropped object position
|
||||||
|
@ -857,6 +877,18 @@ namespace MWBase
|
||||||
|
|
||||||
virtual bool hasCollisionWithDoor(const MWWorld::ConstPtr& door, const osg::Vec3f& position, const osg::Vec3f& destination) const = 0;
|
virtual bool hasCollisionWithDoor(const MWWorld::ConstPtr& door, const osg::Vec3f& position, const osg::Vec3f& destination) const = 0;
|
||||||
|
|
||||||
|
/// @result pointer to the object and/or node the given node is currently pointing at
|
||||||
|
/// @Return distance to the target object, or -1 if no object was targeted / in range
|
||||||
|
virtual float getTargetObject(MWRender::RayResult& result, const osg::Vec3f& origin, const osg::Quat& orientation, float maxDistance, bool ignorePlayer) = 0;
|
||||||
|
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
virtual MWVR::UserPointer& getUserPointer() = 0;
|
||||||
|
virtual MWWorld::Ptr getPointerTarget() = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// @Return ESM::Weapon::Type enum describing the type of weapon currently drawn by the player.
|
||||||
|
virtual int getActiveWeaponType(void) = 0;
|
||||||
|
|
||||||
virtual bool isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius, const MWWorld::ConstPtr& ignore) const = 0;
|
virtual bool isAreaOccupiedByOtherActor(const osg::Vec3f& position, const float radius, const MWWorld::ConstPtr& ignore) const = 0;
|
||||||
|
|
||||||
virtual void reportStats(unsigned int frameNumber, osg::Stats& stats) const = 0;
|
virtual void reportStats(unsigned int frameNumber, osg::Stats& stats) const = 0;
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
#include "../mwmechanics/difficultyscaling.hpp"
|
#include "../mwmechanics/difficultyscaling.hpp"
|
||||||
|
|
||||||
#include "../mwbase/environment.hpp"
|
#include "../mwbase/environment.hpp"
|
||||||
|
#include "../mwbase/inputmanager.hpp"
|
||||||
#include "../mwbase/mechanicsmanager.hpp"
|
#include "../mwbase/mechanicsmanager.hpp"
|
||||||
#include "../mwbase/windowmanager.hpp"
|
#include "../mwbase/windowmanager.hpp"
|
||||||
#include "../mwbase/world.hpp"
|
#include "../mwbase/world.hpp"
|
||||||
|
@ -237,7 +238,7 @@ namespace MWClass
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Creature::hit(const MWWorld::Ptr& ptr, float attackStrength, int type) const
|
bool Creature::hit(const MWWorld::Ptr& ptr, float attackStrength, int type, bool) const
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
Start of tes3mp addition
|
Start of tes3mp addition
|
||||||
|
@ -246,7 +247,7 @@ namespace MWClass
|
||||||
*/
|
*/
|
||||||
if (mwmp::PlayerList::isDedicatedPlayer(ptr) || mwmp::Main::get().getCellController()->isDedicatedActor(ptr))
|
if (mwmp::PlayerList::isDedicatedPlayer(ptr) || mwmp::Main::get().getCellController()->isDedicatedActor(ptr))
|
||||||
{
|
{
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
End of tes3mp addition
|
End of tes3mp addition
|
||||||
|
@ -258,7 +259,7 @@ namespace MWClass
|
||||||
MWMechanics::CreatureStats &stats = getCreatureStats(ptr);
|
MWMechanics::CreatureStats &stats = getCreatureStats(ptr);
|
||||||
|
|
||||||
if (stats.getDrawState() != MWMechanics::DrawState_Weapon)
|
if (stats.getDrawState() != MWMechanics::DrawState_Weapon)
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
// Get the weapon used (if hand-to-hand, weapon = inv.end())
|
// Get the weapon used (if hand-to-hand, weapon = inv.end())
|
||||||
MWWorld::Ptr weapon;
|
MWWorld::Ptr weapon;
|
||||||
|
@ -282,7 +283,7 @@ namespace MWClass
|
||||||
|
|
||||||
std::pair<MWWorld::Ptr, osg::Vec3f> result = MWBase::Environment::get().getWorld()->getHitContact(ptr, dist, targetActors);
|
std::pair<MWWorld::Ptr, osg::Vec3f> result = MWBase::Environment::get().getWorld()->getHitContact(ptr, dist, targetActors);
|
||||||
if (result.first.isEmpty())
|
if (result.first.isEmpty())
|
||||||
return; // Didn't hit anything
|
return false; // Didn't hit anything
|
||||||
|
|
||||||
MWWorld::Ptr victim = result.first;
|
MWWorld::Ptr victim = result.first;
|
||||||
|
|
||||||
|
@ -299,6 +300,7 @@ namespace MWClass
|
||||||
objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;
|
objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;
|
||||||
objectList->addObjectHit(victim, ptr);
|
objectList->addObjectHit(victim, ptr);
|
||||||
objectList->sendObjectHit();
|
objectList->sendObjectHit();
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
End of tes3mp change (major)
|
End of tes3mp change (major)
|
||||||
|
@ -355,7 +357,7 @@ namespace MWClass
|
||||||
|
|
||||||
victim.getClass().onHit(victim, 0.0f, false, MWWorld::Ptr(), ptr, osg::Vec3f(), false);
|
victim.getClass().onHit(victim, 0.0f, false, MWWorld::Ptr(), ptr, osg::Vec3f(), false);
|
||||||
MWMechanics::reduceWeaponCondition(0.f, false, weapon, ptr);
|
MWMechanics::reduceWeaponCondition(0.f, false, weapon, ptr);
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int min,max;
|
int min,max;
|
||||||
|
@ -423,10 +425,11 @@ namespace MWClass
|
||||||
|
|
||||||
MWMechanics::diseaseContact(victim, ptr);
|
MWMechanics::diseaseContact(victim, ptr);
|
||||||
|
|
||||||
victim.getClass().onHit(victim, damage, healthdmg, weapon, ptr, hitPosition, true);
|
victim.getClass().onHit(victim, damage, healthdmg, weapon, ptr, hitPosition, true, attackStrength);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Creature::onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, const osg::Vec3f &hitPosition, bool successful) const
|
void Creature::onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, const osg::Vec3f &hitPosition, bool successful, float hitStrength) const
|
||||||
{
|
{
|
||||||
MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
|
MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
|
||||||
|
|
||||||
|
@ -441,6 +444,8 @@ namespace MWClass
|
||||||
if (isMobile(ptr) && !attacker.isEmpty())
|
if (isMobile(ptr) && !attacker.isEmpty())
|
||||||
setOnPcHitMe = MWBase::Environment::get().getMechanicsManager()->actorAttacked(ptr, attacker);
|
setOnPcHitMe = MWBase::Environment::get().getMechanicsManager()->actorAttacked(ptr, attacker);
|
||||||
|
|
||||||
|
bool attackerIsPlayer = attacker == MWMechanics::getPlayer();
|
||||||
|
|
||||||
// Attacker and target store each other as hitattemptactor if they have no one stored yet
|
// Attacker and target store each other as hitattemptactor if they have no one stored yet
|
||||||
if (!attacker.isEmpty() && attacker.getClass().isActor())
|
if (!attacker.isEmpty() && attacker.getClass().isActor())
|
||||||
{
|
{
|
||||||
|
@ -480,7 +485,7 @@ namespace MWClass
|
||||||
if (!object.isEmpty())
|
if (!object.isEmpty())
|
||||||
stats.setLastHitAttemptObject(object.getCellRef().getRefId());
|
stats.setLastHitAttemptObject(object.getCellRef().getRefId());
|
||||||
|
|
||||||
if (setOnPcHitMe && !attacker.isEmpty() && attacker == MWMechanics::getPlayer())
|
if (setOnPcHitMe && !attacker.isEmpty() && attackerIsPlayer)
|
||||||
{
|
{
|
||||||
const std::string &script = ptr.get<ESM::Creature>()->mBase->mScript;
|
const std::string &script = ptr.get<ESM::Creature>()->mBase->mScript;
|
||||||
/* Set the OnPCHitMe script variable. The script is responsible for clearing it. */
|
/* Set the OnPCHitMe script variable. The script is responsible for clearing it. */
|
||||||
|
@ -491,7 +496,7 @@ namespace MWClass
|
||||||
if (!successful)
|
if (!successful)
|
||||||
{
|
{
|
||||||
// Missed
|
// Missed
|
||||||
if (!attacker.isEmpty() && attacker == MWMechanics::getPlayer())
|
if (!attacker.isEmpty() && attackerIsPlayer)
|
||||||
MWBase::Environment::get().getSoundManager()->playSound3D(ptr, "miss", 1.0f, 1.0f);
|
MWBase::Environment::get().getSoundManager()->playSound3D(ptr, "miss", 1.0f, 1.0f);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -607,6 +612,16 @@ namespace MWClass
|
||||||
/*
|
/*
|
||||||
End of tes3mp addition
|
End of tes3mp addition
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
if(successful)
|
||||||
|
{
|
||||||
|
auto* inputManager = MWBase::Environment::get().getInputManager();
|
||||||
|
if (attackerIsPlayer && hitStrength > 0.f)
|
||||||
|
{
|
||||||
|
float hapticIntensity = std::max(0.25f, std::min(1.f, hitStrength));
|
||||||
|
inputManager->applyHapticsRightHand(hapticIntensity);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<MWWorld::Action> Creature::activate (const MWWorld::Ptr& ptr,
|
std::shared_ptr<MWWorld::Action> Creature::activate (const MWWorld::Ptr& ptr,
|
||||||
|
|
|
@ -55,9 +55,9 @@ namespace MWClass
|
||||||
MWMechanics::CreatureStats& getCreatureStats (const MWWorld::Ptr& ptr) const override;
|
MWMechanics::CreatureStats& getCreatureStats (const MWWorld::Ptr& ptr) const override;
|
||||||
///< Return creature stats
|
///< Return creature stats
|
||||||
|
|
||||||
void hit(const MWWorld::Ptr& ptr, float attackStrength, int type) const override;
|
bool hit(const MWWorld::Ptr& ptr, float attackStrength, int type, bool simulated) const override;
|
||||||
|
|
||||||
void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, const osg::Vec3f &hitPosition, bool successful) const override;
|
void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, const osg::Vec3f &hitPosition, bool successful, float hitStrength) const override;
|
||||||
|
|
||||||
std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,
|
std::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,
|
||||||
const MWWorld::Ptr& actor) const override;
|
const MWWorld::Ptr& actor) const override;
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "../mwbase/environment.hpp"
|
#include "../mwbase/environment.hpp"
|
||||||
|
#include "../mwbase/inputmanager.hpp"
|
||||||
#include "../mwbase/world.hpp"
|
#include "../mwbase/world.hpp"
|
||||||
#include "../mwbase/mechanicsmanager.hpp"
|
#include "../mwbase/mechanicsmanager.hpp"
|
||||||
#include "../mwbase/windowmanager.hpp"
|
#include "../mwbase/windowmanager.hpp"
|
||||||
|
@ -548,7 +549,7 @@ namespace MWClass
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Npc::hit(const MWWorld::Ptr& ptr, float attackStrength, int type) const
|
bool Npc::hit(const MWWorld::Ptr& ptr, float attackStrength, int type, bool simulated) const
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
Start of tes3mp addition
|
Start of tes3mp addition
|
||||||
|
@ -557,7 +558,7 @@ namespace MWClass
|
||||||
*/
|
*/
|
||||||
if (mwmp::PlayerList::isDedicatedPlayer(ptr) || mwmp::Main::get().getCellController()->isDedicatedActor(ptr))
|
if (mwmp::PlayerList::isDedicatedPlayer(ptr) || mwmp::Main::get().getCellController()->isDedicatedActor(ptr))
|
||||||
{
|
{
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
End of tes3mp addition
|
End of tes3mp addition
|
||||||
|
@ -574,6 +575,10 @@ namespace MWClass
|
||||||
if(!weapon.isEmpty() && weapon.getTypeName() != typeid(ESM::Weapon).name())
|
if(!weapon.isEmpty() && weapon.getTypeName() != typeid(ESM::Weapon).name())
|
||||||
weapon = MWWorld::Ptr();
|
weapon = MWWorld::Ptr();
|
||||||
|
|
||||||
|
if (getCreatureStats(ptr).getDrawState() != MWMechanics::DrawState_Weapon)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!simulated)
|
||||||
MWMechanics::applyFatigueLoss(ptr, weapon, attackStrength);
|
MWMechanics::applyFatigueLoss(ptr, weapon, attackStrength);
|
||||||
|
|
||||||
const float fCombatDistance = store.find("fCombatDistance")->mValue.getFloat();
|
const float fCombatDistance = store.find("fCombatDistance")->mValue.getFloat();
|
||||||
|
@ -591,9 +596,10 @@ namespace MWClass
|
||||||
MWWorld::Ptr victim = result.first;
|
MWWorld::Ptr victim = result.first;
|
||||||
osg::Vec3f hitPosition (result.second);
|
osg::Vec3f hitPosition (result.second);
|
||||||
if(victim.isEmpty()) // Didn't hit anything
|
if(victim.isEmpty()) // Didn't hit anything
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
const MWWorld::Class &othercls = victim.getClass();
|
const MWWorld::Class &othercls = victim.getClass();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Start of tes3mp change (major)
|
Start of tes3mp change (major)
|
||||||
|
|
||||||
|
@ -607,13 +613,18 @@ namespace MWClass
|
||||||
objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;
|
objectList->packetOrigin = mwmp::CLIENT_GAMEPLAY;
|
||||||
objectList->addObjectHit(victim, ptr);
|
objectList->addObjectHit(victim, ptr);
|
||||||
objectList->sendObjectHit();
|
objectList->sendObjectHit();
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
End of tes3mp change (major)
|
End of tes3mp change (major)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
MWMechanics::CreatureStats &otherstats = othercls.getCreatureStats(victim);
|
MWMechanics::CreatureStats &otherstats = othercls.getCreatureStats(victim);
|
||||||
if(otherstats.isDead()) // Can't hit dead actors
|
if(otherstats.isDead()) // Can't hit dead actors
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
|
if (simulated)
|
||||||
|
return true;
|
||||||
|
|
||||||
if(ptr == MWMechanics::getPlayer())
|
if(ptr == MWMechanics::getPlayer())
|
||||||
MWBase::Environment::get().getWindowManager()->setEnemy(victim);
|
MWBase::Environment::get().getWindowManager()->setEnemy(victim);
|
||||||
|
@ -671,7 +682,7 @@ namespace MWClass
|
||||||
|
|
||||||
othercls.onHit(victim, 0.0f, false, weapon, ptr, osg::Vec3f(), false);
|
othercls.onHit(victim, 0.0f, false, weapon, ptr, osg::Vec3f(), false);
|
||||||
MWMechanics::reduceWeaponCondition(0.f, false, weapon, ptr);
|
MWMechanics::reduceWeaponCondition(0.f, false, weapon, ptr);
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool healthdmg;
|
bool healthdmg;
|
||||||
|
@ -744,14 +755,16 @@ namespace MWClass
|
||||||
|
|
||||||
MWMechanics::diseaseContact(victim, ptr);
|
MWMechanics::diseaseContact(victim, ptr);
|
||||||
|
|
||||||
othercls.onHit(victim, damage, healthdmg, weapon, ptr, hitPosition, true);
|
othercls.onHit(victim, damage, healthdmg, weapon, ptr, hitPosition, true, attackStrength);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Npc::onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, const osg::Vec3f &hitPosition, bool successful) const
|
void Npc::onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, const osg::Vec3f &hitPosition, bool successful, float hitStrength) const
|
||||||
{
|
{
|
||||||
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
|
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
|
||||||
MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
|
MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
|
||||||
bool wasDead = stats.isDead();
|
bool wasDead = stats.isDead();
|
||||||
|
float rawDamage = damage;
|
||||||
|
|
||||||
// Note OnPcHitMe is not set for friendly hits.
|
// Note OnPcHitMe is not set for friendly hits.
|
||||||
bool setOnPcHitMe = true;
|
bool setOnPcHitMe = true;
|
||||||
|
@ -762,6 +775,8 @@ namespace MWClass
|
||||||
stats.setAttacked(true);
|
stats.setAttacked(true);
|
||||||
setOnPcHitMe = MWBase::Environment::get().getMechanicsManager()->actorAttacked(ptr, attacker);
|
setOnPcHitMe = MWBase::Environment::get().getMechanicsManager()->actorAttacked(ptr, attacker);
|
||||||
}
|
}
|
||||||
|
bool attackerIsPlayer = attacker == MWMechanics::getPlayer();
|
||||||
|
bool victimIsPlayer = ptr == MWMechanics::getPlayer();
|
||||||
|
|
||||||
// Attacker and target store each other as hitattemptactor if they have no one stored yet
|
// Attacker and target store each other as hitattemptactor if they have no one stored yet
|
||||||
if (!attacker.isEmpty() && attacker.getClass().isActor())
|
if (!attacker.isEmpty() && attacker.getClass().isActor())
|
||||||
|
@ -802,7 +817,7 @@ namespace MWClass
|
||||||
if (!object.isEmpty())
|
if (!object.isEmpty())
|
||||||
stats.setLastHitAttemptObject(object.getCellRef().getRefId());
|
stats.setLastHitAttemptObject(object.getCellRef().getRefId());
|
||||||
|
|
||||||
if (setOnPcHitMe && !attacker.isEmpty() && attacker == MWMechanics::getPlayer())
|
if (setOnPcHitMe && !attacker.isEmpty() && attackerIsPlayer)
|
||||||
{
|
{
|
||||||
const std::string &script = getScript(ptr);
|
const std::string &script = getScript(ptr);
|
||||||
/* Set the OnPCHitMe script variable. The script is responsible for clearing it. */
|
/* Set the OnPCHitMe script variable. The script is responsible for clearing it. */
|
||||||
|
@ -813,7 +828,7 @@ namespace MWClass
|
||||||
if (!successful)
|
if (!successful)
|
||||||
{
|
{
|
||||||
// Missed
|
// Missed
|
||||||
if (!attacker.isEmpty() && attacker == MWMechanics::getPlayer())
|
if (!attacker.isEmpty() && attackerIsPlayer)
|
||||||
sndMgr->playSound3D(ptr, "miss", 1.0f, 1.0f);
|
sndMgr->playSound3D(ptr, "miss", 1.0f, 1.0f);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -828,7 +843,7 @@ namespace MWClass
|
||||||
if (damage < 0.001f)
|
if (damage < 0.001f)
|
||||||
damage = 0;
|
damage = 0;
|
||||||
|
|
||||||
bool godmode = ptr == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState();
|
bool godmode = victimIsPlayer && MWBase::Environment::get().getWorld()->getGodModeState();
|
||||||
|
|
||||||
if (godmode)
|
if (godmode)
|
||||||
damage = 0;
|
damage = 0;
|
||||||
|
@ -1046,6 +1061,23 @@ namespace MWClass
|
||||||
/*
|
/*
|
||||||
End of tes3mp addition
|
End of tes3mp addition
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// Apply haptics
|
||||||
|
if (successful)
|
||||||
|
{
|
||||||
|
auto* inputManager = MWBase::Environment::get().getInputManager();
|
||||||
|
if (victimIsPlayer)
|
||||||
|
{
|
||||||
|
float maxHealth = getCreatureStats(ptr).getHealth().getModified();
|
||||||
|
float hapticIntensity = std::max(0.25f, std::min(1.f, rawDamage / ( maxHealth / 4.f)));
|
||||||
|
inputManager->applyHapticsLeftHand(hapticIntensity);
|
||||||
|
}
|
||||||
|
else if (attackerIsPlayer && hitStrength > 0.f)
|
||||||
|
{
|
||||||
|
float hapticIntensity = std::max(0.25f, std::min(1.f, hitStrength));
|
||||||
|
inputManager->applyHapticsRightHand(hapticIntensity);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<MWWorld::Action> Npc::activate (const MWWorld::Ptr& ptr,
|
std::shared_ptr<MWWorld::Action> Npc::activate (const MWWorld::Ptr& ptr,
|
||||||
|
|
|
@ -80,9 +80,9 @@ namespace MWClass
|
||||||
End of tes3mp addition
|
End of tes3mp addition
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void hit(const MWWorld::Ptr& ptr, float attackStrength, int type) const override;
|
bool hit(const MWWorld::Ptr& ptr, float attackStrength, int type, bool simulated) const override;
|
||||||
|
|
||||||
void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, const osg::Vec3f &hitPosition, bool successful) const override;
|
void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, const osg::Vec3f &hitPosition, bool successful, float hitStrength) const override;
|
||||||
|
|
||||||
void getModelsToPreload(const MWWorld::Ptr& ptr, std::vector<std::string>& models) const override;
|
void getModelsToPreload(const MWWorld::Ptr& ptr, std::vector<std::string>& models) const override;
|
||||||
///< Get a list of models to preload that this object may use (directly or indirectly). default implementation: list getModel().
|
///< Get a list of models to preload that this object may use (directly or indirectly). default implementation: list getModel().
|
||||||
|
|
|
@ -39,6 +39,10 @@
|
||||||
#include "itemwidget.hpp"
|
#include "itemwidget.hpp"
|
||||||
#include "widgets.hpp"
|
#include "widgets.hpp"
|
||||||
|
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
#include "../mwvr/vrlistbox.hpp"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace MWGui
|
namespace MWGui
|
||||||
{
|
{
|
||||||
AlchemyWindow::AlchemyWindow()
|
AlchemyWindow::AlchemyWindow()
|
||||||
|
@ -46,6 +50,9 @@ namespace MWGui
|
||||||
, mCurrentFilter(FilterType::ByName)
|
, mCurrentFilter(FilterType::ByName)
|
||||||
, mModel(nullptr)
|
, mModel(nullptr)
|
||||||
, mSortModel(nullptr)
|
, mSortModel(nullptr)
|
||||||
|
, mFilterCombo(nullptr)
|
||||||
|
, mFilterEdit(nullptr)
|
||||||
|
, mFilterButton(nullptr)
|
||||||
, mAlchemy(new MWMechanics::Alchemy())
|
, mAlchemy(new MWMechanics::Alchemy())
|
||||||
, mApparatus (4)
|
, mApparatus (4)
|
||||||
, mIngredients (4)
|
, mIngredients (4)
|
||||||
|
@ -66,8 +73,31 @@ namespace MWGui
|
||||||
getWidget(mDecreaseButton, "DecreaseButton");
|
getWidget(mDecreaseButton, "DecreaseButton");
|
||||||
getWidget(mNameEdit, "NameEdit");
|
getWidget(mNameEdit, "NameEdit");
|
||||||
getWidget(mItemView, "ItemView");
|
getWidget(mItemView, "ItemView");
|
||||||
getWidget(mFilterValue, "FilterValue");
|
getWidget(mFilterCombo, "FilterValue");
|
||||||
getWidget(mFilterType, "FilterType");
|
getWidget(mFilterType, "FilterType");
|
||||||
|
getWidget(mFilterEdit, "FilterEdit");
|
||||||
|
getWidget(mFilterButton, "FilterButton");
|
||||||
|
|
||||||
|
if (MWBase::Environment::get().getVrMode())
|
||||||
|
{
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
mFilterListBox = new MWVR::VrListBox();
|
||||||
|
#endif
|
||||||
|
mFilterCombo->setVisible(false);
|
||||||
|
mFilterCombo->setUserString("Hidden", "true");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mFilterButton->setVisible(false);
|
||||||
|
mFilterButton->setUserString("Hidden", "true");
|
||||||
|
mFilterEdit->setVisible(false);
|
||||||
|
mFilterEdit->setUserString("Hidden", "true");
|
||||||
|
}
|
||||||
|
|
||||||
|
mFilterButton->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onFilterButtonClicked);
|
||||||
|
mFilterEdit->eventEditTextChange += MyGUI::newDelegate(this, &AlchemyWindow::onFilterEdited);
|
||||||
|
mFilterCombo->eventComboChangePosition += MyGUI::newDelegate(this, &AlchemyWindow::onFilterChanged);
|
||||||
|
mFilterCombo->eventEditTextChange += MyGUI::newDelegate(this, &AlchemyWindow::onFilterEdited);
|
||||||
|
|
||||||
mBrewCountEdit->eventValueChanged += MyGUI::newDelegate(this, &AlchemyWindow::onCountValueChanged);
|
mBrewCountEdit->eventValueChanged += MyGUI::newDelegate(this, &AlchemyWindow::onCountValueChanged);
|
||||||
mBrewCountEdit->eventEditSelectAccept += MyGUI::newDelegate(this, &AlchemyWindow::onAccept);
|
mBrewCountEdit->eventEditSelectAccept += MyGUI::newDelegate(this, &AlchemyWindow::onAccept);
|
||||||
|
@ -90,8 +120,7 @@ namespace MWGui
|
||||||
mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onCancelButtonClicked);
|
mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::onCancelButtonClicked);
|
||||||
|
|
||||||
mNameEdit->eventEditSelectAccept += MyGUI::newDelegate(this, &AlchemyWindow::onAccept);
|
mNameEdit->eventEditSelectAccept += MyGUI::newDelegate(this, &AlchemyWindow::onAccept);
|
||||||
mFilterValue->eventComboChangePosition += MyGUI::newDelegate(this, &AlchemyWindow::onFilterChanged);
|
|
||||||
mFilterValue->eventEditTextChange += MyGUI::newDelegate(this, &AlchemyWindow::onFilterEdited);
|
|
||||||
mFilterType->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::switchFilterType);
|
mFilterType->eventMouseButtonClick += MyGUI::newDelegate(this, &AlchemyWindow::switchFilterType);
|
||||||
|
|
||||||
center();
|
center();
|
||||||
|
@ -210,7 +239,8 @@ namespace MWGui
|
||||||
else
|
else
|
||||||
mCurrentFilter = FilterType::ByEffect;
|
mCurrentFilter = FilterType::ByEffect;
|
||||||
updateFilters();
|
updateFilters();
|
||||||
mFilterValue->clearIndexSelected();
|
mFilterCombo->clearIndexSelected();
|
||||||
|
mFilterEdit->setCaption("");
|
||||||
updateFilters();
|
updateFilters();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -233,39 +263,44 @@ namespace MWGui
|
||||||
}
|
}
|
||||||
mSortModel->setNameFilter({});
|
mSortModel->setNameFilter({});
|
||||||
mSortModel->setEffectFilter({});
|
mSortModel->setEffectFilter({});
|
||||||
mFilterValue->clearIndexSelected();
|
mFilterCombo->clearIndexSelected();
|
||||||
|
mFilterEdit->setCaption("");
|
||||||
updateFilters();
|
updateFilters();
|
||||||
mItemView->update();
|
mItemView->update();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AlchemyWindow::updateFilters()
|
void AlchemyWindow::updateFilters()
|
||||||
{
|
{
|
||||||
std::set<std::string> itemNames, itemEffects;
|
mItemEffects.clear();
|
||||||
|
mItemNames.clear();
|
||||||
for (size_t i = 0; i < mModel->getItemCount(); ++i)
|
for (size_t i = 0; i < mModel->getItemCount(); ++i)
|
||||||
{
|
{
|
||||||
MWWorld::Ptr item = mModel->getItem(i).mBase;
|
MWWorld::Ptr item = mModel->getItem(i).mBase;
|
||||||
if (item.getTypeName() != typeid(ESM::Ingredient).name())
|
if (item.getTypeName() != typeid(ESM::Ingredient).name())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
itemNames.insert(item.getClass().getName(item));
|
mItemNames.insert(item.getClass().getName(item));
|
||||||
|
|
||||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr();
|
MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr();
|
||||||
auto const alchemySkill = player.getClass().getSkill(player, ESM::Skill::Alchemy);
|
auto const alchemySkill = player.getClass().getSkill(player, ESM::Skill::Alchemy);
|
||||||
|
|
||||||
auto const effects = MWMechanics::Alchemy::effectsDescription(item, alchemySkill);
|
auto const effects = MWMechanics::Alchemy::effectsDescription(item, alchemySkill);
|
||||||
itemEffects.insert(effects.begin(), effects.end());
|
mItemEffects.insert(effects.begin(), effects.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
mFilterValue->removeAllItems();
|
mFilterCombo->removeAllItems();
|
||||||
auto const addItems = [&](auto const& container)
|
for (auto const& item : items())
|
||||||
|
{
|
||||||
|
mFilterCombo->addItem(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::set<std::string>& AlchemyWindow::items()
|
||||||
{
|
{
|
||||||
for (auto const& item : container)
|
|
||||||
mFilterValue->addItem(item);
|
|
||||||
};
|
|
||||||
switch (mCurrentFilter)
|
switch (mCurrentFilter)
|
||||||
{
|
{
|
||||||
case FilterType::ByName: addItems(itemNames); break;
|
case FilterType::ByName: return mItemNames; break;
|
||||||
case FilterType::ByEffect: addItems(itemEffects); break;
|
case FilterType::ByEffect: return mItemEffects; break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -292,6 +327,20 @@ namespace MWGui
|
||||||
applyFilter(_sender->getCaption());
|
applyFilter(_sender->getCaption());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AlchemyWindow::onFilterButtonClicked(MyGUI::Widget* _sender)
|
||||||
|
{
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
mFilterListBox->open(mFilterCombo, [this](std::size_t index) {
|
||||||
|
if (index != MyGUI::ITEM_NONE)
|
||||||
|
{
|
||||||
|
auto filter = mFilterCombo->getItemNameAt(index);
|
||||||
|
mFilterEdit->setCaption(filter);
|
||||||
|
applyFilter(filter);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void AlchemyWindow::onOpen()
|
void AlchemyWindow::onOpen()
|
||||||
{
|
{
|
||||||
mAlchemy->clear();
|
mAlchemy->clear();
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
#include <MyGUI_ControllerItem.h>
|
#include <MyGUI_ControllerItem.h>
|
||||||
#include <MyGUI_ComboBox.h>
|
#include <MyGUI_ComboBox.h>
|
||||||
|
#include <MyGUI_ListBox.h>
|
||||||
|
|
||||||
#include <components/widgets/box.hpp>
|
#include <components/widgets/box.hpp>
|
||||||
#include <components/widgets/numericeditbox.hpp>
|
#include <components/widgets/numericeditbox.hpp>
|
||||||
|
@ -17,6 +18,11 @@ namespace MWMechanics
|
||||||
class Alchemy;
|
class Alchemy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace MWVR
|
||||||
|
{
|
||||||
|
class VrListBox;
|
||||||
|
}
|
||||||
|
|
||||||
namespace MWGui
|
namespace MWGui
|
||||||
{
|
{
|
||||||
class ItemView;
|
class ItemView;
|
||||||
|
@ -54,10 +60,16 @@ namespace MWGui
|
||||||
MyGUI::Button* mIncreaseButton;
|
MyGUI::Button* mIncreaseButton;
|
||||||
MyGUI::Button* mDecreaseButton;
|
MyGUI::Button* mDecreaseButton;
|
||||||
Gui::AutoSizedButton* mFilterType;
|
Gui::AutoSizedButton* mFilterType;
|
||||||
MyGUI::ComboBox* mFilterValue;
|
MyGUI::ComboBox* mFilterCombo;
|
||||||
|
MyGUI::EditBox* mFilterEdit;
|
||||||
|
MyGUI::Button* mFilterButton;
|
||||||
|
MWVR::VrListBox* mFilterListBox;
|
||||||
MyGUI::EditBox* mNameEdit;
|
MyGUI::EditBox* mNameEdit;
|
||||||
Gui::NumericEditBox* mBrewCountEdit;
|
Gui::NumericEditBox* mBrewCountEdit;
|
||||||
|
|
||||||
|
std::set<std::string> mItemNames;
|
||||||
|
std::set<std::string> mItemEffects;
|
||||||
|
|
||||||
void onCancelButtonClicked(MyGUI::Widget* _sender);
|
void onCancelButtonClicked(MyGUI::Widget* _sender);
|
||||||
void onCreateButtonClicked(MyGUI::Widget* _sender);
|
void onCreateButtonClicked(MyGUI::Widget* _sender);
|
||||||
void onIngredientSelected(MyGUI::Widget* _sender);
|
void onIngredientSelected(MyGUI::Widget* _sender);
|
||||||
|
@ -72,8 +84,10 @@ namespace MWGui
|
||||||
void initFilter();
|
void initFilter();
|
||||||
void onFilterChanged(MyGUI::ComboBox* _sender, size_t _index);
|
void onFilterChanged(MyGUI::ComboBox* _sender, size_t _index);
|
||||||
void onFilterEdited(MyGUI::EditBox* _sender);
|
void onFilterEdited(MyGUI::EditBox* _sender);
|
||||||
|
void onFilterButtonClicked(MyGUI::Widget* _sender);
|
||||||
void switchFilterType(MyGUI::Widget* _sender);
|
void switchFilterType(MyGUI::Widget* _sender);
|
||||||
void updateFilters();
|
void updateFilters();
|
||||||
|
const std::set<std::string>& items();
|
||||||
|
|
||||||
void addRepeatController(MyGUI::Widget* widget);
|
void addRepeatController(MyGUI::Widget* widget);
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,11 @@ namespace MWGui
|
||||||
{
|
{
|
||||||
|
|
||||||
ContainerWindow::ContainerWindow(DragAndDrop* dragAndDrop)
|
ContainerWindow::ContainerWindow(DragAndDrop* dragAndDrop)
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
: WindowBase("openmw_container_window_vr.layout")
|
||||||
|
#else
|
||||||
: WindowBase("openmw_container_window.layout")
|
: WindowBase("openmw_container_window.layout")
|
||||||
|
#endif
|
||||||
, mDragAndDrop(dragAndDrop)
|
, mDragAndDrop(dragAndDrop)
|
||||||
, mSortModel(nullptr)
|
, mSortModel(nullptr)
|
||||||
, mModel(nullptr)
|
, mModel(nullptr)
|
||||||
|
|
|
@ -279,7 +279,11 @@ namespace MWGui
|
||||||
// --------------------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
DialogueWindow::DialogueWindow()
|
DialogueWindow::DialogueWindow()
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
: WindowBase("openmw_dialogue_window_vr.layout")
|
||||||
|
#else
|
||||||
: WindowBase("openmw_dialogue_window.layout")
|
: WindowBase("openmw_dialogue_window.layout")
|
||||||
|
#endif
|
||||||
, mIsCompanion(false)
|
, mIsCompanion(false)
|
||||||
, mGoodbye(false)
|
, mGoodbye(false)
|
||||||
, mPersuasionDialog(new ResponseCallback(this))
|
, mPersuasionDialog(new ResponseCallback(this))
|
||||||
|
|
|
@ -106,7 +106,11 @@ namespace MWGui
|
||||||
|
|
||||||
|
|
||||||
HUD::HUD(CustomMarkerCollection &customMarkers, DragAndDrop* dragAndDrop, MWRender::LocalMap* localMapRender)
|
HUD::HUD(CustomMarkerCollection &customMarkers, DragAndDrop* dragAndDrop, MWRender::LocalMap* localMapRender)
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
: WindowBase("openmw_hud_vr.layout")
|
||||||
|
#else
|
||||||
: WindowBase("openmw_hud.layout")
|
: WindowBase("openmw_hud.layout")
|
||||||
|
#endif
|
||||||
, LocalMapBase(customMarkers, localMapRender, Settings::Manager::getBool("local map hud fog of war", "Map"))
|
, LocalMapBase(customMarkers, localMapRender, Settings::Manager::getBool("local map hud fog of war", "Map"))
|
||||||
, mHealth(nullptr)
|
, mHealth(nullptr)
|
||||||
, mMagicka(nullptr)
|
, mMagicka(nullptr)
|
||||||
|
@ -139,7 +143,13 @@ namespace MWGui
|
||||||
, mIsDrowning(false)
|
, mIsDrowning(false)
|
||||||
, mDrowningFlashTheta(0.f)
|
, mDrowningFlashTheta(0.f)
|
||||||
{
|
{
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
mMainWidgetBaseSize = mMainWidget->getSize();
|
||||||
|
mMainWidget->setSize(mMainWidgetBaseSize);
|
||||||
|
#else
|
||||||
mMainWidget->setSize(MyGUI::RenderManager::getInstance().getViewSize());
|
mMainWidget->setSize(MyGUI::RenderManager::getInstance().getViewSize());
|
||||||
|
mMainWidgetBaseSize = mMainWidget->getSize();
|
||||||
|
#endif
|
||||||
|
|
||||||
// Energy bars
|
// Energy bars
|
||||||
getWidget(mHealthFrame, "HealthFrame");
|
getWidget(mHealthFrame, "HealthFrame");
|
||||||
|
@ -327,6 +337,7 @@ namespace MWGui
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// XR-TODO: Implement equivalent
|
||||||
void HUD::onWorldMouseOver(MyGUI::Widget* _sender, int x, int y)
|
void HUD::onWorldMouseOver(MyGUI::Widget* _sender, int x, int y)
|
||||||
{
|
{
|
||||||
if (mDragAndDrop->mIsOnDragAndDrop)
|
if (mDragAndDrop->mIsOnDragAndDrop)
|
||||||
|
@ -641,8 +652,8 @@ namespace MWGui
|
||||||
mSpellBox->setPosition(mSpellBoxBaseLeft - spellDx, mSpellBox->getTop());
|
mSpellBox->setPosition(mSpellBoxBaseLeft - spellDx, mSpellBox->getTop());
|
||||||
mSneakBox->setPosition(mSneakBoxBaseLeft - sneakDx, mSneakBox->getTop());
|
mSneakBox->setPosition(mSneakBoxBaseLeft - sneakDx, mSneakBox->getTop());
|
||||||
|
|
||||||
|
#ifndef USE_OPENXR
|
||||||
const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize();
|
const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize();
|
||||||
|
|
||||||
// effect box can have variable width -> variable left coordinate
|
// effect box can have variable width -> variable left coordinate
|
||||||
int effectsDx = 0;
|
int effectsDx = 0;
|
||||||
if (!mMinimapBox->getVisible ())
|
if (!mMinimapBox->getVisible ())
|
||||||
|
@ -653,6 +664,11 @@ namespace MWGui
|
||||||
mCellNameBox->setVisible(false);
|
mCellNameBox->setVisible(false);
|
||||||
|
|
||||||
mEffectBox->setPosition((viewSize.width - mEffectBoxBaseRight) - mEffectBox->getWidth() + effectsDx, mEffectBox->getTop());
|
mEffectBox->setPosition((viewSize.width - mEffectBoxBaseRight) - mEffectBox->getWidth() + effectsDx, mEffectBox->getTop());
|
||||||
|
#else
|
||||||
|
// in VR mode, the effect box grows to the right and does not need repositioning
|
||||||
|
int width = std::max(mMainWidgetBaseSize.width, mEffectBox->getSize().width);
|
||||||
|
mMainWidget->setSize(width, mMainWidget->getHeight());
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void HUD::updateEnemyHealthBar()
|
void HUD::updateEnemyHealthBar()
|
||||||
|
|
|
@ -79,6 +79,8 @@ namespace MWGui
|
||||||
int mHealthManaStaminaBaseLeft, mWeapBoxBaseLeft, mSpellBoxBaseLeft, mSneakBoxBaseLeft;
|
int mHealthManaStaminaBaseLeft, mWeapBoxBaseLeft, mSpellBoxBaseLeft, mSneakBoxBaseLeft;
|
||||||
// bottom right elements
|
// bottom right elements
|
||||||
int mMinimapBoxBaseRight, mEffectBoxBaseRight;
|
int mMinimapBoxBaseRight, mEffectBoxBaseRight;
|
||||||
|
// initial size
|
||||||
|
MyGUI::IntSize mMainWidgetBaseSize;
|
||||||
|
|
||||||
DragAndDrop* mDragAndDrop;
|
DragAndDrop* mDragAndDrop;
|
||||||
|
|
||||||
|
|
|
@ -71,7 +71,11 @@ namespace MWGui
|
||||||
{
|
{
|
||||||
|
|
||||||
InventoryWindow::InventoryWindow(DragAndDrop* dragAndDrop, osg::Group* parent, Resource::ResourceSystem* resourceSystem)
|
InventoryWindow::InventoryWindow(DragAndDrop* dragAndDrop, osg::Group* parent, Resource::ResourceSystem* resourceSystem)
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
: WindowPinnableBase("openmw_inventory_window_vr.layout")
|
||||||
|
#else
|
||||||
: WindowPinnableBase("openmw_inventory_window.layout")
|
: WindowPinnableBase("openmw_inventory_window.layout")
|
||||||
|
#endif
|
||||||
, mDragAndDrop(dragAndDrop)
|
, mDragAndDrop(dragAndDrop)
|
||||||
, mSelectedItem(-1)
|
, mSelectedItem(-1)
|
||||||
, mSortModel(nullptr)
|
, mSortModel(nullptr)
|
||||||
|
|
|
@ -118,7 +118,7 @@ namespace MWGui
|
||||||
void sellItem(MyGUI::Widget* sender, int count);
|
void sellItem(MyGUI::Widget* sender, int count);
|
||||||
void dragItem(MyGUI::Widget* sender, int count);
|
void dragItem(MyGUI::Widget* sender, int count);
|
||||||
|
|
||||||
void onWindowResize(MyGUI::Window* _sender);
|
void onWindowResize(MyGUI::Window* _sender) override;
|
||||||
void onFilterChanged(MyGUI::Widget* _sender);
|
void onFilterChanged(MyGUI::Widget* _sender);
|
||||||
void onNameFilterChanged(MyGUI::EditBox* _sender);
|
void onNameFilterChanged(MyGUI::EditBox* _sender);
|
||||||
void onAvatarClicked(MyGUI::Widget* _sender);
|
void onAvatarClicked(MyGUI::Widget* _sender);
|
||||||
|
|
|
@ -22,6 +22,11 @@
|
||||||
#include "../mwbase/windowmanager.hpp"
|
#include "../mwbase/windowmanager.hpp"
|
||||||
#include "../mwbase/environment.hpp"
|
#include "../mwbase/environment.hpp"
|
||||||
|
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
#include "../mwvr/vrenvironment.hpp"
|
||||||
|
#include "../mwvr/vrgui.hpp"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace MWGui
|
namespace MWGui
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -102,6 +107,11 @@ void KeyboardNavigation::_unlinkWidget(MyGUI::Widget *widget)
|
||||||
w.second = nullptr;
|
w.second = nullptr;
|
||||||
if (widget == mCurrentFocus)
|
if (widget == mCurrentFocus)
|
||||||
mCurrentFocus = nullptr;
|
mCurrentFocus = nullptr;
|
||||||
|
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
if (MWBase::Environment::get().getVrMode())
|
||||||
|
MWVR::Environment::get().getGUIManager()->notifyWidgetUnlinked(widget);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#if MYGUI_VERSION < MYGUI_DEFINE_VERSION(3,2,3)
|
#if MYGUI_VERSION < MYGUI_DEFINE_VERSION(3,2,3)
|
||||||
|
|
|
@ -5,6 +5,13 @@
|
||||||
#include <MyGUI_Gui.h>
|
#include <MyGUI_Gui.h>
|
||||||
#include <MyGUI_TextBox.h>
|
#include <MyGUI_TextBox.h>
|
||||||
#include <MyGUI_Window.h>
|
#include <MyGUI_Window.h>
|
||||||
|
#include <MyGUI_OverlappedLayer.h>
|
||||||
|
#include <MyGUI_SharedLayer.h>
|
||||||
|
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
#include "../mwvr/vrgui.hpp"
|
||||||
|
#include "../mwvr/vrenvironment.hpp"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace MWGui
|
namespace MWGui
|
||||||
{
|
{
|
||||||
|
@ -45,9 +52,21 @@ namespace MWGui
|
||||||
mMainWidget->setCoord(x,y,w,h);
|
mMainWidget->setCoord(x,y,w,h);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Layout::setCoordf(float x, float y, float w, float h)
|
||||||
|
{
|
||||||
|
mMainWidget->setRealCoord(x, y, w, h);
|
||||||
|
}
|
||||||
|
|
||||||
void Layout::setVisible(bool b)
|
void Layout::setVisible(bool b)
|
||||||
{
|
{
|
||||||
mMainWidget->setVisible(b);
|
mMainWidget->setVisible(b);
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
auto* vrGUIManager = MWVR::Environment::get().getGUIManager();
|
||||||
|
if (!vrGUIManager)
|
||||||
|
// May end up here before before rendering has been fully set up
|
||||||
|
return;
|
||||||
|
vrGUIManager->setVisible(this, b);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void Layout::setText(const std::string &name, const std::string &caption)
|
void Layout::setText(const std::string &name, const std::string &caption)
|
||||||
|
|
|
@ -55,6 +55,7 @@ namespace MWGui
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void setCoord(int x, int y, int w, int h);
|
void setCoord(int x, int y, int w, int h);
|
||||||
|
void setCoordf(float x, float y, float w, float h);
|
||||||
|
|
||||||
virtual void setVisible(bool b);
|
virtual void setVisible(bool b);
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include <MyGUI_TextBox.h>
|
#include <MyGUI_TextBox.h>
|
||||||
|
|
||||||
#include <components/misc/rng.hpp>
|
#include <components/misc/rng.hpp>
|
||||||
|
#include <components/misc/callbackmanager.hpp>
|
||||||
#include <components/debug/debuglog.hpp>
|
#include <components/debug/debuglog.hpp>
|
||||||
#include <components/myguiplatform/myguitexture.hpp>
|
#include <components/myguiplatform/myguitexture.hpp>
|
||||||
#include <components/settings/settings.hpp>
|
#include <components/settings/settings.hpp>
|
||||||
|
@ -331,12 +332,7 @@ namespace MWGui
|
||||||
mCopyFramebufferToTextureCallback = new CopyFramebufferToTextureCallback(mTexture);
|
mCopyFramebufferToTextureCallback = new CopyFramebufferToTextureCallback(mTexture);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if OSG_VERSION_GREATER_OR_EQUAL(3, 5, 10)
|
Misc::CallbackManager::instance().addCallbackOneshot(Misc::CallbackManager::DrawStage::Initial, mCopyFramebufferToTextureCallback);
|
||||||
mViewer->getCamera()->removeInitialDrawCallback(mCopyFramebufferToTextureCallback);
|
|
||||||
mViewer->getCamera()->addInitialDrawCallback(mCopyFramebufferToTextureCallback);
|
|
||||||
#else
|
|
||||||
mViewer->getCamera()->setInitialDrawCallback(mCopyFramebufferToTextureCallback);
|
|
||||||
#endif
|
|
||||||
mCopyFramebufferToTextureCallback->reset();
|
mCopyFramebufferToTextureCallback->reset();
|
||||||
|
|
||||||
mBackgroundImage->setBackgroundImage("");
|
mBackgroundImage->setBackgroundImage("");
|
||||||
|
@ -375,9 +371,7 @@ namespace MWGui
|
||||||
// at the time this function is called we are in the middle of a frame,
|
// at the time this function is called we are in the middle of a frame,
|
||||||
// so out of order calls are necessary to get a correct frameNumber for the next frame.
|
// so out of order calls are necessary to get a correct frameNumber for the next frame.
|
||||||
// refer to the advance() and frame() order in Engine::go()
|
// refer to the advance() and frame() order in Engine::go()
|
||||||
mViewer->eventTraversal();
|
MWBase::Environment::get().getWindowManager()->viewerTraversals(false);
|
||||||
mViewer->updateTraversal();
|
|
||||||
mViewer->renderingTraversals();
|
|
||||||
mViewer->advance(mViewer->getFrameStamp()->getSimulationTime());
|
mViewer->advance(mViewer->getFrameStamp()->getSimulationTime());
|
||||||
|
|
||||||
mLastRenderTime = mTimer.time_m();
|
mLastRenderTime = mTimer.time_m();
|
||||||
|
|
|
@ -700,7 +700,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)
|
||||||
|
|
|
@ -138,6 +138,8 @@ namespace MWGui
|
||||||
messageBox->update(height);
|
messageBox->update(height);
|
||||||
height += messageBox->getHeight();
|
height += messageBox->getHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
box->setVisible(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MessageBoxManager::removeStaticMessageBox ()
|
void MessageBoxManager::removeStaticMessageBox ()
|
||||||
|
@ -228,6 +230,11 @@ namespace MWGui
|
||||||
mMessageWidget->setCaptionWithReplacing(mMessage);
|
mMessageWidget->setCaptionWithReplacing(mMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MessageBox::~MessageBox()
|
||||||
|
{
|
||||||
|
setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
void MessageBox::update (int height)
|
void MessageBox::update (int height)
|
||||||
{
|
{
|
||||||
MyGUI::IntSize gameWindowSize = MyGUI::RenderManager::getInstance().getViewSize();
|
MyGUI::IntSize gameWindowSize = MyGUI::RenderManager::getInstance().getViewSize();
|
||||||
|
|
|
@ -68,6 +68,7 @@ namespace MWGui
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
MessageBox (MessageBoxManager& parMessageBoxManager, const std::string& message);
|
MessageBox (MessageBoxManager& parMessageBoxManager, const std::string& message);
|
||||||
|
~MessageBox();
|
||||||
void setMessage (const std::string& message);
|
void setMessage (const std::string& message);
|
||||||
int getHeight ();
|
int getHeight ();
|
||||||
void update (int height);
|
void update (int height);
|
||||||
|
|
|
@ -46,7 +46,8 @@ namespace MWGui
|
||||||
GM_LoadingWallpaper,
|
GM_LoadingWallpaper,
|
||||||
GM_Jail,
|
GM_Jail,
|
||||||
|
|
||||||
GM_QuickKeysMenu
|
GM_QuickKeysMenu,
|
||||||
|
GM_VrMetaMenu
|
||||||
};
|
};
|
||||||
|
|
||||||
// Windows shown in inventory mode
|
// Windows shown in inventory mode
|
||||||
|
|
|
@ -30,6 +30,10 @@
|
||||||
|
|
||||||
#include "../mwstate/character.hpp"
|
#include "../mwstate/character.hpp"
|
||||||
|
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
#include "../mwvr/vrlistbox.hpp"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "confirmationdialog.hpp"
|
#include "confirmationdialog.hpp"
|
||||||
|
|
||||||
namespace MWGui
|
namespace MWGui
|
||||||
|
@ -41,7 +45,6 @@ namespace MWGui
|
||||||
, mCurrentSlot(nullptr)
|
, mCurrentSlot(nullptr)
|
||||||
{
|
{
|
||||||
getWidget(mScreenshot, "Screenshot");
|
getWidget(mScreenshot, "Screenshot");
|
||||||
getWidget(mCharacterSelection, "SelectCharacter");
|
|
||||||
getWidget(mInfoText, "InfoText");
|
getWidget(mInfoText, "InfoText");
|
||||||
getWidget(mOkButton, "OkButton");
|
getWidget(mOkButton, "OkButton");
|
||||||
getWidget(mCancelButton, "CancelButton");
|
getWidget(mCancelButton, "CancelButton");
|
||||||
|
@ -49,11 +52,28 @@ namespace MWGui
|
||||||
getWidget(mSaveList, "SaveList");
|
getWidget(mSaveList, "SaveList");
|
||||||
getWidget(mSaveNameEdit, "SaveNameEdit");
|
getWidget(mSaveNameEdit, "SaveNameEdit");
|
||||||
getWidget(mSpacer, "Spacer");
|
getWidget(mSpacer, "Spacer");
|
||||||
|
getWidget(mCharacterSelection, "SelectCharacter");
|
||||||
|
getWidget(mCharacterSelectionButton, "SelectCharacterButton");
|
||||||
|
|
||||||
|
if (MWBase::Environment::get().getVrMode())
|
||||||
|
{
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
mCharacterSelectionListBox = new MWVR::VrListBox();
|
||||||
|
#endif
|
||||||
|
mCharacterSelection->setVisible(false);
|
||||||
|
mCharacterSelection->setUserString("Hidden", "true");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mCharacterSelectionButton->setVisible(false);
|
||||||
|
mCharacterSelectionButton->setUserString("Hidden", "true");
|
||||||
|
}
|
||||||
|
mCharacterSelectionButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SaveGameDialog::onCharacterSelectionButtonClicked);
|
||||||
|
mCharacterSelection->eventComboChangePosition += MyGUI::newDelegate(this, &SaveGameDialog::onCharacterSelected);
|
||||||
|
mCharacterSelection->eventComboAccept += MyGUI::newDelegate(this, &SaveGameDialog::onCharacterAccept);
|
||||||
mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SaveGameDialog::onOkButtonClicked);
|
mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SaveGameDialog::onOkButtonClicked);
|
||||||
mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SaveGameDialog::onCancelButtonClicked);
|
mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SaveGameDialog::onCancelButtonClicked);
|
||||||
mDeleteButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SaveGameDialog::onDeleteButtonClicked);
|
mDeleteButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SaveGameDialog::onDeleteButtonClicked);
|
||||||
mCharacterSelection->eventComboChangePosition += MyGUI::newDelegate(this, &SaveGameDialog::onCharacterSelected);
|
|
||||||
mCharacterSelection->eventComboAccept += MyGUI::newDelegate(this, &SaveGameDialog::onCharacterAccept);
|
|
||||||
mSaveList->eventListChangePosition += MyGUI::newDelegate(this, &SaveGameDialog::onSlotSelected);
|
mSaveList->eventListChangePosition += MyGUI::newDelegate(this, &SaveGameDialog::onSlotSelected);
|
||||||
mSaveList->eventListMouseItemActivate += MyGUI::newDelegate(this, &SaveGameDialog::onSlotMouseClick);
|
mSaveList->eventListMouseItemActivate += MyGUI::newDelegate(this, &SaveGameDialog::onSlotMouseClick);
|
||||||
mSaveList->eventListSelectAccept += MyGUI::newDelegate(this, &SaveGameDialog::onSlotActivated);
|
mSaveList->eventListSelectAccept += MyGUI::newDelegate(this, &SaveGameDialog::onSlotActivated);
|
||||||
|
@ -146,12 +166,16 @@ namespace MWGui
|
||||||
mSaveNameEdit->setCaption ("");
|
mSaveNameEdit->setCaption ("");
|
||||||
if (mSaving)
|
if (mSaving)
|
||||||
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mSaveNameEdit);
|
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mSaveNameEdit);
|
||||||
|
else
|
||||||
|
if (MWBase::Environment::get().getVrMode())
|
||||||
|
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCharacterSelectionButton);
|
||||||
else
|
else
|
||||||
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mSaveList);
|
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mSaveList);
|
||||||
|
|
||||||
center();
|
center();
|
||||||
|
|
||||||
mCharacterSelection->setCaption("");
|
mCharacterSelection->setCaption("");
|
||||||
|
mCharacterSelectionButton->setCaption("");
|
||||||
mCharacterSelection->removeAllItems();
|
mCharacterSelection->removeAllItems();
|
||||||
mCurrentCharacter = nullptr;
|
mCurrentCharacter = nullptr;
|
||||||
mCurrentSlot = nullptr;
|
mCurrentSlot = nullptr;
|
||||||
|
@ -209,6 +233,8 @@ namespace MWGui
|
||||||
mCharacterSelection->setIndexSelected(selectedIndex);
|
mCharacterSelection->setIndexSelected(selectedIndex);
|
||||||
if (selectedIndex == MyGUI::ITEM_NONE)
|
if (selectedIndex == MyGUI::ITEM_NONE)
|
||||||
mCharacterSelection->setCaption("Select Character ...");
|
mCharacterSelection->setCaption("Select Character ...");
|
||||||
|
else
|
||||||
|
mCharacterSelectionButton->setCaption(mCharacterSelection->getCaption());
|
||||||
|
|
||||||
fillSaveList();
|
fillSaveList();
|
||||||
|
|
||||||
|
@ -218,8 +244,17 @@ namespace MWGui
|
||||||
{
|
{
|
||||||
mSaving = !load;
|
mSaving = !load;
|
||||||
mSaveNameEdit->setVisible(!load);
|
mSaveNameEdit->setVisible(!load);
|
||||||
|
|
||||||
|
if (MWBase::Environment::get().getVrMode())
|
||||||
|
{
|
||||||
|
mCharacterSelectionButton->setUserString("Hidden", load ? "false" : "true");
|
||||||
|
mCharacterSelectionButton->setVisible(load);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
mCharacterSelection->setUserString("Hidden", load ? "false" : "true");
|
mCharacterSelection->setUserString("Hidden", load ? "false" : "true");
|
||||||
mCharacterSelection->setVisible(load);
|
mCharacterSelection->setVisible(load);
|
||||||
|
}
|
||||||
mSpacer->setUserString("Hidden", load ? "false" : "true");
|
mSpacer->setUserString("Hidden", load ? "false" : "true");
|
||||||
|
|
||||||
mDeleteButton->setUserString("Hidden", load ? "false" : "true");
|
mDeleteButton->setUserString("Hidden", load ? "false" : "true");
|
||||||
|
@ -244,6 +279,21 @@ namespace MWGui
|
||||||
confirmDeleteSave();
|
confirmDeleteSave();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SaveGameDialog::onCharacterSelectionButtonClicked(MyGUI::Widget* sender)
|
||||||
|
{
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
mCharacterSelectionListBox->open(mCharacterSelection, [this](std::size_t index) {
|
||||||
|
if (index != MyGUI::ITEM_NONE)
|
||||||
|
{
|
||||||
|
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mSaveList);
|
||||||
|
auto caption = mCharacterSelection->getItemNameAt(index);
|
||||||
|
mCharacterSelectionButton->setCaption(caption);
|
||||||
|
onCharacterSelected(mCharacterSelection, index);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void SaveGameDialog::onConfirmationGiven()
|
void SaveGameDialog::onConfirmationGiven()
|
||||||
{
|
{
|
||||||
accept(true);
|
accept(true);
|
||||||
|
|
|
@ -11,6 +11,11 @@ namespace MWState
|
||||||
struct Slot;
|
struct Slot;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace MWVR
|
||||||
|
{
|
||||||
|
class VrListBox;
|
||||||
|
}
|
||||||
|
|
||||||
namespace MWGui
|
namespace MWGui
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -31,6 +36,7 @@ namespace MWGui
|
||||||
void onCancelButtonClicked (MyGUI::Widget* sender);
|
void onCancelButtonClicked (MyGUI::Widget* sender);
|
||||||
void onOkButtonClicked (MyGUI::Widget* sender);
|
void onOkButtonClicked (MyGUI::Widget* sender);
|
||||||
void onDeleteButtonClicked (MyGUI::Widget* sender);
|
void onDeleteButtonClicked (MyGUI::Widget* sender);
|
||||||
|
void onCharacterSelectionButtonClicked(MyGUI::Widget* sender);
|
||||||
void onCharacterSelected (MyGUI::ComboBox* sender, size_t pos);
|
void onCharacterSelected (MyGUI::ComboBox* sender, size_t pos);
|
||||||
void onCharacterAccept(MyGUI::ComboBox* sender, size_t pos);
|
void onCharacterAccept(MyGUI::ComboBox* sender, size_t pos);
|
||||||
// Slot selected (mouse click or arrow keys)
|
// Slot selected (mouse click or arrow keys)
|
||||||
|
@ -57,6 +63,8 @@ namespace MWGui
|
||||||
bool mSaving;
|
bool mSaving;
|
||||||
|
|
||||||
MyGUI::ComboBox* mCharacterSelection;
|
MyGUI::ComboBox* mCharacterSelection;
|
||||||
|
MWVR::VrListBox* mCharacterSelectionListBox;
|
||||||
|
MyGUI::Button* mCharacterSelectionButton;
|
||||||
MyGUI::EditBox* mInfoText;
|
MyGUI::EditBox* mInfoText;
|
||||||
MyGUI::Button* mOkButton;
|
MyGUI::Button* mOkButton;
|
||||||
MyGUI::Button* mCancelButton;
|
MyGUI::Button* mCancelButton;
|
||||||
|
|
|
@ -31,6 +31,14 @@
|
||||||
|
|
||||||
#include "confirmationdialog.hpp"
|
#include "confirmationdialog.hpp"
|
||||||
|
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
#include "../mwvr/vrenvironment.hpp"
|
||||||
|
#include "../mwvr/vrsession.hpp"
|
||||||
|
#include "../mwvr/vrviewer.hpp"
|
||||||
|
#include "../mwvr/vrgui.hpp"
|
||||||
|
#include "../mwvr/vrinputmanager.hpp"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -221,7 +229,11 @@ namespace MWGui
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsWindow::SettingsWindow() :
|
SettingsWindow::SettingsWindow() :
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
WindowBase("openmw_settings_window_vr.layout"),
|
||||||
|
#else
|
||||||
WindowBase("openmw_settings_window.layout"),
|
WindowBase("openmw_settings_window.layout"),
|
||||||
|
#endif
|
||||||
mKeyboardMode(true)
|
mKeyboardMode(true)
|
||||||
{
|
{
|
||||||
bool terrain = Settings::Manager::getBool("distant terrain", "Terrain");
|
bool terrain = Settings::Manager::getBool("distant terrain", "Terrain");
|
||||||
|
@ -251,6 +263,12 @@ namespace MWGui
|
||||||
getWidget(mLightsResetButton, "LightsResetButton");
|
getWidget(mLightsResetButton, "LightsResetButton");
|
||||||
getWidget(mMaxLights, "MaxLights");
|
getWidget(mMaxLights, "MaxLights");
|
||||||
|
|
||||||
|
if (MWBase::Environment::get().getVrMode())
|
||||||
|
{
|
||||||
|
getWidget(mVRMirrorTextureEye, "VRMirrorTextureEye");
|
||||||
|
getWidget(mVRLeftHudPosition, "VRLeftHudPosition");
|
||||||
|
}
|
||||||
|
|
||||||
#ifndef WIN32
|
#ifndef WIN32
|
||||||
// hide gamma controls since it currently does not work under Linux
|
// hide gamma controls since it currently does not work under Linux
|
||||||
MyGUI::ScrollBar *gammaSlider;
|
MyGUI::ScrollBar *gammaSlider;
|
||||||
|
@ -282,6 +300,12 @@ namespace MWGui
|
||||||
mKeyboardSwitch->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onKeyboardSwitchClicked);
|
mKeyboardSwitch->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onKeyboardSwitchClicked);
|
||||||
mControllerSwitch->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onControllerSwitchClicked);
|
mControllerSwitch->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onControllerSwitchClicked);
|
||||||
|
|
||||||
|
if (MWBase::Environment::get().getVrMode())
|
||||||
|
{
|
||||||
|
mVRMirrorTextureEye->eventComboChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onVRMirrorTextureEyeChanged);
|
||||||
|
mVRLeftHudPosition->eventComboChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onVRLeftHudPositionChanged);
|
||||||
|
}
|
||||||
|
|
||||||
center();
|
center();
|
||||||
|
|
||||||
mResetControlsButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onResetDefaultBindings);
|
mResetControlsButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onResetDefaultBindings);
|
||||||
|
@ -312,6 +336,19 @@ namespace MWGui
|
||||||
std::string tmip = Settings::Manager::getString("texture mipmap", "General");
|
std::string tmip = Settings::Manager::getString("texture mipmap", "General");
|
||||||
mTextureFilteringButton->setCaption(textureMipmappingToStr(tmip));
|
mTextureFilteringButton->setCaption(textureMipmappingToStr(tmip));
|
||||||
|
|
||||||
|
if (MWBase::Environment::get().getVrMode())
|
||||||
|
{
|
||||||
|
std::string mirrorTextureEye = Settings::Manager::getString("mirror texture eye", "VR");
|
||||||
|
for (unsigned i = 0; i < mVRMirrorTextureEye->getItemCount(); i++)
|
||||||
|
if (Misc::StringUtils::ciEqual(mirrorTextureEye, mVRMirrorTextureEye->getItem(i)))
|
||||||
|
mVRMirrorTextureEye->setIndexSelected(i);
|
||||||
|
|
||||||
|
std::string leftHandHudPosition = Settings::Manager::getString("left hand hud position", "VR");
|
||||||
|
for (unsigned i = 0; i < mVRLeftHudPosition->getItemCount(); i++)
|
||||||
|
if (Misc::StringUtils::ciEqual(leftHandHudPosition, mVRLeftHudPosition->getItem(i)))
|
||||||
|
mVRLeftHudPosition->setIndexSelected(i);
|
||||||
|
}
|
||||||
|
|
||||||
int waterTextureSize = Settings::Manager::getInt("rtt size", "Water");
|
int waterTextureSize = Settings::Manager::getInt("rtt size", "Water");
|
||||||
if (waterTextureSize >= 512)
|
if (waterTextureSize >= 512)
|
||||||
mWaterTextureSize->setIndexSelected(0);
|
mWaterTextureSize->setIndexSelected(0);
|
||||||
|
@ -392,6 +429,20 @@ namespace MWGui
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SettingsWindow::onVRMirrorTextureEyeChanged(MyGUI::ComboBox* _sender, size_t pos)
|
||||||
|
{
|
||||||
|
auto setting = Misc::StringUtils::lowerCase(_sender->getItem(pos));
|
||||||
|
Settings::Manager::setString("mirror texture eye", "VR", setting);
|
||||||
|
apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsWindow::onVRLeftHudPositionChanged(MyGUI::ComboBox* _sender, size_t pos)
|
||||||
|
{
|
||||||
|
auto setting = Misc::StringUtils::lowerCase(_sender->getItem(pos));
|
||||||
|
Settings::Manager::setString("left hand hud position", "VR", setting);
|
||||||
|
apply();
|
||||||
|
}
|
||||||
|
|
||||||
void SettingsWindow::onWaterTextureSizeChanged(MyGUI::ComboBox* _sender, size_t pos)
|
void SettingsWindow::onWaterTextureSizeChanged(MyGUI::ComboBox* _sender, size_t pos)
|
||||||
{
|
{
|
||||||
int size = 0;
|
int size = 0;
|
||||||
|
@ -590,6 +641,15 @@ namespace MWGui
|
||||||
MWBase::Environment::get().getWindowManager()->processChangedSettings(changed);
|
MWBase::Environment::get().getWindowManager()->processChangedSettings(changed);
|
||||||
MWBase::Environment::get().getInputManager()->processChangedSettings(changed);
|
MWBase::Environment::get().getInputManager()->processChangedSettings(changed);
|
||||||
MWBase::Environment::get().getMechanicsManager()->processChangedSettings(changed);
|
MWBase::Environment::get().getMechanicsManager()->processChangedSettings(changed);
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
if (MWBase::Environment::get().getVrMode())
|
||||||
|
{
|
||||||
|
MWVR::Environment::get().getSession()->processChangedSettings(changed);
|
||||||
|
MWVR::Environment::get().getTrackingManager()->processChangedSettings(changed);
|
||||||
|
MWVR::Environment::get().getViewer()->processChangedSettings(changed);
|
||||||
|
MWVR::Environment::get().getGUIManager()->processChangedSettings(changed);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
Settings::Manager::resetPendingChanges();
|
Settings::Manager::resetPendingChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,10 @@ namespace MWGui
|
||||||
MyGUI::TabControl* mSettingsTab;
|
MyGUI::TabControl* mSettingsTab;
|
||||||
MyGUI::Button* mOkButton;
|
MyGUI::Button* mOkButton;
|
||||||
|
|
||||||
|
// VR
|
||||||
|
MyGUI::ComboBox* mVRLeftHudPosition;
|
||||||
|
MyGUI::ComboBox* mVRMirrorTextureEye;
|
||||||
|
|
||||||
// graphics
|
// graphics
|
||||||
MyGUI::ListBox* mResolutionList;
|
MyGUI::ListBox* mResolutionList;
|
||||||
MyGUI::Button* mFullscreenButton;
|
MyGUI::Button* mFullscreenButton;
|
||||||
|
@ -53,6 +57,9 @@ namespace MWGui
|
||||||
void onResolutionCancel();
|
void onResolutionCancel();
|
||||||
void highlightCurrentResolution();
|
void highlightCurrentResolution();
|
||||||
|
|
||||||
|
void onVRMirrorTextureEyeChanged(MyGUI::ComboBox* _sender, size_t pos);
|
||||||
|
void onVRLeftHudPositionChanged(MyGUI::ComboBox* _sender, size_t pos);
|
||||||
|
|
||||||
void onWaterTextureSizeChanged(MyGUI::ComboBox* _sender, size_t pos);
|
void onWaterTextureSizeChanged(MyGUI::ComboBox* _sender, size_t pos);
|
||||||
void onWaterReflectionDetailChanged(MyGUI::ComboBox* _sender, size_t pos);
|
void onWaterReflectionDetailChanged(MyGUI::ComboBox* _sender, size_t pos);
|
||||||
|
|
||||||
|
|
|
@ -65,8 +65,14 @@ namespace MWGui
|
||||||
|
|
||||||
int w=2;
|
int w=2;
|
||||||
|
|
||||||
|
for(auto rit = effects.rbegin(); rit != effects.rend(); rit++)
|
||||||
|
{
|
||||||
|
auto& effectInfoPair = *rit;
|
||||||
|
#if 0
|
||||||
|
// in VR mode, the effect box grows to the right so we want to invert the order to avoid reordering effects.
|
||||||
for (auto& effectInfoPair : effects)
|
for (auto& effectInfoPair : effects)
|
||||||
{
|
{
|
||||||
|
#endif
|
||||||
const int effectId = effectInfoPair.first;
|
const int effectId = effectInfoPair.first;
|
||||||
const ESM::MagicEffect* effect =
|
const ESM::MagicEffect* effect =
|
||||||
MWBase::Environment::get().getWorld ()->getStore ().get<ESM::MagicEffect>().find(effectId);
|
MWBase::Environment::get().getWorld ()->getStore ().get<ESM::MagicEffect>().find(effectId);
|
||||||
|
@ -194,7 +200,10 @@ namespace MWGui
|
||||||
s = 0;
|
s = 0;
|
||||||
int diff = parent->getWidth() - s;
|
int diff = parent->getWidth() - s;
|
||||||
parent->setSize(s, parent->getHeight());
|
parent->setSize(s, parent->getHeight());
|
||||||
|
#ifndef USE_OPENXR
|
||||||
|
// in VR mode, the effect box grows to the right and does not need repositioning
|
||||||
parent->setPosition(parent->getLeft()+diff, parent->getTop());
|
parent->setPosition(parent->getLeft()+diff, parent->getTop());
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// hide inactive effects
|
// hide inactive effects
|
||||||
|
|
|
@ -40,7 +40,11 @@ namespace MWGui
|
||||||
{
|
{
|
||||||
|
|
||||||
SpellWindow::SpellWindow(DragAndDrop* drag)
|
SpellWindow::SpellWindow(DragAndDrop* drag)
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
: WindowPinnableBase("openmw_spell_window_vr.layout")
|
||||||
|
#else
|
||||||
: WindowPinnableBase("openmw_spell_window.layout")
|
: WindowPinnableBase("openmw_spell_window.layout")
|
||||||
|
#endif
|
||||||
, NoDrop(drag, mMainWidget)
|
, NoDrop(drag, mMainWidget)
|
||||||
, mSpellView(nullptr)
|
, mSpellView(nullptr)
|
||||||
, mUpdateTimer(0.0f)
|
, mUpdateTimer(0.0f)
|
||||||
|
|
|
@ -26,7 +26,11 @@
|
||||||
namespace MWGui
|
namespace MWGui
|
||||||
{
|
{
|
||||||
StatsWindow::StatsWindow (DragAndDrop* drag)
|
StatsWindow::StatsWindow (DragAndDrop* drag)
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
: WindowPinnableBase("openmw_stats_window_vr.layout")
|
||||||
|
#else
|
||||||
: WindowPinnableBase("openmw_stats_window.layout")
|
: WindowPinnableBase("openmw_stats_window.layout")
|
||||||
|
#endif
|
||||||
, NoDrop(drag, mMainWidget)
|
, NoDrop(drag, mMainWidget)
|
||||||
, mSkillView(nullptr)
|
, mSkillView(nullptr)
|
||||||
, mMajorSkills()
|
, mMajorSkills()
|
||||||
|
|
|
@ -46,7 +46,7 @@ namespace MWGui
|
||||||
void setExpelled (const std::set<std::string>& expelled);
|
void setExpelled (const std::set<std::string>& expelled);
|
||||||
void setBirthSign (const std::string &signId);
|
void setBirthSign (const std::string &signId);
|
||||||
|
|
||||||
void onWindowResize(MyGUI::Window* window);
|
void onWindowResize(MyGUI::Window* window) override;
|
||||||
void onMouseWheel(MyGUI::Widget* _sender, int _rel);
|
void onMouseWheel(MyGUI::Widget* _sender, int _rel);
|
||||||
|
|
||||||
MyGUI::Widget* mLeftPane;
|
MyGUI::Widget* mLeftPane;
|
||||||
|
|
|
@ -30,7 +30,11 @@ namespace MWGui
|
||||||
std::string ToolTips::sSchoolNames[] = {"#{sSchoolAlteration}", "#{sSchoolConjuration}", "#{sSchoolDestruction}", "#{sSchoolIllusion}", "#{sSchoolMysticism}", "#{sSchoolRestoration}"};
|
std::string ToolTips::sSchoolNames[] = {"#{sSchoolAlteration}", "#{sSchoolConjuration}", "#{sSchoolDestruction}", "#{sSchoolIllusion}", "#{sSchoolMysticism}", "#{sSchoolRestoration}"};
|
||||||
|
|
||||||
ToolTips::ToolTips() :
|
ToolTips::ToolTips() :
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
Layout("openmw_tooltips_vr.layout")
|
||||||
|
#else
|
||||||
Layout("openmw_tooltips.layout")
|
Layout("openmw_tooltips.layout")
|
||||||
|
#endif
|
||||||
, mFocusToolTipX(0.0)
|
, mFocusToolTipX(0.0)
|
||||||
, mFocusToolTipY(0.0)
|
, mFocusToolTipY(0.0)
|
||||||
, mHorizontalScrollIndex(0)
|
, mHorizontalScrollIndex(0)
|
||||||
|
@ -52,6 +56,8 @@ namespace MWGui
|
||||||
mDynamicToolTipBox->setNeedMouseFocus(false);
|
mDynamicToolTipBox->setNeedMouseFocus(false);
|
||||||
mMainWidget->setNeedMouseFocus(false);
|
mMainWidget->setNeedMouseFocus(false);
|
||||||
|
|
||||||
|
// Tooltip delay is not useful in vr as a player cannot be perfectly still.
|
||||||
|
if (!MWBase::Environment::get().getVrMode())
|
||||||
mDelay = Settings::Manager::getFloat("tooltip delay", "GUI");
|
mDelay = Settings::Manager::getFloat("tooltip delay", "GUI");
|
||||||
mRemainingDelay = mDelay;
|
mRemainingDelay = mDelay;
|
||||||
|
|
||||||
|
|
|
@ -58,7 +58,11 @@ namespace
|
||||||
namespace MWGui
|
namespace MWGui
|
||||||
{
|
{
|
||||||
TradeWindow::TradeWindow()
|
TradeWindow::TradeWindow()
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
: WindowBase("openmw_trade_window_vr.layout")
|
||||||
|
#else
|
||||||
: WindowBase("openmw_trade_window.layout")
|
: WindowBase("openmw_trade_window.layout")
|
||||||
|
#endif
|
||||||
, mSortModel(nullptr)
|
, mSortModel(nullptr)
|
||||||
, mTradeModel(nullptr)
|
, mTradeModel(nullptr)
|
||||||
, mItemToSell(-1)
|
, mItemToSell(-1)
|
||||||
|
|
|
@ -12,6 +12,11 @@
|
||||||
#include "draganddrop.hpp"
|
#include "draganddrop.hpp"
|
||||||
#include "exposedwindow.hpp"
|
#include "exposedwindow.hpp"
|
||||||
|
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
#include "../mwvr/vrgui.hpp"
|
||||||
|
#include "../mwvr/vrenvironment.hpp"
|
||||||
|
#endif
|
||||||
|
|
||||||
using namespace MWGui;
|
using namespace MWGui;
|
||||||
|
|
||||||
WindowBase::WindowBase(const std::string& parLayout)
|
WindowBase::WindowBase(const std::string& parLayout)
|
||||||
|
@ -55,6 +60,19 @@ void WindowBase::setVisible(bool visible)
|
||||||
onOpen();
|
onOpen();
|
||||||
else if (wasVisible)
|
else if (wasVisible)
|
||||||
onClose();
|
onClose();
|
||||||
|
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
// Check that onOpen/onClose didn't reverse the change before forwarding it
|
||||||
|
// to the VR GUI manager.
|
||||||
|
if (this->isVisible() == visible)
|
||||||
|
{
|
||||||
|
auto* vrGUIManager = MWVR::Environment::get().getGUIManager();
|
||||||
|
if (!vrGUIManager)
|
||||||
|
// May end up here before before rendering has been fully set up
|
||||||
|
return;
|
||||||
|
vrGUIManager->setVisible(this, visible);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WindowBase::isVisible()
|
bool WindowBase::isVisible()
|
||||||
|
@ -109,26 +127,42 @@ void NoDrop::onFrame(float dt)
|
||||||
|
|
||||||
MyGUI::IntPoint mousePos = MyGUI::InputManager::getInstance().getMousePosition();
|
MyGUI::IntPoint mousePos = MyGUI::InputManager::getInstance().getMousePosition();
|
||||||
|
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
// Since VR mode stretches some windows to full screen, the usual outside condition
|
||||||
|
// won't work
|
||||||
|
mTransparent = false;
|
||||||
|
#endif
|
||||||
if (mDrag->mIsOnDragAndDrop)
|
if (mDrag->mIsOnDragAndDrop)
|
||||||
{
|
{
|
||||||
MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getMouseFocusWidget();
|
MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getMouseFocusWidget();
|
||||||
while (focus && focus != mWidget)
|
while (focus && focus != mWidget)
|
||||||
|
{
|
||||||
focus = focus->getParent();
|
focus = focus->getParent();
|
||||||
|
}
|
||||||
|
|
||||||
if (focus == mWidget)
|
if (focus == mWidget)
|
||||||
|
{
|
||||||
mTransparent = true;
|
mTransparent = true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (!mWidget->getAbsoluteCoord().inside(mousePos))
|
if (!mWidget->getAbsoluteCoord().inside(mousePos))
|
||||||
mTransparent = false;
|
mTransparent = false;
|
||||||
|
|
||||||
if (mTransparent)
|
if (mTransparent)
|
||||||
{
|
{
|
||||||
|
#ifndef USE_OPENXR
|
||||||
|
// These makes focus null, which messes up the logic for VR
|
||||||
|
// since i reset mTransparent to false every update.
|
||||||
|
// TODO: Is there a cleaner way?
|
||||||
mWidget->setNeedMouseFocus(false); // Allow click-through
|
mWidget->setNeedMouseFocus(false); // Allow click-through
|
||||||
|
#endif
|
||||||
setAlpha(std::max(0.13f, mWidget->getAlpha() - dt*5));
|
setAlpha(std::max(0.13f, mWidget->getAlpha() - dt*5));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mWidget->setNeedMouseFocus(true);
|
#ifndef USE_OPENXR
|
||||||
|
mWidget->setNeedMouseFocus(true); // Allow click-through
|
||||||
|
#endif
|
||||||
setAlpha(std::min(1.0f, mWidget->getAlpha() + dt*5));
|
setAlpha(std::min(1.0f, mWidget->getAlpha() + dt*5));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,6 +47,9 @@ namespace MWGui
|
||||||
/// Called when GUI viewport changes size
|
/// Called when GUI viewport changes size
|
||||||
virtual void onResChange(int width, int height) {}
|
virtual void onResChange(int width, int height) {}
|
||||||
|
|
||||||
|
/// Called when Window widget changes in size
|
||||||
|
virtual void onWindowResize(MyGUI::Window* window) {}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void onTitleDoubleClicked();
|
virtual void onTitleDoubleClicked();
|
||||||
|
|
||||||
|
|
|
@ -131,6 +131,16 @@
|
||||||
#include "keyboardnavigation.hpp"
|
#include "keyboardnavigation.hpp"
|
||||||
#include "resourceskin.hpp"
|
#include "resourceskin.hpp"
|
||||||
|
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
#include "../mwvr/vrmetamenu.hpp"
|
||||||
|
#include "../mwvr/vrenvironment.hpp"
|
||||||
|
#include "../mwvr/vrgui.hpp"
|
||||||
|
#include "../mwvr/vrvirtualkeyboard.hpp"
|
||||||
|
#include "../mwvr/vrviewer.hpp"
|
||||||
|
#include "../mwvr/vrsession.hpp"
|
||||||
|
#include "../mwvr/vrtracking.hpp"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace MWGui
|
namespace MWGui
|
||||||
{
|
{
|
||||||
WindowManager::WindowManager(
|
WindowManager::WindowManager(
|
||||||
|
@ -174,6 +184,8 @@ namespace MWGui
|
||||||
, mScreenFader(nullptr)
|
, mScreenFader(nullptr)
|
||||||
, mDebugWindow(nullptr)
|
, mDebugWindow(nullptr)
|
||||||
, mJailScreen(nullptr)
|
, mJailScreen(nullptr)
|
||||||
|
, mVrMetaMenu(nullptr)
|
||||||
|
, mVirtualKeyboardManager(nullptr)
|
||||||
, mTranslationDataStorage (translationDataStorage)
|
, mTranslationDataStorage (translationDataStorage)
|
||||||
, mCharGen(nullptr)
|
, mCharGen(nullptr)
|
||||||
, mInputBlocker(nullptr)
|
, mInputBlocker(nullptr)
|
||||||
|
@ -184,6 +196,7 @@ namespace MWGui
|
||||||
, mHudEnabled(true)
|
, mHudEnabled(true)
|
||||||
, mCursorVisible(true)
|
, mCursorVisible(true)
|
||||||
, mCursorActive(true)
|
, mCursorActive(true)
|
||||||
|
, mVideoEnabled(false)
|
||||||
, mPlayerBounty(-1)
|
, mPlayerBounty(-1)
|
||||||
, mGui(nullptr)
|
, mGui(nullptr)
|
||||||
, mGuiModes()
|
, mGuiModes()
|
||||||
|
@ -202,6 +215,11 @@ namespace MWGui
|
||||||
mGuiPlatform = new osgMyGUI::Platform(viewer, guiRoot, resourceSystem->getImageManager(), mScalingFactor);
|
mGuiPlatform = new osgMyGUI::Platform(viewer, guiRoot, resourceSystem->getImageManager(), mScalingFactor);
|
||||||
mGuiPlatform->initialise(resourcePath, (boost::filesystem::path(logpath) / "MyGUI.log").generic_string());
|
mGuiPlatform->initialise(resourcePath, (boost::filesystem::path(logpath) / "MyGUI.log").generic_string());
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
mGuiPlatform->getRenderManagerPtr()->setViewSize(1024, 1024);
|
||||||
|
#endif
|
||||||
|
|
||||||
mGui = new MyGUI::Gui;
|
mGui = new MyGUI::Gui;
|
||||||
mGui->initialise("");
|
mGui->initialise("");
|
||||||
|
|
||||||
|
@ -236,7 +254,17 @@ namespace MWGui
|
||||||
|
|
||||||
MyGUI::FactoryManager::getInstance().registerFactory<ResourceImageSetPointerFix>("Resource", "ResourceImageSetPointer");
|
MyGUI::FactoryManager::getInstance().registerFactory<ResourceImageSetPointerFix>("Resource", "ResourceImageSetPointer");
|
||||||
MyGUI::FactoryManager::getInstance().registerFactory<AutoSizedResourceSkin>("Resource", "AutoSizedResourceSkin");
|
MyGUI::FactoryManager::getInstance().registerFactory<AutoSizedResourceSkin>("Resource", "AutoSizedResourceSkin");
|
||||||
|
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
if (MWBase::Environment::get().getVrMode())
|
||||||
|
MWVR::VRGUIManager::registerMyGUIFactories();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
MyGUI::ResourceManager::getInstance().load("core_vr.xml");
|
||||||
|
#else
|
||||||
MyGUI::ResourceManager::getInstance().load("core.xml");
|
MyGUI::ResourceManager::getInstance().load("core.xml");
|
||||||
|
#endif
|
||||||
WindowManager::loadUserFonts();
|
WindowManager::loadUserFonts();
|
||||||
|
|
||||||
bool keyboardNav = Settings::Manager::getBool("keyboard navigation", "GUI");
|
bool keyboardNav = Settings::Manager::getBool("keyboard navigation", "GUI");
|
||||||
|
@ -269,7 +297,7 @@ namespace MWGui
|
||||||
mVideoBackground->setNeedMouseFocus(true);
|
mVideoBackground->setNeedMouseFocus(true);
|
||||||
mVideoBackground->setNeedKeyFocus(true);
|
mVideoBackground->setNeedKeyFocus(true);
|
||||||
|
|
||||||
mVideoWidget = mVideoBackground->createWidgetReal<VideoWidget>("ImageBox", 0,0,1,1, MyGUI::Align::Default);
|
mVideoWidget = mVideoBackground->createWidgetReal<VideoWidget>("ImageBox", 0,0,1,1, MyGUI::Align::Default, "InputBlocker");
|
||||||
mVideoWidget->setNeedMouseFocus(true);
|
mVideoWidget->setNeedMouseFocus(true);
|
||||||
mVideoWidget->setNeedKeyFocus(true);
|
mVideoWidget->setNeedKeyFocus(true);
|
||||||
mVideoWidget->setVFS(resourceSystem->getVFS());
|
mVideoWidget->setVFS(resourceSystem->getVFS());
|
||||||
|
@ -283,7 +311,7 @@ namespace MWGui
|
||||||
|
|
||||||
mShowOwned = Settings::Manager::getInt("show owned", "Game");
|
mShowOwned = Settings::Manager::getInt("show owned", "Game");
|
||||||
|
|
||||||
mVideoWrapper = new SDLUtil::VideoWrapper(window, viewer);
|
mVideoWrapper = new SDLUtil::VideoWrapper(window, viewer, MWBase::Environment::get().getVrMode() != true);
|
||||||
mVideoWrapper->setGammaContrast(Settings::Manager::getFloat("gamma", "Video"),
|
mVideoWrapper->setGammaContrast(Settings::Manager::getFloat("gamma", "Video"),
|
||||||
Settings::Manager::getFloat("contrast", "Video"));
|
Settings::Manager::getFloat("contrast", "Video"));
|
||||||
|
|
||||||
|
@ -305,6 +333,14 @@ namespace MWGui
|
||||||
|
|
||||||
mDragAndDrop = new DragAndDrop();
|
mDragAndDrop = new DragAndDrop();
|
||||||
|
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
mVrMetaMenu = new MWVR::VrMetaMenu(w, h);
|
||||||
|
mWindows.push_back(mVrMetaMenu);
|
||||||
|
mGuiModeStates[GM_VrMetaMenu] = GuiModeState(mVrMetaMenu);
|
||||||
|
|
||||||
|
mVirtualKeyboardManager = new MWVR::VirtualKeyboardManager;
|
||||||
|
#endif
|
||||||
|
|
||||||
Recharge* recharge = new Recharge();
|
Recharge* recharge = new Recharge();
|
||||||
mGuiModeStates[GM_Recharge] = GuiModeState(recharge);
|
mGuiModeStates[GM_Recharge] = GuiModeState(recharge);
|
||||||
mWindows.push_back(recharge);
|
mWindows.push_back(recharge);
|
||||||
|
@ -582,18 +618,28 @@ 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;
|
||||||
|
|
||||||
|
// VR mode needs to render the 3D gui
|
||||||
|
if (MWBase::Environment::get().getVrMode())
|
||||||
|
disablemask = MWRender::Mask_Pointer | MWRender::Mask_3DGUI | MWRender::Mask_PreCompile | MWRender::Mask_RenderToTexture;
|
||||||
|
|
||||||
if (!enable && mViewer->getCamera()->getCullMask() != disablemask)
|
if (!enable && mViewer->getCamera()->getCullMask() != disablemask)
|
||||||
{
|
{
|
||||||
mOldUpdateMask = mViewer->getUpdateVisitor()->getTraversalMask();
|
mOldUpdateMask = mViewer->getUpdateVisitor()->getTraversalMask();
|
||||||
mOldCullMask = mViewer->getCamera()->getCullMask();
|
mOldCullMask = mViewer->getCamera()->getCullMask();
|
||||||
mViewer->getUpdateVisitor()->setTraversalMask(disablemask);
|
mViewer->getUpdateVisitor()->setTraversalMask(disablemask);
|
||||||
mViewer->getCamera()->setCullMask(disablemask);
|
mViewer->getCamera()->setCullMask(disablemask);
|
||||||
|
mViewer->getCamera()->setCullMaskLeft(disablemask);
|
||||||
|
mViewer->getCamera()->setCullMaskRight(disablemask);
|
||||||
}
|
}
|
||||||
else if (enable && mViewer->getCamera()->getCullMask() == disablemask)
|
else if (enable && mViewer->getCamera()->getCullMask() == disablemask)
|
||||||
{
|
{
|
||||||
mViewer->getUpdateVisitor()->setTraversalMask(mOldUpdateMask);
|
mViewer->getUpdateVisitor()->setTraversalMask(mOldUpdateMask);
|
||||||
mViewer->getCamera()->setCullMask(mOldCullMask);
|
mViewer->getCamera()->setCullMask(mOldCullMask);
|
||||||
|
mViewer->getCamera()->setCullMaskLeft(mOldCullMask);
|
||||||
|
mViewer->getCamera()->setCullMaskRight(mOldCullMask);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -732,6 +778,8 @@ namespace MWGui
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mVideoEnabled = false;
|
||||||
|
|
||||||
popGuiMode();
|
popGuiMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -766,11 +814,7 @@ namespace MWGui
|
||||||
if (!mWindowVisible)
|
if (!mWindowVisible)
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
||||||
else
|
else
|
||||||
{
|
viewerTraversals(false);
|
||||||
mViewer->eventTraversal();
|
|
||||||
mViewer->updateTraversal();
|
|
||||||
mViewer->renderingTraversals();
|
|
||||||
}
|
|
||||||
// at the time this function is called we are in the middle of a frame,
|
// at the time this function is called we are in the middle of a frame,
|
||||||
// so out of order calls are necessary to get a correct frameNumber for the next frame.
|
// so out of order calls are necessary to get a correct frameNumber for the next frame.
|
||||||
// refer to the advance() and frame() order in Engine::go()
|
// refer to the advance() and frame() order in Engine::go()
|
||||||
|
@ -1173,6 +1217,9 @@ namespace MWGui
|
||||||
|
|
||||||
void WindowManager::windowResized(int x, int y)
|
void WindowManager::windowResized(int x, int y)
|
||||||
{
|
{
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
// Note: this is a side effect of resolution change or window resize.
|
// Note: this is a side effect of resolution change or window resize.
|
||||||
// There is no need to track these changes.
|
// There is no need to track these changes.
|
||||||
Settings::Manager::setInt("resolution x", "Video", x);
|
Settings::Manager::setInt("resolution x", "Video", x);
|
||||||
|
@ -1534,6 +1581,11 @@ namespace MWGui
|
||||||
updateVisible();
|
updateVisible();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DragAndDrop& WindowManager::getDragAndDrop(void)
|
||||||
|
{
|
||||||
|
return *mDragAndDrop;
|
||||||
|
}
|
||||||
|
|
||||||
void WindowManager::forceHide(GuiWindow wnd)
|
void WindowManager::forceHide(GuiWindow wnd)
|
||||||
{
|
{
|
||||||
mForceHidden = (GuiWindow)(mForceHidden | wnd);
|
mForceHidden = (GuiWindow)(mForceHidden | wnd);
|
||||||
|
@ -1886,6 +1938,7 @@ namespace MWGui
|
||||||
|
|
||||||
void WindowManager::playVideo(const std::string &name, bool allowSkipping)
|
void WindowManager::playVideo(const std::string &name, bool allowSkipping)
|
||||||
{
|
{
|
||||||
|
mVideoEnabled = true;
|
||||||
mVideoWidget->playVideo("video\\" + name);
|
mVideoWidget->playVideo("video\\" + name);
|
||||||
|
|
||||||
mVideoWidget->eventKeyButtonPressed.clear();
|
mVideoWidget->eventKeyButtonPressed.clear();
|
||||||
|
@ -1906,6 +1959,11 @@ namespace MWGui
|
||||||
|
|
||||||
mVideoBackground->setVisible(true);
|
mVideoBackground->setVisible(true);
|
||||||
|
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
auto* vrGuiManager = MWVR::Environment::get().getGUIManager();
|
||||||
|
vrGuiManager->insertLayer(mVideoBackground->getLayer()->getName());
|
||||||
|
#endif
|
||||||
|
|
||||||
bool cursorWasVisible = mCursorVisible;
|
bool cursorWasVisible = mCursorVisible;
|
||||||
setCursorVisible(false);
|
setCursorVisible(false);
|
||||||
|
|
||||||
|
@ -1915,7 +1973,7 @@ namespace MWGui
|
||||||
);
|
);
|
||||||
|
|
||||||
Misc::FrameRateLimiter frameRateLimiter = Misc::makeFrameRateLimiter(MWBase::Environment::get().getFrameRateLimit());
|
Misc::FrameRateLimiter frameRateLimiter = Misc::makeFrameRateLimiter(MWBase::Environment::get().getFrameRateLimit());
|
||||||
while (mVideoWidget->update() && !MWBase::Environment::get().getStateManager()->hasQuitRequest())
|
while (mVideoEnabled && mVideoWidget->update() && !MWBase::Environment::get().getStateManager()->hasQuitRequest())
|
||||||
{
|
{
|
||||||
const double dt = std::chrono::duration_cast<std::chrono::duration<double>>(frameRateLimiter.getLastFrameDuration()).count();
|
const double dt = std::chrono::duration_cast<std::chrono::duration<double>>(frameRateLimiter.getLastFrameDuration()).count();
|
||||||
|
|
||||||
|
@ -1931,9 +1989,7 @@ namespace MWGui
|
||||||
if (mVideoWidget->isPaused())
|
if (mVideoWidget->isPaused())
|
||||||
mVideoWidget->resume();
|
mVideoWidget->resume();
|
||||||
|
|
||||||
mViewer->eventTraversal();
|
viewerTraversals(false);
|
||||||
mViewer->updateTraversal();
|
|
||||||
mViewer->renderingTraversals();
|
|
||||||
}
|
}
|
||||||
// at the time this function is called we are in the middle of a frame,
|
// at the time this function is called we are in the middle of a frame,
|
||||||
// so out of order calls are necessary to get a correct frameNumber for the next frame.
|
// so out of order calls are necessary to get a correct frameNumber for the next frame.
|
||||||
|
@ -1953,7 +2009,16 @@ namespace MWGui
|
||||||
// Restore normal rendering
|
// Restore normal rendering
|
||||||
updateVisible();
|
updateVisible();
|
||||||
|
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
vrGuiManager->removeLayer(mVideoBackground->getLayer()->getName());
|
||||||
|
#endif
|
||||||
mVideoBackground->setVisible(false);
|
mVideoBackground->setVisible(false);
|
||||||
|
mVideoEnabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WindowManager::isPlayingVideo(void) const
|
||||||
|
{
|
||||||
|
return mVideoEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindowManager::sizeVideo(int screenWidth, int screenHeight)
|
void WindowManager::sizeVideo(int screenWidth, int screenHeight)
|
||||||
|
@ -2370,6 +2435,15 @@ namespace MWGui
|
||||||
return MyGUI::InputManager::getInstance().injectKeyRelease(key);
|
return MyGUI::InputManager::getInstance().injectKeyRelease(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WindowManager::viewerTraversals(bool updateWindowManager)
|
||||||
|
{
|
||||||
|
mViewer->eventTraversal();
|
||||||
|
mViewer->updateTraversal();
|
||||||
|
if (updateWindowManager)
|
||||||
|
MWBase::Environment::get().getWorld()->updateWindowManager();
|
||||||
|
mViewer->renderingTraversals();
|
||||||
|
}
|
||||||
|
|
||||||
void WindowManager::GuiModeState::update(bool visible)
|
void WindowManager::GuiModeState::update(bool visible)
|
||||||
{
|
{
|
||||||
for (unsigned int i=0; i<mWindows.size(); ++i)
|
for (unsigned int i=0; i<mWindows.size(); ++i)
|
||||||
|
|
|
@ -81,6 +81,7 @@ namespace osgMyGUI
|
||||||
namespace Gui
|
namespace Gui
|
||||||
{
|
{
|
||||||
class FontLoader;
|
class FontLoader;
|
||||||
|
class VirtualKeyboardManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace MWRender
|
namespace MWRender
|
||||||
|
@ -88,6 +89,11 @@ namespace MWRender
|
||||||
class LocalMap;
|
class LocalMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace MWVR
|
||||||
|
{
|
||||||
|
class VrMetaMenu;
|
||||||
|
}
|
||||||
|
|
||||||
namespace MWGui
|
namespace MWGui
|
||||||
{
|
{
|
||||||
class WindowBase;
|
class WindowBase;
|
||||||
|
@ -147,6 +153,7 @@ namespace MWGui
|
||||||
/// @note This method will block until the video finishes playing
|
/// @note This method will block until the video finishes playing
|
||||||
/// (and will continually update the window while doing so)
|
/// (and will continually update the window while doing so)
|
||||||
void playVideo(const std::string& name, bool allowSkipping) override;
|
void playVideo(const std::string& name, bool allowSkipping) override;
|
||||||
|
bool isPlayingVideo(void) const override;
|
||||||
|
|
||||||
/// Warning: do not use MyGUI::InputManager::setKeyFocusWidget directly. Instead use this.
|
/// Warning: do not use MyGUI::InputManager::setKeyFocusWidget directly. Instead use this.
|
||||||
void setKeyFocusWidget (MyGUI::Widget* widget) override;
|
void setKeyFocusWidget (MyGUI::Widget* widget) override;
|
||||||
|
@ -171,7 +178,7 @@ namespace MWGui
|
||||||
|
|
||||||
void forceHide(MWGui::GuiWindow wnd) override;
|
void forceHide(MWGui::GuiWindow wnd) override;
|
||||||
void unsetForceHide(MWGui::GuiWindow wnd) override;
|
void unsetForceHide(MWGui::GuiWindow wnd) override;
|
||||||
|
DragAndDrop& getDragAndDrop(void) override;
|
||||||
/// Disallow all inventory mode windows
|
/// Disallow all inventory mode windows
|
||||||
void disallowAll() override;
|
void disallowAll() override;
|
||||||
|
|
||||||
|
@ -478,10 +485,12 @@ namespace MWGui
|
||||||
bool injectKeyPress(MyGUI::KeyCode key, unsigned int text, bool repeat=false) override;
|
bool injectKeyPress(MyGUI::KeyCode key, unsigned int text, bool repeat=false) override;
|
||||||
bool injectKeyRelease(MyGUI::KeyCode key) override;
|
bool injectKeyRelease(MyGUI::KeyCode key) override;
|
||||||
|
|
||||||
|
void viewerTraversals(bool updateWindowManager) override;
|
||||||
private:
|
private:
|
||||||
unsigned int mOldUpdateMask; unsigned int mOldCullMask;
|
unsigned int mOldUpdateMask; unsigned int mOldCullMask;
|
||||||
|
|
||||||
const MWWorld::ESMStore* mStore;
|
const MWWorld::ESMStore* mStore;
|
||||||
|
bool mVRMode;
|
||||||
Resource::ResourceSystem* mResourceSystem;
|
Resource::ResourceSystem* mResourceSystem;
|
||||||
osg::ref_ptr<SceneUtil::WorkQueue> mWorkQueue;
|
osg::ref_ptr<SceneUtil::WorkQueue> mWorkQueue;
|
||||||
|
|
||||||
|
@ -535,6 +544,9 @@ namespace MWGui
|
||||||
ScreenFader* mScreenFader;
|
ScreenFader* mScreenFader;
|
||||||
DebugWindow* mDebugWindow;
|
DebugWindow* mDebugWindow;
|
||||||
JailScreen* mJailScreen;
|
JailScreen* mJailScreen;
|
||||||
|
MWVR::VrMetaMenu* mVrMetaMenu;
|
||||||
|
|
||||||
|
Gui::VirtualKeyboardManager* mVirtualKeyboardManager;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Start of tes3mp addition
|
Start of tes3mp addition
|
||||||
|
@ -562,6 +574,7 @@ namespace MWGui
|
||||||
bool mHudEnabled;
|
bool mHudEnabled;
|
||||||
bool mCursorVisible;
|
bool mCursorVisible;
|
||||||
bool mCursorActive;
|
bool mCursorActive;
|
||||||
|
bool mVideoEnabled;
|
||||||
|
|
||||||
int mPlayerBounty;
|
int mPlayerBounty;
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
End of tes3mp addition
|
End of tes3mp addition
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <components/debug/debuglog.hpp>
|
||||||
|
|
||||||
#include "../mwbase/environment.hpp"
|
#include "../mwbase/environment.hpp"
|
||||||
#include "../mwbase/inputmanager.hpp"
|
#include "../mwbase/inputmanager.hpp"
|
||||||
#include "../mwbase/windowmanager.hpp"
|
#include "../mwbase/windowmanager.hpp"
|
||||||
|
@ -59,6 +61,12 @@ namespace MWInput
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ICS::InputControlSystem&
|
||||||
|
BindingsManager::ics()
|
||||||
|
{
|
||||||
|
return *mInputBinder;
|
||||||
|
}
|
||||||
|
|
||||||
class BindingsListener :
|
class BindingsListener :
|
||||||
public ICS::ChannelListener,
|
public ICS::ChannelListener,
|
||||||
public ICS::DetectingBindingListener
|
public ICS::DetectingBindingListener
|
||||||
|
|
|
@ -7,6 +7,11 @@
|
||||||
|
|
||||||
#include <components/sdlutil/events.hpp>
|
#include <components/sdlutil/events.hpp>
|
||||||
|
|
||||||
|
namespace ICS
|
||||||
|
{
|
||||||
|
class InputControlSystem;
|
||||||
|
}
|
||||||
|
|
||||||
namespace MWInput
|
namespace MWInput
|
||||||
{
|
{
|
||||||
class BindingsListener;
|
class BindingsListener;
|
||||||
|
@ -62,6 +67,8 @@ namespace MWInput
|
||||||
|
|
||||||
void actionValueChanged(int action, float currentValue, float previousValue);
|
void actionValueChanged(int action, float currentValue, float previousValue);
|
||||||
|
|
||||||
|
ICS::InputControlSystem& ics();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setupSDLKeyMappings();
|
void setupSDLKeyMappings();
|
||||||
|
|
||||||
|
|
|
@ -95,14 +95,16 @@ namespace MWInput
|
||||||
void executeAction(int action) override;
|
void executeAction(int action) override;
|
||||||
|
|
||||||
bool controlsDisabled() override { return mControlsDisabled; }
|
bool controlsDisabled() override { return mControlsDisabled; }
|
||||||
|
void applyHapticsLeftHand(float intensity) override {};
|
||||||
|
void applyHapticsRightHand(float intensity) override {};
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
void convertMousePosForMyGUI(int& x, int& y);
|
void convertMousePosForMyGUI(int& x, int& y);
|
||||||
|
|
||||||
void handleGuiArrowKey(int action);
|
void handleGuiArrowKey(int action);
|
||||||
|
|
||||||
void quickKey(int index);
|
//void quickKey(int index);
|
||||||
void showQuickKeysMenu();
|
//void showQuickKeysMenu();
|
||||||
|
|
||||||
void loadKeyDefaults(bool force = false);
|
void loadKeyDefaults(bool force = false);
|
||||||
void loadControllerDefaults(bool force = false);
|
void loadControllerDefaults(bool force = false);
|
||||||
|
|
|
@ -63,6 +63,9 @@ namespace MWInput
|
||||||
|
|
||||||
void MouseManager::mouseMoved(const SDLUtil::MouseMotionEvent &arg)
|
void MouseManager::mouseMoved(const SDLUtil::MouseMotionEvent &arg)
|
||||||
{
|
{
|
||||||
|
if (MWBase::Environment::get().getVrMode())
|
||||||
|
return;
|
||||||
|
|
||||||
mBindingsManager->mouseMoved(arg);
|
mBindingsManager->mouseMoved(arg);
|
||||||
|
|
||||||
MWBase::InputManager* input = MWBase::Environment::get().getInputManager();
|
MWBase::InputManager* input = MWBase::Environment::get().getInputManager();
|
||||||
|
@ -235,6 +238,8 @@ namespace MWInput
|
||||||
|
|
||||||
void MouseManager::injectMouseMove(float xMove, float yMove, float mouseWheelMove)
|
void MouseManager::injectMouseMove(float xMove, float yMove, float mouseWheelMove)
|
||||||
{
|
{
|
||||||
|
if (MWBase::Environment::get().getVrMode())
|
||||||
|
return;
|
||||||
mGuiCursorX += xMove;
|
mGuiCursorX += xMove;
|
||||||
mGuiCursorY += yMove;
|
mGuiCursorY += yMove;
|
||||||
mMouseWheel += mouseWheelMove;
|
mMouseWheel += mouseWheelMove;
|
||||||
|
@ -251,4 +256,11 @@ namespace MWInput
|
||||||
float uiScale = MWBase::Environment::get().getWindowManager()->getScalingFactor();
|
float uiScale = MWBase::Environment::get().getWindowManager()->getScalingFactor();
|
||||||
mInputWrapper->warpMouse(static_cast<int>(mGuiCursorX*uiScale), static_cast<int>(mGuiCursorY*uiScale));
|
mInputWrapper->warpMouse(static_cast<int>(mGuiCursorX*uiScale), static_cast<int>(mGuiCursorY*uiScale));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MouseManager::setMousePosition(int x, int y)
|
||||||
|
{
|
||||||
|
float uiScale = MWBase::Environment::get().getWindowManager()->getScalingFactor();
|
||||||
|
mGuiCursorX = x / uiScale;
|
||||||
|
mGuiCursorY = y / uiScale;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,9 @@ namespace MWInput
|
||||||
void setMouseLookEnabled(bool enabled) { mMouseLookEnabled = enabled; }
|
void setMouseLookEnabled(bool enabled) { mMouseLookEnabled = enabled; }
|
||||||
void setGuiCursorEnabled(bool enabled) { mGuiCursorEnabled = enabled; }
|
void setGuiCursorEnabled(bool enabled) { mGuiCursorEnabled = enabled; }
|
||||||
|
|
||||||
|
// Used to override mouse position when using controllers not through SDL, such as OpenXR.
|
||||||
|
void setMousePosition(int x, int y);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool mInvertX;
|
bool mInvertX;
|
||||||
bool mInvertY;
|
bool mInvertY;
|
||||||
|
|
|
@ -57,6 +57,7 @@
|
||||||
#include "../mwworld/inventorystore.hpp"
|
#include "../mwworld/inventorystore.hpp"
|
||||||
#include "../mwworld/esmstore.hpp"
|
#include "../mwworld/esmstore.hpp"
|
||||||
#include "../mwworld/player.hpp"
|
#include "../mwworld/player.hpp"
|
||||||
|
#include "../mwrender/npcanimation.hpp"
|
||||||
|
|
||||||
#include "aicombataction.hpp"
|
#include "aicombataction.hpp"
|
||||||
#include "movement.hpp"
|
#include "movement.hpp"
|
||||||
|
@ -66,6 +67,12 @@
|
||||||
#include "actorutil.hpp"
|
#include "actorutil.hpp"
|
||||||
#include "spellcasting.hpp"
|
#include "spellcasting.hpp"
|
||||||
|
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
#include "../mwvr/vrenvironment.hpp"
|
||||||
|
#include "../mwvr/vranimation.hpp"
|
||||||
|
#include "../mwvr/vrutil.hpp"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -1106,7 +1113,7 @@ void CharacterController::handleTextKey(const std::string &groupname, SceneUtil:
|
||||||
else if (groupname == "attack3" || groupname == "swimattack3")
|
else if (groupname == "attack3" || groupname == "swimattack3")
|
||||||
mPtr.getClass().hit(mPtr, mAttackStrength, ESM::Weapon::AT_Thrust);
|
mPtr.getClass().hit(mPtr, mAttackStrength, ESM::Weapon::AT_Thrust);
|
||||||
else
|
else
|
||||||
mPtr.getClass().hit(mPtr, mAttackStrength);
|
mPtr.getClass().hit(mPtr, mAttackStrength, -1);
|
||||||
}
|
}
|
||||||
else if (!groupname.empty()
|
else if (!groupname.empty()
|
||||||
&& (groupname.compare(0, groupname.size()-1, "attack") == 0 || groupname.compare(0, groupname.size()-1, "swimattack") == 0)
|
&& (groupname.compare(0, groupname.size()-1, "attack") == 0 || groupname.compare(0, groupname.size()-1, "swimattack") == 0)
|
||||||
|
@ -1720,9 +1727,13 @@ bool CharacterController::updateWeaponState(CharacterState& idle)
|
||||||
{
|
{
|
||||||
MWWorld::ContainerStoreIterator weapon = mPtr.getClass().getInventoryStore(mPtr).getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
|
MWWorld::ContainerStoreIterator weapon = mPtr.getClass().getInventoryStore(mPtr).getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
|
||||||
MWWorld::Ptr item = *weapon;
|
MWWorld::Ptr item = *weapon;
|
||||||
// TODO: this will only work for the player, and needs to be fixed if NPCs should ever use lockpicks/probes.
|
|
||||||
MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getFacedObject();
|
|
||||||
std::string resultMessage, resultSound;
|
std::string resultMessage, resultSound;
|
||||||
|
// TODO: this will only work for the player, and needs to be fixed if NPCs should ever use lockpicks/probes.
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
MWWorld::Ptr target = MWVR::Util::getWeaponTarget().first;
|
||||||
|
#else
|
||||||
|
MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getFacedObject();
|
||||||
|
#endif
|
||||||
|
|
||||||
if(!target.isEmpty())
|
if(!target.isEmpty())
|
||||||
{
|
{
|
||||||
|
@ -2663,6 +2674,18 @@ void CharacterController::update(float duration)
|
||||||
mSkipAnim = false;
|
mSkipAnim = false;
|
||||||
|
|
||||||
mAnimation->enableHeadAnimation(cls.isActor() && !cls.getCreatureStats(mPtr).isDead());
|
mAnimation->enableHeadAnimation(cls.isActor() && !cls.getCreatureStats(mPtr).isDead());
|
||||||
|
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
if (isPlayer)
|
||||||
|
{
|
||||||
|
auto disabled = MWBase::Environment::get().getWorld()->getPlayer().isDisabled();
|
||||||
|
auto animation = static_cast<MWRender::NpcAnimation*>(mAnimation);
|
||||||
|
if (disabled)
|
||||||
|
animation->setViewMode(MWRender::NpcAnimation::VM_VRNormal);
|
||||||
|
else
|
||||||
|
animation->setViewMode(MWRender::NpcAnimation::VM_VRFirstPerson);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void CharacterController::persistAnimationState()
|
void CharacterController::persistAnimationState()
|
||||||
|
|
|
@ -294,7 +294,7 @@ namespace MWMechanics
|
||||||
End of tes3mp addition
|
End of tes3mp addition
|
||||||
*/
|
*/
|
||||||
|
|
||||||
victim.getClass().onHit(victim, damage, false, projectile, attacker, osg::Vec3f(), false);
|
victim.getClass().onHit(victim, damage, false, projectile, attacker, osg::Vec3f(), false, attackStrength);
|
||||||
MWMechanics::reduceWeaponCondition(damage, false, weapon, attacker);
|
MWMechanics::reduceWeaponCondition(damage, false, weapon, attacker);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -354,7 +354,7 @@ namespace MWMechanics
|
||||||
victim.getClass().getContainerStore(victim).add(projectile, 1, victim);
|
victim.getClass().getContainerStore(victim).add(projectile, 1, victim);
|
||||||
}
|
}
|
||||||
|
|
||||||
victim.getClass().onHit(victim, damage, true, projectile, attacker, hitPosition, true);
|
victim.getClass().onHit(victim, damage, true, projectile, attacker, hitPosition, true, attackStrength);
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
Start of tes3mp addition
|
Start of tes3mp addition
|
||||||
|
|
|
@ -14,6 +14,15 @@
|
||||||
#include "../mwworld/esmstore.hpp"
|
#include "../mwworld/esmstore.hpp"
|
||||||
#include "../mwworld/refdata.hpp"
|
#include "../mwworld/refdata.hpp"
|
||||||
|
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
#include "../mwvr/vrsession.hpp"
|
||||||
|
#include "../mwvr/vrcamera.hpp"
|
||||||
|
#include "../mwvr/vrenvironment.hpp"
|
||||||
|
#include "../mwrender/renderingmanager.hpp"
|
||||||
|
#include "../mwworld/player.hpp"
|
||||||
|
#endif
|
||||||
|
#include "../mwmechanics/actorutil.hpp"
|
||||||
|
|
||||||
#include "actor.hpp"
|
#include "actor.hpp"
|
||||||
#include "collisiontype.hpp"
|
#include "collisiontype.hpp"
|
||||||
#include "constants.hpp"
|
#include "constants.hpp"
|
||||||
|
@ -119,7 +128,8 @@ namespace MWPhysics
|
||||||
WorldFrameData& worldData)
|
WorldFrameData& worldData)
|
||||||
{
|
{
|
||||||
auto* physicActor = actor.mActorRaw;
|
auto* physicActor = actor.mActorRaw;
|
||||||
const ESM::Position& refpos = actor.mRefpos;
|
ESM::Position refpos = actor.mRefpos;
|
||||||
|
|
||||||
// Early-out for totally static creatures
|
// Early-out for totally static creatures
|
||||||
// (Not sure if gravity should still apply?)
|
// (Not sure if gravity should still apply?)
|
||||||
{
|
{
|
||||||
|
@ -128,6 +138,36 @@ namespace MWPhysics
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const bool isPlayer = (physicActor->getPtr() == MWMechanics::getPlayer());
|
||||||
|
auto* world = MWBase::Environment::get().getWorld();
|
||||||
|
|
||||||
|
// In VR, player should move according to current direction of
|
||||||
|
// a selected limb, rather than current orientation of camera.
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
// Regarding this and the duplicate movement solver later in this method:
|
||||||
|
// As my two edits in this code are obviously hacks, I could use feedback on how i could implement
|
||||||
|
// VR movement mechanics as not-a-hack. This hack, for instance, does not trigger movement animations
|
||||||
|
// and will obviously be a poor fit for a future merge with tes3mp.
|
||||||
|
|
||||||
|
// The exact mechanics are:
|
||||||
|
// 1. When moving with the controller, the player moves in the direction he is currently pointing his left controller.
|
||||||
|
// 2. The game should seek to eliminate all distance between the player character and the player's position within VR,
|
||||||
|
// without teleporting the player or ignoring collisions.
|
||||||
|
|
||||||
|
// I assume (1.) is easily solved, i just haven't taken the effort to study openmw's code enough.
|
||||||
|
// But 2. is not so obvious. I guess it's doable if i compute the direction between current position and the player's
|
||||||
|
// position in the VR stage, and just let it catch up at the character's own move speed, but it still needs to reach the position as exactly as possible.
|
||||||
|
if (isPlayer)
|
||||||
|
{
|
||||||
|
auto tm = MWVR::Environment::get().getTrackingManager();
|
||||||
|
float pitch = 0.f;
|
||||||
|
float yaw = 0.f;
|
||||||
|
tm->movementAngles(yaw, pitch);
|
||||||
|
refpos.rot[0] += pitch;
|
||||||
|
refpos.rot[2] += yaw;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Reset per-frame data
|
// Reset per-frame data
|
||||||
physicActor->setWalkingOnWater(false);
|
physicActor->setWalkingOnWater(false);
|
||||||
// Anything to collide with?
|
// Anything to collide with?
|
||||||
|
@ -148,7 +188,7 @@ namespace MWPhysics
|
||||||
osg::Vec3f halfExtents = physicActor->getHalfExtents();
|
osg::Vec3f halfExtents = physicActor->getHalfExtents();
|
||||||
actor.mPosition.z() += halfExtents.z(); // vanilla-accurate
|
actor.mPosition.z() += halfExtents.z(); // vanilla-accurate
|
||||||
|
|
||||||
static const float fSwimHeightScale = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fSwimHeightScale")->mValue.getFloat();
|
static const float fSwimHeightScale = world->getStore().get<ESM::GameSetting>().find("fSwimHeightScale")->mValue.getFloat();
|
||||||
float swimlevel = actor.mWaterlevel + halfExtents.z() - (physicActor->getRenderingHalfExtents().z() * 2 * fSwimHeightScale);
|
float swimlevel = actor.mWaterlevel + halfExtents.z() - (physicActor->getRenderingHalfExtents().z() * 2 * fSwimHeightScale);
|
||||||
|
|
||||||
ActorTracer tracer;
|
ActorTracer tracer;
|
||||||
|
@ -184,13 +224,103 @@ namespace MWPhysics
|
||||||
{
|
{
|
||||||
osg::Vec3f stormDirection = worldData.mStormDirection;
|
osg::Vec3f stormDirection = worldData.mStormDirection;
|
||||||
float angleDegrees = osg::RadiansToDegrees(std::acos(stormDirection * velocity / (stormDirection.length() * velocity.length())));
|
float angleDegrees = osg::RadiansToDegrees(std::acos(stormDirection * velocity / (stormDirection.length() * velocity.length())));
|
||||||
static const float fStromWalkMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fStromWalkMult")->mValue.getFloat();
|
static const float fStromWalkMult = world->getStore().get<ESM::GameSetting>().find("fStromWalkMult")->mValue.getFloat();
|
||||||
velocity *= 1.f-(fStromWalkMult * (angleDegrees/180.f));
|
velocity *= 1.f-(fStromWalkMult * (angleDegrees/180.f));
|
||||||
}
|
}
|
||||||
|
|
||||||
Stepper stepper(collisionWorld, colobj);
|
Stepper stepper(collisionWorld, colobj);
|
||||||
osg::Vec3f origVelocity = velocity;
|
osg::Vec3f origVelocity = velocity;
|
||||||
osg::Vec3f newPosition = actor.mPosition;
|
osg::Vec3f newPosition = actor.mPosition;
|
||||||
|
|
||||||
|
//#ifdef USE_OPENXR
|
||||||
|
// // Catch the player character up to the real world position of the player.
|
||||||
|
// // But only if play is not seated.
|
||||||
|
// // TODO: This solution is a hack.
|
||||||
|
// if (isPlayer)
|
||||||
|
// {
|
||||||
|
// bool shouldMove = true;
|
||||||
|
// if (session && session->seatedPlay())
|
||||||
|
// shouldMove = false;
|
||||||
|
// if (world->getPlayer().isDisabled())
|
||||||
|
// shouldMove = false;
|
||||||
|
//
|
||||||
|
// if (shouldMove)
|
||||||
|
// {
|
||||||
|
// auto* inputManager = reinterpret_cast<MWVR::VRCamera*>(MWBase::Environment::get().getWorld()->getRenderingManager().getCamera());
|
||||||
|
//
|
||||||
|
// osg::Vec3 headOffset = inputManager->headOffset();
|
||||||
|
// osg::Vec3 trackingOffset = headOffset;
|
||||||
|
// // Player's tracking height should not affect character position
|
||||||
|
// trackingOffset.z() = 0;
|
||||||
|
//
|
||||||
|
// float remainingTime = time;
|
||||||
|
// bool seenGround = physicActor->getOnGround() && !physicActor->getOnSlope() && !actor.mFlying;
|
||||||
|
// float remainder = 1.f;
|
||||||
|
//
|
||||||
|
// for (int iterations = 0; iterations < sMaxIterations && remainingTime > 0.01f && remainder > 0.01; ++iterations)
|
||||||
|
// {
|
||||||
|
// osg::Vec3 toMove = trackingOffset * remainder;
|
||||||
|
// osg::Vec3 nextpos = newPosition + toMove;
|
||||||
|
//
|
||||||
|
// if ((newPosition - nextpos).length2() > 0.0001)
|
||||||
|
// {
|
||||||
|
// // trace to where character would go if there were no obstructions
|
||||||
|
// tracer.doTrace(colobj, newPosition, nextpos, collisionWorld);
|
||||||
|
//
|
||||||
|
// // check for obstructions
|
||||||
|
// if (tracer.mFraction >= 1.0f)
|
||||||
|
// {
|
||||||
|
// newPosition = tracer.mEndPos; // ok to move, so set newPosition
|
||||||
|
// remainder = 0.f;
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// // The current position and next position are nearly the same, so just exit.
|
||||||
|
// // Note: Bullet can trigger an assert in debug modes if the positions
|
||||||
|
// // are the same, since that causes it to attempt to normalize a zero
|
||||||
|
// // length vector (which can also happen with nearly identical vectors, since
|
||||||
|
// // precision can be lost due to any math Bullet does internally). Since we
|
||||||
|
// // aren't performing any collision detection, we want to reject the next
|
||||||
|
// // position, so that we don't slowly move inside another object.
|
||||||
|
// remainder = 0.f;
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (isWalkableSlope(tracer.mPlaneNormal) && !actor.mFlying && newPosition.z() >= swimlevel)
|
||||||
|
// seenGround = true;
|
||||||
|
//
|
||||||
|
// // We are touching something.
|
||||||
|
// if (tracer.mFraction < 1E-9f)
|
||||||
|
// {
|
||||||
|
// // Try to separate by backing off slighly to unstuck the solver
|
||||||
|
// osg::Vec3f backOff = (newPosition - tracer.mHitPoint) * 1E-2f;
|
||||||
|
// newPosition += backOff;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // We hit something. Check if we can step up.
|
||||||
|
// float hitHeight = tracer.mHitPoint.z() - tracer.mEndPos.z() + halfExtents.z();
|
||||||
|
// osg::Vec3f oldPosition = newPosition;
|
||||||
|
// bool result = false;
|
||||||
|
// if (hitHeight < sStepSizeUp && !isActor(tracer.mHitObject))
|
||||||
|
// {
|
||||||
|
// // Try to step up onto it.
|
||||||
|
// // NOTE: stepMove does not allow stepping over, modifies newPosition if successful
|
||||||
|
// result = stepper.step(newPosition, toMove, remainingTime, seenGround, iterations == 0);
|
||||||
|
// remainder = remainingTime / time;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Try not to lose any tracking
|
||||||
|
// osg::Vec3 moved = newPosition - actor.mPosition;
|
||||||
|
// headOffset.x() -= moved.x();
|
||||||
|
// headOffset.y() -= moved.y();
|
||||||
|
// inputManager->setHeadOffset(headOffset);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* A loop to find newPosition using tracer, if successful different from the starting position.
|
* A loop to find newPosition using tracer, if successful different from the starting position.
|
||||||
* nextpos is the local variable used to find potential newPosition, using velocity and remainingTime
|
* nextpos is the local variable used to find potential newPosition, using velocity and remainingTime
|
||||||
|
|
|
@ -42,6 +42,7 @@
|
||||||
#include "../mwworld/cellstore.hpp"
|
#include "../mwworld/cellstore.hpp"
|
||||||
|
|
||||||
#include "../mwmechanics/character.hpp" // FIXME: for MWMechanics::Priority
|
#include "../mwmechanics/character.hpp" // FIXME: for MWMechanics::Priority
|
||||||
|
#include "../mwmechanics/actorutil.hpp"
|
||||||
|
|
||||||
#include "vismask.hpp"
|
#include "vismask.hpp"
|
||||||
#include "util.hpp"
|
#include "util.hpp"
|
||||||
|
@ -1052,8 +1053,56 @@ namespace MWRender
|
||||||
return mNodeMap;
|
return mNodeMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool vrOverride(const std::string& groupname, const std::string& bone)
|
||||||
|
{
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
// TODO: It's difficult to design a good override system when
|
||||||
|
// I don't have a good understanding of the animation code. So for
|
||||||
|
// now i just hardcode blocking of updaters for nodes that should not be animated in VR.
|
||||||
|
// Add any bone+groupname pair that is messing with Vr comfort here.
|
||||||
|
using Overrides = std::set<std::string>;
|
||||||
|
using GroupOverrides = std::map<std::string, Overrides>;
|
||||||
|
static GroupOverrides sVrOverrides =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
"crossbow",
|
||||||
|
{
|
||||||
|
"weapon bone"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"throwweapon",
|
||||||
|
{
|
||||||
|
"weapon bone"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bowandarrow",
|
||||||
|
{
|
||||||
|
"weapon bone"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
bool override = false;
|
||||||
|
auto find = sVrOverrides.find(groupname);
|
||||||
|
if (find != sVrOverrides.end())
|
||||||
|
{
|
||||||
|
override = !!find->second.count(bone);
|
||||||
|
}
|
||||||
|
|
||||||
|
return override;
|
||||||
|
#else
|
||||||
|
(void)bone;
|
||||||
|
(void)groupname;
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void Animation::resetActiveGroups()
|
void Animation::resetActiveGroups()
|
||||||
{
|
{
|
||||||
|
const bool isPlayer = (mPtr == MWMechanics::getPlayer());
|
||||||
|
|
||||||
// remove all previous external controllers from the scene graph
|
// remove all previous external controllers from the scene graph
|
||||||
for (auto it = mActiveControllers.begin(); it != mActiveControllers.end(); ++it)
|
for (auto it = mActiveControllers.begin(); it != mActiveControllers.end(); ++it)
|
||||||
{
|
{
|
||||||
|
@ -1092,11 +1141,16 @@ namespace MWRender
|
||||||
for (AnimSource::ControllerMap::iterator it = animsrc->mControllerMap[blendMask].begin(); it != animsrc->mControllerMap[blendMask].end(); ++it)
|
for (AnimSource::ControllerMap::iterator it = animsrc->mControllerMap[blendMask].begin(); it != animsrc->mControllerMap[blendMask].end(); ++it)
|
||||||
{
|
{
|
||||||
osg::ref_ptr<osg::Node> node = getNodeMap().at(it->first); // this should not throw, we already checked for the node existing in addAnimSource
|
osg::ref_ptr<osg::Node> node = getNodeMap().at(it->first); // this should not throw, we already checked for the node existing in addAnimSource
|
||||||
|
if(!isPlayer || !vrOverride(active->first, it->first))
|
||||||
node->addUpdateCallback(it->second);
|
node->addUpdateCallback(it->second);
|
||||||
mActiveControllers.emplace_back(node, it->second);
|
mActiveControllers.emplace_back(node, it->second);
|
||||||
|
|
||||||
if (blendMask == 0 && node == mAccumRoot)
|
if (blendMask == 0 && node == mAccumRoot
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
// TODO: Little hack to keep certain animations from wobbling the camera in VR
|
||||||
|
&& (!isPlayer)
|
||||||
|
#endif
|
||||||
|
)
|
||||||
{
|
{
|
||||||
mAccumCtrl = it->second;
|
mAccumCtrl = it->second;
|
||||||
|
|
||||||
|
|
|
@ -448,7 +448,7 @@ public:
|
||||||
void disable(const std::string &groupname);
|
void disable(const std::string &groupname);
|
||||||
|
|
||||||
/** Retrieves the velocity (in units per second) that the animation will move. */
|
/** Retrieves the velocity (in units per second) that the animation will move. */
|
||||||
float getVelocity(const std::string &groupname) const;
|
virtual float getVelocity(const std::string &groupname) const;
|
||||||
|
|
||||||
virtual osg::Vec3f runAnimation(float duration);
|
virtual osg::Vec3f runAnimation(float duration);
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include <components/misc/mathutil.hpp>
|
#include <components/misc/mathutil.hpp>
|
||||||
#include <components/sceneutil/positionattitudetransform.hpp>
|
#include <components/sceneutil/positionattitudetransform.hpp>
|
||||||
#include <components/settings/settings.hpp>
|
#include <components/settings/settings.hpp>
|
||||||
|
#include <components/debug/debuglog.hpp>
|
||||||
|
|
||||||
#include "../mwbase/environment.hpp"
|
#include "../mwbase/environment.hpp"
|
||||||
#include "../mwbase/windowmanager.hpp"
|
#include "../mwbase/windowmanager.hpp"
|
||||||
|
@ -126,6 +127,16 @@ namespace MWRender
|
||||||
return position;
|
return position;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
osg::Camera* Camera::getOsgCamera()
|
||||||
|
{
|
||||||
|
return mCamera;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Camera::updateCamera()
|
||||||
|
{
|
||||||
|
updateCamera(mCamera);
|
||||||
|
}
|
||||||
|
|
||||||
osg::Vec3d Camera::getFocalPointOffset() const
|
osg::Vec3d Camera::getFocalPointOffset() const
|
||||||
{
|
{
|
||||||
osg::Vec3d offset(0, 0, 10.f);
|
osg::Vec3d offset(0, 0, 10.f);
|
||||||
|
@ -147,12 +158,19 @@ namespace MWRender
|
||||||
camera = focal + offset;
|
camera = focal + offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Camera::getOrientation(osg::Quat& orientation) const
|
||||||
|
{
|
||||||
|
orientation = osg::Quat(mRoll, osg::Vec3d(0, 1, 0)) * osg::Quat(mPitch, osg::Vec3d(1, 0, 0)) * osg::Quat(mYaw, osg::Vec3d(0, 0, 1));
|
||||||
|
}
|
||||||
|
|
||||||
void Camera::updateCamera(osg::Camera *cam)
|
void Camera::updateCamera(osg::Camera *cam)
|
||||||
{
|
{
|
||||||
osg::Vec3d focal, position;
|
osg::Vec3d focal, position;
|
||||||
getPosition(focal, position);
|
getPosition(focal, position);
|
||||||
|
|
||||||
osg::Quat orient = osg::Quat(mRoll, osg::Vec3d(0, 1, 0)) * osg::Quat(mPitch, osg::Vec3d(1, 0, 0)) * osg::Quat(mYaw, osg::Vec3d(0, 0, 1));
|
osg::Quat orient;
|
||||||
|
getOrientation(orient);
|
||||||
|
|
||||||
osg::Vec3d forward = orient * osg::Vec3d(0,1,0);
|
osg::Vec3d forward = orient * osg::Vec3d(0,1,0);
|
||||||
osg::Vec3d up = orient * osg::Vec3d(0,0,1);
|
osg::Vec3d up = orient * osg::Vec3d(0,0,1);
|
||||||
|
|
||||||
|
@ -185,8 +203,10 @@ namespace MWRender
|
||||||
toggleViewMode();
|
toggleViewMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Camera::rotateCamera(float pitch, float yaw, bool adjust)
|
void Camera::rotateCamera(float pitch, float roll, float yaw, bool adjust)
|
||||||
{
|
{
|
||||||
|
(void)roll;
|
||||||
|
|
||||||
if (adjust)
|
if (adjust)
|
||||||
{
|
{
|
||||||
pitch += getPitch();
|
pitch += getPitch();
|
||||||
|
@ -223,7 +243,7 @@ namespace MWRender
|
||||||
&& (mFirstPersonView || mShowCrosshairInThirdPersonMode));
|
&& (mFirstPersonView || mShowCrosshairInThirdPersonMode));
|
||||||
|
|
||||||
if(mMode == Mode::Vanity)
|
if(mMode == Mode::Vanity)
|
||||||
rotateCamera(0.f, osg::DegreesToRadians(3.f * duration), true);
|
rotateCamera(0.f, 0.f, osg::DegreesToRadians(3.f * duration), true);
|
||||||
|
|
||||||
if (isFirstPerson() && mHeadBobbingEnabled)
|
if (isFirstPerson() && mHeadBobbingEnabled)
|
||||||
updateHeadBobbing(duration);
|
updateHeadBobbing(duration);
|
||||||
|
@ -503,7 +523,7 @@ namespace MWRender
|
||||||
else
|
else
|
||||||
mHeightScale = 1.f;
|
mHeightScale = 1.f;
|
||||||
}
|
}
|
||||||
rotateCamera(getPitch(), getYaw(), false);
|
rotateCamera(getPitch(), 0.f, getYaw(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Camera::applyDeferredPreviewRotationToPlayer(float dt)
|
void Camera::applyDeferredPreviewRotationToPlayer(float dt)
|
||||||
|
|
|
@ -14,6 +14,7 @@ namespace osg
|
||||||
class Camera;
|
class Camera;
|
||||||
class NodeCallback;
|
class NodeCallback;
|
||||||
class Node;
|
class Node;
|
||||||
|
class Quat;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace MWRender
|
namespace MWRender
|
||||||
|
@ -26,7 +27,7 @@ namespace MWRender
|
||||||
public:
|
public:
|
||||||
enum class Mode { Normal, Vanity, Preview, StandingPreview };
|
enum class Mode { Normal, Vanity, Preview, StandingPreview };
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
MWWorld::Ptr mTrackingPtr;
|
MWWorld::Ptr mTrackingPtr;
|
||||||
osg::ref_ptr<const osg::Node> mTrackingNode;
|
osg::ref_ptr<const osg::Node> mTrackingNode;
|
||||||
float mHeightScale;
|
float mHeightScale;
|
||||||
|
@ -92,7 +93,7 @@ namespace MWRender
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Camera(osg::Camera* camera);
|
Camera(osg::Camera* camera);
|
||||||
~Camera();
|
virtual ~Camera();
|
||||||
|
|
||||||
/// Attach camera to object
|
/// Attach camera to object
|
||||||
void attachTo(const MWWorld::Ptr &ptr) { mTrackingPtr = ptr; }
|
void attachTo(const MWWorld::Ptr &ptr) { mTrackingPtr = ptr; }
|
||||||
|
@ -100,20 +101,23 @@ namespace MWRender
|
||||||
|
|
||||||
void setFocalPointTransitionSpeed(float v) { mFocalPointTransitionSpeedCoef = v; }
|
void setFocalPointTransitionSpeed(float v) { mFocalPointTransitionSpeedCoef = v; }
|
||||||
void setFocalPointTargetOffset(osg::Vec2d v);
|
void setFocalPointTargetOffset(osg::Vec2d v);
|
||||||
void instantTransition();
|
virtual void instantTransition();
|
||||||
void enableDynamicCameraDistance(bool v) { mDynamicCameraDistanceEnabled = v; }
|
void enableDynamicCameraDistance(bool v) { mDynamicCameraDistanceEnabled = v; }
|
||||||
void enableCrosshairInThirdPersonMode(bool v) { mShowCrosshairInThirdPersonMode = v; }
|
void enableCrosshairInThirdPersonMode(bool v) { mShowCrosshairInThirdPersonMode = v; }
|
||||||
|
|
||||||
/// Update the view matrix of \a cam
|
/// Update the view matrix of \a cam
|
||||||
void updateCamera(osg::Camera* cam);
|
virtual void updateCamera(osg::Camera* cam);
|
||||||
|
|
||||||
|
/// Update the view matrix of the current camera
|
||||||
|
virtual void updateCamera();
|
||||||
|
|
||||||
/// Reset to defaults
|
/// Reset to defaults
|
||||||
void reset();
|
virtual void reset();
|
||||||
|
|
||||||
/// Set where the camera is looking at. Uses Morrowind (euler) angles
|
/// Set where the camera is looking at. Uses Morrowind (euler) angles
|
||||||
/// \param rot Rotation angles in radians
|
/// \param rot Rotation angles in radians
|
||||||
void rotateCamera(float pitch, float yaw, bool adjust);
|
virtual void rotateCamera(float pitch, float roll, float yaw, bool adjust);
|
||||||
void rotateCameraToTrackingPtr();
|
virtual void rotateCameraToTrackingPtr();
|
||||||
|
|
||||||
float getYaw() const { return mYaw; }
|
float getYaw() const { return mYaw; }
|
||||||
void setYaw(float angle);
|
void setYaw(float angle);
|
||||||
|
@ -122,10 +126,10 @@ namespace MWRender
|
||||||
void setPitch(float angle);
|
void setPitch(float angle);
|
||||||
|
|
||||||
/// @param Force view mode switch, even if currently not allowed by the animation.
|
/// @param Force view mode switch, even if currently not allowed by the animation.
|
||||||
void toggleViewMode(bool force=false);
|
virtual void toggleViewMode(bool force=false);
|
||||||
|
|
||||||
bool toggleVanityMode(bool enable);
|
virtual bool toggleVanityMode(bool enable);
|
||||||
void allowVanityMode(bool allow);
|
virtual void allowVanityMode(bool allow);
|
||||||
|
|
||||||
/// @note this may be ignored if an important animation is currently playing
|
/// @note this may be ignored if an important animation is currently playing
|
||||||
void togglePreviewMode(bool enable);
|
void togglePreviewMode(bool enable);
|
||||||
|
@ -138,7 +142,7 @@ namespace MWRender
|
||||||
|
|
||||||
bool isFirstPerson() const { return mFirstPersonView && mMode == Mode::Normal; }
|
bool isFirstPerson() const { return mFirstPersonView && mMode == Mode::Normal; }
|
||||||
|
|
||||||
void processViewChange();
|
virtual void processViewChange();
|
||||||
|
|
||||||
void update(float duration, bool paused=false);
|
void update(float duration, bool paused=false);
|
||||||
|
|
||||||
|
@ -149,11 +153,16 @@ namespace MWRender
|
||||||
|
|
||||||
void setAnimation(NpcAnimation *anim);
|
void setAnimation(NpcAnimation *anim);
|
||||||
|
|
||||||
|
osg::Camera* getOsgCamera();
|
||||||
|
|
||||||
osg::Vec3d getFocalPoint() const;
|
osg::Vec3d getFocalPoint() const;
|
||||||
osg::Vec3d getFocalPointOffset() const;
|
osg::Vec3d getFocalPointOffset() const;
|
||||||
|
|
||||||
/// Stores focal and camera world positions in passed arguments
|
/// Stores focal and camera world positions in passed arguments
|
||||||
void getPosition(osg::Vec3d &focal, osg::Vec3d &camera) const;
|
virtual void getPosition(osg::Vec3d &focal, osg::Vec3d &camera) const;
|
||||||
|
|
||||||
|
/// Store camera orientation in passed arguments
|
||||||
|
virtual void getOrientation(osg::Quat& orientation) const;
|
||||||
|
|
||||||
bool isVanityOrPreviewModeEnabled() const { return mMode != Mode::Normal; }
|
bool isVanityOrPreviewModeEnabled() const { return mMode != Mode::Normal; }
|
||||||
Mode getMode() const { return mMode; }
|
Mode getMode() const { return mMode; }
|
||||||
|
|
|
@ -959,7 +959,7 @@ void NpcAnimation::addControllers()
|
||||||
mActiveControllers.emplace_back(node, mFirstPersonNeckController);
|
mActiveControllers.emplace_back(node, mFirstPersonNeckController);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (mViewMode == VM_Normal)
|
else if (mViewMode != VM_HeadOnly)
|
||||||
{
|
{
|
||||||
WeaponAnimation::addControllers(mNodeMap, mActiveControllers, mObjectRoot.get());
|
WeaponAnimation::addControllers(mNodeMap, mActiveControllers, mObjectRoot.get());
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,10 @@ namespace MWRender
|
||||||
class NeckController;
|
class NeckController;
|
||||||
class HeadAnimationTime;
|
class HeadAnimationTime;
|
||||||
|
|
||||||
class NpcAnimation : public ActorAnimation, public WeaponAnimation, public MWWorld::InventoryStoreListener
|
class NpcAnimation :
|
||||||
|
public ActorAnimation
|
||||||
|
, public WeaponAnimation
|
||||||
|
, public MWWorld::InventoryStoreListener
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void equipmentChanged() override;
|
void equipmentChanged() override;
|
||||||
|
@ -39,10 +42,12 @@ public:
|
||||||
enum ViewMode {
|
enum ViewMode {
|
||||||
VM_Normal,
|
VM_Normal,
|
||||||
VM_FirstPerson,
|
VM_FirstPerson,
|
||||||
VM_HeadOnly
|
VM_HeadOnly,
|
||||||
|
VM_VRNormal,
|
||||||
|
VM_VRFirstPerson,
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
static const PartBoneMap sPartList;
|
static const PartBoneMap sPartList;
|
||||||
|
|
||||||
// Bounded Parts
|
// Bounded Parts
|
||||||
|
@ -152,9 +157,9 @@ public:
|
||||||
// WeaponAnimation
|
// WeaponAnimation
|
||||||
void showWeapon(bool show) override { showWeapons(show); }
|
void showWeapon(bool show) override { showWeapons(show); }
|
||||||
|
|
||||||
void setViewMode(ViewMode viewMode);
|
virtual void setViewMode(ViewMode viewMode);
|
||||||
|
|
||||||
void updateParts();
|
virtual void updateParts();
|
||||||
|
|
||||||
/// Rebuilds the NPC, updating their root model, animation sources, and equipment.
|
/// Rebuilds the NPC, updating their root model, animation sources, and equipment.
|
||||||
void rebuild();
|
void rebuild();
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include <osg/Group>
|
#include <osg/Group>
|
||||||
#include <osg/UserDataContainer>
|
#include <osg/UserDataContainer>
|
||||||
#include <osg/ComputeBoundsVisitor>
|
#include <osg/ComputeBoundsVisitor>
|
||||||
|
#include <osg/ViewportIndexed>
|
||||||
|
|
||||||
#include <osgUtil/LineSegmentIntersector>
|
#include <osgUtil/LineSegmentIntersector>
|
||||||
|
|
||||||
|
@ -31,6 +32,7 @@
|
||||||
#include <components/settings/settings.hpp>
|
#include <components/settings/settings.hpp>
|
||||||
|
|
||||||
#include <components/sceneutil/util.hpp>
|
#include <components/sceneutil/util.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>
|
||||||
|
@ -69,6 +71,15 @@
|
||||||
#include "screenshotmanager.hpp"
|
#include "screenshotmanager.hpp"
|
||||||
#include "groundcover.hpp"
|
#include "groundcover.hpp"
|
||||||
|
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
#include "../mwvr/vranimation.hpp"
|
||||||
|
#include "../mwvr/vrpointer.hpp"
|
||||||
|
#include "../mwvr/vrviewer.hpp"
|
||||||
|
#include "../mwvr/vrenvironment.hpp"
|
||||||
|
#include "../mwvr/vrcamera.hpp"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
namespace MWRender
|
namespace MWRender
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -184,7 +195,7 @@ namespace MWRender
|
||||||
Resource::ResourceSystem* mResourceSystem;
|
Resource::ResourceSystem* mResourceSystem;
|
||||||
};
|
};
|
||||||
|
|
||||||
RenderingManager::RenderingManager(osgViewer::Viewer* viewer, osg::ref_ptr<osg::Group> rootNode,
|
RenderingManager::RenderingManager(osgViewer::Viewer* viewer, osg::ref_ptr<osg::Group> rootNode, std::unique_ptr<Camera> camera,
|
||||||
Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue,
|
Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue,
|
||||||
const std::string& resourcePath, DetourNavigator::Navigator& navigator)
|
const std::string& resourcePath, DetourNavigator::Navigator& navigator)
|
||||||
: mViewer(viewer)
|
: mViewer(viewer)
|
||||||
|
@ -193,6 +204,9 @@ namespace MWRender
|
||||||
, mWorkQueue(workQueue)
|
, mWorkQueue(workQueue)
|
||||||
, mUnrefQueue(new SceneUtil::UnrefQueue)
|
, mUnrefQueue(new SceneUtil::UnrefQueue)
|
||||||
, mNavigator(navigator)
|
, mNavigator(navigator)
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
, mUserPointer(new MWVR::UserPointer(rootNode))
|
||||||
|
#endif
|
||||||
, mMinimumAmbientLuminance(0.f)
|
, mMinimumAmbientLuminance(0.f)
|
||||||
, mNightEyeFactor(0.f)
|
, mNightEyeFactor(0.f)
|
||||||
, mFieldOfViewOverridden(false)
|
, mFieldOfViewOverridden(false)
|
||||||
|
@ -207,7 +221,8 @@ namespace MWRender
|
||||||
|| 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;
|
||||||
resourceSystem->getSceneManager()->setForceShaders(forceShaders);
|
//resourceSystem->getSceneManager()->setForceShaders(forceShaders);
|
||||||
|
resourceSystem->getSceneManager()->setForceShaders(true);
|
||||||
// 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"));
|
||||||
|
@ -356,8 +371,8 @@ namespace MWRender
|
||||||
}
|
}
|
||||||
// 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 = std::move(camera);
|
||||||
|
|
||||||
mCamera.reset(new Camera(mViewer->getCamera()));
|
|
||||||
if (Settings::Manager::getBool("view over shoulder", "Camera"))
|
if (Settings::Manager::getBool("view over shoulder", "Camera"))
|
||||||
mViewOverShoulderController.reset(new ViewOverShoulderController(mCamera.get()));
|
mViewOverShoulderController.reset(new ViewOverShoulderController(mCamera.get()));
|
||||||
|
|
||||||
|
@ -411,7 +426,10 @@ 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);
|
||||||
|
mViewer->getCamera()->setCullMask(mask);
|
||||||
|
mViewer->getCamera()->setCullMaskLeft(mask);
|
||||||
|
mViewer->getCamera()->setCullMaskRight(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"));
|
||||||
|
@ -424,6 +442,7 @@ namespace MWRender
|
||||||
mFirstPersonFieldOfView = std::min(std::max(1.f, firstPersonFov), 179.f);
|
mFirstPersonFieldOfView = std::min(std::max(1.f, firstPersonFov), 179.f);
|
||||||
mStateUpdater->setFogEnd(mViewDistance);
|
mStateUpdater->setFogEnd(mViewDistance);
|
||||||
|
|
||||||
|
////// Near far uniforms
|
||||||
mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("near", mNearClip));
|
mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("near", mNearClip));
|
||||||
mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("far", mViewDistance));
|
mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("far", mViewDistance));
|
||||||
mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("simpleWater", false));
|
mRootNode->getOrCreateStateSet()->addUniform(new osg::Uniform("simpleWater", false));
|
||||||
|
@ -435,6 +454,7 @@ namespace MWRender
|
||||||
|
|
||||||
mUniformNear = mRootNode->getOrCreateStateSet()->getUniform("near");
|
mUniformNear = mRootNode->getOrCreateStateSet()->getUniform("near");
|
||||||
mUniformFar = mRootNode->getOrCreateStateSet()->getUniform("far");
|
mUniformFar = mRootNode->getOrCreateStateSet()->getUniform("far");
|
||||||
|
|
||||||
updateProjectionMatrix();
|
updateProjectionMatrix();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -669,6 +689,8 @@ namespace MWRender
|
||||||
else
|
else
|
||||||
mask &= ~Mask_Scene;
|
mask &= ~Mask_Scene;
|
||||||
mViewer->getCamera()->setCullMask(mask);
|
mViewer->getCamera()->setCullMask(mask);
|
||||||
|
mViewer->getCamera()->setCullMaskLeft(mask);
|
||||||
|
mViewer->getCamera()->setCullMaskRight(mask);
|
||||||
return enabled;
|
return enabled;
|
||||||
}
|
}
|
||||||
else if (mode == Render_NavMesh)
|
else if (mode == Render_NavMesh)
|
||||||
|
@ -857,21 +879,26 @@ namespace MWRender
|
||||||
return osg::Vec4f(min_x, min_y, max_x, max_y);
|
return osg::Vec4f(min_x, min_y, max_x, max_y);
|
||||||
}
|
}
|
||||||
|
|
||||||
RenderingManager::RayResult getIntersectionResult (osgUtil::LineSegmentIntersector* intersector)
|
RayResult getIntersectionResult (osgUtil::LineSegmentIntersector* intersector)
|
||||||
{
|
{
|
||||||
RenderingManager::RayResult result;
|
RayResult result;
|
||||||
result.mHit = false;
|
result.mHit = false;
|
||||||
result.mHitRefnum.unset();
|
result.mHitRefnum.unset();
|
||||||
result.mRatio = 0;
|
result.mRatio = 0;
|
||||||
|
result.mHitNode = nullptr;
|
||||||
if (intersector->containsIntersections())
|
if (intersector->containsIntersections())
|
||||||
{
|
{
|
||||||
result.mHit = true;
|
result.mHit = true;
|
||||||
osgUtil::LineSegmentIntersector::Intersection intersection = intersector->getFirstIntersection();
|
osgUtil::LineSegmentIntersector::Intersection intersection = intersector->getFirstIntersection();
|
||||||
|
|
||||||
|
result.mHitPointLocal = intersection.getLocalIntersectPoint();
|
||||||
result.mHitPointWorld = intersection.getWorldIntersectPoint();
|
result.mHitPointWorld = intersection.getWorldIntersectPoint();
|
||||||
result.mHitNormalWorld = intersection.getWorldIntersectNormal();
|
result.mHitNormalWorld = intersection.getWorldIntersectNormal();
|
||||||
result.mRatio = intersection.ratio;
|
result.mRatio = intersection.ratio;
|
||||||
|
|
||||||
|
if(!intersection.nodePath.empty())
|
||||||
|
result.mHitNode = intersection.nodePath.back();
|
||||||
|
|
||||||
PtrHolder* ptrHolder = nullptr;
|
PtrHolder* ptrHolder = nullptr;
|
||||||
std::vector<RefnumMarker*> refnumMarkers;
|
std::vector<RefnumMarker*> refnumMarkers;
|
||||||
for (osg::NodePath::const_iterator it = intersection.nodePath.begin(); it != intersection.nodePath.end(); ++it)
|
for (osg::NodePath::const_iterator it = intersection.nodePath.begin(); it != intersection.nodePath.end(); ++it)
|
||||||
|
@ -920,15 +947,15 @@ namespace MWRender
|
||||||
unsigned int mask = ~0u;
|
unsigned int mask = ~0u;
|
||||||
mask &= ~(Mask_RenderToTexture|Mask_Sky|Mask_Debug|Mask_Effect|Mask_Water|Mask_SimpleWater|Mask_Groundcover);
|
mask &= ~(Mask_RenderToTexture|Mask_Sky|Mask_Debug|Mask_Effect|Mask_Water|Mask_SimpleWater|Mask_Groundcover);
|
||||||
if (ignorePlayer)
|
if (ignorePlayer)
|
||||||
mask &= ~(Mask_Player);
|
mask &= ~(Mask_Player|Mask_Pointer);
|
||||||
if (ignoreActors)
|
if (ignoreActors)
|
||||||
mask &= ~(Mask_Actor|Mask_Player);
|
mask &= ~(Mask_Actor|Mask_Player|Mask_Pointer);
|
||||||
|
|
||||||
mIntersectionVisitor->setTraversalMask(mask);
|
mIntersectionVisitor->setTraversalMask(mask);
|
||||||
return mIntersectionVisitor;
|
return mIntersectionVisitor;
|
||||||
}
|
}
|
||||||
|
|
||||||
RenderingManager::RayResult RenderingManager::castRay(const osg::Vec3f& origin, const osg::Vec3f& dest, bool ignorePlayer, bool ignoreActors)
|
RayResult RenderingManager::castRay(const osg::Vec3f& origin, const osg::Vec3f& dest, bool ignorePlayer, bool ignoreActors)
|
||||||
{
|
{
|
||||||
osg::ref_ptr<osgUtil::LineSegmentIntersector> intersector (new osgUtil::LineSegmentIntersector(osgUtil::LineSegmentIntersector::MODEL,
|
osg::ref_ptr<osgUtil::LineSegmentIntersector> intersector (new osgUtil::LineSegmentIntersector(osgUtil::LineSegmentIntersector::MODEL,
|
||||||
origin, dest));
|
origin, dest));
|
||||||
|
@ -939,7 +966,25 @@ namespace MWRender
|
||||||
return getIntersectionResult(intersector);
|
return getIntersectionResult(intersector);
|
||||||
}
|
}
|
||||||
|
|
||||||
RenderingManager::RayResult RenderingManager::castCameraToViewportRay(const float nX, const float nY, float maxDistance, bool ignorePlayer, bool ignoreActors)
|
RayResult RenderingManager::castRay(const osg::Transform* source, float maxDistance, bool ignorePlayer, bool ignoreActors)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (source)
|
||||||
|
{
|
||||||
|
osg::Matrix worldMatrix = osg::computeLocalToWorld(source->getParentalNodePaths()[0]);
|
||||||
|
|
||||||
|
osg::Vec3f direction = worldMatrix.getRotate() * osg::Vec3f(0, 1, 0);
|
||||||
|
direction.normalize();
|
||||||
|
|
||||||
|
osg::Vec3f raySource = worldMatrix.getTrans();
|
||||||
|
osg::Vec3f rayTarget = worldMatrix.getTrans() + direction * maxDistance;
|
||||||
|
|
||||||
|
return castRay(raySource, rayTarget, ignorePlayer, ignoreActors);
|
||||||
|
}
|
||||||
|
return RayResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
RayResult RenderingManager::castCameraToViewportRay(const float nX, const float nY, float maxDistance, bool ignorePlayer, bool ignoreActors)
|
||||||
{
|
{
|
||||||
osg::ref_ptr<osgUtil::LineSegmentIntersector> intersector (new osgUtil::LineSegmentIntersector(osgUtil::LineSegmentIntersector::PROJECTION,
|
osg::ref_ptr<osgUtil::LineSegmentIntersector> intersector (new osgUtil::LineSegmentIntersector(osgUtil::LineSegmentIntersector::PROJECTION,
|
||||||
nX * 2.f - 1.f, nY * (-2.f) + 1.f));
|
nX * 2.f - 1.f, nY * (-2.f) + 1.f));
|
||||||
|
@ -982,6 +1027,10 @@ namespace MWRender
|
||||||
notifyWorldSpaceChanged();
|
notifyWorldSpaceChanged();
|
||||||
if (mObjectPaging)
|
if (mObjectPaging)
|
||||||
mObjectPaging->clear();
|
mObjectPaging->clear();
|
||||||
|
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
mUserPointer->setParent(nullptr);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
MWRender::Animation* RenderingManager::getAnimation(const MWWorld::Ptr &ptr)
|
MWRender::Animation* RenderingManager::getAnimation(const MWWorld::Ptr &ptr)
|
||||||
|
@ -1021,8 +1070,13 @@ namespace MWRender
|
||||||
|
|
||||||
void RenderingManager::renderPlayer(const MWWorld::Ptr &player)
|
void RenderingManager::renderPlayer(const MWWorld::Ptr &player)
|
||||||
{
|
{
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
MWVR::Environment::get().setPlayerAnimation(new MWVR::VRAnimation(player, player.getRefData().getBaseNode(), mResourceSystem, false, mUserPointer));
|
||||||
|
mPlayerAnimation = MWVR::Environment::get().getPlayerAnimation();
|
||||||
|
#else
|
||||||
mPlayerAnimation = new NpcAnimation(player, player.getRefData().getBaseNode(), mResourceSystem, 0, NpcAnimation::VM_Normal,
|
mPlayerAnimation = new NpcAnimation(player, player.getRefData().getBaseNode(), mResourceSystem, 0, NpcAnimation::VM_Normal,
|
||||||
mFirstPersonFieldOfView);
|
mFirstPersonFieldOfView);
|
||||||
|
#endif
|
||||||
|
|
||||||
mCamera->setAnimation(mPlayerAnimation.get());
|
mCamera->setAnimation(mPlayerAnimation.get());
|
||||||
mCamera->attachTo(player);
|
mCamera->attachTo(player);
|
||||||
|
@ -1114,6 +1168,12 @@ namespace MWRender
|
||||||
void RenderingManager::setFogColor(const osg::Vec4f &color)
|
void RenderingManager::setFogColor(const osg::Vec4f &color)
|
||||||
{
|
{
|
||||||
mViewer->getCamera()->setClearColor(color);
|
mViewer->getCamera()->setClearColor(color);
|
||||||
|
for (unsigned int i = 0; i < mViewer->getNumSlaves(); i++)
|
||||||
|
{
|
||||||
|
const auto& slave = mViewer->getSlave(i);
|
||||||
|
if (slave._camera)
|
||||||
|
slave._camera->setClearColor(color);
|
||||||
|
}
|
||||||
|
|
||||||
mStateUpdater->setFogColor(color);
|
mStateUpdater->setFogColor(color);
|
||||||
}
|
}
|
||||||
|
@ -1350,4 +1410,11 @@ namespace MWRender
|
||||||
if (mObjectPaging)
|
if (mObjectPaging)
|
||||||
mObjectPaging->getPagedRefnums(activeGrid, out);
|
mObjectPaging->getPagedRefnums(activeGrid, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
MWVR::UserPointer& RenderingManager::userPointer()
|
||||||
|
{
|
||||||
|
return *mUserPointer;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,6 +68,11 @@ namespace DetourNavigator
|
||||||
struct Settings;
|
struct Settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace MWVR
|
||||||
|
{
|
||||||
|
class UserPointer;
|
||||||
|
}
|
||||||
|
|
||||||
namespace MWRender
|
namespace MWRender
|
||||||
{
|
{
|
||||||
class GroundcoverUpdater;
|
class GroundcoverUpdater;
|
||||||
|
@ -90,10 +95,25 @@ namespace MWRender
|
||||||
class ObjectPaging;
|
class ObjectPaging;
|
||||||
class Groundcover;
|
class Groundcover;
|
||||||
|
|
||||||
|
// Result data of ray cast methods.
|
||||||
|
// Needs to be declared outside the RenderingManager class to be forward declarable
|
||||||
|
struct RayResult
|
||||||
|
{
|
||||||
|
bool mHit;
|
||||||
|
osg::Vec3f mHitNormalWorld;
|
||||||
|
osg::Vec3f mHitPointWorld;
|
||||||
|
osg::Vec3f mHitPointLocal;
|
||||||
|
MWWorld::Ptr mHitObject;
|
||||||
|
osg::Node* mHitNode;
|
||||||
|
/// Cast a ray between two points
|
||||||
|
ESM::RefNum mHitRefnum;
|
||||||
|
float mRatio;
|
||||||
|
};
|
||||||
|
|
||||||
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, std::unique_ptr<Camera> camera,
|
||||||
Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue,
|
Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue,
|
||||||
const std::string& resourcePath, DetourNavigator::Navigator& navigator);
|
const std::string& resourcePath, DetourNavigator::Navigator& navigator);
|
||||||
~RenderingManager();
|
~RenderingManager();
|
||||||
|
@ -110,6 +130,8 @@ namespace MWRender
|
||||||
|
|
||||||
osg::Uniform* mUniformNear;
|
osg::Uniform* mUniformNear;
|
||||||
osg::Uniform* mUniformFar;
|
osg::Uniform* mUniformFar;
|
||||||
|
osg::Uniform* mUniformStereoViewOffsets;
|
||||||
|
osg::Uniform* mUniformStereoProjections;
|
||||||
|
|
||||||
void preloadCommonAssets();
|
void preloadCommonAssets();
|
||||||
|
|
||||||
|
@ -153,18 +175,11 @@ namespace MWRender
|
||||||
void screenshot(osg::Image* image, int w, int h);
|
void screenshot(osg::Image* image, int w, int h);
|
||||||
bool screenshot360(osg::Image* image);
|
bool screenshot360(osg::Image* image);
|
||||||
|
|
||||||
struct RayResult
|
|
||||||
{
|
|
||||||
bool mHit;
|
|
||||||
osg::Vec3f mHitNormalWorld;
|
|
||||||
osg::Vec3f mHitPointWorld;
|
|
||||||
MWWorld::Ptr mHitObject;
|
|
||||||
ESM::RefNum mHitRefnum;
|
|
||||||
float mRatio;
|
|
||||||
};
|
|
||||||
|
|
||||||
RayResult castRay(const osg::Vec3f& origin, const osg::Vec3f& dest, bool ignorePlayer, bool ignoreActors=false);
|
RayResult castRay(const osg::Vec3f& origin, const osg::Vec3f& dest, bool ignorePlayer, bool ignoreActors=false);
|
||||||
|
|
||||||
|
/// Cast a ray from a node in the scene graph
|
||||||
|
RayResult castRay(const osg::Transform* source, float maxDistance, bool ignorePlayer, bool ignoreActors=false);
|
||||||
|
|
||||||
/// Return the object under the mouse cursor / crosshair position, given by nX and nY normalized screen coordinates,
|
/// Return the object under the mouse cursor / crosshair position, given by nX and nY normalized screen coordinates,
|
||||||
/// where (0,0) is the top left corner.
|
/// where (0,0) is the top left corner.
|
||||||
RayResult castCameraToViewportRay(const float nX, const float nY, float maxDistance, bool ignorePlayer, bool ignoreActors=false);
|
RayResult castCameraToViewportRay(const float nX, const float nY, float maxDistance, bool ignorePlayer, bool ignoreActors=false);
|
||||||
|
@ -240,6 +255,10 @@ namespace MWRender
|
||||||
bool pagingUnlockCache();
|
bool pagingUnlockCache();
|
||||||
void getPagedRefnums(const osg::Vec4i &activeGrid, std::set<ESM::RefNum> &out);
|
void getPagedRefnums(const osg::Vec4i &activeGrid, std::set<ESM::RefNum> &out);
|
||||||
|
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
MWVR::UserPointer& userPointer();
|
||||||
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void updateProjectionMatrix();
|
void updateProjectionMatrix();
|
||||||
void updateTextureFiltering();
|
void updateTextureFiltering();
|
||||||
|
@ -293,6 +312,10 @@ namespace MWRender
|
||||||
std::unique_ptr<ViewOverShoulderController> mViewOverShoulderController;
|
std::unique_ptr<ViewOverShoulderController> mViewOverShoulderController;
|
||||||
osg::Vec3f mCurrentCameraPos;
|
osg::Vec3f mCurrentCameraPos;
|
||||||
|
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
std::shared_ptr<MWVR::UserPointer> mUserPointer;
|
||||||
|
#endif
|
||||||
|
|
||||||
osg::ref_ptr<StateUpdater> mStateUpdater;
|
osg::ref_ptr<StateUpdater> mStateUpdater;
|
||||||
|
|
||||||
osg::Vec4f mAmbientColor;
|
osg::Vec4f mAmbientColor;
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include <osg/TextureCubeMap>
|
#include <osg/TextureCubeMap>
|
||||||
|
|
||||||
#include <components/misc/stringops.hpp>
|
#include <components/misc/stringops.hpp>
|
||||||
|
#include <components/misc/callbackmanager.hpp>
|
||||||
#include <components/resource/resourcesystem.hpp>
|
#include <components/resource/resourcesystem.hpp>
|
||||||
#include <components/resource/scenemanager.hpp>
|
#include <components/resource/scenemanager.hpp>
|
||||||
#include <components/shader/shadermanager.hpp>
|
#include <components/shader/shadermanager.hpp>
|
||||||
|
@ -324,8 +325,8 @@ namespace MWRender
|
||||||
rttCamera->setUpdateCallback(new NoTraverseCallback);
|
rttCamera->setUpdateCallback(new NoTraverseCallback);
|
||||||
rttCamera->addChild(mSceneRoot);
|
rttCamera->addChild(mSceneRoot);
|
||||||
|
|
||||||
rttCamera->addChild(mWater->getReflectionCamera());
|
rttCamera->addChild(mWater->getReflectionNode());
|
||||||
rttCamera->addChild(mWater->getRefractionCamera());
|
rttCamera->addChild(mWater->getRefractionNode());
|
||||||
|
|
||||||
rttCamera->setCullMask(mViewer->getCamera()->getCullMask() & (~Mask_GUI));
|
rttCamera->setCullMask(mViewer->getCamera()->getCullMask() & (~Mask_GUI));
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include <osg/Node>
|
#include <osg/Node>
|
||||||
#include <osg/ValueObject>
|
#include <osg/ValueObject>
|
||||||
|
#include <osg/RenderInfo>
|
||||||
|
|
||||||
#include <components/resource/resourcesystem.hpp>
|
#include <components/resource/resourcesystem.hpp>
|
||||||
#include <components/resource/imagemanager.hpp>
|
#include <components/resource/imagemanager.hpp>
|
||||||
|
@ -64,4 +65,20 @@ void overrideTexture(const std::string &texture, Resource::ResourceSystem *resou
|
||||||
node->setStateSet(stateset);
|
node->setStateSet(stateset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MipmapCallback::~MipmapCallback()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void MipmapCallback::operator()(osg::RenderInfo& renderInfo) const
|
||||||
|
{
|
||||||
|
auto* gl = renderInfo.getState()->get<osg::GLExtensions>();
|
||||||
|
auto* tex = mTexture->getTextureObject(renderInfo.getContextID());
|
||||||
|
if (tex)
|
||||||
|
{
|
||||||
|
tex->bind();
|
||||||
|
gl->glGenerateMipmap(tex->target());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,12 +2,14 @@
|
||||||
#define OPENMW_MWRENDER_UTIL_H
|
#define OPENMW_MWRENDER_UTIL_H
|
||||||
|
|
||||||
#include <osg/NodeCallback>
|
#include <osg/NodeCallback>
|
||||||
|
#include <osg/Camera>
|
||||||
#include <osg/ref_ptr>
|
#include <osg/ref_ptr>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace osg
|
namespace osg
|
||||||
{
|
{
|
||||||
class Node;
|
class Node;
|
||||||
|
class Texture2D;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Resource
|
namespace Resource
|
||||||
|
@ -32,6 +34,24 @@ namespace MWRender
|
||||||
// no traverse()
|
// no traverse()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Draw callback for RTT that can be used to regenerate mipmaps
|
||||||
|
/// either as a predraw before use or a postdraw after RTT.
|
||||||
|
class MipmapCallback : public osg::Camera::DrawCallback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MipmapCallback(osg::Texture2D* texture)
|
||||||
|
: mTexture(texture)
|
||||||
|
{}
|
||||||
|
|
||||||
|
~MipmapCallback();
|
||||||
|
|
||||||
|
void operator()(osg::RenderInfo& info) const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
osg::ref_ptr<osg::Texture2D> mTexture;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -56,6 +56,10 @@ namespace MWRender
|
||||||
Mask_Lighting = (1<<19),
|
Mask_Lighting = (1<<19),
|
||||||
|
|
||||||
Mask_Groundcover = (1<<20),
|
Mask_Groundcover = (1<<20),
|
||||||
|
|
||||||
|
// Vr masks
|
||||||
|
Mask_3DGUI = (1 << 21),
|
||||||
|
Mask_Pointer = (1 << 22)
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
|
@ -25,12 +26,14 @@
|
||||||
#include <components/resource/imagemanager.hpp>
|
#include <components/resource/imagemanager.hpp>
|
||||||
#include <components/resource/scenemanager.hpp>
|
#include <components/resource/scenemanager.hpp>
|
||||||
|
|
||||||
|
#include <components/sceneutil/rtt.hpp>
|
||||||
#include <components/sceneutil/shadow.hpp>
|
#include <components/sceneutil/shadow.hpp>
|
||||||
#include <components/sceneutil/util.hpp>
|
#include <components/sceneutil/util.hpp>
|
||||||
#include <components/sceneutil/waterutil.hpp>
|
#include <components/sceneutil/waterutil.hpp>
|
||||||
#include <components/sceneutil/lightmanager.hpp>
|
#include <components/sceneutil/lightmanager.hpp>
|
||||||
|
|
||||||
#include <components/misc/constants.hpp>
|
#include <components/misc/constants.hpp>
|
||||||
|
#include <components/misc/stereo.hpp>
|
||||||
|
|
||||||
#include <components/nifosg/controller.hpp>
|
#include <components/nifosg/controller.hpp>
|
||||||
|
|
||||||
|
@ -42,6 +45,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"
|
||||||
|
@ -261,29 +266,24 @@ osg::ref_ptr<osg::Image> readPngImage (const std::string& file)
|
||||||
return result.getImage();
|
return result.getImage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Refraction : public SceneUtil::RTTNode
|
||||||
class Refraction : public osg::Camera
|
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Refraction()
|
Refraction(uint32_t rttSize)
|
||||||
|
: RTTNode(rttSize, rttSize, 1, false)
|
||||||
{
|
{
|
||||||
unsigned int rttSize = Settings::Manager::getInt("rtt size", "Water");
|
mClipCullNode = new ClipCullNode;
|
||||||
setRenderOrder(osg::Camera::PRE_RENDER, 1);
|
}
|
||||||
setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
||||||
setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);
|
|
||||||
setReferenceFrame(osg::Camera::RELATIVE_RF);
|
|
||||||
setSmallFeatureCullingPixelSize(Settings::Manager::getInt("small feature culling pixel size", "Water"));
|
|
||||||
osg::Camera::setName("RefractionCamera");
|
|
||||||
setCullCallback(new InheritViewPointCallback);
|
|
||||||
setComputeNearFarMode(osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR);
|
|
||||||
|
|
||||||
setCullMask(Mask_Effect|Mask_Scene|Mask_Object|Mask_Static|Mask_Terrain|Mask_Actor|Mask_ParticleSystem|Mask_Sky|Mask_Sun|Mask_Player|Mask_Lighting|Mask_Groundcover);
|
void setDefaults(osg::Camera* camera) override
|
||||||
setNodeMask(Mask_RenderToTexture);
|
{
|
||||||
setViewport(0, 0, rttSize, rttSize);
|
camera->setReferenceFrame(osg::Camera::RELATIVE_RF);
|
||||||
|
camera->setSmallFeatureCullingPixelSize(Settings::Manager::getInt("small feature culling pixel size", "Water"));
|
||||||
|
camera->setName("RefractionCamera");
|
||||||
|
camera->addCullCallback(new InheritViewPointCallback);
|
||||||
|
camera->setComputeNearFarMode(osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR);
|
||||||
|
|
||||||
// No need for Update traversal since the scene is already updated as part of the main scene graph
|
camera->setCullMask(Mask_Effect | Mask_Scene | Mask_Object | Mask_Static | Mask_Terrain | Mask_Actor | Mask_ParticleSystem | Mask_Sky | Mask_Sun | Mask_Player | Mask_Lighting);
|
||||||
// A double update would mess with the light collection (in addition to being plain redundant)
|
|
||||||
setUpdateCallback(new NoTraverseCallback);
|
|
||||||
|
|
||||||
// No need for fog here, we are already applying fog on the water surface itself as well as underwater fog
|
// No need for fog here, we are already applying fog on the water surface itself as well as underwater fog
|
||||||
// assign large value to effectively turn off fog
|
// assign large value to effectively turn off fog
|
||||||
|
@ -291,35 +291,21 @@ public:
|
||||||
osg::ref_ptr<osg::Fog> fog(new osg::Fog);
|
osg::ref_ptr<osg::Fog> fog(new osg::Fog);
|
||||||
fog->setStart(10000000);
|
fog->setStart(10000000);
|
||||||
fog->setEnd(10000000);
|
fog->setEnd(10000000);
|
||||||
getOrCreateStateSet()->setAttributeAndModes(fog, osg::StateAttribute::OFF|osg::StateAttribute::OVERRIDE);
|
camera->getOrCreateStateSet()->setAttributeAndModes(fog, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE);
|
||||||
|
|
||||||
mClipCullNode = new ClipCullNode;
|
camera->addChild(mClipCullNode);
|
||||||
osg::Camera::addChild(mClipCullNode);
|
camera->setNodeMask(Mask_RenderToTexture);
|
||||||
|
|
||||||
mRefractionTexture = new osg::Texture2D;
|
|
||||||
mRefractionTexture->setTextureSize(rttSize, rttSize);
|
|
||||||
mRefractionTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
|
|
||||||
mRefractionTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
|
|
||||||
mRefractionTexture->setInternalFormat(GL_RGB);
|
|
||||||
mRefractionTexture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
|
|
||||||
mRefractionTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
|
|
||||||
|
|
||||||
SceneUtil::attachAlphaToCoverageFriendlyFramebufferToCamera(this, osg::Camera::COLOR_BUFFER, mRefractionTexture);
|
|
||||||
|
|
||||||
mRefractionDepthTexture = new osg::Texture2D;
|
|
||||||
mRefractionDepthTexture->setTextureSize(rttSize, rttSize);
|
|
||||||
mRefractionDepthTexture->setSourceFormat(GL_DEPTH_COMPONENT);
|
|
||||||
mRefractionDepthTexture->setInternalFormat(GL_DEPTH_COMPONENT24);
|
|
||||||
mRefractionDepthTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
|
|
||||||
mRefractionDepthTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
|
|
||||||
mRefractionDepthTexture->setSourceType(GL_UNSIGNED_INT);
|
|
||||||
mRefractionDepthTexture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
|
|
||||||
mRefractionDepthTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
|
|
||||||
|
|
||||||
attach(osg::Camera::DEPTH_BUFFER, mRefractionDepthTexture);
|
|
||||||
|
|
||||||
if (Settings::Manager::getFloat("refraction scale", "Water") != 1) // TODO: to be removed with issue #5709
|
if (Settings::Manager::getFloat("refraction scale", "Water") != 1) // TODO: to be removed with issue #5709
|
||||||
SceneUtil::ShadowManager::disableShadowsForStateSet(getOrCreateStateSet());
|
SceneUtil::ShadowManager::disableShadowsForStateSet(camera->getOrCreateStateSet());
|
||||||
|
|
||||||
|
if (MWBase::Environment::get().getResourceSystem()->getSceneManager()->getShaderManager().stereoGeometryShaderEnabled())
|
||||||
|
Misc::enableStereoForCamera(camera, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void apply(osg::Camera* camera) override
|
||||||
|
{
|
||||||
|
camera->setViewMatrix(mViewMatrix);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setScene(osg::Node* scene)
|
void setScene(osg::Node* scene)
|
||||||
|
@ -335,71 +321,54 @@ public:
|
||||||
const float refractionScale = std::min(1.0f, std::max(0.0f,
|
const float refractionScale = std::min(1.0f, std::max(0.0f,
|
||||||
Settings::Manager::getFloat("refraction scale", "Water")));
|
Settings::Manager::getFloat("refraction scale", "Water")));
|
||||||
|
|
||||||
setViewMatrix(osg::Matrix::scale(1,1,refractionScale) *
|
mViewMatrix = osg::Matrix::scale(1, 1, refractionScale) *
|
||||||
osg::Matrix::translate(0,0,(1.0 - refractionScale) * waterLevel));
|
osg::Matrix::translate(0, 0, (1.0 - refractionScale) * waterLevel);
|
||||||
|
|
||||||
mClipCullNode->setPlane(osg::Plane(osg::Vec3d(0, 0, -1), osg::Vec3d(0, 0, waterLevel)));
|
mClipCullNode->setPlane(osg::Plane(osg::Vec3d(0, 0, -1), osg::Vec3d(0, 0, waterLevel)));
|
||||||
}
|
}
|
||||||
|
|
||||||
osg::Texture2D* getRefractionTexture() const
|
|
||||||
{
|
|
||||||
return mRefractionTexture.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
osg::Texture2D* getRefractionDepthTexture() const
|
|
||||||
{
|
|
||||||
return mRefractionDepthTexture.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
osg::ref_ptr<ClipCullNode> mClipCullNode;
|
osg::ref_ptr<ClipCullNode> mClipCullNode;
|
||||||
osg::ref_ptr<osg::Texture2D> mRefractionTexture;
|
|
||||||
osg::ref_ptr<osg::Texture2D> mRefractionDepthTexture;
|
|
||||||
osg::ref_ptr<osg::Node> mScene;
|
osg::ref_ptr<osg::Node> mScene;
|
||||||
|
osg::Matrix mViewMatrix{ osg::Matrix::identity() };
|
||||||
};
|
};
|
||||||
|
|
||||||
class Reflection : public osg::Camera
|
class Reflection : public SceneUtil::RTTNode
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Reflection(bool isInterior)
|
Reflection(uint32_t rttSize, bool isInterior)
|
||||||
|
: RTTNode(rttSize, rttSize, 0, false)
|
||||||
{
|
{
|
||||||
setRenderOrder(osg::Camera::PRE_RENDER);
|
|
||||||
setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
||||||
setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);
|
|
||||||
setReferenceFrame(osg::Camera::RELATIVE_RF);
|
|
||||||
setSmallFeatureCullingPixelSize(Settings::Manager::getInt("small feature culling pixel size", "Water"));
|
|
||||||
osg::Camera::setName("ReflectionCamera");
|
|
||||||
setCullCallback(new InheritViewPointCallback);
|
|
||||||
|
|
||||||
setInterior(isInterior);
|
setInterior(isInterior);
|
||||||
setNodeMask(Mask_RenderToTexture);
|
mClipCullNode = new ClipCullNode;
|
||||||
|
}
|
||||||
|
|
||||||
unsigned int rttSize = Settings::Manager::getInt("rtt size", "Water");
|
void setDefaults(osg::Camera* camera) override
|
||||||
setViewport(0, 0, rttSize, rttSize);
|
{
|
||||||
|
camera->setReferenceFrame(osg::Camera::RELATIVE_RF);
|
||||||
|
camera->setSmallFeatureCullingPixelSize(Settings::Manager::getInt("small feature culling pixel size", "Water"));
|
||||||
|
camera->setName("ReflectionCamera");
|
||||||
|
camera->addCullCallback(new InheritViewPointCallback);
|
||||||
|
|
||||||
// No need for Update traversal since the mSceneRoot is already updated as part of the main scene graph
|
|
||||||
// A double update would mess with the light collection (in addition to being plain redundant)
|
|
||||||
setUpdateCallback(new NoTraverseCallback);
|
|
||||||
|
|
||||||
mReflectionTexture = new osg::Texture2D;
|
|
||||||
mReflectionTexture->setTextureSize(rttSize, rttSize);
|
|
||||||
mReflectionTexture->setInternalFormat(GL_RGB);
|
|
||||||
mReflectionTexture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
|
|
||||||
mReflectionTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
|
|
||||||
mReflectionTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
|
|
||||||
mReflectionTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
|
|
||||||
|
|
||||||
SceneUtil::attachAlphaToCoverageFriendlyFramebufferToCamera(this, osg::Camera::COLOR_BUFFER, mReflectionTexture);
|
|
||||||
|
|
||||||
// XXX: should really flip the FrontFace on each renderable instead of forcing clockwise.
|
// XXX: should really flip the FrontFace on each renderable instead of forcing clockwise.
|
||||||
osg::ref_ptr<osg::FrontFace> frontFace(new osg::FrontFace);
|
osg::ref_ptr<osg::FrontFace> frontFace(new osg::FrontFace);
|
||||||
frontFace->setMode(osg::FrontFace::CLOCKWISE);
|
frontFace->setMode(osg::FrontFace::CLOCKWISE);
|
||||||
getOrCreateStateSet()->setAttributeAndModes(frontFace, osg::StateAttribute::ON);
|
camera->getOrCreateStateSet()->setAttributeAndModes(frontFace, osg::StateAttribute::ON);
|
||||||
|
|
||||||
mClipCullNode = new ClipCullNode;
|
camera->addChild(mClipCullNode);
|
||||||
osg::Camera::addChild(mClipCullNode);
|
camera->setNodeMask(Mask_RenderToTexture);
|
||||||
|
|
||||||
SceneUtil::ShadowManager::disableShadowsForStateSet(getOrCreateStateSet());
|
SceneUtil::ShadowManager::disableShadowsForStateSet(camera->getOrCreateStateSet());
|
||||||
|
|
||||||
|
if (MWBase::Environment::get().getResourceSystem()->getSceneManager()->getShaderManager().stereoGeometryShaderEnabled())
|
||||||
|
Misc::enableStereoForCamera(camera, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void apply(osg::Camera* camera) override
|
||||||
|
{
|
||||||
|
camera->setViewMatrix(mViewMatrix);
|
||||||
|
camera->setCullMask(mNodeMask);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setInterior(bool isInterior)
|
void setInterior(bool isInterior)
|
||||||
|
@ -412,12 +381,12 @@ public:
|
||||||
if(reflectionDetail >= 3) extraMask |= Mask_Effect | Mask_ParticleSystem | Mask_Object;
|
if(reflectionDetail >= 3) extraMask |= Mask_Effect | Mask_ParticleSystem | Mask_Object;
|
||||||
if(reflectionDetail >= 4) extraMask |= Mask_Player | Mask_Actor;
|
if(reflectionDetail >= 4) extraMask |= Mask_Player | Mask_Actor;
|
||||||
if(reflectionDetail >= 5) extraMask |= Mask_Groundcover;
|
if(reflectionDetail >= 5) extraMask |= Mask_Groundcover;
|
||||||
setCullMask(Mask_Scene|Mask_Sky|Mask_Lighting|extraMask);
|
mNodeMask = Mask_Scene | Mask_Sky | Mask_Lighting | extraMask;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setWaterLevel(float waterLevel)
|
void setWaterLevel(float waterLevel)
|
||||||
{
|
{
|
||||||
setViewMatrix(osg::Matrix::scale(1,1,-1) * osg::Matrix::translate(0,0,2 * waterLevel));
|
mViewMatrix = osg::Matrix::scale(1, 1, -1) * osg::Matrix::translate(0, 0, 2 * waterLevel);
|
||||||
mClipCullNode->setPlane(osg::Plane(osg::Vec3d(0, 0, 1), osg::Vec3d(0, 0, waterLevel)));
|
mClipCullNode->setPlane(osg::Plane(osg::Vec3d(0, 0, 1), osg::Vec3d(0, 0, waterLevel)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -429,15 +398,11 @@ public:
|
||||||
mClipCullNode->addChild(scene);
|
mClipCullNode->addChild(scene);
|
||||||
}
|
}
|
||||||
|
|
||||||
osg::Texture2D* getReflectionTexture() const
|
|
||||||
{
|
|
||||||
return mReflectionTexture.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
osg::ref_ptr<osg::Texture2D> mReflectionTexture;
|
|
||||||
osg::ref_ptr<ClipCullNode> mClipCullNode;
|
osg::ref_ptr<ClipCullNode> mClipCullNode;
|
||||||
osg::ref_ptr<osg::Node> mScene;
|
osg::ref_ptr<osg::Node> mScene;
|
||||||
|
osg::Node::NodeMask mNodeMask;
|
||||||
|
osg::Matrix mViewMatrix{ osg::Matrix::identity() };
|
||||||
};
|
};
|
||||||
|
|
||||||
/// DepthClampCallback enables GL_DEPTH_CLAMP for the current draw, if supported.
|
/// DepthClampCallback enables GL_DEPTH_CLAMP for the current draw, if supported.
|
||||||
|
@ -474,6 +439,7 @@ Water::Water(osg::Group *parent, osg::Group* sceneRoot, Resource::ResourceSystem
|
||||||
, mTop(0)
|
, mTop(0)
|
||||||
, mInterior(false)
|
, mInterior(false)
|
||||||
, mCullCallback(nullptr)
|
, mCullCallback(nullptr)
|
||||||
|
, mShaderWaterStateSetUpdater(nullptr)
|
||||||
{
|
{
|
||||||
mSimulation.reset(new RippleSimulation(mSceneRoot, resourceSystem));
|
mSimulation.reset(new RippleSimulation(mSceneRoot, resourceSystem));
|
||||||
|
|
||||||
|
@ -528,22 +494,31 @@ void Water::setCullCallback(osg::Callback* callback)
|
||||||
|
|
||||||
void Water::updateWaterMaterial()
|
void Water::updateWaterMaterial()
|
||||||
{
|
{
|
||||||
|
if (mShaderWaterStateSetUpdater)
|
||||||
|
{
|
||||||
|
mWaterNode->removeCullCallback(mShaderWaterStateSetUpdater);
|
||||||
|
mShaderWaterStateSetUpdater = nullptr;
|
||||||
|
}
|
||||||
if (mReflection)
|
if (mReflection)
|
||||||
{
|
{
|
||||||
mReflection->removeChildren(0, mReflection->getNumChildren());
|
|
||||||
mParent->removeChild(mReflection);
|
mParent->removeChild(mReflection);
|
||||||
mReflection = nullptr;
|
mReflection = nullptr;
|
||||||
}
|
}
|
||||||
if (mRefraction)
|
if (mRefraction)
|
||||||
{
|
{
|
||||||
mRefraction->removeChildren(0, mRefraction->getNumChildren());
|
|
||||||
mParent->removeChild(mRefraction);
|
mParent->removeChild(mRefraction);
|
||||||
mRefraction = nullptr;
|
mRefraction = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mWaterNode->setStateSet(nullptr);
|
||||||
|
mWaterGeom->setStateSet(nullptr);
|
||||||
|
mWaterGeom->setUpdateCallback(nullptr);
|
||||||
|
|
||||||
if (Settings::Manager::getBool("shader", "Water"))
|
if (Settings::Manager::getBool("shader", "Water"))
|
||||||
{
|
{
|
||||||
mReflection = new Reflection(mInterior);
|
unsigned int rttSize = Settings::Manager::getInt("rtt size", "Water");
|
||||||
|
|
||||||
|
mReflection = new Reflection(rttSize, mInterior);
|
||||||
mReflection->setWaterLevel(mTop);
|
mReflection->setWaterLevel(mTop);
|
||||||
mReflection->setScene(mSceneRoot);
|
mReflection->setScene(mSceneRoot);
|
||||||
if (mCullCallback)
|
if (mCullCallback)
|
||||||
|
@ -552,7 +527,7 @@ void Water::updateWaterMaterial()
|
||||||
|
|
||||||
if (Settings::Manager::getBool("refraction", "Water"))
|
if (Settings::Manager::getBool("refraction", "Water"))
|
||||||
{
|
{
|
||||||
mRefraction = new Refraction;
|
mRefraction = new Refraction(rttSize);
|
||||||
mRefraction->setWaterLevel(mTop);
|
mRefraction->setWaterLevel(mTop);
|
||||||
mRefraction->setScene(mSceneRoot);
|
mRefraction->setScene(mSceneRoot);
|
||||||
if (mCullCallback)
|
if (mCullCallback)
|
||||||
|
@ -560,7 +535,7 @@ void Water::updateWaterMaterial()
|
||||||
mParent->addChild(mRefraction);
|
mParent->addChild(mRefraction);
|
||||||
}
|
}
|
||||||
|
|
||||||
createShaderWaterStateSet(mWaterGeom, mReflection, mRefraction);
|
createShaderWaterStateSet(mWaterNode, mReflection, mRefraction);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
createSimpleWaterStateSet(mWaterGeom, Fallback::Map::getFloat("Water_World_Alpha"));
|
createSimpleWaterStateSet(mWaterGeom, Fallback::Map::getFloat("Water_World_Alpha"));
|
||||||
|
@ -568,12 +543,12 @@ void Water::updateWaterMaterial()
|
||||||
updateVisible();
|
updateVisible();
|
||||||
}
|
}
|
||||||
|
|
||||||
osg::Camera *Water::getReflectionCamera()
|
osg::Node *Water::getReflectionNode()
|
||||||
{
|
{
|
||||||
return mReflection;
|
return mReflection;
|
||||||
}
|
}
|
||||||
|
|
||||||
osg::Camera *Water::getRefractionCamera()
|
osg::Node* Water::getRefractionNode()
|
||||||
{
|
{
|
||||||
return mRefraction;
|
return mRefraction;
|
||||||
}
|
}
|
||||||
|
@ -620,15 +595,77 @@ void Water::createSimpleWaterStateSet(osg::Node* node, float alpha)
|
||||||
sceneManager->setForceShaders(oldValue);
|
sceneManager->setForceShaders(oldValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ShaderWaterStateSetUpdater : public SceneUtil::StateSetUpdater
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ShaderWaterStateSetUpdater(Water* water, Reflection* reflection, Refraction* refraction, osg::ref_ptr<osg::Program> program, osg::ref_ptr<osg::Texture2D> normalMap)
|
||||||
|
: mWater(water)
|
||||||
|
, mReflection(reflection)
|
||||||
|
, mRefraction(refraction)
|
||||||
|
, mProgram(program)
|
||||||
|
, mNormalMap(normalMap)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void setDefaults(osg::StateSet* stateset) override
|
||||||
|
{
|
||||||
|
stateset->addUniform(new osg::Uniform("normalMap", 0));
|
||||||
|
stateset->setTextureAttributeAndModes(0, mNormalMap, osg::StateAttribute::ON);
|
||||||
|
stateset->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
|
||||||
|
stateset->setAttributeAndModes(mProgram, osg::StateAttribute::ON);
|
||||||
|
|
||||||
|
stateset->addUniform(new osg::Uniform("reflectionMap", 1));
|
||||||
|
if (mRefraction)
|
||||||
|
{
|
||||||
|
stateset->addUniform(new osg::Uniform("refractionMap", 2));
|
||||||
|
stateset->addUniform(new osg::Uniform("refractionDepthMap", 3));
|
||||||
|
stateset->setRenderBinDetails(MWRender::RenderBin_Default, "RenderBin");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
stateset->setMode(GL_BLEND, osg::StateAttribute::ON);
|
||||||
|
stateset->setRenderBinDetails(MWRender::RenderBin_Water, "RenderBin");
|
||||||
|
osg::ref_ptr<osg::Depth> depth(new osg::Depth);
|
||||||
|
depth->setWriteMask(false);
|
||||||
|
stateset->setAttributeAndModes(depth, osg::StateAttribute::ON);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void apply(osg::StateSet* stateset, osg::NodeVisitor* nv) override
|
||||||
|
{
|
||||||
|
osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>(nv);
|
||||||
|
stateset->setTextureAttributeAndModes(1, mReflection->getColorTexture(cv), osg::StateAttribute::ON);
|
||||||
|
|
||||||
|
if (mRefraction)
|
||||||
|
{
|
||||||
|
stateset->setTextureAttributeAndModes(2, mRefraction->getColorTexture(cv), osg::StateAttribute::ON);
|
||||||
|
stateset->setTextureAttributeAndModes(3, mRefraction->getDepthTexture(cv), osg::StateAttribute::ON);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Water* mWater;
|
||||||
|
Reflection* mReflection;
|
||||||
|
Refraction* mRefraction;
|
||||||
|
osg::ref_ptr<osg::Program> mProgram;
|
||||||
|
osg::ref_ptr<osg::Texture2D> mNormalMap;
|
||||||
|
};
|
||||||
|
|
||||||
void Water::createShaderWaterStateSet(osg::Node* node, Reflection* reflection, Refraction* refraction)
|
void Water::createShaderWaterStateSet(osg::Node* node, Reflection* reflection, Refraction* refraction)
|
||||||
{
|
{
|
||||||
// use a define map to conditionally compile the shader
|
// use a define map to conditionally compile the shader
|
||||||
std::map<std::string, std::string> defineMap;
|
std::map<std::string, std::string> defineMap;
|
||||||
defineMap.insert(std::make_pair(std::string("refraction_enabled"), std::string(refraction ? "1" : "0")));
|
defineMap.insert(std::make_pair(std::string("refraction_enabled"), std::string(mRefraction ? "1" : "0")));
|
||||||
|
|
||||||
|
if (mResourceSystem->getSceneManager()->getShaderManager().stereoGeometryShaderEnabled())
|
||||||
|
{
|
||||||
|
defineMap["geometryShader"] = "1";
|
||||||
|
}
|
||||||
|
|
||||||
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));
|
||||||
osg::ref_ptr<osg::Shader> fragmentShader(shaderMgr.getShader("water_fragment.glsl", defineMap, osg::Shader::FRAGMENT));
|
osg::ref_ptr<osg::Shader> fragmentShader(shaderMgr.getShader("water_fragment.glsl", defineMap, osg::Shader::FRAGMENT));
|
||||||
|
osg::ref_ptr<osg::Program> program = shaderMgr.getProgram(vertexShader, fragmentShader);
|
||||||
|
|
||||||
osg::ref_ptr<osg::Texture2D> normalMap(new osg::Texture2D(readPngImage(mResourcePath + "/shaders/water_nm.png")));
|
osg::ref_ptr<osg::Texture2D> normalMap(new osg::Texture2D(readPngImage(mResourcePath + "/shaders/water_nm.png")));
|
||||||
|
|
||||||
|
@ -640,46 +677,15 @@ void Water::createShaderWaterStateSet(osg::Node* node, Reflection* reflection, R
|
||||||
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);
|
||||||
|
|
||||||
osg::ref_ptr<osg::StateSet> shaderStateset = new osg::StateSet;
|
|
||||||
shaderStateset->addUniform(new osg::Uniform("normalMap", 0));
|
|
||||||
shaderStateset->addUniform(new osg::Uniform("reflectionMap", 1));
|
|
||||||
|
|
||||||
shaderStateset->setTextureAttributeAndModes(0, normalMap, osg::StateAttribute::ON);
|
|
||||||
shaderStateset->setTextureAttributeAndModes(1, reflection->getReflectionTexture(), osg::StateAttribute::ON);
|
|
||||||
|
|
||||||
if (refraction)
|
|
||||||
{
|
|
||||||
shaderStateset->setTextureAttributeAndModes(2, refraction->getRefractionTexture(), osg::StateAttribute::ON);
|
|
||||||
shaderStateset->setTextureAttributeAndModes(3, refraction->getRefractionDepthTexture(), osg::StateAttribute::ON);
|
|
||||||
shaderStateset->addUniform(new osg::Uniform("refractionMap", 2));
|
|
||||||
shaderStateset->addUniform(new osg::Uniform("refractionDepthMap", 3));
|
|
||||||
shaderStateset->setRenderBinDetails(MWRender::RenderBin_Default, "RenderBin");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
shaderStateset->setMode(GL_BLEND, osg::StateAttribute::ON);
|
|
||||||
|
|
||||||
shaderStateset->setRenderBinDetails(MWRender::RenderBin_Water, "RenderBin");
|
|
||||||
|
|
||||||
osg::ref_ptr<osg::Depth> depth (new osg::Depth);
|
|
||||||
depth->setWriteMask(false);
|
|
||||||
shaderStateset->setAttributeAndModes(depth, osg::StateAttribute::ON);
|
|
||||||
}
|
|
||||||
|
|
||||||
shaderStateset->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
|
|
||||||
|
|
||||||
osg::ref_ptr<osg::Program> program (new osg::Program);
|
|
||||||
program->addShader(vertexShader);
|
|
||||||
program->addShader(fragmentShader);
|
|
||||||
auto method = mResourceSystem->getSceneManager()->getLightingMethod();
|
auto method = mResourceSystem->getSceneManager()->getLightingMethod();
|
||||||
if (method == SceneUtil::LightingMethod::SingleUBO)
|
if (method == SceneUtil::LightingMethod::SingleUBO)
|
||||||
program->addBindUniformBlock("LightBufferBinding", static_cast<int>(Shader::UBOBinding::LightBuffer));
|
program->addBindUniformBlock("LightBufferBinding", static_cast<int>(Shader::UBOBinding::LightBuffer));
|
||||||
shaderStateset->setAttributeAndModes(program, osg::StateAttribute::ON);
|
|
||||||
|
|
||||||
node->setStateSet(shaderStateset);
|
|
||||||
|
|
||||||
mRainIntensityUpdater = new RainIntensityUpdater();
|
mRainIntensityUpdater = new RainIntensityUpdater();
|
||||||
node->setUpdateCallback(mRainIntensityUpdater);
|
node->setUpdateCallback(mRainIntensityUpdater);
|
||||||
|
|
||||||
|
mShaderWaterStateSetUpdater = new ShaderWaterStateSetUpdater(this, mReflection, mRefraction, program, normalMap);
|
||||||
|
node->addCullCallback(mShaderWaterStateSetUpdater);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Water::processChangedSettings(const Settings::CategorySettingVector& settings)
|
void Water::processChangedSettings(const Settings::CategorySettingVector& settings)
|
||||||
|
@ -693,13 +699,11 @@ Water::~Water()
|
||||||
|
|
||||||
if (mReflection)
|
if (mReflection)
|
||||||
{
|
{
|
||||||
mReflection->removeChildren(0, mReflection->getNumChildren());
|
|
||||||
mParent->removeChild(mReflection);
|
mParent->removeChild(mReflection);
|
||||||
mReflection = nullptr;
|
mReflection = nullptr;
|
||||||
}
|
}
|
||||||
if (mRefraction)
|
if (mRefraction)
|
||||||
{
|
{
|
||||||
mRefraction->removeChildren(0, mRefraction->getNumChildren());
|
|
||||||
mParent->removeChild(mRefraction);
|
mParent->removeChild(mRefraction);
|
||||||
mRefraction = nullptr;
|
mRefraction = nullptr;
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,6 +72,7 @@ namespace MWRender
|
||||||
bool mInterior;
|
bool mInterior;
|
||||||
|
|
||||||
osg::Callback* mCullCallback;
|
osg::Callback* mCullCallback;
|
||||||
|
osg::ref_ptr<osg::NodeCallback> mShaderWaterStateSetUpdater;
|
||||||
|
|
||||||
osg::Vec3f getSceneNodeCoordinates(int gridX, int gridY);
|
osg::Vec3f getSceneNodeCoordinates(int gridX, int gridY);
|
||||||
void updateVisible();
|
void updateVisible();
|
||||||
|
@ -116,8 +117,8 @@ namespace MWRender
|
||||||
|
|
||||||
void update(float dt);
|
void update(float dt);
|
||||||
|
|
||||||
osg::Camera *getReflectionCamera();
|
osg::Node* getReflectionNode();
|
||||||
osg::Camera *getRefractionCamera();
|
osg::Node* getRefractionNode();
|
||||||
|
|
||||||
void processChangedSettings(const Settings::CategorySettingVector& settings);
|
void processChangedSettings(const Settings::CategorySettingVector& settings);
|
||||||
};
|
};
|
||||||
|
|
|
@ -26,6 +26,12 @@
|
||||||
#include "../mwmechanics/creaturestats.hpp"
|
#include "../mwmechanics/creaturestats.hpp"
|
||||||
#include "../mwmechanics/combat.hpp"
|
#include "../mwmechanics/combat.hpp"
|
||||||
#include "../mwmechanics/weapontype.hpp"
|
#include "../mwmechanics/weapontype.hpp"
|
||||||
|
#include "../mwmechanics/actorutil.hpp"
|
||||||
|
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
#include "../mwvr/vrenvironment.hpp"
|
||||||
|
#include "../mwvr/vranimation.hpp"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "animation.hpp"
|
#include "animation.hpp"
|
||||||
#include "rotatecontroller.hpp"
|
#include "rotatecontroller.hpp"
|
||||||
|
@ -157,6 +163,13 @@ void WeaponAnimation::releaseArrow(MWWorld::Ptr actor, float attackStrength)
|
||||||
osg::Quat orient = osg::Quat(actor.getRefData().getPosition().rot[0], osg::Vec3f(-1, 0, 0))
|
osg::Quat orient = osg::Quat(actor.getRefData().getPosition().rot[0], osg::Vec3f(-1, 0, 0))
|
||||||
* osg::Quat(actor.getRefData().getPosition().rot[2], osg::Vec3f(0, 0, -1));
|
* osg::Quat(actor.getRefData().getPosition().rot[2], osg::Vec3f(0, 0, -1));
|
||||||
|
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
bool isPlayer = actor == MWMechanics::getPlayer();
|
||||||
|
// In VR weapon aim is taken from the real orientation of the weapon.
|
||||||
|
if(isPlayer)
|
||||||
|
orient = MWVR::Environment::get().getPlayerAnimation()->getWeaponTransformMatrix().getRotate();
|
||||||
|
#endif
|
||||||
|
|
||||||
const MWWorld::Store<ESM::GameSetting> &gmst =
|
const MWWorld::Store<ESM::GameSetting> &gmst =
|
||||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
|
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
|
||||||
|
|
||||||
|
@ -228,6 +241,9 @@ void WeaponAnimation::releaseArrow(MWWorld::Ptr actor, float attackStrength)
|
||||||
|
|
||||||
MWWorld::Ptr weaponPtr = *weapon;
|
MWWorld::Ptr weaponPtr = *weapon;
|
||||||
MWWorld::Ptr ammoPtr = *ammo;
|
MWWorld::Ptr ammoPtr = *ammo;
|
||||||
|
#ifdef USE_OPENXR
|
||||||
|
orient = osg::computeLocalToWorld(nodepaths[0]).getRotate();
|
||||||
|
#endif
|
||||||
MWBase::Environment::get().getWorld()->launchProjectile(actor, ammoPtr, launchPos, orient, weaponPtr, speed, attackStrength);
|
MWBase::Environment::get().getWorld()->launchProjectile(actor, ammoPtr, launchPos, orient, weaponPtr, speed, attackStrength);
|
||||||
|
|
||||||
inv.remove(ammoPtr, 1, actor);
|
inv.remove(ammoPtr, 1, actor);
|
||||||
|
|
89
apps/openmw/mwvr/openxraction.cpp
Normal file
89
apps/openmw/mwvr/openxraction.cpp
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
#include "openxraction.hpp"
|
||||||
|
#include "openxrdebug.hpp"
|
||||||
|
#include "vrenvironment.hpp"
|
||||||
|
#include "openxrmanagerimpl.hpp"
|
||||||
|
|
||||||
|
namespace MWVR
|
||||||
|
{
|
||||||
|
|
||||||
|
OpenXRAction::OpenXRAction(
|
||||||
|
XrAction action,
|
||||||
|
XrActionType actionType,
|
||||||
|
const std::string& actionName,
|
||||||
|
const std::string& localName)
|
||||||
|
: mAction(action)
|
||||||
|
, mType(actionType)
|
||||||
|
, mName(actionName)
|
||||||
|
, mLocalName(localName)
|
||||||
|
{
|
||||||
|
VrDebug::setName(action, "OpenMW XR Action " + actionName);
|
||||||
|
};
|
||||||
|
|
||||||
|
OpenXRAction::~OpenXRAction() {
|
||||||
|
if (mAction)
|
||||||
|
{
|
||||||
|
xrDestroyAction(mAction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenXRAction::getFloat(XrPath subactionPath, float& value)
|
||||||
|
{
|
||||||
|
auto* xr = Environment::get().getManager();
|
||||||
|
XrActionStateGetInfo getInfo{ XR_TYPE_ACTION_STATE_GET_INFO };
|
||||||
|
getInfo.action = mAction;
|
||||||
|
getInfo.subactionPath = subactionPath;
|
||||||
|
|
||||||
|
XrActionStateFloat xrValue{ XR_TYPE_ACTION_STATE_FLOAT };
|
||||||
|
CHECK_XRCMD(xrGetActionStateFloat(xr->impl().xrSession(), &getInfo, &xrValue));
|
||||||
|
|
||||||
|
if (xrValue.isActive)
|
||||||
|
value = xrValue.currentState;
|
||||||
|
return xrValue.isActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenXRAction::getBool(XrPath subactionPath, bool& value)
|
||||||
|
{
|
||||||
|
auto* xr = Environment::get().getManager();
|
||||||
|
XrActionStateGetInfo getInfo{ XR_TYPE_ACTION_STATE_GET_INFO };
|
||||||
|
getInfo.action = mAction;
|
||||||
|
getInfo.subactionPath = subactionPath;
|
||||||
|
|
||||||
|
XrActionStateBoolean xrValue{ XR_TYPE_ACTION_STATE_BOOLEAN };
|
||||||
|
CHECK_XRCMD(xrGetActionStateBoolean(xr->impl().xrSession(), &getInfo, &xrValue));
|
||||||
|
|
||||||
|
if (xrValue.isActive)
|
||||||
|
value = xrValue.currentState;
|
||||||
|
return xrValue.isActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pose action only checks if the pose is active or not
|
||||||
|
bool OpenXRAction::getPoseIsActive(XrPath subactionPath)
|
||||||
|
{
|
||||||
|
auto* xr = Environment::get().getManager();
|
||||||
|
XrActionStateGetInfo getInfo{ XR_TYPE_ACTION_STATE_GET_INFO };
|
||||||
|
getInfo.action = mAction;
|
||||||
|
getInfo.subactionPath = subactionPath;
|
||||||
|
|
||||||
|
XrActionStatePose xrValue{ XR_TYPE_ACTION_STATE_POSE };
|
||||||
|
CHECK_XRCMD(xrGetActionStatePose(xr->impl().xrSession(), &getInfo, &xrValue));
|
||||||
|
|
||||||
|
return xrValue.isActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenXRAction::applyHaptics(XrPath subactionPath, float amplitude)
|
||||||
|
{
|
||||||
|
amplitude = std::max(0.f, std::min(1.f, amplitude));
|
||||||
|
|
||||||
|
auto* xr = Environment::get().getManager();
|
||||||
|
XrHapticVibration vibration{ XR_TYPE_HAPTIC_VIBRATION };
|
||||||
|
vibration.amplitude = amplitude;
|
||||||
|
vibration.duration = XR_MIN_HAPTIC_DURATION;
|
||||||
|
vibration.frequency = XR_FREQUENCY_UNSPECIFIED;
|
||||||
|
|
||||||
|
XrHapticActionInfo hapticActionInfo{ XR_TYPE_HAPTIC_ACTION_INFO };
|
||||||
|
hapticActionInfo.action = mAction;
|
||||||
|
hapticActionInfo.subactionPath = subactionPath;
|
||||||
|
CHECK_XRCMD(xrApplyHapticFeedback(xr->impl().xrSession(), &hapticActionInfo, (XrHapticBaseHeader*)&vibration));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
36
apps/openmw/mwvr/openxraction.hpp
Normal file
36
apps/openmw/mwvr/openxraction.hpp
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
#ifndef OPENXR_ACTION_HPP
|
||||||
|
#define OPENXR_ACTION_HPP
|
||||||
|
|
||||||
|
#include <openxr/openxr.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
|
||||||
|
namespace MWVR
|
||||||
|
{
|
||||||
|
/// \brief C++ wrapper for the XrAction type
|
||||||
|
struct OpenXRAction
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
OpenXRAction(const OpenXRAction&) = default;
|
||||||
|
OpenXRAction& operator=(const OpenXRAction&) = default;
|
||||||
|
public:
|
||||||
|
OpenXRAction(XrAction action, XrActionType actionType, const std::string& actionName, const std::string& localName);
|
||||||
|
~OpenXRAction();
|
||||||
|
|
||||||
|
//! Convenience
|
||||||
|
operator XrAction() { return mAction; }
|
||||||
|
|
||||||
|
bool getFloat(XrPath subactionPath, float& value);
|
||||||
|
bool getBool(XrPath subactionPath, bool& value);
|
||||||
|
bool getPoseIsActive(XrPath subactionPath);
|
||||||
|
bool applyHaptics(XrPath subactionPath, float amplitude);
|
||||||
|
|
||||||
|
XrAction mAction = XR_NULL_HANDLE;
|
||||||
|
XrActionType mType;
|
||||||
|
std::string mName;
|
||||||
|
std::string mLocalName;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
232
apps/openmw/mwvr/openxractionset.cpp
Normal file
232
apps/openmw/mwvr/openxractionset.cpp
Normal file
|
@ -0,0 +1,232 @@
|
||||||
|
#include "openxractionset.hpp"
|
||||||
|
#include "openxrdebug.hpp"
|
||||||
|
|
||||||
|
#include "vrenvironment.hpp"
|
||||||
|
#include "openxrmanager.hpp"
|
||||||
|
#include "openxrmanagerimpl.hpp"
|
||||||
|
#include "openxraction.hpp"
|
||||||
|
|
||||||
|
#include <openxr/openxr.h>
|
||||||
|
|
||||||
|
#include <components/misc/stringops.hpp>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
// TODO: should implement actual safe strcpy
|
||||||
|
#ifdef __linux__
|
||||||
|
#define strcpy_s(dst, src) int(strcpy(dst, src) != nullptr)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace MWVR
|
||||||
|
{
|
||||||
|
|
||||||
|
OpenXRActionSet::OpenXRActionSet(const std::string& actionSetName, std::shared_ptr<AxisAction::Deadzone> deadzone)
|
||||||
|
: mActionSet(nullptr)
|
||||||
|
, mLocalizedName(actionSetName)
|
||||||
|
, mInternalName(Misc::StringUtils::lowerCase(actionSetName))
|
||||||
|
, mDeadzone(deadzone)
|
||||||
|
{
|
||||||
|
mActionSet = createActionSet(actionSetName);
|
||||||
|
// When starting to account for more devices than oculus touch, this section may need some expansion/redesign.
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
OpenXRActionSet::createPoseAction(
|
||||||
|
TrackedLimb limb,
|
||||||
|
const std::string& actionName,
|
||||||
|
const std::string& localName)
|
||||||
|
{
|
||||||
|
mTrackerMap.emplace(limb, new PoseAction(std::move(createXRAction(XR_ACTION_TYPE_POSE_INPUT, actionName, localName))));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
OpenXRActionSet::createHapticsAction(
|
||||||
|
TrackedLimb limb,
|
||||||
|
const std::string& actionName,
|
||||||
|
const std::string& localName)
|
||||||
|
{
|
||||||
|
mHapticsMap.emplace(limb, new HapticsAction(std::move(createXRAction(XR_ACTION_TYPE_VIBRATION_OUTPUT, actionName, localName))));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
void
|
||||||
|
OpenXRActionSet::createMWAction<AxisAction>(
|
||||||
|
int openMWAction,
|
||||||
|
const std::string& actionName,
|
||||||
|
const std::string& localName)
|
||||||
|
{
|
||||||
|
auto xrAction = createXRAction(AxisAction::ActionType, mInternalName + "_" + actionName, mLocalizedName + " " + localName);
|
||||||
|
mActionMap.emplace(actionName, new AxisAction(openMWAction, std::move(xrAction), mDeadzone));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename A>
|
||||||
|
void
|
||||||
|
OpenXRActionSet::createMWAction(
|
||||||
|
int openMWAction,
|
||||||
|
const std::string& actionName,
|
||||||
|
const std::string& localName)
|
||||||
|
{
|
||||||
|
auto xrAction = createXRAction(A::ActionType, mInternalName + "_" + actionName, mLocalizedName + " " + localName);
|
||||||
|
mActionMap.emplace(actionName, new A(openMWAction, std::move(xrAction)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
OpenXRActionSet::createMWAction(
|
||||||
|
VrControlType controlType,
|
||||||
|
int openMWAction,
|
||||||
|
const std::string& actionName,
|
||||||
|
const std::string& localName)
|
||||||
|
{
|
||||||
|
switch (controlType)
|
||||||
|
{
|
||||||
|
case VrControlType::Press:
|
||||||
|
return createMWAction<ButtonPressAction>(openMWAction, actionName, localName);
|
||||||
|
case VrControlType::LongPress:
|
||||||
|
return createMWAction<ButtonLongPressAction>(openMWAction, actionName, localName);
|
||||||
|
case VrControlType::Hold:
|
||||||
|
return createMWAction<ButtonHoldAction>(openMWAction, actionName, localName);
|
||||||
|
case VrControlType::Axis:
|
||||||
|
return createMWAction<AxisAction>(openMWAction, actionName, localName);
|
||||||
|
//case VrControlType::Pose:
|
||||||
|
// return createMWAction<PoseAction>(openMWAction, actionName, localName);
|
||||||
|
//case VrControlType::Haptic:
|
||||||
|
// return createMWAction<HapticsAction>(openMWAction, actionName, localName);
|
||||||
|
default:
|
||||||
|
Log(Debug::Warning) << "createMWAction: pose/haptics Not implemented here";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
XrActionSet
|
||||||
|
OpenXRActionSet::createActionSet(const std::string& name)
|
||||||
|
{
|
||||||
|
std::string localized_name = name;
|
||||||
|
std::string internal_name = Misc::StringUtils::lowerCase(name);
|
||||||
|
auto* xr = Environment::get().getManager();
|
||||||
|
XrActionSet actionSet = XR_NULL_HANDLE;
|
||||||
|
XrActionSetCreateInfo createInfo{ XR_TYPE_ACTION_SET_CREATE_INFO };
|
||||||
|
strcpy_s(createInfo.actionSetName, internal_name.c_str());
|
||||||
|
strcpy_s(createInfo.localizedActionSetName, localized_name.c_str());
|
||||||
|
createInfo.priority = 0;
|
||||||
|
CHECK_XRCMD(xrCreateActionSet(xr->impl().xrInstance(), &createInfo, &actionSet));
|
||||||
|
VrDebug::setName(actionSet, "OpenMW XR Action Set " + name);
|
||||||
|
return actionSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenXRActionSet::suggestBindings(std::vector<XrActionSuggestedBinding>& xrSuggestedBindings, const SuggestedBindings& mwSuggestedBindings)
|
||||||
|
{
|
||||||
|
std::vector<XrActionSuggestedBinding> suggestedBindings;
|
||||||
|
if (!mTrackerMap.empty())
|
||||||
|
{
|
||||||
|
suggestedBindings.emplace_back(XrActionSuggestedBinding{ *mTrackerMap[TrackedLimb::LEFT_HAND], getXrPath("/user/hand/left/input/aim/pose") });
|
||||||
|
suggestedBindings.emplace_back(XrActionSuggestedBinding{ *mTrackerMap[TrackedLimb::RIGHT_HAND], getXrPath("/user/hand/right/input/aim/pose") });
|
||||||
|
}
|
||||||
|
if(!mHapticsMap.empty())
|
||||||
|
{
|
||||||
|
suggestedBindings.emplace_back(XrActionSuggestedBinding{ *mHapticsMap[TrackedLimb::LEFT_HAND], getXrPath("/user/hand/left/output/haptic") });
|
||||||
|
suggestedBindings.emplace_back(XrActionSuggestedBinding{ *mHapticsMap[TrackedLimb::RIGHT_HAND], getXrPath("/user/hand/right/output/haptic") });
|
||||||
|
};
|
||||||
|
|
||||||
|
for (auto& mwSuggestedBinding : mwSuggestedBindings)
|
||||||
|
{
|
||||||
|
auto xrAction = mActionMap.find(mwSuggestedBinding.action);
|
||||||
|
if (xrAction == mActionMap.end())
|
||||||
|
{
|
||||||
|
Log(Debug::Error) << "OpenXRActionSet: Unknown action " << mwSuggestedBinding.action;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
suggestedBindings.push_back({ *xrAction->second, getXrPath(mwSuggestedBinding.path) });
|
||||||
|
}
|
||||||
|
|
||||||
|
xrSuggestedBindings.insert(xrSuggestedBindings.end(), suggestedBindings.begin(), suggestedBindings.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
XrSpace OpenXRActionSet::xrActionSpace(TrackedLimb limb)
|
||||||
|
{
|
||||||
|
return mTrackerMap[limb]->xrSpace();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::unique_ptr<OpenXRAction>
|
||||||
|
OpenXRActionSet::createXRAction(
|
||||||
|
XrActionType actionType,
|
||||||
|
const std::string& actionName,
|
||||||
|
const std::string& localName)
|
||||||
|
{
|
||||||
|
std::vector<XrPath> subactionPaths;
|
||||||
|
XrActionCreateInfo createInfo{ XR_TYPE_ACTION_CREATE_INFO };
|
||||||
|
createInfo.actionType = actionType;
|
||||||
|
strcpy_s(createInfo.actionName, actionName.c_str());
|
||||||
|
strcpy_s(createInfo.localizedActionName, localName.c_str());
|
||||||
|
|
||||||
|
XrAction action = XR_NULL_HANDLE;
|
||||||
|
CHECK_XRCMD(xrCreateAction(mActionSet, &createInfo, &action));
|
||||||
|
return std::unique_ptr<OpenXRAction>{new OpenXRAction{ action, actionType, actionName, localName }};
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
OpenXRActionSet::updateControls()
|
||||||
|
{
|
||||||
|
auto* xr = Environment::get().getManager();
|
||||||
|
if (!xr->impl().appShouldReadInput())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const XrActiveActionSet activeActionSet{ mActionSet, XR_NULL_PATH };
|
||||||
|
XrActionsSyncInfo syncInfo{ XR_TYPE_ACTIONS_SYNC_INFO };
|
||||||
|
syncInfo.countActiveActionSets = 1;
|
||||||
|
syncInfo.activeActionSets = &activeActionSet;
|
||||||
|
CHECK_XRCMD(xrSyncActions(xr->impl().xrSession(), &syncInfo));
|
||||||
|
|
||||||
|
mActionQueue.clear();
|
||||||
|
for (auto& action : mActionMap)
|
||||||
|
action.second->updateAndQueue(mActionQueue);
|
||||||
|
}
|
||||||
|
|
||||||
|
XrPath OpenXRActionSet::getXrPath(const std::string& path)
|
||||||
|
{
|
||||||
|
auto* xr = Environment::get().getManager();
|
||||||
|
XrPath xrpath = 0;
|
||||||
|
CHECK_XRCMD(xrStringToPath(xr->impl().xrInstance(), path.c_str(), &xrpath));
|
||||||
|
return xrpath;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Action* OpenXRActionSet::nextAction()
|
||||||
|
{
|
||||||
|
if (mActionQueue.empty())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
const auto* action = mActionQueue.front();
|
||||||
|
mActionQueue.pop_front();
|
||||||
|
return action;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Pose
|
||||||
|
OpenXRActionSet::getLimbPose(
|
||||||
|
int64_t time,
|
||||||
|
TrackedLimb limb)
|
||||||
|
{
|
||||||
|
auto it = mTrackerMap.find(limb);
|
||||||
|
if (it == mTrackerMap.end())
|
||||||
|
{
|
||||||
|
Log(Debug::Error) << "OpenXRActionSet: No such tracker: " << limb;
|
||||||
|
return Pose{};
|
||||||
|
}
|
||||||
|
|
||||||
|
it->second->update(time);
|
||||||
|
return it->second->value();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenXRActionSet::applyHaptics(TrackedLimb limb, float intensity)
|
||||||
|
{
|
||||||
|
auto it = mHapticsMap.find(limb);
|
||||||
|
if (it == mHapticsMap.end())
|
||||||
|
{
|
||||||
|
Log(Debug::Error) << "OpenXRActionSet: No such tracker: " << limb;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
it->second->apply(intensity);
|
||||||
|
}
|
||||||
|
}
|
58
apps/openmw/mwvr/openxractionset.hpp
Normal file
58
apps/openmw/mwvr/openxractionset.hpp
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
#ifndef OPENXR_ACTIONSET_HPP
|
||||||
|
#define OPENXR_ACTIONSET_HPP
|
||||||
|
|
||||||
|
#include "vrinput.hpp"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
namespace MWVR
|
||||||
|
{
|
||||||
|
/// \brief Generates and manages an OpenXR ActionSet and associated actions.
|
||||||
|
class OpenXRActionSet
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using Actions = MWInput::Actions;
|
||||||
|
|
||||||
|
OpenXRActionSet(const std::string& actionSetName, std::shared_ptr<AxisAction::Deadzone> deadzone);
|
||||||
|
|
||||||
|
//! Update all controls and queue any actions
|
||||||
|
void updateControls();
|
||||||
|
|
||||||
|
//! Get next action from queue (repeat until null is returned)
|
||||||
|
const Action* nextAction();
|
||||||
|
|
||||||
|
//! Get current pose of limb in space.
|
||||||
|
Pose getLimbPose(int64_t time, TrackedLimb limb);
|
||||||
|
|
||||||
|
//! Apply haptics of the given intensity to the given limb
|
||||||
|
void applyHaptics(TrackedLimb limb, float intensity);
|
||||||
|
|
||||||
|
XrActionSet xrActionSet() { return mActionSet; };
|
||||||
|
void suggestBindings(std::vector<XrActionSuggestedBinding>& xrSuggestedBindings, const SuggestedBindings& mwSuggestedBindings);
|
||||||
|
|
||||||
|
XrSpace xrActionSpace(TrackedLimb limb);
|
||||||
|
|
||||||
|
void createMWAction(VrControlType controlType, int openMWAction, const std::string& actionName, const std::string& localName);
|
||||||
|
void createPoseAction(TrackedLimb limb, const std::string& actionName, const std::string& localName);
|
||||||
|
void createHapticsAction(TrackedLimb limb, const std::string& actionName, const std::string& localName);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
template<typename A>
|
||||||
|
void createMWAction(int openMWAction, const std::string& actionName, const std::string& localName);
|
||||||
|
std::unique_ptr<OpenXRAction> createXRAction(XrActionType actionType, const std::string& actionName, const std::string& localName);
|
||||||
|
XrPath getXrPath(const std::string& path);
|
||||||
|
XrActionSet createActionSet(const std::string& name);
|
||||||
|
|
||||||
|
XrActionSet mActionSet{ nullptr };
|
||||||
|
std::string mLocalizedName{};
|
||||||
|
std::string mInternalName{};
|
||||||
|
std::map<std::string, std::unique_ptr<Action>> mActionMap;
|
||||||
|
std::map<TrackedLimb, std::unique_ptr<PoseAction>> mTrackerMap;
|
||||||
|
std::map<TrackedLimb, std::unique_ptr<HapticsAction>> mHapticsMap;
|
||||||
|
std::deque<const Action*> mActionQueue{};
|
||||||
|
std::shared_ptr<AxisAction::Deadzone> mDeadzone;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
36
apps/openmw/mwvr/openxrdebug.cpp
Normal file
36
apps/openmw/mwvr/openxrdebug.cpp
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
#include "openxrdebug.hpp"
|
||||||
|
#include "openxrmanagerimpl.hpp"
|
||||||
|
#include "vrenvironment.hpp"
|
||||||
|
|
||||||
|
// The OpenXR SDK's platform headers assume we've included these windows headers
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <objbase.h>
|
||||||
|
|
||||||
|
#elif __linux__
|
||||||
|
#include <X11/Xlib.h>
|
||||||
|
#include <GL/glx.h>
|
||||||
|
#undef None
|
||||||
|
|
||||||
|
#else
|
||||||
|
#error Unsupported platform
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <openxr/openxr_platform_defines.h>
|
||||||
|
#include <openxr/openxr_reflection.h>
|
||||||
|
|
||||||
|
void MWVR::VrDebug::setName(uint64_t handle, XrObjectType type, const std::string& name)
|
||||||
|
{
|
||||||
|
auto& xrManager = Environment::get().getManager()->impl();
|
||||||
|
if (xrManager.xrExtensionIsEnabled(XR_EXT_DEBUG_UTILS_EXTENSION_NAME))
|
||||||
|
{
|
||||||
|
XrDebugUtilsObjectNameInfoEXT nameInfo{ XR_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT, nullptr };
|
||||||
|
nameInfo.objectHandle = handle;
|
||||||
|
nameInfo.objectType = type;
|
||||||
|
nameInfo.objectName = name.c_str();
|
||||||
|
|
||||||
|
static PFN_xrSetDebugUtilsObjectNameEXT setDebugUtilsObjectNameEXT
|
||||||
|
= reinterpret_cast<PFN_xrSetDebugUtilsObjectNameEXT>(xrManager.xrGetFunction("xrSetDebugUtilsObjectNameEXT"));
|
||||||
|
CHECK_XRCMD(setDebugUtilsObjectNameEXT(xrManager.xrInstance(), &nameInfo));
|
||||||
|
}
|
||||||
|
}
|
73
apps/openmw/mwvr/openxrdebug.hpp
Normal file
73
apps/openmw/mwvr/openxrdebug.hpp
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
#ifndef OPENXR_DEBUG_HPP
|
||||||
|
#define OPENXR_DEBUG_HPP
|
||||||
|
|
||||||
|
#include <openxr/openxr.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace MWVR
|
||||||
|
{
|
||||||
|
namespace VrDebug
|
||||||
|
{
|
||||||
|
//! Translates an OpenXR object to the associated XrObjectType enum value
|
||||||
|
template<typename T> XrObjectType getObjectType(T t);
|
||||||
|
|
||||||
|
//! Associates a name with an OpenXR symbol if XR_EXT_debug_utils is enabled
|
||||||
|
template<typename T> void setName(T t, const std::string& name);
|
||||||
|
|
||||||
|
//! Associates a name with an OpenXR symbol if XR_EXT_debug_utils is enabled
|
||||||
|
void setName(uint64_t handle, XrObjectType type, const std::string& name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T> inline void MWVR::VrDebug::setName(T t, const std::string& name)
|
||||||
|
{
|
||||||
|
setName(reinterpret_cast<uint64_t>(t), getObjectType(t), name);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> inline XrObjectType MWVR::VrDebug::getObjectType<XrInstance>(XrInstance)
|
||||||
|
{
|
||||||
|
return XR_OBJECT_TYPE_INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> inline XrObjectType MWVR::VrDebug::getObjectType<XrSession>(XrSession)
|
||||||
|
{
|
||||||
|
return XR_OBJECT_TYPE_SESSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> inline XrObjectType MWVR::VrDebug::getObjectType<XrSpace>(XrSpace)
|
||||||
|
{
|
||||||
|
return XR_OBJECT_TYPE_SPACE;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> inline XrObjectType MWVR::VrDebug::getObjectType<XrActionSet>(XrActionSet)
|
||||||
|
{
|
||||||
|
return XR_OBJECT_TYPE_ACTION_SET;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> inline XrObjectType MWVR::VrDebug::getObjectType<XrAction>(XrAction)
|
||||||
|
{
|
||||||
|
return XR_OBJECT_TYPE_ACTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> inline XrObjectType MWVR::VrDebug::getObjectType<XrDebugUtilsMessengerEXT>(XrDebugUtilsMessengerEXT)
|
||||||
|
{
|
||||||
|
return XR_OBJECT_TYPE_DEBUG_UTILS_MESSENGER_EXT;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> inline XrObjectType MWVR::VrDebug::getObjectType<XrSpatialAnchorMSFT>(XrSpatialAnchorMSFT)
|
||||||
|
{
|
||||||
|
return XR_OBJECT_TYPE_SPATIAL_ANCHOR_MSFT;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> inline XrObjectType MWVR::VrDebug::getObjectType<XrHandTrackerEXT>(XrHandTrackerEXT)
|
||||||
|
{
|
||||||
|
return XR_OBJECT_TYPE_HAND_TRACKER_EXT;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T> inline XrObjectType MWVR::VrDebug::getObjectType(T t)
|
||||||
|
{
|
||||||
|
return XR_OBJECT_TYPE_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
188
apps/openmw/mwvr/openxrinput.cpp
Normal file
188
apps/openmw/mwvr/openxrinput.cpp
Normal file
|
@ -0,0 +1,188 @@
|
||||||
|
#include "openxrinput.hpp"
|
||||||
|
|
||||||
|
#include "vrenvironment.hpp"
|
||||||
|
#include "openxrmanager.hpp"
|
||||||
|
#include "openxrmanagerimpl.hpp"
|
||||||
|
#include "openxraction.hpp"
|
||||||
|
|
||||||
|
#include <openxr/openxr.h>
|
||||||
|
|
||||||
|
#include <components/misc/stringops.hpp>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace MWVR
|
||||||
|
{
|
||||||
|
|
||||||
|
OpenXRInput::OpenXRInput(std::shared_ptr<AxisAction::Deadzone> deadzone)
|
||||||
|
{
|
||||||
|
mActionSets.emplace(ActionSet::Gameplay, OpenXRActionSet("Gameplay", deadzone));
|
||||||
|
mActionSets.emplace(ActionSet::GUI, OpenXRActionSet("GUI", deadzone));
|
||||||
|
mActionSets.emplace(ActionSet::Tracking, OpenXRActionSet("Tracking", deadzone));
|
||||||
|
mActionSets.emplace(ActionSet::Haptics, OpenXRActionSet("Haptics", deadzone));
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Applicable actions not (yet) included
|
||||||
|
A_QuickKey1,
|
||||||
|
A_QuickKey2,
|
||||||
|
A_QuickKey3,
|
||||||
|
A_QuickKey4,
|
||||||
|
A_QuickKey5,
|
||||||
|
A_QuickKey6,
|
||||||
|
A_QuickKey7,
|
||||||
|
A_QuickKey8,
|
||||||
|
A_QuickKey9,
|
||||||
|
A_QuickKey10,
|
||||||
|
A_QuickKeysMenu,
|
||||||
|
A_QuickLoad,
|
||||||
|
A_CycleSpellLeft,
|
||||||
|
A_CycleSpellRight,
|
||||||
|
A_CycleWeaponLeft,
|
||||||
|
A_CycleWeaponRight,
|
||||||
|
A_Screenshot, // Generate a VR screenshot?
|
||||||
|
A_Console, // Currently awkward due to a lack of virtual keyboard, but should be included when that's in place
|
||||||
|
*/
|
||||||
|
|
||||||
|
getActionSet(ActionSet::Gameplay).createMWAction(VrControlType::Press, MWInput::A_GameMenu, "game_menu", "Game Menu");
|
||||||
|
getActionSet(ActionSet::Gameplay).createMWAction(VrControlType::Press, A_VrMetaMenu, "meta_menu", "Meta Menu");
|
||||||
|
getActionSet(ActionSet::Gameplay).createMWAction(VrControlType::LongPress, A_Recenter, "reposition_menu", "Reposition Menu");
|
||||||
|
getActionSet(ActionSet::Gameplay).createMWAction(VrControlType::Press, MWInput::A_Inventory, "inventory", "Inventory");
|
||||||
|
getActionSet(ActionSet::Gameplay).createMWAction(VrControlType::Press, MWInput::A_Activate, "activate", "Activate");
|
||||||
|
getActionSet(ActionSet::Gameplay).createMWAction(VrControlType::Hold, MWInput::A_Use, "use", "Use");
|
||||||
|
getActionSet(ActionSet::Gameplay).createMWAction(VrControlType::Hold, MWInput::A_Jump, "jump", "Jump");
|
||||||
|
getActionSet(ActionSet::Gameplay).createMWAction(VrControlType::Press, MWInput::A_ToggleWeapon, "weapon", "Weapon");
|
||||||
|
getActionSet(ActionSet::Gameplay).createMWAction(VrControlType::Press, MWInput::A_ToggleSpell, "spell", "Spell");
|
||||||
|
getActionSet(ActionSet::Gameplay).createMWAction(VrControlType::Press, MWInput::A_CycleSpellLeft, "cycle_spell_left", "Cycle Spell Left");
|
||||||
|
getActionSet(ActionSet::Gameplay).createMWAction(VrControlType::Press, MWInput::A_CycleSpellRight, "cycle_spell_right", "Cycle Spell Right");
|
||||||
|
getActionSet(ActionSet::Gameplay).createMWAction(VrControlType::Press, MWInput::A_CycleWeaponLeft, "cycle_weapon_left", "Cycle Weapon Left");
|
||||||
|
getActionSet(ActionSet::Gameplay).createMWAction(VrControlType::Press, MWInput::A_CycleWeaponRight, "cycle_weapon_right", "Cycle Weapon Right");
|
||||||
|
getActionSet(ActionSet::Gameplay).createMWAction(VrControlType::Hold, MWInput::A_Sneak, "sneak", "Sneak");
|
||||||
|
getActionSet(ActionSet::Gameplay).createMWAction(VrControlType::Press, MWInput::A_QuickKeysMenu, "quick_menu", "Quick Menu");
|
||||||
|
getActionSet(ActionSet::Gameplay).createMWAction(VrControlType::Axis, MWInput::A_LookLeftRight, "look_left_right", "Look Left Right");
|
||||||
|
getActionSet(ActionSet::Gameplay).createMWAction(VrControlType::Axis, MWInput::A_MoveForwardBackward, "move_forward_backward", "Move Forward Backward");
|
||||||
|
getActionSet(ActionSet::Gameplay).createMWAction(VrControlType::Axis, MWInput::A_MoveLeftRight, "move_left_right", "Move Left Right");
|
||||||
|
getActionSet(ActionSet::Gameplay).createMWAction(VrControlType::Press, MWInput::A_Journal, "journal_book", "Journal Book");
|
||||||
|
getActionSet(ActionSet::Gameplay).createMWAction(VrControlType::Press, MWInput::A_QuickSave, "quick_save", "Quick Save");
|
||||||
|
getActionSet(ActionSet::Gameplay).createMWAction(VrControlType::Press, MWInput::A_Rest, "rest", "Rest");
|
||||||
|
getActionSet(ActionSet::Gameplay).createMWAction(VrControlType::Axis, A_ActivateTouch, "activate_touched", "Activate Touch");
|
||||||
|
getActionSet(ActionSet::Gameplay).createMWAction(VrControlType::Press, MWInput::A_AlwaysRun, "always_run", "Always Run");
|
||||||
|
getActionSet(ActionSet::Gameplay).createMWAction(VrControlType::Press, MWInput::A_AutoMove, "auto_move", "Auto Move");
|
||||||
|
getActionSet(ActionSet::Gameplay).createMWAction(VrControlType::Press, MWInput::A_ToggleHUD, "toggle_hud", "Toggle HUD");
|
||||||
|
getActionSet(ActionSet::Gameplay).createMWAction(VrControlType::Press, MWInput::A_ToggleDebug, "toggle_debug", "Toggle the debug hud");
|
||||||
|
|
||||||
|
getActionSet(ActionSet::GUI).createMWAction(VrControlType::Press, MWInput::A_GameMenu, "game_menu", "Game Menu");
|
||||||
|
getActionSet(ActionSet::GUI).createMWAction(VrControlType::LongPress, A_Recenter, "reposition_menu", "Reposition Menu");
|
||||||
|
getActionSet(ActionSet::GUI).createMWAction(VrControlType::Axis, A_MenuUpDown, "menu_up_down", "Menu Up Down");
|
||||||
|
getActionSet(ActionSet::GUI).createMWAction(VrControlType::Axis, A_MenuLeftRight, "menu_left_right", "Menu Left Right");
|
||||||
|
getActionSet(ActionSet::GUI).createMWAction(VrControlType::Press, A_MenuSelect, "menu_select", "Menu Select");
|
||||||
|
getActionSet(ActionSet::GUI).createMWAction(VrControlType::Press, A_MenuBack, "menu_back", "Menu Back");
|
||||||
|
getActionSet(ActionSet::GUI).createMWAction(VrControlType::Hold, MWInput::A_Use, "use", "Use");
|
||||||
|
|
||||||
|
getActionSet(ActionSet::Tracking).createPoseAction(TrackedLimb::LEFT_HAND, "left_hand_pose", "Left Hand Pose");
|
||||||
|
getActionSet(ActionSet::Tracking).createPoseAction(TrackedLimb::RIGHT_HAND, "right_hand_pose", "Right Hand Pose");
|
||||||
|
|
||||||
|
getActionSet(ActionSet::Haptics).createHapticsAction(TrackedLimb::RIGHT_HAND, "right_hand_haptics", "Right Hand Haptics");
|
||||||
|
getActionSet(ActionSet::Haptics).createHapticsAction(TrackedLimb::LEFT_HAND, "left_hand_haptics", "Left Hand Haptics");
|
||||||
|
|
||||||
|
|
||||||
|
auto* xr = Environment::get().getManager();
|
||||||
|
auto* trackingManager = Environment::get().getTrackingManager();
|
||||||
|
|
||||||
|
auto leftHandPath = trackingManager->stringToVRPath("/user/hand/left/input/aim/pose");
|
||||||
|
auto rightHandPath = trackingManager->stringToVRPath("/user/hand/right/input/aim/pose");
|
||||||
|
|
||||||
|
xr->impl().tracker().addTrackingSpace(leftHandPath, getActionSet(ActionSet::Tracking).xrActionSpace(TrackedLimb::LEFT_HAND));
|
||||||
|
xr->impl().tracker().addTrackingSpace(rightHandPath, getActionSet(ActionSet::Tracking).xrActionSpace(TrackedLimb::RIGHT_HAND));
|
||||||
|
};
|
||||||
|
|
||||||
|
OpenXRActionSet& OpenXRInput::getActionSet(ActionSet actionSet)
|
||||||
|
{
|
||||||
|
auto it = mActionSets.find(actionSet);
|
||||||
|
if (it == mActionSets.end())
|
||||||
|
throw std::logic_error("No such action set");
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenXRInput::suggestBindings(ActionSet actionSet, std::string profilePath, const SuggestedBindings& mwSuggestedBindings)
|
||||||
|
{
|
||||||
|
getActionSet(actionSet).suggestBindings(mSuggestedBindings[profilePath], mwSuggestedBindings);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenXRInput::attachActionSets()
|
||||||
|
{
|
||||||
|
auto* xr = Environment::get().getManager();
|
||||||
|
|
||||||
|
// Suggest bindings before attaching
|
||||||
|
for (auto& profile : mSuggestedBindings)
|
||||||
|
{
|
||||||
|
XrPath profilePath = 0;
|
||||||
|
CHECK_XRCMD(
|
||||||
|
xrStringToPath(xr->impl().xrInstance(), profile.first.c_str(), &profilePath));
|
||||||
|
XrInteractionProfileSuggestedBinding xrProfileSuggestedBindings{ XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING };
|
||||||
|
xrProfileSuggestedBindings.interactionProfile = profilePath;
|
||||||
|
xrProfileSuggestedBindings.suggestedBindings = profile.second.data();
|
||||||
|
xrProfileSuggestedBindings.countSuggestedBindings = (uint32_t)profile.second.size();
|
||||||
|
CHECK_XRCMD(xrSuggestInteractionProfileBindings(xr->impl().xrInstance(), &xrProfileSuggestedBindings));
|
||||||
|
mInteractionProfileNames[profilePath] = profile.first;
|
||||||
|
mInteractionProfilePaths[profile.first] = profilePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenXR requires that xrAttachSessionActionSets be called at most once per session.
|
||||||
|
// So collect all action sets
|
||||||
|
std::vector<XrActionSet> actionSets;
|
||||||
|
for (auto& actionSet : mActionSets)
|
||||||
|
actionSets.push_back(actionSet.second.xrActionSet());
|
||||||
|
|
||||||
|
// Attach
|
||||||
|
XrSessionActionSetsAttachInfo attachInfo{ XR_TYPE_SESSION_ACTION_SETS_ATTACH_INFO };
|
||||||
|
attachInfo.countActionSets = actionSets.size();
|
||||||
|
attachInfo.actionSets = actionSets.data();
|
||||||
|
CHECK_XRCMD(xrAttachSessionActionSets(xr->impl().xrSession(), &attachInfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenXRInput::notifyInteractionProfileChanged()
|
||||||
|
{
|
||||||
|
auto xr = MWVR::Environment::get().getManager();
|
||||||
|
xr->impl().xrSession();
|
||||||
|
|
||||||
|
// Unfortunately, openxr does not tell us WHICH profile has changed.
|
||||||
|
std::array<std::string, 5> topLevelUserPaths =
|
||||||
|
{
|
||||||
|
"/user/hand/left",
|
||||||
|
"/user/hand/right",
|
||||||
|
"/user/head",
|
||||||
|
"/user/gamepad",
|
||||||
|
"/user/treadmill"
|
||||||
|
};
|
||||||
|
|
||||||
|
for (auto& userPath : topLevelUserPaths)
|
||||||
|
{
|
||||||
|
auto pathIt = mInteractionProfilePaths.find(userPath);
|
||||||
|
if (pathIt == mInteractionProfilePaths.end())
|
||||||
|
{
|
||||||
|
XrPath xrUserPath = XR_NULL_PATH;
|
||||||
|
CHECK_XRCMD(
|
||||||
|
xrStringToPath(xr->impl().xrInstance(), userPath.c_str(), &xrUserPath));
|
||||||
|
mInteractionProfilePaths[userPath] = xrUserPath;
|
||||||
|
pathIt = mInteractionProfilePaths.find(userPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
XrInteractionProfileState interactionProfileState{
|
||||||
|
XR_TYPE_INTERACTION_PROFILE_STATE
|
||||||
|
};
|
||||||
|
|
||||||
|
xrGetCurrentInteractionProfile(xr->impl().xrSession(), pathIt->second, &interactionProfileState);
|
||||||
|
if (interactionProfileState.interactionProfile)
|
||||||
|
{
|
||||||
|
auto activeProfileIt = mActiveInteractionProfiles.find(pathIt->second);
|
||||||
|
if (activeProfileIt == mActiveInteractionProfiles.end() || interactionProfileState.interactionProfile != activeProfileIt->second)
|
||||||
|
{
|
||||||
|
auto activeProfileNameIt = mInteractionProfileNames.find(interactionProfileState.interactionProfile);
|
||||||
|
Log(Debug::Verbose) << userPath << ": Interaction profile changed to '" << activeProfileNameIt->second << "'";
|
||||||
|
mActiveInteractionProfiles[pathIt->second] = interactionProfileState.interactionProfile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
44
apps/openmw/mwvr/openxrinput.hpp
Normal file
44
apps/openmw/mwvr/openxrinput.hpp
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
#ifndef OPENXR_INPUT_HPP
|
||||||
|
#define OPENXR_INPUT_HPP
|
||||||
|
|
||||||
|
#include "vrinput.hpp"
|
||||||
|
#include "openxractionset.hpp"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
namespace MWVR
|
||||||
|
{
|
||||||
|
/// \brief Generates and manages OpenXR Actions and ActionSets by generating openxr bindings from a list of SuggestedBindings structs.
|
||||||
|
class OpenXRInput
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using XrSuggestedBindings = std::vector<XrActionSuggestedBinding>;
|
||||||
|
using XrProfileSuggestedBindings = std::map<std::string, XrSuggestedBindings>;
|
||||||
|
|
||||||
|
//! Default constructor, creates two ActionSets: Gameplay and GUI
|
||||||
|
OpenXRInput(std::shared_ptr<AxisAction::Deadzone> deadzone);
|
||||||
|
|
||||||
|
//! Get the specified actionSet.
|
||||||
|
OpenXRActionSet& getActionSet(ActionSet actionSet);
|
||||||
|
|
||||||
|
//! Suggest bindings for the specific actionSet and profile pair. Call things after calling attachActionSets is an error.
|
||||||
|
void suggestBindings(ActionSet actionSet, std::string profile, const SuggestedBindings& mwSuggestedBindings);
|
||||||
|
|
||||||
|
//! Set bindings and attach actionSets to the session.
|
||||||
|
void attachActionSets();
|
||||||
|
|
||||||
|
//! Notify that active interaction profile has changed
|
||||||
|
void notifyInteractionProfileChanged();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::map<ActionSet, OpenXRActionSet> mActionSets{};
|
||||||
|
std::map<XrPath, std::string> mInteractionProfileNames{};
|
||||||
|
std::map<std::string, XrPath> mInteractionProfilePaths{};
|
||||||
|
std::map<XrPath, XrPath> mActiveInteractionProfiles;
|
||||||
|
XrProfileSuggestedBindings mSuggestedBindings{};
|
||||||
|
bool mAttached = false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
155
apps/openmw/mwvr/openxrmanager.cpp
Normal file
155
apps/openmw/mwvr/openxrmanager.cpp
Normal file
|
@ -0,0 +1,155 @@
|
||||||
|
#include "openxrmanager.hpp"
|
||||||
|
#include "openxrdebug.hpp"
|
||||||
|
#include "vrenvironment.hpp"
|
||||||
|
#include "openxrmanagerimpl.hpp"
|
||||||
|
#include "../mwinput/inputmanagerimp.hpp"
|
||||||
|
|
||||||
|
#include <components/debug/debuglog.hpp>
|
||||||
|
|
||||||
|
namespace MWVR
|
||||||
|
{
|
||||||
|
OpenXRManager::OpenXRManager()
|
||||||
|
: mPrivate(nullptr)
|
||||||
|
, mMutex()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
OpenXRManager::~OpenXRManager()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
OpenXRManager::realized() const
|
||||||
|
{
|
||||||
|
return !!mPrivate;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenXRManager::handleEvents()
|
||||||
|
{
|
||||||
|
if (realized())
|
||||||
|
return impl().handleEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
FrameInfo OpenXRManager::waitFrame()
|
||||||
|
{
|
||||||
|
return impl().waitFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenXRManager::beginFrame()
|
||||||
|
{
|
||||||
|
return impl().beginFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenXRManager::endFrame(FrameInfo frameInfo, const std::array<CompositionLayerProjectionView, 2>* layerStack)
|
||||||
|
{
|
||||||
|
return impl().endFrame(frameInfo, layerStack);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenXRManager::appShouldSyncFrameLoop() const
|
||||||
|
{
|
||||||
|
if (realized())
|
||||||
|
return impl().appShouldSyncFrameLoop();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenXRManager::appShouldRender() const
|
||||||
|
{
|
||||||
|
if (realized())
|
||||||
|
return impl().appShouldRender();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenXRManager::appShouldReadInput() const
|
||||||
|
{
|
||||||
|
if (realized())
|
||||||
|
return impl().appShouldReadInput();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
OpenXRManager::realize(
|
||||||
|
osg::GraphicsContext* gc)
|
||||||
|
{
|
||||||
|
lock_guard lock(mMutex);
|
||||||
|
if (!realized())
|
||||||
|
{
|
||||||
|
gc->makeCurrent();
|
||||||
|
mPrivate = std::make_shared<OpenXRManagerImpl>(gc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenXRManager::enablePredictions()
|
||||||
|
{
|
||||||
|
return impl().enablePredictions();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenXRManager::disablePredictions()
|
||||||
|
{
|
||||||
|
return impl().disablePredictions();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenXRManager::xrResourceAcquired()
|
||||||
|
{
|
||||||
|
return impl().xrResourceAcquired();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenXRManager::xrResourceReleased()
|
||||||
|
{
|
||||||
|
return impl().xrResourceReleased();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array<View, 2> OpenXRManager::getPredictedViews(int64_t predictedDisplayTime, ReferenceSpace space)
|
||||||
|
{
|
||||||
|
return impl().getPredictedViews(predictedDisplayTime, space);
|
||||||
|
}
|
||||||
|
|
||||||
|
MWVR::Pose OpenXRManager::getPredictedHeadPose(int64_t predictedDisplayTime, ReferenceSpace space)
|
||||||
|
{
|
||||||
|
return impl().getPredictedHeadPose(predictedDisplayTime, space);
|
||||||
|
}
|
||||||
|
|
||||||
|
long long OpenXRManager::getLastPredictedDisplayTime()
|
||||||
|
{
|
||||||
|
return impl().getLastPredictedDisplayTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
long long OpenXRManager::getLastPredictedDisplayPeriod()
|
||||||
|
{
|
||||||
|
return impl().getLastPredictedDisplayPeriod();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array<SwapchainConfig, 2> OpenXRManager::getRecommendedSwapchainConfig() const
|
||||||
|
{
|
||||||
|
return impl().getRecommendedSwapchainConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenXRManager::xrExtensionIsEnabled(const char* extensionName) const
|
||||||
|
{
|
||||||
|
return impl().xrExtensionIsEnabled(extensionName);
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t OpenXRManager::selectColorFormat()
|
||||||
|
{
|
||||||
|
return impl().selectColorFormat();
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t OpenXRManager::selectDepthFormat()
|
||||||
|
{
|
||||||
|
return impl().selectDepthFormat();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenXRManager::eraseFormat(int64_t format)
|
||||||
|
{
|
||||||
|
return impl().eraseFormat(format);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
OpenXRManager::CleanupOperation::operator()(
|
||||||
|
osg::GraphicsContext* gc)
|
||||||
|
{
|
||||||
|
// TODO: Use this to make proper cleanup such as cleaning up VRFramebuffers.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
122
apps/openmw/mwvr/openxrmanager.hpp
Normal file
122
apps/openmw/mwvr/openxrmanager.hpp
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
#ifndef MWVR_OPENRXMANAGER_H
|
||||||
|
#define MWVR_OPENRXMANAGER_H
|
||||||
|
#ifndef USE_OPENXR
|
||||||
|
#error "openxrmanager.hpp included without USE_OPENXR defined"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <array>
|
||||||
|
#include <mutex>
|
||||||
|
#include <components/debug/debuglog.hpp>
|
||||||
|
#include <components/sdlutil/sdlgraphicswindow.hpp>
|
||||||
|
#include <components/settings/settings.hpp>
|
||||||
|
#include <osg/Camera>
|
||||||
|
#include <osgViewer/Viewer>
|
||||||
|
#include "vrtypes.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
struct XrSwapchainSubImage;
|
||||||
|
struct XrCompositionLayerBaseHeader;
|
||||||
|
|
||||||
|
namespace MWVR
|
||||||
|
{
|
||||||
|
class OpenXRManagerImpl;
|
||||||
|
|
||||||
|
/// \brief Manage the openxr runtime and session
|
||||||
|
class OpenXRManager : public osg::Referenced
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
class CleanupOperation : public osg::GraphicsOperation
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CleanupOperation() : osg::GraphicsOperation("OpenXRCleanupOperation", false) {};
|
||||||
|
void operator()(osg::GraphicsContext* gc) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
OpenXRManager();
|
||||||
|
|
||||||
|
~OpenXRManager();
|
||||||
|
|
||||||
|
/// Manager has been initialized.
|
||||||
|
bool realized() const;
|
||||||
|
|
||||||
|
//! Forward call to xrWaitFrame()
|
||||||
|
FrameInfo waitFrame();
|
||||||
|
|
||||||
|
//! Forward call to xrBeginFrame()
|
||||||
|
void beginFrame();
|
||||||
|
|
||||||
|
//! Forward call to xrEndFrame()
|
||||||
|
void endFrame(FrameInfo frameInfo, const std::array<CompositionLayerProjectionView, 2>* layerStack);
|
||||||
|
|
||||||
|
//! Whether the app should call the openxr frame sync functions ( xr*Frame() )
|
||||||
|
bool appShouldSyncFrameLoop() const;
|
||||||
|
|
||||||
|
//! Whether the app should render anything.
|
||||||
|
bool appShouldRender() const;
|
||||||
|
|
||||||
|
//! Whether the session is focused and can read input
|
||||||
|
bool appShouldReadInput() const;
|
||||||
|
|
||||||
|
//! Process all openxr events
|
||||||
|
void handleEvents();
|
||||||
|
|
||||||
|
//! Instantiate implementation
|
||||||
|
void realize(osg::GraphicsContext* gc);
|
||||||
|
|
||||||
|
//! Enable pose predictions. Exist to police that predictions are never made out of turn.
|
||||||
|
void enablePredictions();
|
||||||
|
|
||||||
|
//! Disable pose predictions.
|
||||||
|
void disablePredictions();
|
||||||
|
|
||||||
|
//! Must be called every time an openxr resource is acquired to keep track
|
||||||
|
void xrResourceAcquired();
|
||||||
|
|
||||||
|
//! Must be called every time an openxr resource is released to keep track
|
||||||
|
void xrResourceReleased();
|
||||||
|
|
||||||
|
//! Get poses and fov of both eyes at the predicted time, relative to the given reference space. \note Will throw if predictions are disabled.
|
||||||
|
std::array<View, 2> getPredictedViews(int64_t predictedDisplayTime, ReferenceSpace space);
|
||||||
|
|
||||||
|
//! Get the pose of the player's head at the predicted time, relative to the given reference space. \note Will throw if predictions are disabled.
|
||||||
|
MWVR::Pose getPredictedHeadPose(int64_t predictedDisplayTime, ReferenceSpace space);
|
||||||
|
|
||||||
|
//! Last predicted display time returned from xrWaitFrame();
|
||||||
|
long long getLastPredictedDisplayTime();
|
||||||
|
|
||||||
|
//! Last predicted display period returned from xrWaitFrame();
|
||||||
|
long long getLastPredictedDisplayPeriod();
|
||||||
|
|
||||||
|
//! Configuration hints for instantiating swapchains, queried from openxr.
|
||||||
|
std::array<SwapchainConfig, 2> getRecommendedSwapchainConfig() const;
|
||||||
|
|
||||||
|
//! Check whether a given openxr extension is enabled or not
|
||||||
|
bool xrExtensionIsEnabled(const char* extensionName) const;
|
||||||
|
|
||||||
|
//! Selects a color format from among formats offered by the runtime
|
||||||
|
//! Returns 0 if no format is supported.
|
||||||
|
int64_t selectColorFormat();
|
||||||
|
|
||||||
|
//! Selects a depth format from among formats offered by the runtime
|
||||||
|
//! Returns 0 if no format is supported.
|
||||||
|
int64_t selectDepthFormat();
|
||||||
|
|
||||||
|
//! Erase format from list of format candidates
|
||||||
|
void eraseFormat(int64_t format);
|
||||||
|
|
||||||
|
OpenXRManagerImpl& impl() { return *mPrivate; }
|
||||||
|
const OpenXRManagerImpl& impl() const { return *mPrivate; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<OpenXRManagerImpl> mPrivate;
|
||||||
|
std::mutex mMutex;
|
||||||
|
using lock_guard = std::lock_guard<std::mutex>;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
686
apps/openmw/mwvr/openxrmanagerimpl.cpp
Normal file
686
apps/openmw/mwvr/openxrmanagerimpl.cpp
Normal file
|
@ -0,0 +1,686 @@
|
||||||
|
#include "openxrmanagerimpl.hpp"
|
||||||
|
#include "openxrdebug.hpp"
|
||||||
|
#include "openxrplatform.hpp"
|
||||||
|
#include "openxrswapchain.hpp"
|
||||||
|
#include "openxrswapchainimpl.hpp"
|
||||||
|
#include "openxrtypeconversions.hpp"
|
||||||
|
#include "vrenvironment.hpp"
|
||||||
|
#include "vrinputmanager.hpp"
|
||||||
|
|
||||||
|
#include <components/debug/debuglog.hpp>
|
||||||
|
#include <components/sdlutil/sdlgraphicswindow.hpp>
|
||||||
|
#include <components/esm/loadrace.hpp>
|
||||||
|
|
||||||
|
#include "../mwmechanics/actorutil.hpp"
|
||||||
|
|
||||||
|
#include "../mwbase/world.hpp"
|
||||||
|
#include "../mwbase/environment.hpp"
|
||||||
|
|
||||||
|
#include "../mwworld/class.hpp"
|
||||||
|
#include "../mwworld/player.hpp"
|
||||||
|
#include "../mwworld/esmstore.hpp"
|
||||||
|
|
||||||
|
#include <openxr/openxr_reflection.h>
|
||||||
|
|
||||||
|
#include <osg/Camera>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <array>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#define ENUM_CASE_STR(name, val) case name: return #name;
|
||||||
|
#define MAKE_TO_STRING_FUNC(enumType) \
|
||||||
|
inline const char* to_string(enumType e) { \
|
||||||
|
switch (e) { \
|
||||||
|
XR_LIST_ENUM_##enumType(ENUM_CASE_STR) \
|
||||||
|
default: return "Unknown " #enumType; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
MAKE_TO_STRING_FUNC(XrReferenceSpaceType);
|
||||||
|
MAKE_TO_STRING_FUNC(XrViewConfigurationType);
|
||||||
|
MAKE_TO_STRING_FUNC(XrEnvironmentBlendMode);
|
||||||
|
MAKE_TO_STRING_FUNC(XrSessionState);
|
||||||
|
MAKE_TO_STRING_FUNC(XrResult);
|
||||||
|
MAKE_TO_STRING_FUNC(XrFormFactor);
|
||||||
|
MAKE_TO_STRING_FUNC(XrStructureType);
|
||||||
|
|
||||||
|
namespace MWVR
|
||||||
|
{
|
||||||
|
OpenXRManagerImpl::OpenXRManagerImpl(osg::GraphicsContext* gc)
|
||||||
|
: mPlatform(gc)
|
||||||
|
{
|
||||||
|
mInstance = mPlatform.createXrInstance("openmw_vr");
|
||||||
|
|
||||||
|
LogInstanceInfo();
|
||||||
|
|
||||||
|
setupDebugMessenger();
|
||||||
|
|
||||||
|
setupLayerDepth();
|
||||||
|
|
||||||
|
getSystem();
|
||||||
|
|
||||||
|
enumerateViews();
|
||||||
|
|
||||||
|
// TODO: Blend mode
|
||||||
|
// setupBlendMode();
|
||||||
|
|
||||||
|
mSession = mPlatform.createXrSession(mInstance, mSystemId);
|
||||||
|
|
||||||
|
LogReferenceSpaces();
|
||||||
|
|
||||||
|
createReferenceSpaces();
|
||||||
|
|
||||||
|
initTracker();
|
||||||
|
|
||||||
|
getSystemProperties();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenXRManagerImpl::createReferenceSpaces()
|
||||||
|
{
|
||||||
|
XrReferenceSpaceCreateInfo createInfo{ XR_TYPE_REFERENCE_SPACE_CREATE_INFO };
|
||||||
|
createInfo.poseInReferenceSpace.orientation.w = 1.f; // Identity pose
|
||||||
|
createInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_VIEW;
|
||||||
|
CHECK_XRCMD(xrCreateReferenceSpace(mSession, &createInfo, &mReferenceSpaceView));
|
||||||
|
createInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_STAGE;
|
||||||
|
CHECK_XRCMD(xrCreateReferenceSpace(mSession, &createInfo, &mReferenceSpaceStage));
|
||||||
|
createInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL;
|
||||||
|
CHECK_XRCMD(xrCreateReferenceSpace(mSession, &createInfo, &mReferenceSpaceLocal));
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenXRManagerImpl::getSystem()
|
||||||
|
{
|
||||||
|
XrSystemGetInfo systemInfo{ XR_TYPE_SYSTEM_GET_INFO };
|
||||||
|
systemInfo.formFactor = mFormFactor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY;
|
||||||
|
auto res = CHECK_XRCMD(xrGetSystem(mInstance, &systemInfo, &mSystemId));
|
||||||
|
if (!XR_SUCCEEDED(res))
|
||||||
|
mPlatform.initFailure(res, mInstance);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenXRManagerImpl::getSystemProperties()
|
||||||
|
{// Read and log graphics properties for the swapchain
|
||||||
|
CHECK_XRCMD(xrGetSystemProperties(mInstance, mSystemId, &mSystemProperties));
|
||||||
|
|
||||||
|
// Log system properties.
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "System Properties: Name=" << mSystemProperties.systemName << " VendorId=" << mSystemProperties.vendorId << std::endl;
|
||||||
|
ss << "System Graphics Properties: MaxWidth=" << mSystemProperties.graphicsProperties.maxSwapchainImageWidth;
|
||||||
|
ss << " MaxHeight=" << mSystemProperties.graphicsProperties.maxSwapchainImageHeight;
|
||||||
|
ss << " MaxLayers=" << mSystemProperties.graphicsProperties.maxLayerCount << std::endl;
|
||||||
|
ss << "System Tracking Properties: OrientationTracking=" << mSystemProperties.trackingProperties.orientationTracking ? "True" : "False";
|
||||||
|
ss << " PositionTracking=" << mSystemProperties.trackingProperties.positionTracking ? "True" : "False";
|
||||||
|
Log(Debug::Verbose) << ss.str();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenXRManagerImpl::enumerateViews()
|
||||||
|
{
|
||||||
|
uint32_t viewCount = 0;
|
||||||
|
CHECK_XRCMD(xrEnumerateViewConfigurationViews(mInstance, mSystemId, mViewConfigType, 2, &viewCount, mConfigViews.data()));
|
||||||
|
|
||||||
|
if (viewCount != 2)
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "xrEnumerateViewConfigurationViews returned " << viewCount << " views";
|
||||||
|
Log(Debug::Verbose) << ss.str();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenXRManagerImpl::setupLayerDepth()
|
||||||
|
{
|
||||||
|
// Layer depth is enabled, cache the invariant values
|
||||||
|
if (xrExtensionIsEnabled(XR_KHR_COMPOSITION_LAYER_DEPTH_EXTENSION_NAME))
|
||||||
|
{
|
||||||
|
GLfloat depthRange[2] = { 0.f, 1.f };
|
||||||
|
glGetFloatv(GL_DEPTH_RANGE, depthRange);
|
||||||
|
auto nearClip = Settings::Manager::getFloat("near clip", "Camera");
|
||||||
|
|
||||||
|
for (auto& layer : mLayerDepth)
|
||||||
|
{
|
||||||
|
layer.type = XR_TYPE_COMPOSITION_LAYER_DEPTH_INFO_KHR;
|
||||||
|
layer.next = nullptr;
|
||||||
|
layer.minDepth = depthRange[0];
|
||||||
|
layer.maxDepth = depthRange[1];
|
||||||
|
layer.nearZ = nearClip;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string XrResultString(XrResult res)
|
||||||
|
{
|
||||||
|
return to_string(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
OpenXRManagerImpl::~OpenXRManagerImpl()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenXRManagerImpl::setupExtensionsAndLayers()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
static XrBool32 xrDebugCallback(
|
||||||
|
XrDebugUtilsMessageSeverityFlagsEXT messageSeverity,
|
||||||
|
XrDebugUtilsMessageTypeFlagsEXT messageType,
|
||||||
|
const XrDebugUtilsMessengerCallbackDataEXT* callbackData,
|
||||||
|
void* userData)
|
||||||
|
{
|
||||||
|
OpenXRManagerImpl* manager = reinterpret_cast<OpenXRManagerImpl*>(userData);
|
||||||
|
(void)manager;
|
||||||
|
std::string severityStr = "";
|
||||||
|
std::string typeStr = "";
|
||||||
|
|
||||||
|
switch (messageSeverity)
|
||||||
|
{
|
||||||
|
case XR_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT:
|
||||||
|
severityStr = "Verbose"; break;
|
||||||
|
case XR_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT:
|
||||||
|
severityStr = "Info"; break;
|
||||||
|
case XR_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT:
|
||||||
|
severityStr = "Warning"; break;
|
||||||
|
case XR_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT:
|
||||||
|
severityStr = "Error"; break;
|
||||||
|
default:
|
||||||
|
severityStr = "Unknown"; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (messageType)
|
||||||
|
{
|
||||||
|
case XR_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT:
|
||||||
|
typeStr = "General"; break;
|
||||||
|
case XR_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT:
|
||||||
|
typeStr = "Validation"; break;
|
||||||
|
case XR_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT:
|
||||||
|
typeStr = "Performance"; break;
|
||||||
|
case XR_DEBUG_UTILS_MESSAGE_TYPE_CONFORMANCE_BIT_EXT:
|
||||||
|
typeStr = "Conformance"; break;
|
||||||
|
default:
|
||||||
|
typeStr = "Unknown"; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log(Debug::Verbose) << "XrCallback: [" << severityStr << "][" << typeStr << "][ID=" << (callbackData->messageId ? callbackData->messageId : "null") << "]: " << callbackData->message;
|
||||||
|
|
||||||
|
return XR_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenXRManagerImpl::setupDebugMessenger(void)
|
||||||
|
{
|
||||||
|
if (xrExtensionIsEnabled(XR_EXT_DEBUG_UTILS_EXTENSION_NAME))
|
||||||
|
{
|
||||||
|
XrDebugUtilsMessengerCreateInfoEXT createInfo{ XR_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, nullptr };
|
||||||
|
|
||||||
|
// Debug message severity levels
|
||||||
|
if (Settings::Manager::getBool("XR_EXT_debug_utils message level verbose", "VR Debug"))
|
||||||
|
createInfo.messageSeverities |= XR_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT;
|
||||||
|
if (Settings::Manager::getBool("XR_EXT_debug_utils message level info", "VR Debug"))
|
||||||
|
createInfo.messageSeverities |= XR_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT;
|
||||||
|
if (Settings::Manager::getBool("XR_EXT_debug_utils message level warning", "VR Debug"))
|
||||||
|
createInfo.messageSeverities |= XR_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT;
|
||||||
|
if (Settings::Manager::getBool("XR_EXT_debug_utils message level error", "VR Debug"))
|
||||||
|
createInfo.messageSeverities |= XR_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
|
||||||
|
|
||||||
|
// Debug message types
|
||||||
|
if (Settings::Manager::getBool("XR_EXT_debug_utils message type general", "VR Debug"))
|
||||||
|
createInfo.messageTypes |= XR_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT;
|
||||||
|
if (Settings::Manager::getBool("XR_EXT_debug_utils message type validation", "VR Debug"))
|
||||||
|
createInfo.messageTypes |= XR_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT;
|
||||||
|
if (Settings::Manager::getBool("XR_EXT_debug_utils message type performance", "VR Debug"))
|
||||||
|
createInfo.messageTypes |= XR_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
|
||||||
|
if (Settings::Manager::getBool("XR_EXT_debug_utils message type conformance", "VR Debug"))
|
||||||
|
createInfo.messageTypes |= XR_DEBUG_UTILS_MESSAGE_TYPE_CONFORMANCE_BIT_EXT;
|
||||||
|
|
||||||
|
createInfo.userCallback = &xrDebugCallback;
|
||||||
|
createInfo.userData = this;
|
||||||
|
|
||||||
|
PFN_xrCreateDebugUtilsMessengerEXT createDebugUtilsMessenger = reinterpret_cast<PFN_xrCreateDebugUtilsMessengerEXT>(xrGetFunction("xrCreateDebugUtilsMessengerEXT"));
|
||||||
|
assert(createDebugUtilsMessenger);
|
||||||
|
CHECK_XRCMD(createDebugUtilsMessenger(mInstance, &createInfo, &mDebugMessenger));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
OpenXRManagerImpl::LogInstanceInfo() {
|
||||||
|
|
||||||
|
XrInstanceProperties instanceProperties{ XR_TYPE_INSTANCE_PROPERTIES };
|
||||||
|
CHECK_XRCMD(xrGetInstanceProperties(mInstance, &instanceProperties));
|
||||||
|
Log(Debug::Verbose) << "Instance RuntimeName=" << instanceProperties.runtimeName << " RuntimeVersion=" << instanceProperties.runtimeVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
OpenXRManagerImpl::LogReferenceSpaces() {
|
||||||
|
|
||||||
|
uint32_t spaceCount = 0;
|
||||||
|
CHECK_XRCMD(xrEnumerateReferenceSpaces(mSession, 0, &spaceCount, nullptr));
|
||||||
|
std::vector<XrReferenceSpaceType> spaces(spaceCount);
|
||||||
|
CHECK_XRCMD(xrEnumerateReferenceSpaces(mSession, spaceCount, &spaceCount, spaces.data()));
|
||||||
|
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "Available reference spaces=" << spaceCount << std::endl;
|
||||||
|
|
||||||
|
for (XrReferenceSpaceType space : spaces)
|
||||||
|
ss << " Name: " << to_string(space) << std::endl;
|
||||||
|
Log(Debug::Verbose) << ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
FrameInfo
|
||||||
|
OpenXRManagerImpl::waitFrame()
|
||||||
|
{
|
||||||
|
XrFrameWaitInfo frameWaitInfo{ XR_TYPE_FRAME_WAIT_INFO };
|
||||||
|
XrFrameState frameState{ XR_TYPE_FRAME_STATE };
|
||||||
|
|
||||||
|
CHECK_XRCMD(xrWaitFrame(mSession, &frameWaitInfo, &frameState));
|
||||||
|
mFrameState = frameState;
|
||||||
|
|
||||||
|
FrameInfo frameInfo;
|
||||||
|
|
||||||
|
frameInfo.runtimePredictedDisplayTime = mFrameState.predictedDisplayTime;
|
||||||
|
frameInfo.runtimePredictedDisplayPeriod = mFrameState.predictedDisplayPeriod;
|
||||||
|
frameInfo.runtimeRequestsRender = !!mFrameState.shouldRender;
|
||||||
|
|
||||||
|
return frameInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
OpenXRManagerImpl::beginFrame()
|
||||||
|
{
|
||||||
|
XrFrameBeginInfo frameBeginInfo{ XR_TYPE_FRAME_BEGIN_INFO };
|
||||||
|
CHECK_XRCMD(xrBeginFrame(mSession, &frameBeginInfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
OpenXRManagerImpl::endFrame(FrameInfo frameInfo, const std::array<CompositionLayerProjectionView, 2>* layerStack)
|
||||||
|
{
|
||||||
|
std::array<XrCompositionLayerProjectionView, 2> compositionLayerProjectionViews{};
|
||||||
|
XrCompositionLayerProjection layer{};
|
||||||
|
std::array<XrCompositionLayerDepthInfoKHR, 2> compositionLayerDepth{};
|
||||||
|
XrFrameEndInfo frameEndInfo{ XR_TYPE_FRAME_END_INFO };
|
||||||
|
frameEndInfo.displayTime = frameInfo.runtimePredictedDisplayTime;
|
||||||
|
frameEndInfo.environmentBlendMode = mEnvironmentBlendMode;
|
||||||
|
if (layerStack && frameInfo.runtimeRequestsRender)
|
||||||
|
{
|
||||||
|
compositionLayerProjectionViews[(int)Side::LEFT_SIDE] = toXR((*layerStack)[(int)Side::LEFT_SIDE]);
|
||||||
|
compositionLayerProjectionViews[(int)Side::RIGHT_SIDE] = toXR((*layerStack)[(int)Side::RIGHT_SIDE]);
|
||||||
|
layer.type = XR_TYPE_COMPOSITION_LAYER_PROJECTION;
|
||||||
|
layer.space = mReferenceSpaceStage;
|
||||||
|
layer.viewCount = 2;
|
||||||
|
layer.views = compositionLayerProjectionViews.data();
|
||||||
|
auto* xrLayerStack = reinterpret_cast<XrCompositionLayerBaseHeader*>(&layer);
|
||||||
|
|
||||||
|
if (xrExtensionIsEnabled(XR_KHR_COMPOSITION_LAYER_DEPTH_EXTENSION_NAME))
|
||||||
|
{
|
||||||
|
auto farClip = Settings::Manager::getFloat("viewing distance", "Camera");
|
||||||
|
// All values not set here are set previously as they are constant
|
||||||
|
compositionLayerDepth = mLayerDepth;
|
||||||
|
compositionLayerDepth[(int)Side::LEFT_SIDE].farZ = farClip;
|
||||||
|
compositionLayerDepth[(int)Side::RIGHT_SIDE].farZ = farClip;
|
||||||
|
compositionLayerDepth[(int)Side::LEFT_SIDE].subImage = toXR((*layerStack)[(int)Side::LEFT_SIDE].subImage, true);
|
||||||
|
compositionLayerDepth[(int)Side::RIGHT_SIDE].subImage = toXR((*layerStack)[(int)Side::RIGHT_SIDE].subImage, true);
|
||||||
|
if (compositionLayerDepth[(int)Side::LEFT_SIDE].subImage.swapchain != XR_NULL_HANDLE
|
||||||
|
&& compositionLayerDepth[(int)Side::RIGHT_SIDE].subImage.swapchain != XR_NULL_HANDLE)
|
||||||
|
{
|
||||||
|
compositionLayerProjectionViews[(int)Side::LEFT_SIDE].next = &compositionLayerDepth[(int)Side::LEFT_SIDE];
|
||||||
|
compositionLayerProjectionViews[(int)Side::RIGHT_SIDE].next = &compositionLayerDepth[(int)Side::RIGHT_SIDE];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
frameEndInfo.layerCount = 1;
|
||||||
|
frameEndInfo.layers = &xrLayerStack;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
frameEndInfo.layerCount = 0;
|
||||||
|
frameEndInfo.layers = nullptr;
|
||||||
|
}
|
||||||
|
CHECK_XRCMD(xrEndFrame(mSession, &frameEndInfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array<View, 2>
|
||||||
|
OpenXRManagerImpl::getPredictedViews(
|
||||||
|
int64_t predictedDisplayTime,
|
||||||
|
ReferenceSpace space)
|
||||||
|
{
|
||||||
|
//if (!mPredictionsEnabled)
|
||||||
|
//{
|
||||||
|
// Log(Debug::Error) << "Prediction out of order";
|
||||||
|
// throw std::logic_error("Prediction out of order");
|
||||||
|
//}
|
||||||
|
std::array<XrView, 2> xrViews{ {{XR_TYPE_VIEW}, {XR_TYPE_VIEW}} };
|
||||||
|
XrViewState viewState{ XR_TYPE_VIEW_STATE };
|
||||||
|
uint32_t viewCount = 2;
|
||||||
|
|
||||||
|
XrViewLocateInfo viewLocateInfo{ XR_TYPE_VIEW_LOCATE_INFO };
|
||||||
|
viewLocateInfo.viewConfigurationType = mViewConfigType;
|
||||||
|
viewLocateInfo.displayTime = predictedDisplayTime;
|
||||||
|
switch (space)
|
||||||
|
{
|
||||||
|
case ReferenceSpace::STAGE:
|
||||||
|
viewLocateInfo.space = mReferenceSpaceStage;
|
||||||
|
break;
|
||||||
|
case ReferenceSpace::VIEW:
|
||||||
|
viewLocateInfo.space = mReferenceSpaceView;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
CHECK_XRCMD(xrLocateViews(mSession, &viewLocateInfo, &viewState, viewCount, &viewCount, xrViews.data()));
|
||||||
|
|
||||||
|
std::array<View, 2> vrViews{};
|
||||||
|
vrViews[(int)Side::LEFT_SIDE].pose = fromXR(xrViews[(int)Side::LEFT_SIDE].pose);
|
||||||
|
vrViews[(int)Side::RIGHT_SIDE].pose = fromXR(xrViews[(int)Side::RIGHT_SIDE].pose);
|
||||||
|
vrViews[(int)Side::LEFT_SIDE].fov = fromXR(xrViews[(int)Side::LEFT_SIDE].fov);
|
||||||
|
vrViews[(int)Side::RIGHT_SIDE].fov = fromXR(xrViews[(int)Side::RIGHT_SIDE].fov);
|
||||||
|
return vrViews;
|
||||||
|
}
|
||||||
|
|
||||||
|
MWVR::Pose OpenXRManagerImpl::getPredictedHeadPose(
|
||||||
|
int64_t predictedDisplayTime,
|
||||||
|
ReferenceSpace space)
|
||||||
|
{
|
||||||
|
if (!mPredictionsEnabled)
|
||||||
|
{
|
||||||
|
Log(Debug::Error) << "Prediction out of order";
|
||||||
|
throw std::logic_error("Prediction out of order");
|
||||||
|
}
|
||||||
|
XrSpaceLocation location{ XR_TYPE_SPACE_LOCATION };
|
||||||
|
XrSpace limbSpace = mReferenceSpaceView;
|
||||||
|
XrSpace referenceSpace = XR_NULL_HANDLE;
|
||||||
|
|
||||||
|
switch (space)
|
||||||
|
{
|
||||||
|
case ReferenceSpace::STAGE:
|
||||||
|
referenceSpace = mReferenceSpaceStage;
|
||||||
|
break;
|
||||||
|
case ReferenceSpace::VIEW:
|
||||||
|
referenceSpace = mReferenceSpaceView;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
CHECK_XRCMD(xrLocateSpace(limbSpace, referenceSpace, predictedDisplayTime, &location));
|
||||||
|
|
||||||
|
if (!location.locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT)
|
||||||
|
{
|
||||||
|
// Quat must have a magnitude of 1 but openxr sets it to 0 when tracking is unavailable.
|
||||||
|
// I want a no-track pose to still be valid
|
||||||
|
location.pose.orientation.w = 1;
|
||||||
|
}
|
||||||
|
return MWVR::Pose{
|
||||||
|
fromXR(location.pose.position),
|
||||||
|
fromXR(location.pose.orientation)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenXRManagerImpl::handleEvents()
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(mMutex);
|
||||||
|
|
||||||
|
xrQueueEvents();
|
||||||
|
|
||||||
|
while (auto* event = nextEvent())
|
||||||
|
{
|
||||||
|
if (!processEvent(event))
|
||||||
|
{
|
||||||
|
// Do not consider processing an event optional.
|
||||||
|
// Retry once per frame until every event has been successfully processed
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
popEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mXrSessionShouldStop)
|
||||||
|
{
|
||||||
|
if (checkStopCondition())
|
||||||
|
{
|
||||||
|
CHECK_XRCMD(xrEndSession(mSession));
|
||||||
|
mXrSessionShouldStop = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const XrEventDataBaseHeader* OpenXRManagerImpl::nextEvent()
|
||||||
|
{
|
||||||
|
if (mEventQueue.size() > 0)
|
||||||
|
return reinterpret_cast<XrEventDataBaseHeader*> (&mEventQueue.front());
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenXRManagerImpl::processEvent(const XrEventDataBaseHeader* header)
|
||||||
|
{
|
||||||
|
Log(Debug::Verbose) << "OpenXR: Event received: " << to_string(header->type);
|
||||||
|
switch (header->type)
|
||||||
|
{
|
||||||
|
case XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED:
|
||||||
|
{
|
||||||
|
const auto* stateChangeEvent = reinterpret_cast<const XrEventDataSessionStateChanged*>(header);
|
||||||
|
return handleSessionStateChanged(*stateChangeEvent);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED:
|
||||||
|
MWVR::Environment::get().getInputManager()->notifyInteractionProfileChanged();
|
||||||
|
break;
|
||||||
|
case XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING:
|
||||||
|
case XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING:
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
Log(Debug::Verbose) << "OpenXR: Event ignored";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
OpenXRManagerImpl::handleSessionStateChanged(
|
||||||
|
const XrEventDataSessionStateChanged& stateChangedEvent)
|
||||||
|
{
|
||||||
|
Log(Debug::Verbose) << "XrEventDataSessionStateChanged: state " << to_string(mSessionState) << "->" << to_string(stateChangedEvent.state);
|
||||||
|
mSessionState = stateChangedEvent.state;
|
||||||
|
|
||||||
|
// Ref: https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#session-states
|
||||||
|
switch (mSessionState)
|
||||||
|
{
|
||||||
|
case XR_SESSION_STATE_IDLE:
|
||||||
|
{
|
||||||
|
mAppShouldSyncFrameLoop = false;
|
||||||
|
mAppShouldRender = false;
|
||||||
|
mAppShouldReadInput = false;
|
||||||
|
mXrSessionShouldStop = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case XR_SESSION_STATE_READY:
|
||||||
|
{
|
||||||
|
mAppShouldSyncFrameLoop = true;
|
||||||
|
mAppShouldRender = false;
|
||||||
|
mAppShouldReadInput = false;
|
||||||
|
mXrSessionShouldStop = false;
|
||||||
|
|
||||||
|
XrSessionBeginInfo beginInfo{ XR_TYPE_SESSION_BEGIN_INFO };
|
||||||
|
beginInfo.primaryViewConfigurationType = mViewConfigType;
|
||||||
|
CHECK_XRCMD(xrBeginSession(mSession, &beginInfo));
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case XR_SESSION_STATE_STOPPING:
|
||||||
|
{
|
||||||
|
mAppShouldSyncFrameLoop = false;
|
||||||
|
mAppShouldRender = false;
|
||||||
|
mAppShouldReadInput = false;
|
||||||
|
mXrSessionShouldStop = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case XR_SESSION_STATE_SYNCHRONIZED:
|
||||||
|
{
|
||||||
|
mAppShouldSyncFrameLoop = true;
|
||||||
|
mAppShouldRender = false;
|
||||||
|
mAppShouldReadInput = false;
|
||||||
|
mXrSessionShouldStop = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case XR_SESSION_STATE_VISIBLE:
|
||||||
|
{
|
||||||
|
mAppShouldSyncFrameLoop = true;
|
||||||
|
mAppShouldRender = true;
|
||||||
|
mAppShouldReadInput = false;
|
||||||
|
mXrSessionShouldStop = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case XR_SESSION_STATE_FOCUSED:
|
||||||
|
{
|
||||||
|
mAppShouldSyncFrameLoop = true;
|
||||||
|
mAppShouldRender = true;
|
||||||
|
mAppShouldReadInput = true;
|
||||||
|
mXrSessionShouldStop = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
Log(Debug::Warning) << "XrEventDataSessionStateChanged: Ignoring new state " << to_string(mSessionState);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenXRManagerImpl::checkStopCondition()
|
||||||
|
{
|
||||||
|
return mAcquiredResources == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenXRManagerImpl::xrNextEvent(XrEventDataBuffer& eventBuffer)
|
||||||
|
{
|
||||||
|
XrEventDataBaseHeader* baseHeader = reinterpret_cast<XrEventDataBaseHeader*>(&eventBuffer);
|
||||||
|
*baseHeader = { XR_TYPE_EVENT_DATA_BUFFER };
|
||||||
|
const XrResult result = xrPollEvent(mInstance, &eventBuffer);
|
||||||
|
if (result == XR_SUCCESS)
|
||||||
|
{
|
||||||
|
if (baseHeader->type == XR_TYPE_EVENT_DATA_EVENTS_LOST) {
|
||||||
|
const XrEventDataEventsLost* const eventsLost = reinterpret_cast<const XrEventDataEventsLost*>(baseHeader);
|
||||||
|
Log(Debug::Warning) << "OpenXRManagerImpl: Lost " << eventsLost->lostEventCount << " events";
|
||||||
|
}
|
||||||
|
|
||||||
|
return baseHeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result != XR_EVENT_UNAVAILABLE)
|
||||||
|
CHECK_XRRESULT(result, "xrPollEvent");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenXRManagerImpl::popEvent()
|
||||||
|
{
|
||||||
|
if (mEventQueue.size() > 0)
|
||||||
|
mEventQueue.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
OpenXRManagerImpl::xrQueueEvents()
|
||||||
|
{
|
||||||
|
XrEventDataBuffer eventBuffer;
|
||||||
|
while (xrNextEvent(eventBuffer))
|
||||||
|
{
|
||||||
|
mEventQueue.push(eventBuffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenXRManagerImpl::xrExtensionIsEnabled(const char* extensionName) const
|
||||||
|
{
|
||||||
|
return mPlatform.extensionEnabled(extensionName);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenXRManagerImpl::xrResourceAcquired()
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(mMutex);
|
||||||
|
mAcquiredResources++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenXRManagerImpl::xrResourceReleased()
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(mMutex);
|
||||||
|
if (mAcquiredResources == 0)
|
||||||
|
throw std::logic_error("Releasing a nonexistent resource");
|
||||||
|
mAcquiredResources--;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenXRManagerImpl::xrUpdateNames()
|
||||||
|
{
|
||||||
|
VrDebug::setName(mInstance, "OpenMW XR Instance");
|
||||||
|
VrDebug::setName(mSession, "OpenMW XR Session");
|
||||||
|
VrDebug::setName(mReferenceSpaceStage, "OpenMW XR Reference Space Stage");
|
||||||
|
VrDebug::setName(mReferenceSpaceView, "OpenMW XR Reference Space Stage");
|
||||||
|
}
|
||||||
|
|
||||||
|
PFN_xrVoidFunction OpenXRManagerImpl::xrGetFunction(const std::string& name)
|
||||||
|
{
|
||||||
|
PFN_xrVoidFunction function = nullptr;
|
||||||
|
xrGetInstanceProcAddr(mInstance, name.c_str(), &function);
|
||||||
|
return function;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t OpenXRManagerImpl::selectColorFormat()
|
||||||
|
{
|
||||||
|
// Find supported color swapchain format.
|
||||||
|
return mPlatform.selectColorFormat();
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t OpenXRManagerImpl::selectDepthFormat()
|
||||||
|
{
|
||||||
|
// Find supported depth swapchain format.
|
||||||
|
return mPlatform.selectDepthFormat();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenXRManagerImpl::eraseFormat(int64_t format)
|
||||||
|
{
|
||||||
|
mPlatform.eraseFormat(format);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenXRManagerImpl::initTracker()
|
||||||
|
{
|
||||||
|
auto* trackingManager = Environment::get().getTrackingManager();
|
||||||
|
auto headPath = trackingManager->stringToVRPath("/user/head/input/pose");
|
||||||
|
|
||||||
|
mTracker.reset(new OpenXRTracker("pcstage", mReferenceSpaceStage));
|
||||||
|
mTracker->addTrackingSpace(headPath, mReferenceSpaceView);
|
||||||
|
mTrackerToWorldBinding.reset(new VRTrackingToWorldBinding("pcworld", mTracker.get(), headPath));
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenXRManagerImpl::enablePredictions()
|
||||||
|
{
|
||||||
|
mPredictionsEnabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenXRManagerImpl::disablePredictions()
|
||||||
|
{
|
||||||
|
mPredictionsEnabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
long long OpenXRManagerImpl::getLastPredictedDisplayTime()
|
||||||
|
{
|
||||||
|
return mFrameState.predictedDisplayTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
long long OpenXRManagerImpl::getLastPredictedDisplayPeriod()
|
||||||
|
{
|
||||||
|
return mFrameState.predictedDisplayPeriod;
|
||||||
|
}
|
||||||
|
std::array<SwapchainConfig, 2> OpenXRManagerImpl::getRecommendedSwapchainConfig() const
|
||||||
|
{
|
||||||
|
std::array<SwapchainConfig, 2> config{};
|
||||||
|
for (uint32_t i = 0; i < 2; i++)
|
||||||
|
config[i] = SwapchainConfig{
|
||||||
|
(int)mConfigViews[i].recommendedImageRectWidth,
|
||||||
|
(int)mConfigViews[i].recommendedImageRectHeight,
|
||||||
|
(int)mConfigViews[i].recommendedSwapchainSampleCount,
|
||||||
|
(int)mConfigViews[i].maxImageRectWidth,
|
||||||
|
(int)mConfigViews[i].maxImageRectHeight,
|
||||||
|
(int)mConfigViews[i].maxSwapchainSampleCount,
|
||||||
|
};
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
XrSpace OpenXRManagerImpl::getReferenceSpace(ReferenceSpace space)
|
||||||
|
{
|
||||||
|
switch (space)
|
||||||
|
{
|
||||||
|
case ReferenceSpace::STAGE:
|
||||||
|
return mReferenceSpaceStage;
|
||||||
|
case ReferenceSpace::VIEW:
|
||||||
|
return mReferenceSpaceView;
|
||||||
|
}
|
||||||
|
return XR_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
115
apps/openmw/mwvr/openxrmanagerimpl.hpp
Normal file
115
apps/openmw/mwvr/openxrmanagerimpl.hpp
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
#ifndef OPENXR_MANAGER_IMPL_HPP
|
||||||
|
#define OPENXR_MANAGER_IMPL_HPP
|
||||||
|
|
||||||
|
#include "openxrmanager.hpp"
|
||||||
|
#include "openxrplatform.hpp"
|
||||||
|
#include "openxrtracker.hpp"
|
||||||
|
#include "../mwinput/inputmanagerimp.hpp"
|
||||||
|
|
||||||
|
#include <components/debug/debuglog.hpp>
|
||||||
|
#include <components/sdlutil/sdlgraphicswindow.hpp>
|
||||||
|
|
||||||
|
#include <openxr/openxr.h>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <array>
|
||||||
|
#include <map>
|
||||||
|
#include <iostream>
|
||||||
|
#include <thread>
|
||||||
|
#include <chrono>
|
||||||
|
#include <queue>
|
||||||
|
|
||||||
|
namespace MWVR
|
||||||
|
{
|
||||||
|
/// \brief Implementation of OpenXRManager
|
||||||
|
class OpenXRManagerImpl
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
OpenXRManagerImpl(osg::GraphicsContext* gc);
|
||||||
|
~OpenXRManagerImpl(void);
|
||||||
|
|
||||||
|
FrameInfo waitFrame();
|
||||||
|
void beginFrame();
|
||||||
|
void endFrame(FrameInfo frameInfo, const std::array<CompositionLayerProjectionView, 2>* layerStack);
|
||||||
|
bool appShouldSyncFrameLoop() const { return mAppShouldSyncFrameLoop; }
|
||||||
|
bool appShouldRender() const { return mAppShouldRender; }
|
||||||
|
bool appShouldReadInput() const { return mAppShouldReadInput; }
|
||||||
|
std::array<View, 2> getPredictedViews(int64_t predictedDisplayTime, ReferenceSpace space);
|
||||||
|
MWVR::Pose getPredictedHeadPose(int64_t predictedDisplayTime, ReferenceSpace space);
|
||||||
|
void handleEvents();
|
||||||
|
void enablePredictions();
|
||||||
|
void disablePredictions();
|
||||||
|
long long getLastPredictedDisplayTime();
|
||||||
|
long long getLastPredictedDisplayPeriod();
|
||||||
|
std::array<SwapchainConfig, 2> getRecommendedSwapchainConfig() const;
|
||||||
|
XrSpace getReferenceSpace(ReferenceSpace space);
|
||||||
|
XrSession xrSession() const { return mSession; };
|
||||||
|
XrInstance xrInstance() const { return mInstance; };
|
||||||
|
bool xrExtensionIsEnabled(const char* extensionName) const;
|
||||||
|
void xrResourceAcquired();
|
||||||
|
void xrResourceReleased();
|
||||||
|
void xrUpdateNames();
|
||||||
|
PFN_xrVoidFunction xrGetFunction(const std::string& name);
|
||||||
|
int64_t selectColorFormat();
|
||||||
|
int64_t selectDepthFormat();
|
||||||
|
void eraseFormat(int64_t format);
|
||||||
|
OpenXRPlatform& platform() { return mPlatform; }
|
||||||
|
OpenXRTracker& tracker() { return *mTracker; }
|
||||||
|
void initTracker();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void setupExtensionsAndLayers();
|
||||||
|
void setupDebugMessenger(void);
|
||||||
|
void LogInstanceInfo();
|
||||||
|
void LogReferenceSpaces();
|
||||||
|
bool xrNextEvent(XrEventDataBuffer& eventBuffer);
|
||||||
|
void xrQueueEvents();
|
||||||
|
const XrEventDataBaseHeader* nextEvent();
|
||||||
|
bool processEvent(const XrEventDataBaseHeader* header);
|
||||||
|
void popEvent();
|
||||||
|
bool handleSessionStateChanged(const XrEventDataSessionStateChanged& stateChangedEvent);
|
||||||
|
bool checkStopCondition();
|
||||||
|
void createReferenceSpaces();
|
||||||
|
void getSystem();
|
||||||
|
void getSystemProperties();
|
||||||
|
void enumerateViews();
|
||||||
|
void setupLayerDepth();
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
bool initialized = false;
|
||||||
|
bool mPredictionsEnabled = false;
|
||||||
|
XrInstance mInstance = XR_NULL_HANDLE;
|
||||||
|
XrSession mSession = XR_NULL_HANDLE;
|
||||||
|
XrSpace mSpace = XR_NULL_HANDLE;
|
||||||
|
XrFormFactor mFormFactor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY;
|
||||||
|
XrViewConfigurationType mViewConfigType = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO;
|
||||||
|
XrEnvironmentBlendMode mEnvironmentBlendMode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE;
|
||||||
|
XrSystemId mSystemId = XR_NULL_SYSTEM_ID;
|
||||||
|
XrSystemProperties mSystemProperties{ XR_TYPE_SYSTEM_PROPERTIES };
|
||||||
|
std::array<XrViewConfigurationView, 2> mConfigViews{ { {XR_TYPE_VIEW_CONFIGURATION_VIEW}, {XR_TYPE_VIEW_CONFIGURATION_VIEW} } };
|
||||||
|
XrSpace mReferenceSpaceView = XR_NULL_HANDLE;
|
||||||
|
XrSpace mReferenceSpaceStage = XR_NULL_HANDLE;
|
||||||
|
XrSpace mReferenceSpaceLocal = XR_NULL_HANDLE;
|
||||||
|
XrFrameState mFrameState{};
|
||||||
|
XrSessionState mSessionState = XR_SESSION_STATE_UNKNOWN;
|
||||||
|
XrDebugUtilsMessengerEXT mDebugMessenger{ nullptr };
|
||||||
|
OpenXRPlatform mPlatform;
|
||||||
|
|
||||||
|
std::unique_ptr<OpenXRTracker> mTracker{ nullptr };
|
||||||
|
std::unique_ptr<VRTrackingToWorldBinding> mTrackerToWorldBinding{ nullptr };
|
||||||
|
|
||||||
|
bool mXrSessionShouldStop = false;
|
||||||
|
bool mAppShouldSyncFrameLoop = false;
|
||||||
|
bool mAppShouldRender = false;
|
||||||
|
bool mAppShouldReadInput = false;
|
||||||
|
|
||||||
|
uint32_t mAcquiredResources = 0;
|
||||||
|
std::mutex mMutex{};
|
||||||
|
std::queue<XrEventDataBuffer> mEventQueue;
|
||||||
|
|
||||||
|
std::array<XrCompositionLayerDepthInfoKHR, 2> mLayerDepth;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
746
apps/openmw/mwvr/openxrplatform.cpp
Normal file
746
apps/openmw/mwvr/openxrplatform.cpp
Normal file
|
@ -0,0 +1,746 @@
|
||||||
|
#include "openxrswapchainimage.hpp"
|
||||||
|
#include "openxrmanagerimpl.hpp"
|
||||||
|
#include "openxrplatform.hpp"
|
||||||
|
#include "vrenvironment.hpp"
|
||||||
|
|
||||||
|
// The OpenXR SDK's platform headers assume we've included platform headers
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <objbase.h>
|
||||||
|
|
||||||
|
#ifdef XR_USE_GRAPHICS_API_D3D11
|
||||||
|
#include <d3d11_1.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#elif __linux__
|
||||||
|
#include <X11/Xlib.h>
|
||||||
|
#include <GL/glx.h>
|
||||||
|
#undef None
|
||||||
|
|
||||||
|
#else
|
||||||
|
#error Unsupported platform
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <openxr/openxr.h>
|
||||||
|
#include <openxr/openxr_platform.h>
|
||||||
|
#include <openxr/openxr_platform_defines.h>
|
||||||
|
#include <openxr/openxr_reflection.h>
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <deque>
|
||||||
|
#include <cassert>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
namespace MWVR
|
||||||
|
{
|
||||||
|
|
||||||
|
XrResult CheckXrResult(XrResult res, const char* originator, const char* sourceLocation) {
|
||||||
|
static bool initialized = false;
|
||||||
|
static bool sLogAllXrCalls = false;
|
||||||
|
static bool sContinueOnErrors = false;
|
||||||
|
if (!initialized)
|
||||||
|
{
|
||||||
|
initialized = true;
|
||||||
|
sLogAllXrCalls = Settings::Manager::getBool("log all openxr calls", "VR Debug");
|
||||||
|
sContinueOnErrors = Settings::Manager::getBool("continue on errors", "VR Debug");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto resultString = XrResultString(res);
|
||||||
|
|
||||||
|
if (XR_FAILED(res)) {
|
||||||
|
std::stringstream ss;
|
||||||
|
#ifdef _WIN32
|
||||||
|
ss << sourceLocation << ": OpenXR[Error: " << resultString << "][Thread: " << std::this_thread::get_id() << "]: " << originator;
|
||||||
|
#elif __linux__
|
||||||
|
ss << sourceLocation << ": OpenXR[Error: " << resultString << "][Thread: " << std::this_thread::get_id() << "]: " << originator;
|
||||||
|
#endif
|
||||||
|
Log(Debug::Error) << ss.str();
|
||||||
|
if (!sContinueOnErrors)
|
||||||
|
throw std::runtime_error(ss.str().c_str());
|
||||||
|
}
|
||||||
|
else if (res != XR_SUCCESS || sLogAllXrCalls)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
Log(Debug::Verbose) << sourceLocation << ": OpenXR[" << resultString << "][" << std::this_thread::get_id() << "]: " << originator;
|
||||||
|
#elif __linux__
|
||||||
|
Log(Debug::Verbose) << sourceLocation << ": OpenXR[" << resultString << "][" << std::this_thread::get_id() << "]: " << originator;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenXRPlatform::enumerateExtensions(const char* layerName, int logIndent)
|
||||||
|
{
|
||||||
|
uint32_t extensionCount = 0;
|
||||||
|
std::vector<XrExtensionProperties> availableExtensions;
|
||||||
|
CHECK_XRCMD(xrEnumerateInstanceExtensionProperties(layerName, 0, &extensionCount, nullptr));
|
||||||
|
availableExtensions.resize(extensionCount, XrExtensionProperties{ XR_TYPE_EXTENSION_PROPERTIES });
|
||||||
|
CHECK_XRCMD(xrEnumerateInstanceExtensionProperties(layerName, availableExtensions.size(), &extensionCount, availableExtensions.data()));
|
||||||
|
|
||||||
|
std::vector<std::string> extensionNames;
|
||||||
|
const std::string indentStr(logIndent, ' ');
|
||||||
|
for (auto& extension : availableExtensions)
|
||||||
|
{
|
||||||
|
if (layerName)
|
||||||
|
mAvailableLayerExtensions[layerName][extension.extensionName] = extension;
|
||||||
|
else
|
||||||
|
mAvailableExtensions[extension.extensionName] = extension;
|
||||||
|
|
||||||
|
Log(Debug::Verbose) << indentStr << "Name=" << extension.extensionName << " SpecVersion=" << extension.extensionVersion;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct OpenXRPlatformPrivate
|
||||||
|
{
|
||||||
|
OpenXRPlatformPrivate(osg::GraphicsContext* gc);
|
||||||
|
~OpenXRPlatformPrivate();
|
||||||
|
|
||||||
|
#ifdef XR_USE_GRAPHICS_API_D3D11
|
||||||
|
typedef BOOL(WINAPI* P_wglDXSetResourceShareHandleNV)(void* dxObject, HANDLE shareHandle);
|
||||||
|
typedef HANDLE(WINAPI* P_wglDXOpenDeviceNV)(void* dxDevice);
|
||||||
|
typedef BOOL(WINAPI* P_wglDXCloseDeviceNV)(HANDLE hDevice);
|
||||||
|
typedef HANDLE(WINAPI* P_wglDXRegisterObjectNV)(HANDLE hDevice, void* dxObject,
|
||||||
|
GLuint name, GLenum type, GLenum access);
|
||||||
|
typedef BOOL(WINAPI* P_wglDXUnregisterObjectNV)(HANDLE hDevice, HANDLE hObject);
|
||||||
|
typedef BOOL(WINAPI* P_wglDXObjectAccessNV)(HANDLE hObject, GLenum access);
|
||||||
|
typedef BOOL(WINAPI* P_wglDXLockObjectsNV)(HANDLE hDevice, GLint count, HANDLE* hObjects);
|
||||||
|
typedef BOOL(WINAPI* P_wglDXUnlockObjectsNV)(HANDLE hDevice, GLint count, HANDLE* hObjects);
|
||||||
|
|
||||||
|
void initializeD3D11(XrGraphicsRequirementsD3D11KHR requirements)
|
||||||
|
{
|
||||||
|
mD3D11Dll = LoadLibrary("D3D11.dll");
|
||||||
|
|
||||||
|
if (!mD3D11Dll)
|
||||||
|
throw std::runtime_error("Current OpenXR runtime requires DirectX >= 11.0 but D3D11.dll was not found.");
|
||||||
|
|
||||||
|
pD3D11CreateDevice = reinterpret_cast<PFN_D3D11_CREATE_DEVICE>(GetProcAddress(mD3D11Dll, "D3D11CreateDevice"));
|
||||||
|
|
||||||
|
if (!pD3D11CreateDevice)
|
||||||
|
throw std::runtime_error("Symbol 'D3D11CreateDevice' not found in D3D11.dll");
|
||||||
|
|
||||||
|
// Create the device and device context objects
|
||||||
|
pD3D11CreateDevice(
|
||||||
|
nullptr,
|
||||||
|
D3D_DRIVER_TYPE_HARDWARE,
|
||||||
|
nullptr,
|
||||||
|
0,
|
||||||
|
nullptr,
|
||||||
|
0,
|
||||||
|
D3D11_SDK_VERSION,
|
||||||
|
&mD3D11Device,
|
||||||
|
nullptr,
|
||||||
|
&mD3D11ImmediateContext);
|
||||||
|
|
||||||
|
mD3D11bindings.device = mD3D11Device;
|
||||||
|
|
||||||
|
//typedef HANDLE (WINAPI* P_wglDXOpenDeviceNV)(void* dxDevice);
|
||||||
|
//P_wglDXOpenDeviceNV wglDXOpenDeviceNV = reinterpret_cast<P_wglDXOpenDeviceNV>(wglGetProcAddress("wglDXOpenDeviceNV"));
|
||||||
|
//P_wglDXOpenDeviceNV wglDXOpenDeviceNV = reinterpret_cast<P_wglDXOpenDeviceNV>(wglGetProcAddress("wglDXOpenDeviceNV"));
|
||||||
|
|
||||||
|
#define LOAD_WGL(a) a = reinterpret_cast<decltype(a)>(wglGetProcAddress(#a)); if(!a) throw std::runtime_error("Extension WGL_NV_DX_interop2 required to run OpenMW VR via DirectX missing expected symbol '" #a "'.")
|
||||||
|
LOAD_WGL(wglDXSetResourceShareHandleNV);
|
||||||
|
LOAD_WGL(wglDXOpenDeviceNV);
|
||||||
|
LOAD_WGL(wglDXCloseDeviceNV);
|
||||||
|
LOAD_WGL(wglDXRegisterObjectNV);
|
||||||
|
LOAD_WGL(wglDXUnregisterObjectNV);
|
||||||
|
LOAD_WGL(wglDXObjectAccessNV);
|
||||||
|
LOAD_WGL(wglDXLockObjectsNV);
|
||||||
|
LOAD_WGL(wglDXUnlockObjectsNV);
|
||||||
|
#undef LOAD_WGL
|
||||||
|
|
||||||
|
wglDXDevice = wglDXOpenDeviceNV(mD3D11Device);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mWGL_NV_DX_interop2 = false;
|
||||||
|
XrGraphicsBindingD3D11KHR mD3D11bindings{ XR_TYPE_GRAPHICS_BINDING_D3D11_KHR };
|
||||||
|
ID3D11Device* mD3D11Device = nullptr;
|
||||||
|
ID3D11DeviceContext* mD3D11ImmediateContext = nullptr;
|
||||||
|
HMODULE mD3D11Dll = nullptr;
|
||||||
|
PFN_D3D11_CREATE_DEVICE pD3D11CreateDevice = nullptr;
|
||||||
|
|
||||||
|
P_wglDXSetResourceShareHandleNV wglDXSetResourceShareHandleNV = nullptr;
|
||||||
|
P_wglDXOpenDeviceNV wglDXOpenDeviceNV = nullptr;
|
||||||
|
P_wglDXCloseDeviceNV wglDXCloseDeviceNV = nullptr;
|
||||||
|
P_wglDXRegisterObjectNV wglDXRegisterObjectNV = nullptr;
|
||||||
|
P_wglDXUnregisterObjectNV wglDXUnregisterObjectNV = nullptr;
|
||||||
|
P_wglDXObjectAccessNV wglDXObjectAccessNV = nullptr;
|
||||||
|
P_wglDXLockObjectsNV wglDXLockObjectsNV = nullptr;
|
||||||
|
P_wglDXUnlockObjectsNV wglDXUnlockObjectsNV = nullptr;
|
||||||
|
|
||||||
|
HANDLE wglDXDevice = nullptr;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
OpenXRPlatformPrivate::OpenXRPlatformPrivate(osg::GraphicsContext* gc)
|
||||||
|
{
|
||||||
|
#ifdef XR_USE_GRAPHICS_API_D3D11
|
||||||
|
typedef const char* (WINAPI* PFNWGLGETEXTENSIONSSTRINGARBPROC) (HDC hdc);
|
||||||
|
PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB = 0;
|
||||||
|
wglGetExtensionsStringARB = reinterpret_cast<PFNWGLGETEXTENSIONSSTRINGARBPROC>(wglGetProcAddress("wglGetExtensionsStringARB"));
|
||||||
|
if (wglGetExtensionsStringARB)
|
||||||
|
{
|
||||||
|
std::string wglExtensions = wglGetExtensionsStringARB(wglGetCurrentDC());
|
||||||
|
Log(Debug::Verbose) << "WGL Extensions: " << wglExtensions;
|
||||||
|
mWGL_NV_DX_interop2 = wglExtensions.find("WGL_NV_DX_interop2") != std::string::npos;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Log(Debug::Verbose) << "Unable to query WGL extensions";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
OpenXRPlatformPrivate::~OpenXRPlatformPrivate()
|
||||||
|
{
|
||||||
|
#ifdef XR_USE_GRAPHICS_API_D3D11
|
||||||
|
if (wglDXDevice)
|
||||||
|
wglDXCloseDeviceNV(wglDXDevice);
|
||||||
|
if (mD3D11ImmediateContext)
|
||||||
|
mD3D11ImmediateContext->Release();
|
||||||
|
if (mD3D11Device)
|
||||||
|
mD3D11Device->Release();
|
||||||
|
if (mD3D11Dll)
|
||||||
|
FreeLibrary(mD3D11Dll);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
OpenXRPlatform::OpenXRPlatform(osg::GraphicsContext* gc)
|
||||||
|
: mPrivate(new OpenXRPlatformPrivate(gc))
|
||||||
|
{
|
||||||
|
// Enumerate layers and their extensions.
|
||||||
|
uint32_t layerCount;
|
||||||
|
CHECK_XRCMD(xrEnumerateApiLayerProperties(0, &layerCount, nullptr));
|
||||||
|
std::vector<XrApiLayerProperties> layers(layerCount, XrApiLayerProperties{ XR_TYPE_API_LAYER_PROPERTIES });
|
||||||
|
CHECK_XRCMD(xrEnumerateApiLayerProperties((uint32_t)layers.size(), &layerCount, layers.data()));
|
||||||
|
|
||||||
|
Log(Debug::Verbose) << "Available Extensions: ";
|
||||||
|
enumerateExtensions(nullptr, 2);
|
||||||
|
Log(Debug::Verbose) << "Available Layers: ";
|
||||||
|
|
||||||
|
if (layers.size() == 0)
|
||||||
|
{
|
||||||
|
Log(Debug::Verbose) << " No layers available";
|
||||||
|
}
|
||||||
|
for (const XrApiLayerProperties& layer : layers) {
|
||||||
|
Log(Debug::Verbose) << " Name=" << layer.layerName << " SpecVersion=" << layer.layerVersion;
|
||||||
|
mAvailableLayers[layer.layerName] = layer;
|
||||||
|
enumerateExtensions(layer.layerName, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
setupExtensions();
|
||||||
|
}
|
||||||
|
|
||||||
|
OpenXRPlatform::~OpenXRPlatform()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !XR_KHR_composition_layer_depth \
|
||||||
|
|| !XR_EXT_hp_mixed_reality_controller \
|
||||||
|
|| !XR_EXT_debug_utils \
|
||||||
|
|| !XR_HTC_vive_cosmos_controller_interaction \
|
||||||
|
|| !XR_HUAWEI_controller_interaction
|
||||||
|
|
||||||
|
#error "OpenXR extensions missing. Please upgrade your copy of the OpenXR SDK to 1.0.13 minimum"
|
||||||
|
#endif
|
||||||
|
void OpenXRPlatform::setupExtensions()
|
||||||
|
{
|
||||||
|
std::vector<const char*> optionalExtensions = {
|
||||||
|
XR_EXT_HP_MIXED_REALITY_CONTROLLER_EXTENSION_NAME,
|
||||||
|
XR_HTC_VIVE_COSMOS_CONTROLLER_INTERACTION_EXTENSION_NAME,
|
||||||
|
XR_HUAWEI_CONTROLLER_INTERACTION_EXTENSION_NAME
|
||||||
|
};
|
||||||
|
|
||||||
|
if (Settings::Manager::getBool("enable XR_KHR_composition_layer_depth", "VR Debug"))
|
||||||
|
optionalExtensions.emplace_back(XR_KHR_COMPOSITION_LAYER_DEPTH_EXTENSION_NAME);
|
||||||
|
|
||||||
|
if (Settings::Manager::getBool("enable XR_EXT_debug_utils", "VR Debug"))
|
||||||
|
optionalExtensions.emplace_back(XR_EXT_DEBUG_UTILS_EXTENSION_NAME);
|
||||||
|
|
||||||
|
selectGraphicsAPIExtension();
|
||||||
|
|
||||||
|
Log(Debug::Verbose) << "Using extensions:";
|
||||||
|
|
||||||
|
auto* graphicsAPIExtension = graphicsAPIExtensionName();
|
||||||
|
if (!graphicsAPIExtension || !enableExtension(graphicsAPIExtension, true))
|
||||||
|
{
|
||||||
|
throw std::runtime_error("No graphics APIs supported by openmw are supported by the OpenXR runtime.");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto optionalExtension : optionalExtensions)
|
||||||
|
enableExtension(optionalExtension, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenXRPlatform::selectDirectX()
|
||||||
|
{
|
||||||
|
#ifdef XR_USE_GRAPHICS_API_D3D11
|
||||||
|
if (mPrivate->mWGL_NV_DX_interop2)
|
||||||
|
{
|
||||||
|
if (mAvailableExtensions.count(XR_KHR_D3D11_ENABLE_EXTENSION_NAME))
|
||||||
|
{
|
||||||
|
mGraphicsAPIExtension = XR_KHR_D3D11_ENABLE_EXTENSION_NAME;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Log(Debug::Warning) << "Warning: Failed to select DirectX swapchains: OpenXR runtime does not support essential extension '" << XR_KHR_D3D11_ENABLE_EXTENSION_NAME << "'";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Log(Debug::Warning) << "Warning: Failed to select DirectX swapchains: Essential WGL extension 'WGL_NV_DX_interop2' not supported by the graphics driver.";
|
||||||
|
#endif
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenXRPlatform::selectOpenGL()
|
||||||
|
{
|
||||||
|
#ifdef XR_USE_GRAPHICS_API_OPENGL
|
||||||
|
if (mAvailableExtensions.count(XR_KHR_OPENGL_ENABLE_EXTENSION_NAME))
|
||||||
|
{
|
||||||
|
mGraphicsAPIExtension = XR_KHR_OPENGL_ENABLE_EXTENSION_NAME;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Log(Debug::Warning) << "Warning: Failed to select OpenGL swapchains: OpenXR runtime does not support essential extension '" << XR_KHR_OPENGL_ENABLE_EXTENSION_NAME << "'";
|
||||||
|
#endif
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef XR_USE_GRAPHICS_API_OPENGL
|
||||||
|
#if !XR_KHR_opengl_enable
|
||||||
|
#error "OpenXR extensions missing. Please upgrade your copy of the OpenXR SDK to 1.0.13 minimum"
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef XR_USE_GRAPHICS_API_D3D11
|
||||||
|
#if !XR_KHR_D3D11_enable
|
||||||
|
#error "OpenXR extensions missing. Please upgrade your copy of the OpenXR SDK to 1.0.13 minimum"
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const char* OpenXRPlatform::graphicsAPIExtensionName()
|
||||||
|
{
|
||||||
|
return mGraphicsAPIExtension;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenXRPlatform::selectGraphicsAPIExtension()
|
||||||
|
{
|
||||||
|
bool preferDirectX = Settings::Manager::getBool("Prefer DirectX swapchains", "VR");
|
||||||
|
|
||||||
|
if (preferDirectX)
|
||||||
|
if (selectDirectX() || selectOpenGL())
|
||||||
|
return;
|
||||||
|
if (selectOpenGL() || selectDirectX())
|
||||||
|
return;
|
||||||
|
|
||||||
|
Log(Debug::Verbose) << "Error: No graphics API supported by OpenMW VR is supported by the OpenXR runtime.";
|
||||||
|
throw std::runtime_error("Error: No graphics API supported by OpenMW VR is supported by the OpenXR runtime.");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenXRPlatform::supportsExtension(const std::string& extensionName) const
|
||||||
|
{
|
||||||
|
return mAvailableExtensions.count(extensionName) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenXRPlatform::supportsExtension(const std::string& extensionName, uint32_t minimumVersion) const
|
||||||
|
{
|
||||||
|
auto it = mAvailableExtensions.find(extensionName);
|
||||||
|
return it != mAvailableExtensions.end() && it->second.extensionVersion > minimumVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenXRPlatform::supportsLayer(const std::string& layerName) const
|
||||||
|
{
|
||||||
|
return mAvailableLayers.count(layerName) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenXRPlatform::supportsLayer(const std::string& layerName, uint32_t minimumVersion) const
|
||||||
|
{
|
||||||
|
auto it = mAvailableLayers.find(layerName);
|
||||||
|
return it != mAvailableLayers.end() && it->second.layerVersion > minimumVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenXRPlatform::enableExtension(const std::string& extensionName, bool optional)
|
||||||
|
{
|
||||||
|
auto it = mAvailableExtensions.find(extensionName);
|
||||||
|
if (it != mAvailableExtensions.end())
|
||||||
|
{
|
||||||
|
Log(Debug::Verbose) << " " << extensionName << ": enabled";
|
||||||
|
mEnabledExtensions.push_back(it->second.extensionName);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log(Debug::Verbose) << " " << extensionName << ": disabled (not supported)";
|
||||||
|
if (!optional)
|
||||||
|
{
|
||||||
|
throw std::runtime_error(std::string("Required OpenXR extension ") + extensionName + " not supported by the runtime");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenXRPlatform::enableExtension(const std::string& extensionName, bool optional, uint32_t minimumVersion)
|
||||||
|
{
|
||||||
|
auto it = mAvailableExtensions.find(extensionName);
|
||||||
|
if (it != mAvailableExtensions.end() && it->second.extensionVersion > minimumVersion)
|
||||||
|
{
|
||||||
|
Log(Debug::Verbose) << " " << extensionName << ": enabled";
|
||||||
|
mEnabledExtensions.push_back(it->second.extensionName);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log(Debug::Verbose) << " " << extensionName << ": disabled (not supported)";
|
||||||
|
if (!optional)
|
||||||
|
{
|
||||||
|
throw std::runtime_error(std::string("Required OpenXR extension ") + extensionName + " not supported by the runtime");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool OpenXRPlatform::extensionEnabled(const std::string& extensionName) const
|
||||||
|
{
|
||||||
|
for (auto* extension : mEnabledExtensions)
|
||||||
|
if (extension == extensionName)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
XrInstance OpenXRPlatform::createXrInstance(const std::string& name)
|
||||||
|
{
|
||||||
|
XrInstance instance = XR_NULL_HANDLE;
|
||||||
|
XrInstanceCreateInfo createInfo{ XR_TYPE_INSTANCE_CREATE_INFO };
|
||||||
|
createInfo.next = nullptr;
|
||||||
|
createInfo.enabledExtensionCount = mEnabledExtensions.size();
|
||||||
|
createInfo.enabledExtensionNames = mEnabledExtensions.data();
|
||||||
|
strcpy(createInfo.applicationInfo.applicationName, "openmw_vr");
|
||||||
|
createInfo.applicationInfo.apiVersion = XR_CURRENT_API_VERSION;
|
||||||
|
|
||||||
|
auto res = CHECK_XRCMD(xrCreateInstance(&createInfo, &instance));
|
||||||
|
if (!XR_SUCCEEDED(res))
|
||||||
|
initFailure(res, instance);
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
XrSession OpenXRPlatform::createXrSession(XrInstance instance, XrSystemId systemId)
|
||||||
|
{
|
||||||
|
XrSession session = XR_NULL_HANDLE;
|
||||||
|
XrResult res = XR_SUCCESS;
|
||||||
|
#ifdef _WIN32
|
||||||
|
std::string graphicsAPIExtension = graphicsAPIExtensionName();
|
||||||
|
if(graphicsAPIExtension == XR_KHR_OPENGL_ENABLE_EXTENSION_NAME)
|
||||||
|
{
|
||||||
|
// Get system requirements
|
||||||
|
PFN_xrGetOpenGLGraphicsRequirementsKHR p_getRequirements = nullptr;
|
||||||
|
CHECK_XRCMD(xrGetInstanceProcAddr(instance, "xrGetOpenGLGraphicsRequirementsKHR", reinterpret_cast<PFN_xrVoidFunction*>(&p_getRequirements)));
|
||||||
|
XrGraphicsRequirementsOpenGLKHR requirements{ XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_KHR };
|
||||||
|
CHECK_XRCMD(p_getRequirements(instance, systemId, &requirements));
|
||||||
|
|
||||||
|
// TODO: Actually get system version
|
||||||
|
const XrVersion systemApiVersion = XR_MAKE_VERSION(4, 6, 0);
|
||||||
|
if (requirements.minApiVersionSupported > systemApiVersion) {
|
||||||
|
std::cout << "Runtime does not support desired Graphics API and/or version" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create Session
|
||||||
|
auto DC = wglGetCurrentDC();
|
||||||
|
auto GLRC = wglGetCurrentContext();
|
||||||
|
|
||||||
|
XrGraphicsBindingOpenGLWin32KHR graphicsBindings;
|
||||||
|
graphicsBindings.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_WIN32_KHR;
|
||||||
|
graphicsBindings.next = nullptr;
|
||||||
|
graphicsBindings.hDC = DC;
|
||||||
|
graphicsBindings.hGLRC = GLRC;
|
||||||
|
|
||||||
|
if (!graphicsBindings.hDC)
|
||||||
|
Log(Debug::Warning) << "Missing DC";
|
||||||
|
|
||||||
|
XrSessionCreateInfo createInfo{ XR_TYPE_SESSION_CREATE_INFO };
|
||||||
|
createInfo.next = &graphicsBindings;
|
||||||
|
createInfo.systemId = systemId;
|
||||||
|
res = CHECK_XRCMD(xrCreateSession(instance, &createInfo, &session));
|
||||||
|
}
|
||||||
|
#ifdef XR_USE_GRAPHICS_API_D3D11
|
||||||
|
else if(graphicsAPIExtension == XR_KHR_D3D11_ENABLE_EXTENSION_NAME)
|
||||||
|
{
|
||||||
|
PFN_xrGetD3D11GraphicsRequirementsKHR p_getRequirements = nullptr;
|
||||||
|
CHECK_XRCMD(xrGetInstanceProcAddr(instance, "xrGetD3D11GraphicsRequirementsKHR", reinterpret_cast<PFN_xrVoidFunction*>(&p_getRequirements)));
|
||||||
|
XrGraphicsRequirementsD3D11KHR requirements{ XR_TYPE_GRAPHICS_REQUIREMENTS_D3D11_KHR };
|
||||||
|
CHECK_XRCMD(p_getRequirements(instance, systemId, &requirements));
|
||||||
|
mPrivate->initializeD3D11(requirements);
|
||||||
|
|
||||||
|
XrSessionCreateInfo createInfo{ XR_TYPE_SESSION_CREATE_INFO };
|
||||||
|
createInfo.next = &mPrivate->mD3D11bindings;
|
||||||
|
createInfo.systemId = systemId;
|
||||||
|
res = CHECK_XRCMD(xrCreateSession(instance, &createInfo, &session));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw std::logic_error("Enum value not implemented");
|
||||||
|
}
|
||||||
|
#elif __linux__
|
||||||
|
{
|
||||||
|
// Get system requirements
|
||||||
|
PFN_xrGetOpenGLGraphicsRequirementsKHR p_getRequirements = nullptr;
|
||||||
|
xrGetInstanceProcAddr(instance, "xrGetOpenGLGraphicsRequirementsKHR", reinterpret_cast<PFN_xrVoidFunction*>(&p_getRequirements));
|
||||||
|
XrGraphicsRequirementsOpenGLKHR requirements{ XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_KHR };
|
||||||
|
CHECK_XRCMD(p_getRequirements(instance, systemId, &requirements));
|
||||||
|
|
||||||
|
// TODO: Actually get system version
|
||||||
|
const XrVersion systemApiVersion = XR_MAKE_VERSION(4, 6, 0);
|
||||||
|
if (requirements.minApiVersionSupported > systemApiVersion) {
|
||||||
|
std::cout << "Runtime does not support desired Graphics API and/or version" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create Session
|
||||||
|
Display* xDisplay = XOpenDisplay(NULL);
|
||||||
|
GLXContext glxContext = glXGetCurrentContext();
|
||||||
|
GLXDrawable glxDrawable = glXGetCurrentDrawable();
|
||||||
|
|
||||||
|
// TODO: runtimes don't actually care (yet)
|
||||||
|
GLXFBConfig glxFBConfig = 0;
|
||||||
|
uint32_t visualid = 0;
|
||||||
|
|
||||||
|
XrGraphicsBindingOpenGLXlibKHR graphicsBindings;
|
||||||
|
graphicsBindings.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_XLIB_KHR;
|
||||||
|
graphicsBindings.next = nullptr;
|
||||||
|
graphicsBindings.xDisplay = xDisplay;
|
||||||
|
graphicsBindings.glxContext = glxContext;
|
||||||
|
graphicsBindings.glxDrawable = glxDrawable;
|
||||||
|
graphicsBindings.glxFBConfig = glxFBConfig;
|
||||||
|
graphicsBindings.visualid = visualid;
|
||||||
|
|
||||||
|
if (!graphicsBindings.glxContext)
|
||||||
|
Log(Debug::Warning) << "Missing glxContext";
|
||||||
|
|
||||||
|
if (!graphicsBindings.glxDrawable)
|
||||||
|
Log(Debug::Warning) << "Missing glxDrawable";
|
||||||
|
|
||||||
|
XrSessionCreateInfo createInfo{ XR_TYPE_SESSION_CREATE_INFO };
|
||||||
|
createInfo.next = &graphicsBindings;
|
||||||
|
createInfo.systemId = systemId;
|
||||||
|
res = CHECK_XRCMD(xrCreateSession(instance, &createInfo, &session));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (!XR_SUCCEEDED(res))
|
||||||
|
initFailure(res, instance);
|
||||||
|
|
||||||
|
uint32_t swapchainFormatCount;
|
||||||
|
CHECK_XRCMD(xrEnumerateSwapchainFormats(session, 0, &swapchainFormatCount, nullptr));
|
||||||
|
mSwapchainFormats.resize(swapchainFormatCount);
|
||||||
|
CHECK_XRCMD(xrEnumerateSwapchainFormats(session, (uint32_t)mSwapchainFormats.size(), &swapchainFormatCount, mSwapchainFormats.data()));
|
||||||
|
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "Available Swapchain formats: (" << swapchainFormatCount << ")" << std::endl;
|
||||||
|
|
||||||
|
for (auto format : mSwapchainFormats)
|
||||||
|
{
|
||||||
|
ss << " Enum=" << std::dec << format << " (0x=" << std::hex << format << ")" << std::dec << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log(Debug::Verbose) << ss.str();
|
||||||
|
|
||||||
|
return session;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For reference: These are the DXGI formats offered by WMR when using D3D11:
|
||||||
|
Enum=29 //(0x=1d) DXGI_FORMAT_R8G8B8A8_UNORM_SRGB
|
||||||
|
Enum=91 //(0x=5b) DXGI_FORMAT_B8G8R8A8_UNORM_SRGB
|
||||||
|
Enum=28 //(0x=1c) DXGI_FORMAT_R8G8B8A8_UNORM
|
||||||
|
Enum=87 //(0x=57) DXGI_FORMAT_B8G8R8A8_UNORM
|
||||||
|
Enum=40 //(0x=28) DXGI_FORMAT_D32_FLOAT
|
||||||
|
Enum=20 //(0x=14) DXGI_FORMAT_D32_FLOAT_S8X24_UINT
|
||||||
|
Enum=45 //(0x=2d) DXGI_FORMAT_D24_UNORM_S8_UINT
|
||||||
|
Enum=55 //(0x=37) DXGI_FORMAT_D16_UNORM
|
||||||
|
* And these extra formats are offered by SteamVR:
|
||||||
|
0xa , // DXGI_FORMAT_R16G16B16A16_FLOAT
|
||||||
|
0x18 , // DXGI_FORMAT_R10G10B10A2_UNORM
|
||||||
|
*/
|
||||||
|
|
||||||
|
int64_t OpenXRPlatform::selectColorFormat()
|
||||||
|
{
|
||||||
|
std::string graphicsAPIExtension = graphicsAPIExtensionName();
|
||||||
|
if (graphicsAPIExtension == XR_KHR_OPENGL_ENABLE_EXTENSION_NAME)
|
||||||
|
{
|
||||||
|
std::vector<int64_t> requestedColorSwapchainFormats;
|
||||||
|
|
||||||
|
if (Settings::Manager::getBool("Prefer sRGB swapchains", "VR"))
|
||||||
|
{
|
||||||
|
requestedColorSwapchainFormats.push_back(0x8C43); // GL_SRGB8_ALPHA8
|
||||||
|
requestedColorSwapchainFormats.push_back(0x8C41); // GL_SRGB8
|
||||||
|
}
|
||||||
|
|
||||||
|
requestedColorSwapchainFormats.push_back(0x8058); // GL_RGBA8
|
||||||
|
requestedColorSwapchainFormats.push_back(0x8F97); // GL_RGBA8_SNORM
|
||||||
|
requestedColorSwapchainFormats.push_back(0x881A); // GL_RGBA16F
|
||||||
|
requestedColorSwapchainFormats.push_back(0x881B); // GL_RGB16F
|
||||||
|
requestedColorSwapchainFormats.push_back(0x8C3A); // GL_R11F_G11F_B10F
|
||||||
|
|
||||||
|
return selectFormat(requestedColorSwapchainFormats);
|
||||||
|
}
|
||||||
|
#ifdef XR_USE_GRAPHICS_API_D3D11
|
||||||
|
else if (graphicsAPIExtension == XR_KHR_D3D11_ENABLE_EXTENSION_NAME)
|
||||||
|
{
|
||||||
|
std::vector<int64_t> requestedColorSwapchainFormats;
|
||||||
|
|
||||||
|
if (Settings::Manager::getBool("Prefer sRGB swapchains", "VR"))
|
||||||
|
{
|
||||||
|
requestedColorSwapchainFormats.push_back(0x1d); // DXGI_FORMAT_R8G8B8A8_UNORM_SRGB
|
||||||
|
requestedColorSwapchainFormats.push_back(0x5b); // DXGI_FORMAT_B8G8R8A8_UNORM_SRGB
|
||||||
|
}
|
||||||
|
|
||||||
|
requestedColorSwapchainFormats.push_back(0x1c); // DXGI_FORMAT_R8G8B8A8_UNORM
|
||||||
|
requestedColorSwapchainFormats.push_back(0x57); // DXGI_FORMAT_B8G8R8A8_UNORM
|
||||||
|
requestedColorSwapchainFormats.push_back(0xa); // DXGI_FORMAT_R16G16B16A16_FLOAT
|
||||||
|
requestedColorSwapchainFormats.push_back(0x18); // DXGI_FORMAT_R10G10B10A2_UNORM
|
||||||
|
|
||||||
|
return selectFormat(requestedColorSwapchainFormats);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw std::logic_error("Enum value not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t OpenXRPlatform::selectDepthFormat()
|
||||||
|
{
|
||||||
|
std::string graphicsAPIExtension = graphicsAPIExtensionName();
|
||||||
|
if (graphicsAPIExtension == XR_KHR_OPENGL_ENABLE_EXTENSION_NAME)
|
||||||
|
{
|
||||||
|
// Find supported depth swapchain format.
|
||||||
|
std::vector<int64_t> requestedDepthSwapchainFormats = {
|
||||||
|
0x88F0, // GL_DEPTH24_STENCIL8
|
||||||
|
0x8CAC, // GL_DEPTH_COMPONENT32F
|
||||||
|
0x81A7, // GL_DEPTH_COMPONENT32
|
||||||
|
0x8DAB, // GL_DEPTH_COMPONENT32F_NV
|
||||||
|
0x8CAD, // GL_DEPTH32_STENCIL8
|
||||||
|
0x81A6, // GL_DEPTH_COMPONENT24
|
||||||
|
// Need 32bit minimum: // 0x81A5, // GL_DEPTH_COMPONENT16
|
||||||
|
};
|
||||||
|
|
||||||
|
return selectFormat(requestedDepthSwapchainFormats);
|
||||||
|
}
|
||||||
|
#ifdef XR_USE_GRAPHICS_API_D3D11
|
||||||
|
else if (graphicsAPIExtension == XR_KHR_D3D11_ENABLE_EXTENSION_NAME)
|
||||||
|
{
|
||||||
|
// Find supported color swapchain format.
|
||||||
|
std::vector<int64_t> requestedDepthSwapchainFormats = {
|
||||||
|
0x2d, // DXGI_FORMAT_D24_UNORM_S8_UINT
|
||||||
|
0x14, // DXGI_FORMAT_D32_FLOAT_S8X24_UINT
|
||||||
|
0x28, // DXGI_FORMAT_D32_FLOAT
|
||||||
|
// Need 32bit minimum: 0x37, // DXGI_FORMAT_D16_UNORM
|
||||||
|
};
|
||||||
|
return selectFormat(requestedDepthSwapchainFormats);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw std::logic_error("Enum value not implemented");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenXRPlatform::eraseFormat(int64_t format)
|
||||||
|
{
|
||||||
|
for (auto it = mSwapchainFormats.begin(); it != mSwapchainFormats.end(); it++)
|
||||||
|
{
|
||||||
|
if (*it == format)
|
||||||
|
{
|
||||||
|
mSwapchainFormats.erase(it);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t OpenXRPlatform::selectFormat(const std::vector<int64_t>& requestedFormats)
|
||||||
|
{
|
||||||
|
auto it =
|
||||||
|
std::find_first_of(std::begin(requestedFormats), std::end(requestedFormats),
|
||||||
|
mSwapchainFormats.begin(), mSwapchainFormats.end());
|
||||||
|
if (it == std::end(requestedFormats))
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return *it;
|
||||||
|
}
|
||||||
|
void* OpenXRPlatform::DXRegisterObject(void* dxResource, uint32_t glName, uint32_t glType, bool discard, void* ntShareHandle)
|
||||||
|
{
|
||||||
|
#ifdef XR_USE_GRAPHICS_API_D3D11
|
||||||
|
if (ntShareHandle)
|
||||||
|
{
|
||||||
|
mPrivate->wglDXSetResourceShareHandleNV(dxResource, ntShareHandle);
|
||||||
|
}
|
||||||
|
return mPrivate->wglDXRegisterObjectNV(mPrivate->wglDXDevice, dxResource, glName, glType, 1);
|
||||||
|
#else
|
||||||
|
return nullptr;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
void OpenXRPlatform::DXUnregisterObject(void* dxResourceShareHandle)
|
||||||
|
{
|
||||||
|
#ifdef XR_USE_GRAPHICS_API_D3D11
|
||||||
|
mPrivate->wglDXUnregisterObjectNV(mPrivate->wglDXDevice, dxResourceShareHandle);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
void OpenXRPlatform::DXLockObject(void* dxResourceShareHandle)
|
||||||
|
{
|
||||||
|
#ifdef XR_USE_GRAPHICS_API_D3D11
|
||||||
|
mPrivate->wglDXLockObjectsNV(mPrivate->wglDXDevice, 1, &dxResourceShareHandle);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
void OpenXRPlatform::DXUnlockObject(void* dxResourceShareHandle)
|
||||||
|
{
|
||||||
|
#ifdef XR_USE_GRAPHICS_API_D3D11
|
||||||
|
mPrivate->wglDXUnlockObjectsNV(mPrivate->wglDXDevice, 1, &dxResourceShareHandle);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static XrInstanceProperties
|
||||||
|
getInstanceProperties(XrInstance instance)
|
||||||
|
{
|
||||||
|
XrInstanceProperties properties{ XR_TYPE_INSTANCE_PROPERTIES };
|
||||||
|
if (instance)
|
||||||
|
xrGetInstanceProperties(instance, &properties);
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string OpenXRPlatform::getInstanceName(XrInstance instance)
|
||||||
|
{
|
||||||
|
if (instance)
|
||||||
|
return getInstanceProperties(instance).runtimeName;
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
XrVersion OpenXRPlatform::getInstanceVersion(XrInstance instance)
|
||||||
|
{
|
||||||
|
if (instance)
|
||||||
|
return getInstanceProperties(instance).runtimeVersion;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
void OpenXRPlatform::initFailure(XrResult res, XrInstance instance)
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
std::string runtimeName = getInstanceName(instance);
|
||||||
|
XrVersion runtimeVersion = getInstanceVersion(instance);
|
||||||
|
ss << "Error caught while initializing VR device: " << XrResultString(res) << std::endl;
|
||||||
|
ss << "Device: " << runtimeName << std::endl;
|
||||||
|
ss << "Version: " << runtimeVersion << std::endl;
|
||||||
|
if (res == XR_ERROR_FORM_FACTOR_UNAVAILABLE)
|
||||||
|
{
|
||||||
|
ss << "Cause: Unable to open VR device. Make sure your device is plugged in and the VR driver is running." << std::endl;
|
||||||
|
ss << std::endl;
|
||||||
|
if (runtimeName == "Oculus" || runtimeName == "Quest")
|
||||||
|
{
|
||||||
|
ss << "Your device has been identified as an Oculus device." << std::endl;
|
||||||
|
ss << "The most common cause for this error when using an oculus device, is quest users attempting to run the game via Virtual Desktop." << std::endl;
|
||||||
|
ss << "Unfortunately this is currently broken, and quest users will need to play via a link cable." << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (res == XR_ERROR_LIMIT_REACHED)
|
||||||
|
{
|
||||||
|
ss << "Cause: Device resources exhausted. Close other VR applications if you have any open. If you have none, you may need to reboot to reset the driver." << std::endl;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ss << "Cause: Unknown. Make sure your device is plugged in and ready." << std::endl;
|
||||||
|
}
|
||||||
|
throw std::runtime_error(ss.str());
|
||||||
|
}
|
||||||
|
}
|
86
apps/openmw/mwvr/openxrplatform.hpp
Normal file
86
apps/openmw/mwvr/openxrplatform.hpp
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
#ifndef OPENXR_PLATFORM_HPP
|
||||||
|
#define OPENXR_PLATFORM_HPP
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
#include <set>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <openxr/openxr.h>
|
||||||
|
|
||||||
|
namespace MWVR
|
||||||
|
{
|
||||||
|
// Error management macros and functions. Should be used on every openxr call.
|
||||||
|
#define CHK_STRINGIFY(x) #x
|
||||||
|
#define TOSTRING(x) CHK_STRINGIFY(x)
|
||||||
|
#define FILE_AND_LINE __FILE__ ":" TOSTRING(__LINE__)
|
||||||
|
#define CHECK_XRCMD(cmd) CheckXrResult(cmd, #cmd, FILE_AND_LINE)
|
||||||
|
#define CHECK_XRRESULT(res, cmdStr) CheckXrResult(res, cmdStr, FILE_AND_LINE)
|
||||||
|
XrResult CheckXrResult(XrResult res, const char* originator = nullptr, const char* sourceLocation = nullptr);
|
||||||
|
std::string XrResultString(XrResult res);
|
||||||
|
|
||||||
|
struct OpenXRPlatformPrivate;
|
||||||
|
|
||||||
|
class OpenXRPlatform
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using ExtensionMap = std::map<std::string, XrExtensionProperties>;
|
||||||
|
using LayerMap = std::map<std::string, XrApiLayerProperties>;
|
||||||
|
using LayerExtensionMap = std::map<std::string, ExtensionMap>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
OpenXRPlatform(osg::GraphicsContext* gc);
|
||||||
|
~OpenXRPlatform();
|
||||||
|
|
||||||
|
const char* graphicsAPIExtensionName();
|
||||||
|
bool supportsExtension(const std::string& extensionName) const;
|
||||||
|
bool supportsExtension(const std::string& extensionName, uint32_t minimumVersion) const;
|
||||||
|
bool supportsLayer(const std::string& layerName) const;
|
||||||
|
bool supportsLayer(const std::string& layerName, uint32_t minimumVersion) const;
|
||||||
|
|
||||||
|
bool enableExtension(const std::string& extensionName, bool optional);
|
||||||
|
bool enableExtension(const std::string& extensionName, bool optional, uint32_t minimumVersion);
|
||||||
|
|
||||||
|
bool extensionEnabled(const std::string& extensionName) const;
|
||||||
|
|
||||||
|
XrInstance createXrInstance(const std::string& name);
|
||||||
|
XrSession createXrSession(XrInstance instance, XrSystemId systemId);
|
||||||
|
|
||||||
|
int64_t selectColorFormat();
|
||||||
|
int64_t selectDepthFormat();
|
||||||
|
int64_t selectFormat(const std::vector<int64_t>& requestedFormats);
|
||||||
|
void eraseFormat(int64_t format);
|
||||||
|
std::vector<int64_t> mSwapchainFormats{};
|
||||||
|
|
||||||
|
/// Registers an object for sharing as if calling wglDXRegisterObjectNV requesting write access.
|
||||||
|
/// If ntShareHandle is not null, wglDXSetResourceShareHandleNV is called first to register the share handle
|
||||||
|
void* DXRegisterObject(void* dxResource, uint32_t glName, uint32_t glType, bool discard, void* ntShareHandle);
|
||||||
|
/// Unregisters an object from sharing as if calling wglDXUnregisterObjectNV
|
||||||
|
void DXUnregisterObject(void* dxResourceShareHandle);
|
||||||
|
/// Locks a DX object for use by OpenGL as if calling wglDXLockObjectsNV
|
||||||
|
void DXLockObject(void* dxResourceShareHandle);
|
||||||
|
/// Unlocks a DX object for use by DirectX as if calling wglDXUnlockObjectsNV
|
||||||
|
void DXUnlockObject(void* dxResourceShareHandle);
|
||||||
|
|
||||||
|
std::string getInstanceName(XrInstance instance);
|
||||||
|
XrVersion getInstanceVersion(XrInstance instance);
|
||||||
|
void initFailure(XrResult, XrInstance);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void enumerateExtensions(const char* layerName, int logIndent);
|
||||||
|
void setupExtensions();
|
||||||
|
void selectGraphicsAPIExtension();
|
||||||
|
bool selectDirectX();
|
||||||
|
bool selectOpenGL();
|
||||||
|
|
||||||
|
ExtensionMap mAvailableExtensions;
|
||||||
|
LayerMap mAvailableLayers;
|
||||||
|
LayerExtensionMap mAvailableLayerExtensions;
|
||||||
|
std::vector<const char*> mEnabledExtensions;
|
||||||
|
const char* mGraphicsAPIExtension = nullptr;
|
||||||
|
|
||||||
|
std::unique_ptr< OpenXRPlatformPrivate > mPrivate;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
48
apps/openmw/mwvr/openxrswapchain.cpp
Normal file
48
apps/openmw/mwvr/openxrswapchain.cpp
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
#include "openxrswapchain.hpp"
|
||||||
|
#include "openxrswapchainimpl.hpp"
|
||||||
|
#include "openxrmanager.hpp"
|
||||||
|
#include "openxrmanagerimpl.hpp"
|
||||||
|
#include "vrenvironment.hpp"
|
||||||
|
|
||||||
|
#include <components/debug/debuglog.hpp>
|
||||||
|
|
||||||
|
namespace MWVR {
|
||||||
|
OpenXRSwapchain::OpenXRSwapchain(osg::ref_ptr<osg::State> state, SwapchainConfig config)
|
||||||
|
: mPrivate(new OpenXRSwapchainImpl(state, config))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
OpenXRSwapchain::~OpenXRSwapchain()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenXRSwapchain::beginFrame(osg::GraphicsContext* gc)
|
||||||
|
{
|
||||||
|
return impl().beginFrame(gc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenXRSwapchain::endFrame(osg::GraphicsContext* gc, VRFramebuffer& readBuffer)
|
||||||
|
{
|
||||||
|
return impl().endFrame(gc, readBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
int OpenXRSwapchain::width() const
|
||||||
|
{
|
||||||
|
return impl().width();
|
||||||
|
}
|
||||||
|
|
||||||
|
int OpenXRSwapchain::height() const
|
||||||
|
{
|
||||||
|
return impl().height();
|
||||||
|
}
|
||||||
|
|
||||||
|
int OpenXRSwapchain::samples() const
|
||||||
|
{
|
||||||
|
return impl().samples();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenXRSwapchain::isAcquired() const
|
||||||
|
{
|
||||||
|
return impl().isAcquired();
|
||||||
|
}
|
||||||
|
}
|
53
apps/openmw/mwvr/openxrswapchain.hpp
Normal file
53
apps/openmw/mwvr/openxrswapchain.hpp
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
#ifndef OPENXR_SWAPCHAIN_HPP
|
||||||
|
#define OPENXR_SWAPCHAIN_HPP
|
||||||
|
|
||||||
|
#include "openxrmanager.hpp"
|
||||||
|
|
||||||
|
struct XrSwapchainSubImage;
|
||||||
|
|
||||||
|
namespace MWVR
|
||||||
|
{
|
||||||
|
class OpenXRSwapchainImpl;
|
||||||
|
class VRFramebuffer;
|
||||||
|
|
||||||
|
/// \brief Creation and management of openxr swapchains
|
||||||
|
class OpenXRSwapchain
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
OpenXRSwapchain(osg::ref_ptr<osg::State> state, SwapchainConfig config);
|
||||||
|
~OpenXRSwapchain();
|
||||||
|
|
||||||
|
public:
|
||||||
|
//! Prepare for render (set FBO)
|
||||||
|
void beginFrame(osg::GraphicsContext* gc);
|
||||||
|
|
||||||
|
//! Finalize render
|
||||||
|
void endFrame(osg::GraphicsContext* gc, VRFramebuffer& readBuffer);
|
||||||
|
|
||||||
|
//! Whether subchain is currently acquired (true) or released (false)
|
||||||
|
bool isAcquired() const;
|
||||||
|
|
||||||
|
//! Width of the view surface
|
||||||
|
int width() const;
|
||||||
|
|
||||||
|
//! Height of the view surface
|
||||||
|
int height() const;
|
||||||
|
|
||||||
|
//! Samples of the view surface
|
||||||
|
int samples() const;
|
||||||
|
|
||||||
|
//! Get the private implementation
|
||||||
|
OpenXRSwapchainImpl& impl() { return *mPrivate; }
|
||||||
|
|
||||||
|
//! Get the private implementation
|
||||||
|
const OpenXRSwapchainImpl& impl() const { return *mPrivate; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
OpenXRSwapchain(const OpenXRSwapchain&) = delete;
|
||||||
|
void operator=(const OpenXRSwapchain&) = delete;
|
||||||
|
private:
|
||||||
|
std::unique_ptr<OpenXRSwapchainImpl> mPrivate;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
221
apps/openmw/mwvr/openxrswapchainimage.cpp
Normal file
221
apps/openmw/mwvr/openxrswapchainimage.cpp
Normal file
|
@ -0,0 +1,221 @@
|
||||||
|
#include "openxrswapchainimage.hpp"
|
||||||
|
#include "openxrmanagerimpl.hpp"
|
||||||
|
#include "vrenvironment.hpp"
|
||||||
|
#include "vrframebuffer.hpp"
|
||||||
|
|
||||||
|
// The OpenXR SDK's platform headers assume we've included platform headers
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <objbase.h>
|
||||||
|
|
||||||
|
#ifdef XR_USE_GRAPHICS_API_D3D11
|
||||||
|
#include <d3d11.h>
|
||||||
|
#include <dxgi1_2.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#elif __linux__
|
||||||
|
#include <X11/Xlib.h>
|
||||||
|
#include <GL/glx.h>
|
||||||
|
#undef None
|
||||||
|
|
||||||
|
#else
|
||||||
|
#error Unsupported platform
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <openxr/openxr.h>
|
||||||
|
#include <openxr/openxr_platform.h>
|
||||||
|
#include <openxr/openxr_platform_defines.h>
|
||||||
|
#include <openxr/openxr_reflection.h>
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#define GLERR if(auto err = glGetError() != GL_NO_ERROR) Log(Debug::Verbose) << __FILE__ << "." << __LINE__ << ": " << glGetError()
|
||||||
|
|
||||||
|
namespace MWVR {
|
||||||
|
|
||||||
|
template<typename Image>
|
||||||
|
class OpenXRSwapchainImageTemplate;
|
||||||
|
|
||||||
|
template<>
|
||||||
|
class OpenXRSwapchainImageTemplate< XrSwapchainImageOpenGLKHR > : public OpenXRSwapchainImage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static constexpr XrStructureType XrType = XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_KHR;
|
||||||
|
|
||||||
|
public:
|
||||||
|
OpenXRSwapchainImageTemplate(osg::GraphicsContext* gc, XrSwapchainCreateInfo swapchainCreateInfo, const XrSwapchainImageOpenGLKHR& xrImage)
|
||||||
|
: OpenXRSwapchainImage()
|
||||||
|
, mXrImage(xrImage)
|
||||||
|
, mBufferBits(0)
|
||||||
|
, mFramebuffer(nullptr)
|
||||||
|
{
|
||||||
|
mFramebuffer.reset(new VRFramebuffer(gc->getState(), swapchainCreateInfo.width, swapchainCreateInfo.height, swapchainCreateInfo.sampleCount));
|
||||||
|
if (swapchainCreateInfo.usageFlags & XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)
|
||||||
|
{
|
||||||
|
mFramebuffer->setDepthBuffer(gc, mXrImage.image, false);
|
||||||
|
mBufferBits = GL_DEPTH_BUFFER_BIT;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mFramebuffer->setColorBuffer(gc, mXrImage.image, false);
|
||||||
|
mBufferBits = GL_COLOR_BUFFER_BIT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void blit(osg::GraphicsContext* gc, VRFramebuffer& readBuffer, int offset_x, int offset_y) override
|
||||||
|
{
|
||||||
|
mFramebuffer->bindFramebuffer(gc, GL_FRAMEBUFFER_EXT);
|
||||||
|
readBuffer.blit(gc, offset_x, offset_y, offset_x + mFramebuffer->width(), offset_y + mFramebuffer->height(), 0, 0, mFramebuffer->width(), mFramebuffer->height(), mBufferBits, GL_NEAREST);
|
||||||
|
}
|
||||||
|
|
||||||
|
XrSwapchainImageOpenGLKHR mXrImage;
|
||||||
|
uint32_t mBufferBits;
|
||||||
|
std::unique_ptr<VRFramebuffer> mFramebuffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef XR_USE_GRAPHICS_API_D3D11
|
||||||
|
template<>
|
||||||
|
class OpenXRSwapchainImageTemplate< XrSwapchainImageD3D11KHR > : public OpenXRSwapchainImage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static constexpr XrStructureType XrType = XR_TYPE_SWAPCHAIN_IMAGE_D3D11_KHR;
|
||||||
|
public:
|
||||||
|
OpenXRSwapchainImageTemplate(osg::GraphicsContext* gc, XrSwapchainCreateInfo swapchainCreateInfo, const XrSwapchainImageD3D11KHR& xrImage)
|
||||||
|
: OpenXRSwapchainImage()
|
||||||
|
, mXrImage(xrImage)
|
||||||
|
, mBufferBits(0)
|
||||||
|
, mFramebuffer(nullptr)
|
||||||
|
{
|
||||||
|
mXrImage.texture->GetDevice(&mDevice);
|
||||||
|
mDevice->GetImmediateContext(&mDeviceContext);
|
||||||
|
|
||||||
|
mXrImage.texture->GetDesc(&mDesc);
|
||||||
|
|
||||||
|
glGenTextures(1, &mGlTextureName);
|
||||||
|
|
||||||
|
auto* xr = Environment::get().getManager();
|
||||||
|
//mDxResourceShareHandle = xr->impl().platform().DXRegisterObject(mXrImage.texture, mGlTextureName, GL_TEXTURE_2D, true, nullptr);
|
||||||
|
|
||||||
|
if (!mDxResourceShareHandle)
|
||||||
|
{
|
||||||
|
// Some OpenXR runtimes return textures that cannot be directly shared.
|
||||||
|
// So we need to make a redundant texture to use as an intermediary...
|
||||||
|
mSharedTextureDesc.Width = mDesc.Width;
|
||||||
|
mSharedTextureDesc.Height = mDesc.Height;
|
||||||
|
mSharedTextureDesc.MipLevels = mDesc.MipLevels;
|
||||||
|
mSharedTextureDesc.ArraySize = mDesc.ArraySize;
|
||||||
|
mSharedTextureDesc.Format = static_cast<DXGI_FORMAT>(swapchainCreateInfo.format);
|
||||||
|
mSharedTextureDesc.SampleDesc = mDesc.SampleDesc;
|
||||||
|
mSharedTextureDesc.Usage = D3D11_USAGE_DEFAULT;
|
||||||
|
mSharedTextureDesc.BindFlags = 0;
|
||||||
|
mSharedTextureDesc.CPUAccessFlags = 0;
|
||||||
|
mSharedTextureDesc.MiscFlags = 0;;
|
||||||
|
|
||||||
|
mDevice->CreateTexture2D(&mSharedTextureDesc, nullptr, &mSharedTexture);
|
||||||
|
mXrImage.texture->GetDesc(&mSharedTextureDesc);
|
||||||
|
mDxResourceShareHandle = xr->impl().platform().DXRegisterObject(mSharedTexture, mGlTextureName, GL_TEXTURE_2D, true, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up shared texture as blit target
|
||||||
|
mFramebuffer.reset(new VRFramebuffer(gc->getState(), swapchainCreateInfo.width, swapchainCreateInfo.height, swapchainCreateInfo.sampleCount));
|
||||||
|
|
||||||
|
if (swapchainCreateInfo.usageFlags & XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)
|
||||||
|
{
|
||||||
|
mFramebuffer->setDepthBuffer(gc, mGlTextureName, false);
|
||||||
|
mBufferBits = GL_DEPTH_BUFFER_BIT;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mFramebuffer->setColorBuffer(gc, mGlTextureName, false);
|
||||||
|
mBufferBits = GL_COLOR_BUFFER_BIT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~OpenXRSwapchainImageTemplate()
|
||||||
|
{
|
||||||
|
auto* xr = Environment::get().getManager();
|
||||||
|
if (mDxResourceShareHandle)
|
||||||
|
xr->impl().platform().DXUnregisterObject(mDxResourceShareHandle);
|
||||||
|
glDeleteTextures(1, &mGlTextureName);
|
||||||
|
}
|
||||||
|
|
||||||
|
void blit(osg::GraphicsContext* gc, VRFramebuffer& readBuffer, int offset_x, int offset_y) override
|
||||||
|
{
|
||||||
|
// Blit readBuffer into directx texture, while flipping the Y axis.
|
||||||
|
auto* xr = Environment::get().getManager();
|
||||||
|
xr->impl().platform().DXLockObject(mDxResourceShareHandle);
|
||||||
|
mFramebuffer->bindFramebuffer(gc, GL_FRAMEBUFFER_EXT);
|
||||||
|
readBuffer.blit(gc, offset_x, offset_y, offset_x + mFramebuffer->width(), offset_y + mFramebuffer->height(), 0, mFramebuffer->height(), mFramebuffer->width(), 0, mBufferBits, GL_NEAREST);
|
||||||
|
xr->impl().platform().DXUnlockObject(mDxResourceShareHandle);
|
||||||
|
|
||||||
|
|
||||||
|
// If the d3d11 texture couldn't be shared directly, blit it again.
|
||||||
|
if (mSharedTexture)
|
||||||
|
{
|
||||||
|
mDeviceContext->CopyResource(mXrImage.texture, mSharedTexture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ID3D11Device* mDevice = nullptr;
|
||||||
|
ID3D11DeviceContext* mDeviceContext = nullptr;
|
||||||
|
D3D11_TEXTURE2D_DESC mDesc;
|
||||||
|
D3D11_TEXTURE2D_DESC mSharedTextureDesc;
|
||||||
|
ID3D11Texture2D* mSharedTexture = nullptr;
|
||||||
|
uint32_t mGlTextureName = 0;
|
||||||
|
void* mDxResourceShareHandle = nullptr;
|
||||||
|
|
||||||
|
XrSwapchainImageD3D11KHR mXrImage;
|
||||||
|
uint32_t mBufferBits;
|
||||||
|
std::unique_ptr<VRFramebuffer> mFramebuffer;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
template< typename Image > static inline
|
||||||
|
std::vector<std::unique_ptr<OpenXRSwapchainImage> >
|
||||||
|
enumerateSwapchainImagesImpl(osg::GraphicsContext* gc, XrSwapchain swapchain, XrSwapchainCreateInfo swapchainCreateInfo)
|
||||||
|
{
|
||||||
|
using SwapchainImage = OpenXRSwapchainImageTemplate<Image>;
|
||||||
|
|
||||||
|
uint32_t imageCount = 0;
|
||||||
|
std::vector< Image > images;
|
||||||
|
CHECK_XRCMD(xrEnumerateSwapchainImages(swapchain, 0, &imageCount, nullptr));
|
||||||
|
images.resize(imageCount, { SwapchainImage::XrType });
|
||||||
|
CHECK_XRCMD(xrEnumerateSwapchainImages(swapchain, imageCount, &imageCount, reinterpret_cast<XrSwapchainImageBaseHeader*>(images.data())));
|
||||||
|
|
||||||
|
std::vector<std::unique_ptr<OpenXRSwapchainImage> > swapchainImages;
|
||||||
|
for(auto& image: images)
|
||||||
|
{
|
||||||
|
swapchainImages.emplace_back(new OpenXRSwapchainImageTemplate<Image>(gc, swapchainCreateInfo, image));
|
||||||
|
}
|
||||||
|
|
||||||
|
return swapchainImages;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::unique_ptr<OpenXRSwapchainImage> >
|
||||||
|
OpenXRSwapchainImage::enumerateSwapchainImages(osg::GraphicsContext* gc, XrSwapchain swapchain, XrSwapchainCreateInfo swapchainCreateInfo)
|
||||||
|
{
|
||||||
|
auto* xr = Environment::get().getManager();
|
||||||
|
|
||||||
|
if (xr->xrExtensionIsEnabled(XR_KHR_OPENGL_ENABLE_EXTENSION_NAME))
|
||||||
|
{
|
||||||
|
return enumerateSwapchainImagesImpl<XrSwapchainImageOpenGLKHR>(gc, swapchain, swapchainCreateInfo);
|
||||||
|
}
|
||||||
|
#ifdef XR_USE_GRAPHICS_API_D3D11
|
||||||
|
else if (xr->xrExtensionIsEnabled(XR_KHR_D3D11_ENABLE_EXTENSION_NAME))
|
||||||
|
{
|
||||||
|
return enumerateSwapchainImagesImpl<XrSwapchainImageD3D11KHR>(gc, swapchain, swapchainCreateInfo);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw std::logic_error("Implementation missing for selected graphics API");
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::vector<std::unique_ptr<OpenXRSwapchainImage>>();
|
||||||
|
}
|
||||||
|
|
||||||
|
OpenXRSwapchainImage::OpenXRSwapchainImage()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
27
apps/openmw/mwvr/openxrswapchainimage.hpp
Normal file
27
apps/openmw/mwvr/openxrswapchainimage.hpp
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
#ifndef OPENXR_SWAPCHAINIMAGE_HPP
|
||||||
|
#define OPENXR_SWAPCHAINIMAGE_HPP
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include <openxr/openxr.h>
|
||||||
|
#include <osg/GraphicsContext>
|
||||||
|
|
||||||
|
#include "vrframebuffer.hpp"
|
||||||
|
|
||||||
|
namespace MWVR
|
||||||
|
{
|
||||||
|
class OpenXRSwapchainImage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static std::vector< std::unique_ptr<OpenXRSwapchainImage> >
|
||||||
|
enumerateSwapchainImages(osg::GraphicsContext* gc, XrSwapchain swapchain, XrSwapchainCreateInfo swapchainCreateInfo);
|
||||||
|
|
||||||
|
OpenXRSwapchainImage();
|
||||||
|
virtual ~OpenXRSwapchainImage() {};
|
||||||
|
|
||||||
|
virtual void blit(osg::GraphicsContext* gc, VRFramebuffer& readBuffer, int offset_x, int offset_y) = 0;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
263
apps/openmw/mwvr/openxrswapchainimpl.cpp
Normal file
263
apps/openmw/mwvr/openxrswapchainimpl.cpp
Normal file
|
@ -0,0 +1,263 @@
|
||||||
|
#include "openxrswapchainimpl.hpp"
|
||||||
|
#include "openxrdebug.hpp"
|
||||||
|
#include "vrenvironment.hpp"
|
||||||
|
#include "vrframebuffer.hpp"
|
||||||
|
|
||||||
|
#include <components/debug/debuglog.hpp>
|
||||||
|
|
||||||
|
namespace MWVR {
|
||||||
|
OpenXRSwapchainImpl::OpenXRSwapchainImpl(osg::ref_ptr<osg::State> state, SwapchainConfig config)
|
||||||
|
: mConfig(config)
|
||||||
|
{
|
||||||
|
if (mConfig.selectedWidth <= 0)
|
||||||
|
throw std::invalid_argument("Width must be a positive integer");
|
||||||
|
if (mConfig.selectedHeight <= 0)
|
||||||
|
throw std::invalid_argument("Height must be a positive integer");
|
||||||
|
if (mConfig.selectedSamples <= 0)
|
||||||
|
throw std::invalid_argument("Samples must be a positive integer");
|
||||||
|
|
||||||
|
mSwapchain.reset(new SwapchainPrivate(state, mConfig, SwapchainPrivate::Use::COLOR));
|
||||||
|
mConfig.selectedSamples = mSwapchain->samples();
|
||||||
|
|
||||||
|
auto* xr = Environment::get().getManager();
|
||||||
|
|
||||||
|
if (xr->xrExtensionIsEnabled(XR_KHR_COMPOSITION_LAYER_DEPTH_EXTENSION_NAME))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
mSwapchainDepth.reset(new SwapchainPrivate(state, mConfig, SwapchainPrivate::Use::DEPTH));
|
||||||
|
}
|
||||||
|
catch (std::exception& e)
|
||||||
|
{
|
||||||
|
Log(Debug::Warning) << "XR_KHR_composition_layer_depth was enabled but creating depth swapchain failed: " << e.what();
|
||||||
|
mSwapchainDepth = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OpenXRSwapchainImpl::~OpenXRSwapchainImpl()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenXRSwapchainImpl::isAcquired() const
|
||||||
|
{
|
||||||
|
return mFormallyAcquired;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenXRSwapchainImpl::beginFrame(osg::GraphicsContext* gc)
|
||||||
|
{
|
||||||
|
acquire(gc);
|
||||||
|
}
|
||||||
|
|
||||||
|
int swapCount = 0;
|
||||||
|
|
||||||
|
void OpenXRSwapchainImpl::endFrame(osg::GraphicsContext* gc, VRFramebuffer& readBuffer)
|
||||||
|
{
|
||||||
|
checkAcquired();
|
||||||
|
release(gc, readBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenXRSwapchainImpl::acquire(osg::GraphicsContext* gc)
|
||||||
|
{
|
||||||
|
if (isAcquired())
|
||||||
|
throw std::logic_error("Trying to acquire already acquired swapchain");
|
||||||
|
|
||||||
|
// The openxr runtime may fail to acquire/release.
|
||||||
|
// Do not re-acquire a swapchain before having successfully released it.
|
||||||
|
// Lest the swapchain fall out of sync.
|
||||||
|
if (!mShouldRelease)
|
||||||
|
{
|
||||||
|
mSwapchain->acquire(gc);
|
||||||
|
mShouldRelease = mSwapchain->isAcquired();
|
||||||
|
if (mSwapchainDepth && mSwapchain->isAcquired())
|
||||||
|
{
|
||||||
|
mSwapchainDepth->acquire(gc);
|
||||||
|
mShouldRelease = mSwapchainDepth->isAcquired();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mFormallyAcquired = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenXRSwapchainImpl::release(osg::GraphicsContext* gc, VRFramebuffer& readBuffer)
|
||||||
|
{
|
||||||
|
// The openxr runtime may fail to acquire/release.
|
||||||
|
// Do not release a swapchain before having successfully acquire it.
|
||||||
|
if (mShouldRelease)
|
||||||
|
{
|
||||||
|
mSwapchain->blitAndRelease(gc, readBuffer);
|
||||||
|
mShouldRelease = mSwapchain->isAcquired();
|
||||||
|
if (mSwapchainDepth)
|
||||||
|
{
|
||||||
|
mSwapchainDepth->blitAndRelease(gc, readBuffer);
|
||||||
|
mShouldRelease = mSwapchainDepth->isAcquired();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mFormallyAcquired = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenXRSwapchainImpl::checkAcquired() const
|
||||||
|
{
|
||||||
|
if (!isAcquired())
|
||||||
|
throw std::logic_error("Swapchain must be acquired before use. Call between OpenXRSwapchain::beginFrame() and OpenXRSwapchain::endFrame()");
|
||||||
|
}
|
||||||
|
|
||||||
|
OpenXRSwapchainImpl::SwapchainPrivate::SwapchainPrivate(osg::ref_ptr<osg::State> state, SwapchainConfig config, Use use)
|
||||||
|
: mConfig(config)
|
||||||
|
, mImages()
|
||||||
|
, mWidth(config.selectedWidth)
|
||||||
|
, mHeight(config.selectedHeight)
|
||||||
|
, mSamples(1)
|
||||||
|
, mUsage(use)
|
||||||
|
{
|
||||||
|
auto* xr = Environment::get().getManager();
|
||||||
|
|
||||||
|
XrSwapchainCreateInfo swapchainCreateInfo{ XR_TYPE_SWAPCHAIN_CREATE_INFO };
|
||||||
|
swapchainCreateInfo.arraySize = 1;
|
||||||
|
swapchainCreateInfo.width = mWidth;
|
||||||
|
swapchainCreateInfo.height = mHeight;
|
||||||
|
swapchainCreateInfo.mipCount = 1;
|
||||||
|
swapchainCreateInfo.faceCount = 1;
|
||||||
|
|
||||||
|
while (mSamples > 0 && mSwapchain == XR_NULL_HANDLE && mFormat == 0)
|
||||||
|
{
|
||||||
|
// Select a swapchain format.
|
||||||
|
if (use == Use::COLOR)
|
||||||
|
mFormat = xr->selectColorFormat();
|
||||||
|
else
|
||||||
|
mFormat = xr->selectDepthFormat();
|
||||||
|
std::string typeString = use == Use::COLOR ? "color" : "depth";
|
||||||
|
if (mFormat == 0) {
|
||||||
|
throw std::runtime_error(std::string("Swapchain ") + typeString + " format not supported");
|
||||||
|
}
|
||||||
|
Log(Debug::Verbose) << "Selected " << typeString << " format: " << std::dec << mFormat << " (" << std::hex << mFormat << ")" << std::dec;
|
||||||
|
|
||||||
|
// Now create the swapchain
|
||||||
|
Log(Debug::Verbose) << "Creating swapchain with dimensions Width=" << mWidth << " Heigh=" << mHeight << " SampleCount=" << mSamples;
|
||||||
|
swapchainCreateInfo.format = mFormat;
|
||||||
|
swapchainCreateInfo.sampleCount = mSamples;
|
||||||
|
if(mUsage == Use::COLOR)
|
||||||
|
swapchainCreateInfo.usageFlags = XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT;
|
||||||
|
else
|
||||||
|
swapchainCreateInfo.usageFlags = XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
|
||||||
|
auto res = xrCreateSwapchain(xr->impl().xrSession(), &swapchainCreateInfo, &mSwapchain);
|
||||||
|
|
||||||
|
// Check errors and try again if possible
|
||||||
|
if (res == XR_ERROR_SWAPCHAIN_FORMAT_UNSUPPORTED)
|
||||||
|
{
|
||||||
|
// We only try swapchain formats enumerated by the runtime itself.
|
||||||
|
// This does not guarantee that that swapchain format is going to be supported for this specific usage.
|
||||||
|
Log(Debug::Verbose) << "Failed to create swapchain with Format=" << mFormat<< ": " << XrResultString(res);
|
||||||
|
xr->eraseFormat(mFormat);
|
||||||
|
mFormat = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (!XR_SUCCEEDED(res))
|
||||||
|
{
|
||||||
|
Log(Debug::Verbose) << "Failed to create swapchain with SampleCount=" << mSamples << ": " << XrResultString(res);
|
||||||
|
mSamples /= 2;
|
||||||
|
if (mSamples == 0)
|
||||||
|
{
|
||||||
|
CHECK_XRRESULT(res, "xrCreateSwapchain");
|
||||||
|
throw std::runtime_error(XrResultString(res));
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
CHECK_XRRESULT(res, "xrCreateSwapchain");
|
||||||
|
VrDebug::setName(mSwapchain, "OpenMW XR Color Swapchain " + config.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: here
|
||||||
|
mImages = OpenXRSwapchainImage::enumerateSwapchainImages(state->getGraphicsContext(), mSwapchain, swapchainCreateInfo);
|
||||||
|
mSubImage.swapchain = mSwapchain;
|
||||||
|
mSubImage.imageRect.offset = { 0, 0 };
|
||||||
|
mSubImage.imageRect.extent = { mWidth, mHeight };
|
||||||
|
}
|
||||||
|
|
||||||
|
OpenXRSwapchainImpl::SwapchainPrivate::~SwapchainPrivate()
|
||||||
|
{
|
||||||
|
if (mSwapchain)
|
||||||
|
CHECK_XRCMD(xrDestroySwapchain(mSwapchain));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t OpenXRSwapchainImpl::SwapchainPrivate::count() const
|
||||||
|
{
|
||||||
|
return mImages.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenXRSwapchainImpl::SwapchainPrivate::isAcquired() const
|
||||||
|
{
|
||||||
|
return mIsReady;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenXRSwapchainImpl::SwapchainPrivate::acquire(osg::GraphicsContext* gc)
|
||||||
|
{
|
||||||
|
auto xr = Environment::get().getManager();
|
||||||
|
XrSwapchainImageAcquireInfo acquireInfo{ XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO };
|
||||||
|
XrSwapchainImageWaitInfo waitInfo{ XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO };
|
||||||
|
waitInfo.timeout = XR_INFINITE_DURATION;
|
||||||
|
if (!mIsIndexAcquired)
|
||||||
|
{
|
||||||
|
mIsIndexAcquired = XR_SUCCEEDED(CHECK_XRCMD(xrAcquireSwapchainImage(mSwapchain, &acquireInfo, &mAcquiredIndex)));
|
||||||
|
if (mIsIndexAcquired)
|
||||||
|
xr->xrResourceAcquired();
|
||||||
|
}
|
||||||
|
if (mIsIndexAcquired && !mIsReady)
|
||||||
|
{
|
||||||
|
mIsReady = XR_SUCCEEDED(CHECK_XRCMD(xrWaitSwapchainImage(mSwapchain, &waitInfo)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void OpenXRSwapchainImpl::SwapchainPrivate::blitAndRelease(osg::GraphicsContext* gc, VRFramebuffer& readBuffer)
|
||||||
|
{
|
||||||
|
auto xr = Environment::get().getManager();
|
||||||
|
|
||||||
|
XrSwapchainImageReleaseInfo releaseInfo{ XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO };
|
||||||
|
if (mIsReady)
|
||||||
|
{
|
||||||
|
mImages[mAcquiredIndex]->blit(gc, readBuffer, mConfig.offsetWidth, mConfig.offsetHeight);
|
||||||
|
|
||||||
|
mIsReady = !XR_SUCCEEDED(CHECK_XRCMD(xrReleaseSwapchainImage(mSwapchain, &releaseInfo)));
|
||||||
|
if (!mIsReady)
|
||||||
|
{
|
||||||
|
mIsIndexAcquired = false;
|
||||||
|
xr->xrResourceReleased();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenXRSwapchainImpl::SwapchainPrivate::checkAcquired() const
|
||||||
|
{
|
||||||
|
if (!isAcquired())
|
||||||
|
throw std::logic_error("Swapchain must be acquired before use. Call between OpenXRSwapchain::beginFrame() and OpenXRSwapchain::endFrame()");
|
||||||
|
}
|
||||||
|
|
||||||
|
XrSwapchain OpenXRSwapchainImpl::xrSwapchain(void) const
|
||||||
|
{
|
||||||
|
if(mSwapchain)
|
||||||
|
return mSwapchain->xrSwapchain();
|
||||||
|
return XR_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
XrSwapchain OpenXRSwapchainImpl::xrSwapchainDepth(void) const
|
||||||
|
{
|
||||||
|
if (mSwapchainDepth)
|
||||||
|
return mSwapchainDepth->xrSwapchain();
|
||||||
|
return XR_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
XrSwapchainSubImage OpenXRSwapchainImpl::xrSubImage(void) const
|
||||||
|
{
|
||||||
|
if (mSwapchain)
|
||||||
|
return mSwapchain->xrSubImage();
|
||||||
|
return XrSwapchainSubImage{ XR_NULL_HANDLE };
|
||||||
|
}
|
||||||
|
|
||||||
|
XrSwapchainSubImage OpenXRSwapchainImpl::xrSubImageDepth(void) const
|
||||||
|
{
|
||||||
|
if (mSwapchainDepth)
|
||||||
|
return mSwapchainDepth->xrSubImage();
|
||||||
|
return XrSwapchainSubImage{ XR_NULL_HANDLE };
|
||||||
|
}
|
||||||
|
}
|
90
apps/openmw/mwvr/openxrswapchainimpl.hpp
Normal file
90
apps/openmw/mwvr/openxrswapchainimpl.hpp
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
#ifndef OPENXR_SWAPCHAINIMPL_HPP
|
||||||
|
#define OPENXR_SWAPCHAINIMPL_HPP
|
||||||
|
|
||||||
|
#include "openxrswapchainimage.hpp"
|
||||||
|
#include "openxrmanagerimpl.hpp"
|
||||||
|
|
||||||
|
struct XrSwapchainSubImage;
|
||||||
|
|
||||||
|
namespace MWVR
|
||||||
|
{
|
||||||
|
class VRFramebuffer;
|
||||||
|
|
||||||
|
/// \brief Implementation of OpenXRSwapchain
|
||||||
|
class OpenXRSwapchainImpl
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
struct SwapchainPrivate
|
||||||
|
{
|
||||||
|
enum class Use {
|
||||||
|
COLOR = 0,
|
||||||
|
DEPTH = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
SwapchainPrivate(osg::ref_ptr<osg::State> state, SwapchainConfig config, Use use);
|
||||||
|
~SwapchainPrivate();
|
||||||
|
|
||||||
|
uint32_t count() const;
|
||||||
|
bool isAcquired() const;
|
||||||
|
uint32_t acuiredIndex() const { return mAcquiredIndex; };
|
||||||
|
XrSwapchain xrSwapchain(void) const { return mSwapchain; };
|
||||||
|
XrSwapchainSubImage xrSubImage(void) const { return mSubImage; };
|
||||||
|
int width() const { return mWidth; };
|
||||||
|
int height() const { return mHeight; };
|
||||||
|
int samples() const { return mSamples; };
|
||||||
|
|
||||||
|
void acquire(osg::GraphicsContext* gc);
|
||||||
|
void blitAndRelease(osg::GraphicsContext* gc, VRFramebuffer& readBuffer);
|
||||||
|
void checkAcquired() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
private:
|
||||||
|
SwapchainConfig mConfig;
|
||||||
|
XrSwapchain mSwapchain = XR_NULL_HANDLE;
|
||||||
|
XrSwapchainSubImage mSubImage{};
|
||||||
|
std::vector< std::unique_ptr<OpenXRSwapchainImage> > mImages;
|
||||||
|
int32_t mWidth = -1;
|
||||||
|
int32_t mHeight = -1;
|
||||||
|
int32_t mSamples = -1;
|
||||||
|
int64_t mFormat = 0;
|
||||||
|
uint32_t mAcquiredIndex{ 0 };
|
||||||
|
Use mUsage;
|
||||||
|
bool mIsIndexAcquired{ false };
|
||||||
|
bool mIsReady{ false };
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
OpenXRSwapchainImpl(osg::ref_ptr<osg::State> state, SwapchainConfig config);
|
||||||
|
~OpenXRSwapchainImpl();
|
||||||
|
|
||||||
|
void beginFrame(osg::GraphicsContext* gc);
|
||||||
|
void endFrame(osg::GraphicsContext* gc, VRFramebuffer& readBuffer);
|
||||||
|
|
||||||
|
bool isAcquired() const;
|
||||||
|
XrSwapchain xrSwapchain(void) const;
|
||||||
|
XrSwapchain xrSwapchainDepth(void) const;
|
||||||
|
XrSwapchainSubImage xrSubImage(void) const;
|
||||||
|
XrSwapchainSubImage xrSubImageDepth(void) const;
|
||||||
|
int width() const { return mConfig.selectedWidth; };
|
||||||
|
int height() const { return mConfig.selectedHeight; };
|
||||||
|
int samples() const { return mConfig.selectedSamples; };
|
||||||
|
|
||||||
|
protected:
|
||||||
|
OpenXRSwapchainImpl(const OpenXRSwapchainImpl&) = delete;
|
||||||
|
void operator=(const OpenXRSwapchainImpl&) = delete;
|
||||||
|
|
||||||
|
void acquire(osg::GraphicsContext* gc);
|
||||||
|
void release(osg::GraphicsContext* gc, VRFramebuffer& readBuffer);
|
||||||
|
void checkAcquired() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
SwapchainConfig mConfig;
|
||||||
|
std::unique_ptr<SwapchainPrivate> mSwapchain{ nullptr };
|
||||||
|
std::unique_ptr<SwapchainPrivate> mSwapchainDepth{ nullptr };
|
||||||
|
bool mFormallyAcquired{ false };
|
||||||
|
bool mShouldRelease{ false };
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
141
apps/openmw/mwvr/openxrtracker.cpp
Normal file
141
apps/openmw/mwvr/openxrtracker.cpp
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
#include "openxrinput.hpp"
|
||||||
|
#include "openxrmanagerimpl.hpp"
|
||||||
|
#include "openxrplatform.hpp"
|
||||||
|
#include "openxrtracker.hpp"
|
||||||
|
#include "openxrtypeconversions.hpp"
|
||||||
|
#include "vrenvironment.hpp"
|
||||||
|
#include "vrinputmanager.hpp"
|
||||||
|
#include "vrsession.hpp"
|
||||||
|
|
||||||
|
#include <components/misc/constants.hpp>
|
||||||
|
|
||||||
|
namespace MWVR
|
||||||
|
{
|
||||||
|
OpenXRTracker::OpenXRTracker(const std::string& name, XrSpace referenceSpace)
|
||||||
|
: VRTrackingSource(name)
|
||||||
|
, mReferenceSpace(referenceSpace)
|
||||||
|
, mTrackingSpaces()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
OpenXRTracker::~OpenXRTracker()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenXRTracker::addTrackingSpace(VRPath path, XrSpace space)
|
||||||
|
{
|
||||||
|
mTrackingSpaces[path] = space;
|
||||||
|
notifyAvailablePosesChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenXRTracker::deleteTrackingSpace(VRPath path)
|
||||||
|
{
|
||||||
|
mTrackingSpaces.erase(path);
|
||||||
|
notifyAvailablePosesChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenXRTracker::setReferenceSpace(XrSpace referenceSpace)
|
||||||
|
{
|
||||||
|
mReferenceSpace = referenceSpace;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<VRPath> OpenXRTracker::listSupportedTrackingPosePaths() const
|
||||||
|
{
|
||||||
|
std::vector<VRPath> path;
|
||||||
|
for (auto& e : mTrackingSpaces)
|
||||||
|
path.push_back(e.first);
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenXRTracker::updateTracking(DisplayTime predictedDisplayTime)
|
||||||
|
{
|
||||||
|
Environment::get().getInputManager()->xrInput().getActionSet(ActionSet::Tracking).updateControls();
|
||||||
|
auto* xr = Environment::get().getManager();
|
||||||
|
auto* session = Environment::get().getSession();
|
||||||
|
|
||||||
|
auto& frame = session->getFrame(VRSession::FramePhase::Update);
|
||||||
|
frame->mViews[(int)ReferenceSpace::STAGE] = locateViews(predictedDisplayTime, xr->impl().getReferenceSpace(ReferenceSpace::STAGE));
|
||||||
|
frame->mViews[(int)ReferenceSpace::VIEW] = locateViews(predictedDisplayTime, xr->impl().getReferenceSpace(ReferenceSpace::VIEW));
|
||||||
|
}
|
||||||
|
|
||||||
|
XrSpace OpenXRTracker::getSpace(VRPath path)
|
||||||
|
{
|
||||||
|
auto it = mTrackingSpaces.find(path);
|
||||||
|
if (it != mTrackingSpaces.end())
|
||||||
|
return it->second;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
VRTrackingPose OpenXRTracker::getTrackingPoseImpl(DisplayTime predictedDisplayTime, VRPath path, VRPath reference)
|
||||||
|
{
|
||||||
|
VRTrackingPose pose;
|
||||||
|
pose.status = TrackingStatus::Good;
|
||||||
|
XrSpace space = getSpace(path);
|
||||||
|
XrSpace ref = reference == 0 ? mReferenceSpace : getSpace(reference);
|
||||||
|
if (space == 0 || ref == 0)
|
||||||
|
pose.status = TrackingStatus::NotTracked;
|
||||||
|
if (!!pose.status)
|
||||||
|
locate(pose, space, ref, predictedDisplayTime);
|
||||||
|
return pose;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenXRTracker::locate(VRTrackingPose& pose, XrSpace space, XrSpace reference, DisplayTime predictedDisplayTime)
|
||||||
|
{
|
||||||
|
XrSpaceLocation location{ XR_TYPE_SPACE_LOCATION };
|
||||||
|
auto res = xrLocateSpace(space, mReferenceSpace, predictedDisplayTime, &location);
|
||||||
|
|
||||||
|
if (XR_FAILED(res))
|
||||||
|
{
|
||||||
|
// Call failed, exit.
|
||||||
|
CHECK_XRRESULT(res, "xrLocateSpace");
|
||||||
|
pose.status = TrackingStatus::RuntimeFailure;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that everything is being tracked
|
||||||
|
if (!(location.locationFlags & (XR_SPACE_LOCATION_ORIENTATION_TRACKED_BIT | XR_SPACE_LOCATION_POSITION_TRACKED_BIT)))
|
||||||
|
{
|
||||||
|
// It's not, data is stale
|
||||||
|
pose.status = TrackingStatus::Stale;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that data is valid
|
||||||
|
if (!(location.locationFlags & (XR_SPACE_LOCATION_ORIENTATION_VALID_BIT | XR_SPACE_LOCATION_POSITION_VALID_BIT)))
|
||||||
|
{
|
||||||
|
// It's not, we've lost tracking
|
||||||
|
pose.status = TrackingStatus::Lost;
|
||||||
|
}
|
||||||
|
|
||||||
|
pose.pose = MWVR::Pose{
|
||||||
|
fromXR(location.pose.position),
|
||||||
|
fromXR(location.pose.orientation)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array<View, 2> OpenXRTracker::locateViews(DisplayTime predictedDisplayTime, XrSpace reference)
|
||||||
|
{
|
||||||
|
std::array<XrView, 2> xrViews{ {{XR_TYPE_VIEW}, {XR_TYPE_VIEW}} };
|
||||||
|
XrViewState viewState{ XR_TYPE_VIEW_STATE };
|
||||||
|
uint32_t viewCount = 2;
|
||||||
|
|
||||||
|
XrViewLocateInfo viewLocateInfo{ XR_TYPE_VIEW_LOCATE_INFO };
|
||||||
|
viewLocateInfo.viewConfigurationType = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO;
|
||||||
|
viewLocateInfo.displayTime = predictedDisplayTime;
|
||||||
|
viewLocateInfo.space = reference;
|
||||||
|
|
||||||
|
auto* xr = Environment::get().getManager();
|
||||||
|
CHECK_XRCMD(xrLocateViews(xr->impl().xrSession(), &viewLocateInfo, &viewState, viewCount, &viewCount, xrViews.data()));
|
||||||
|
|
||||||
|
std::array<View, 2> vrViews{};
|
||||||
|
vrViews[(int)Side::LEFT_SIDE].pose = fromXR(xrViews[(int)Side::LEFT_SIDE].pose);
|
||||||
|
vrViews[(int)Side::RIGHT_SIDE].pose = fromXR(xrViews[(int)Side::RIGHT_SIDE].pose);
|
||||||
|
vrViews[(int)Side::LEFT_SIDE].fov = fromXR(xrViews[(int)Side::LEFT_SIDE].fov);
|
||||||
|
vrViews[(int)Side::RIGHT_SIDE].fov = fromXR(xrViews[(int)Side::RIGHT_SIDE].fov);
|
||||||
|
return vrViews;
|
||||||
|
}
|
||||||
|
|
||||||
|
OpenXRTrackingToWorldBinding::OpenXRTrackingToWorldBinding()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
86
apps/openmw/mwvr/openxrtracker.hpp
Normal file
86
apps/openmw/mwvr/openxrtracker.hpp
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
#ifndef OPENXR_TRACKER_HPP
|
||||||
|
#define OPENXR_TRACKER_HPP
|
||||||
|
|
||||||
|
#include <openxr/openxr.h>
|
||||||
|
#include "vrtracking.hpp"
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
namespace MWVR
|
||||||
|
{
|
||||||
|
//! Serves as a C++ wrapper of openxr spaces, but also bridges stage coordinates and game coordinates.
|
||||||
|
//! Supports the compulsory sets of paths.
|
||||||
|
class OpenXRTracker : public VRTrackingSource
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
OpenXRTracker(const std::string& name, XrSpace referenceSpace);
|
||||||
|
~OpenXRTracker();
|
||||||
|
|
||||||
|
void addTrackingSpace(VRPath path, XrSpace space);
|
||||||
|
void deleteTrackingSpace(VRPath path);
|
||||||
|
|
||||||
|
//! The base space used to reference everything else.
|
||||||
|
void setReferenceSpace(XrSpace referenceSpace);
|
||||||
|
|
||||||
|
std::vector<VRPath> listSupportedTrackingPosePaths() const override;
|
||||||
|
void updateTracking(DisplayTime predictedDisplayTime) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
VRTrackingPose getTrackingPoseImpl(DisplayTime predictedDisplayTime, VRPath path, VRPath reference = 0) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::array<View, 2> locateViews(DisplayTime predictedDisplayTime, XrSpace reference);
|
||||||
|
void locate(VRTrackingPose& pose, XrSpace space, XrSpace reference, DisplayTime predictedDisplayTime);
|
||||||
|
XrSpace getSpace(VRPath);
|
||||||
|
|
||||||
|
XrSpace mReferenceSpace;
|
||||||
|
std::map<VRPath, XrSpace> mTrackingSpaces;
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Ties a tracked pose to the game world.
|
||||||
|
//! A movement tracking pose is selected by passing its path to the constructor.
|
||||||
|
//! All poses are transformed in the horizontal plane by moving the x,y origin to the position of the movement tracking pose, and then reoriented using the current orientation.
|
||||||
|
//! The movement tracking pose is effectively always at the x,y origin
|
||||||
|
//! The movement of the movement tracking pose is accumulated and can be read using the movement() call.
|
||||||
|
//! If this movement is ever consumed (such as by moving the character to follow the player) the consumed movement must be reported using consumeMovement().
|
||||||
|
class OpenXRTrackingToWorldBinding
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
OpenXRTrackingToWorldBinding();
|
||||||
|
|
||||||
|
//! Re-orient the stage.
|
||||||
|
void setOrientation(float yaw, bool adjust);
|
||||||
|
osg::Quat getOrientation() const { return mOrientation; }
|
||||||
|
|
||||||
|
void setEyeLevel(float eyeLevel) { mEyeLevel = eyeLevel; }
|
||||||
|
float getEyeLevel() const { return mEyeLevel; }
|
||||||
|
|
||||||
|
void setSeatedPlay(bool seatedPlay) { mSeatedPlay = seatedPlay; }
|
||||||
|
bool getSeatedPlay() const { return mSeatedPlay; }
|
||||||
|
|
||||||
|
//! The player's movement within the VR stage. This accumulates until the movement has been consumed by calling consumeMovement()
|
||||||
|
osg::Vec3 movement() const;
|
||||||
|
|
||||||
|
//! Consume movement
|
||||||
|
void consumeMovement(const osg::Vec3& movement);
|
||||||
|
|
||||||
|
//! Recenter tracking by consuming all movement.
|
||||||
|
void recenter(bool resetZ);
|
||||||
|
|
||||||
|
void update(Pose movementTrackingPose);
|
||||||
|
|
||||||
|
//! Transforms a stage-referenced pose to be world-aligned.
|
||||||
|
//! \note Unlike VRTrackingSource::getTrackingPose() this does not take a reference path, as re-alignment is only needed when fetching a stage-referenced pose.
|
||||||
|
void alignPose(Pose& pose);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool mSeatedPlay = false;
|
||||||
|
bool mHasTrackingData = false;
|
||||||
|
float mEyeLevel = 0;
|
||||||
|
Pose mLastPose = Pose();
|
||||||
|
osg::Vec3 mMovement = osg::Vec3(0,0,0);
|
||||||
|
osg::Quat mOrientation = osg::Quat(0,0,0,1);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
76
apps/openmw/mwvr/openxrtypeconversions.cpp
Normal file
76
apps/openmw/mwvr/openxrtypeconversions.cpp
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
#include "openxrtypeconversions.hpp"
|
||||||
|
#include "openxrswapchain.hpp"
|
||||||
|
#include "openxrswapchainimpl.hpp"
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace MWVR
|
||||||
|
{
|
||||||
|
osg::Vec3 fromXR(XrVector3f v)
|
||||||
|
{
|
||||||
|
return osg::Vec3{ v.x, -v.z, v.y };
|
||||||
|
}
|
||||||
|
|
||||||
|
osg::Quat fromXR(XrQuaternionf quat)
|
||||||
|
{
|
||||||
|
return osg::Quat{ quat.x, -quat.z, quat.y, quat.w };
|
||||||
|
}
|
||||||
|
|
||||||
|
XrVector3f toXR(osg::Vec3 v)
|
||||||
|
{
|
||||||
|
return XrVector3f{ v.x(), v.z(), -v.y() };
|
||||||
|
}
|
||||||
|
|
||||||
|
XrQuaternionf toXR(osg::Quat quat)
|
||||||
|
{
|
||||||
|
return XrQuaternionf{ static_cast<float>(quat.x()), static_cast<float>(quat.z()), static_cast<float>(-quat.y()), static_cast<float>(quat.w()) };
|
||||||
|
}
|
||||||
|
|
||||||
|
MWVR::Pose fromXR(XrPosef pose)
|
||||||
|
{
|
||||||
|
return MWVR::Pose{ fromXR(pose.position), fromXR(pose.orientation) };
|
||||||
|
}
|
||||||
|
|
||||||
|
XrPosef toXR(MWVR::Pose pose)
|
||||||
|
{
|
||||||
|
return XrPosef{ toXR(pose.orientation), toXR(pose.position) };
|
||||||
|
}
|
||||||
|
|
||||||
|
MWVR::FieldOfView fromXR(XrFovf fov)
|
||||||
|
{
|
||||||
|
return MWVR::FieldOfView{ fov.angleLeft, fov.angleRight, fov.angleUp, fov.angleDown };
|
||||||
|
}
|
||||||
|
|
||||||
|
XrFovf toXR(MWVR::FieldOfView fov)
|
||||||
|
{
|
||||||
|
return XrFovf{ fov.angleLeft, fov.angleRight, fov.angleUp, fov.angleDown };
|
||||||
|
}
|
||||||
|
|
||||||
|
XrCompositionLayerProjectionView toXR(MWVR::CompositionLayerProjectionView layer)
|
||||||
|
{
|
||||||
|
XrCompositionLayerProjectionView xrLayer;
|
||||||
|
xrLayer.type = XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW;
|
||||||
|
xrLayer.subImage = toXR(layer.subImage, false);
|
||||||
|
xrLayer.pose = toXR(layer.pose);
|
||||||
|
xrLayer.fov = toXR(layer.fov);
|
||||||
|
xrLayer.next = nullptr;
|
||||||
|
|
||||||
|
return xrLayer;
|
||||||
|
}
|
||||||
|
|
||||||
|
XrSwapchainSubImage toXR(MWVR::SubImage subImage, bool depthImage)
|
||||||
|
{
|
||||||
|
XrSwapchainSubImage xrSubImage{};
|
||||||
|
if (depthImage)
|
||||||
|
xrSubImage.swapchain = subImage.swapchain->impl().xrSwapchainDepth();
|
||||||
|
else
|
||||||
|
xrSubImage.swapchain = subImage.swapchain->impl().xrSwapchain();
|
||||||
|
xrSubImage.imageRect.extent.width = subImage.width;
|
||||||
|
xrSubImage.imageRect.extent.height = subImage.height;
|
||||||
|
xrSubImage.imageRect.offset.x = subImage.x;
|
||||||
|
xrSubImage.imageRect.offset.y = subImage.y;
|
||||||
|
xrSubImage.imageArrayIndex = 0;
|
||||||
|
return xrSubImage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
25
apps/openmw/mwvr/openxrtypeconversions.hpp
Normal file
25
apps/openmw/mwvr/openxrtypeconversions.hpp
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
#ifndef MWVR_OPENXRTYPECONVERSIONS_H
|
||||||
|
#define MWVR_OPENXRTYPECONVERSIONS_H
|
||||||
|
|
||||||
|
#include <openxr/openxr.h>
|
||||||
|
#include "vrtypes.hpp"
|
||||||
|
#include <osg/Vec3>
|
||||||
|
#include <osg/Quat>
|
||||||
|
|
||||||
|
namespace MWVR
|
||||||
|
{
|
||||||
|
/// Conversion methods between openxr types to osg/mwvr types. Includes managing the differing conventions.
|
||||||
|
Pose fromXR(XrPosef pose);
|
||||||
|
FieldOfView fromXR(XrFovf fov);
|
||||||
|
osg::Vec3 fromXR(XrVector3f);
|
||||||
|
osg::Quat fromXR(XrQuaternionf quat);
|
||||||
|
XrPosef toXR(Pose pose);
|
||||||
|
XrFovf toXR(FieldOfView fov);
|
||||||
|
XrVector3f toXR(osg::Vec3 v);
|
||||||
|
XrQuaternionf toXR(osg::Quat quat);
|
||||||
|
|
||||||
|
XrCompositionLayerProjectionView toXR(CompositionLayerProjectionView layer);
|
||||||
|
XrSwapchainSubImage toXR(SubImage, bool depthImage);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
347
apps/openmw/mwvr/realisticcombat.cpp
Normal file
347
apps/openmw/mwvr/realisticcombat.cpp
Normal file
|
@ -0,0 +1,347 @@
|
||||||
|
#include "realisticcombat.hpp"
|
||||||
|
|
||||||
|
#include "../mwbase/environment.hpp"
|
||||||
|
#include "../mwbase/soundmanager.hpp"
|
||||||
|
|
||||||
|
#include "../mwmechanics/weapontype.hpp"
|
||||||
|
|
||||||
|
#include <components/debug/debuglog.hpp>
|
||||||
|
|
||||||
|
#include <iomanip>
|
||||||
|
|
||||||
|
namespace MWVR {
|
||||||
|
namespace RealisticCombat {
|
||||||
|
|
||||||
|
static const char* stateToString(SwingState florida)
|
||||||
|
{
|
||||||
|
switch (florida)
|
||||||
|
{
|
||||||
|
case SwingState_Cooldown:
|
||||||
|
return "Cooldown";
|
||||||
|
case SwingState_Impact:
|
||||||
|
return "Impact";
|
||||||
|
case SwingState_Ready:
|
||||||
|
return "Ready";
|
||||||
|
case SwingState_Swing:
|
||||||
|
return "Swing";
|
||||||
|
case SwingState_Launch:
|
||||||
|
return "Launch";
|
||||||
|
}
|
||||||
|
return "Error, invalid enum";
|
||||||
|
}
|
||||||
|
static const char* swingTypeToString(int type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case ESM::Weapon::AT_Chop:
|
||||||
|
return "Chop";
|
||||||
|
case ESM::Weapon::AT_Slash:
|
||||||
|
return "Slash";
|
||||||
|
case ESM::Weapon::AT_Thrust:
|
||||||
|
return "Thrust";
|
||||||
|
case -1:
|
||||||
|
return "Fail";
|
||||||
|
default:
|
||||||
|
return "Invalid";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StateMachine::StateMachine(MWWorld::Ptr ptr, VRPath trackingPath)
|
||||||
|
: mPtr(ptr)
|
||||||
|
, mMinVelocity(Settings::Manager::getFloat("realistic combat minimum swing velocity", "VR"))
|
||||||
|
, mMaxVelocity(Settings::Manager::getFloat("realistic combat maximum swing velocity", "VR"))
|
||||||
|
, mTrackingPath(trackingPath)
|
||||||
|
{
|
||||||
|
Log(Debug::Verbose) << "realistic combat minimum swing velocity: " << mMinVelocity;
|
||||||
|
Log(Debug::Verbose) << "realistic combat maximum swing velocity: " << mMaxVelocity;
|
||||||
|
Environment::get().getTrackingManager()->bind(this, "pcstage");
|
||||||
|
}
|
||||||
|
|
||||||
|
void StateMachine::onTrackingUpdated(VRTrackingSource& source, DisplayTime predictedDisplayTime)
|
||||||
|
{
|
||||||
|
mTrackingInput = source.getTrackingPose(predictedDisplayTime, mTrackingPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool StateMachine::canSwing()
|
||||||
|
{
|
||||||
|
if (mSwingType >= 0)
|
||||||
|
if (mVelocity >= mMinVelocity)
|
||||||
|
if (mSwingType != ESM::Weapon::AT_Thrust || mThrustVelocity >= 0.f)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Actions common to all transitions
|
||||||
|
void StateMachine::transition(
|
||||||
|
SwingState newState)
|
||||||
|
{
|
||||||
|
Log(Debug::Verbose) << "Transition:" << stateToString(mState) << "->" << stateToString(newState);
|
||||||
|
mMaxSwingVelocity = 0.f;
|
||||||
|
mTimeSinceEnteredState = 0.f;
|
||||||
|
mMovementSinceEnteredState = 0.f;
|
||||||
|
mState = newState;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StateMachine::reset()
|
||||||
|
{
|
||||||
|
mMaxSwingVelocity = 0.f;
|
||||||
|
mTimeSinceEnteredState = 0.f;
|
||||||
|
mVelocity = 0.f;
|
||||||
|
mPreviousPosition = osg::Vec3(0.f, 0.f, 0.f);
|
||||||
|
mState = SwingState_Ready;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isMeleeWeapon(int type)
|
||||||
|
{
|
||||||
|
if (MWMechanics::getWeaponType(type)->mWeaponClass != ESM::WeaponType::Melee)
|
||||||
|
return false;
|
||||||
|
if (type == ESM::Weapon::HandToHand)
|
||||||
|
return true;
|
||||||
|
if (type >= 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isSideSwingValidForWeapon(int type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case ESM::Weapon::HandToHand:
|
||||||
|
case ESM::Weapon::BluntOneHand:
|
||||||
|
case ESM::Weapon::BluntTwoClose:
|
||||||
|
case ESM::Weapon::BluntTwoWide:
|
||||||
|
case ESM::Weapon::SpearTwoWide:
|
||||||
|
return true;
|
||||||
|
case ESM::Weapon::ShortBladeOneHand:
|
||||||
|
case ESM::Weapon::LongBladeOneHand:
|
||||||
|
case ESM::Weapon::LongBladeTwoHand:
|
||||||
|
case ESM::Weapon::AxeOneHand:
|
||||||
|
case ESM::Weapon::AxeTwoHand:
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StateMachine::update(float dt, bool enabled)
|
||||||
|
{
|
||||||
|
auto* world = MWBase::Environment::get().getWorld();
|
||||||
|
auto& handPose = mTrackingInput.pose;
|
||||||
|
auto weaponType = world->getActiveWeaponType();
|
||||||
|
|
||||||
|
enabled = enabled && isMeleeWeapon(weaponType);
|
||||||
|
enabled = enabled && !!mTrackingInput.status;
|
||||||
|
|
||||||
|
if (mEnabled != enabled)
|
||||||
|
{
|
||||||
|
reset();
|
||||||
|
mEnabled = enabled;
|
||||||
|
}
|
||||||
|
if (!enabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
mTimeSinceEnteredState += dt;
|
||||||
|
|
||||||
|
|
||||||
|
// First determine direction of different swing types
|
||||||
|
|
||||||
|
// Discover orientation of weapon
|
||||||
|
osg::Quat weaponDir = handPose.orientation;
|
||||||
|
|
||||||
|
// Morrowind models do not hold weapons at a natural angle, so i rotate the hand forward
|
||||||
|
// to get a more natural angle on the weapon to allow more comfortable combat.
|
||||||
|
if (weaponType != ESM::Weapon::HandToHand)
|
||||||
|
weaponDir = osg::Quat(osg::PI_4, osg::Vec3{ 1,0,0 }) * weaponDir;
|
||||||
|
|
||||||
|
// Thrust means stabbing in the direction of the weapon
|
||||||
|
osg::Vec3 thrustDirection = weaponDir * osg::Vec3{ 0,1,0 };
|
||||||
|
|
||||||
|
// Slash and Chop are vertical, relative to the orientation of the weapon (direction of the sharp edge / hammer)
|
||||||
|
osg::Vec3 slashChopDirection = weaponDir * osg::Vec3{ 0,0,1 };
|
||||||
|
|
||||||
|
// Side direction of the weapon (i.e. The blunt side of the sword)
|
||||||
|
osg::Vec3 sideDirection = weaponDir * osg::Vec3{ 1,0,0 };
|
||||||
|
|
||||||
|
|
||||||
|
// Next determine current hand movement
|
||||||
|
|
||||||
|
// If tracking is lost, openxr will return a position of 0
|
||||||
|
// So i reset position when tracking is re-acquired to avoid a superspeed strike.
|
||||||
|
// Theoretically, the player's hand really could be at 0,0,0
|
||||||
|
// but that's a super rare case so whatever.
|
||||||
|
if (mPreviousPosition == osg::Vec3(0.f, 0.f, 0.f))
|
||||||
|
mPreviousPosition = handPose.position;
|
||||||
|
|
||||||
|
osg::Vec3 movement = handPose.position - mPreviousPosition;
|
||||||
|
mMovementSinceEnteredState += movement.length();
|
||||||
|
mPreviousPosition = handPose.position;
|
||||||
|
osg::Vec3 swingVector = movement / dt;
|
||||||
|
osg::Vec3 swingDirection = swingVector;
|
||||||
|
swingDirection.normalize();
|
||||||
|
|
||||||
|
// Compute swing velocities
|
||||||
|
|
||||||
|
// Thrust follows the orientation of the weapon. Negative thrust = no attack.
|
||||||
|
mThrustVelocity = swingVector * thrustDirection;
|
||||||
|
mVelocity = swingVector.length();
|
||||||
|
|
||||||
|
|
||||||
|
if (isSideSwingValidForWeapon(weaponType))
|
||||||
|
{
|
||||||
|
// Compute velocity in the plane normal to the thrust direction.
|
||||||
|
float thrustComponent = std::abs(mThrustVelocity / mVelocity);
|
||||||
|
float planeComponent = std::sqrt(1 - thrustComponent * thrustComponent);
|
||||||
|
mSlashChopVelocity = mVelocity * planeComponent;
|
||||||
|
mSideVelocity = -1000.f;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If side swing is not valid for the weapon, count slash/chop only along in
|
||||||
|
// the direction of the weapon's edge.
|
||||||
|
mSlashChopVelocity = std::abs(swingVector * slashChopDirection);
|
||||||
|
mSideVelocity = std::abs(swingVector * sideDirection);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
float orientationVerticality = std::abs(thrustDirection * osg::Vec3{ 0,0,1 });
|
||||||
|
float swingVerticality = std::abs(swingDirection * osg::Vec3{ 0,0,1 });
|
||||||
|
|
||||||
|
// Pick swing type based on greatest current velocity
|
||||||
|
// Note i use abs() of thrust velocity to prevent accidentally triggering
|
||||||
|
// chop/slash when player is withdrawing the weapon.
|
||||||
|
if (mSideVelocity > std::abs(mThrustVelocity) && mSideVelocity > mSlashChopVelocity)
|
||||||
|
{
|
||||||
|
// Player is swinging with the "blunt" side of a weapon that
|
||||||
|
// cannot be used that way.
|
||||||
|
mSwingType = -1;
|
||||||
|
}
|
||||||
|
else if (std::abs(mThrustVelocity) > mSlashChopVelocity)
|
||||||
|
{
|
||||||
|
mSwingType = ESM::Weapon::AT_Thrust;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// First check if the weapon is pointing upwards. In which case slash is not
|
||||||
|
// applicable, and the attack must be a chop.
|
||||||
|
if (orientationVerticality > 0.707)
|
||||||
|
mSwingType = ESM::Weapon::AT_Chop;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Next check if the swing is more horizontal or vertical. A slash
|
||||||
|
// would be more horizontal.
|
||||||
|
if (swingVerticality > 0.707)
|
||||||
|
mSwingType = ESM::Weapon::AT_Chop;
|
||||||
|
else
|
||||||
|
mSwingType = ESM::Weapon::AT_Slash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (mState)
|
||||||
|
{
|
||||||
|
case SwingState_Cooldown:
|
||||||
|
return update_cooldownState();
|
||||||
|
case SwingState_Ready:
|
||||||
|
return update_readyState();
|
||||||
|
case SwingState_Swing:
|
||||||
|
return update_swingState();
|
||||||
|
case SwingState_Impact:
|
||||||
|
return update_impactState();
|
||||||
|
case SwingState_Launch:
|
||||||
|
return update_launchState();
|
||||||
|
default:
|
||||||
|
throw std::logic_error(std::string("You forgot to implement state ") + stateToString(mState) + " ya dingus");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StateMachine::update_cooldownState()
|
||||||
|
{
|
||||||
|
if (mTimeSinceEnteredState >= mMinimumPeriod)
|
||||||
|
transition_cooldownToReady();
|
||||||
|
}
|
||||||
|
|
||||||
|
void StateMachine::transition_cooldownToReady()
|
||||||
|
{
|
||||||
|
transition(SwingState_Ready);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StateMachine::update_readyState()
|
||||||
|
{
|
||||||
|
if (canSwing())
|
||||||
|
return transition_readyToLaunch();
|
||||||
|
}
|
||||||
|
|
||||||
|
void StateMachine::transition_readyToLaunch()
|
||||||
|
{
|
||||||
|
transition(SwingState_Launch);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StateMachine::playSwish()
|
||||||
|
{
|
||||||
|
MWBase::SoundManager* sndMgr = MWBase::Environment::get().getSoundManager();
|
||||||
|
|
||||||
|
std::string sound = "Weapon Swish";
|
||||||
|
if (mStrength < 0.5f)
|
||||||
|
sndMgr->playSound3D(mPtr, sound, 1.0f, 0.8f); //Weak attack
|
||||||
|
if (mStrength < 1.0f)
|
||||||
|
sndMgr->playSound3D(mPtr, sound, 1.0f, 1.0f); //Medium attack
|
||||||
|
else
|
||||||
|
sndMgr->playSound3D(mPtr, sound, 1.0f, 1.2f); //Strong attack
|
||||||
|
|
||||||
|
Log(Debug::Verbose) << "Swing: " << swingTypeToString(mSwingType);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StateMachine::update_launchState()
|
||||||
|
{
|
||||||
|
if (mMovementSinceEnteredState > mMinimumPeriod)
|
||||||
|
transition_launchToSwing();
|
||||||
|
if (!canSwing())
|
||||||
|
return transition_launchToReady();
|
||||||
|
}
|
||||||
|
|
||||||
|
void StateMachine::transition_launchToReady()
|
||||||
|
{
|
||||||
|
transition(SwingState_Ready);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StateMachine::transition_launchToSwing()
|
||||||
|
{
|
||||||
|
playSwish();
|
||||||
|
transition(SwingState_Swing);
|
||||||
|
|
||||||
|
// As a special case, update the new state immediately to allow
|
||||||
|
// same-frame impacts.
|
||||||
|
update_swingState();
|
||||||
|
}
|
||||||
|
|
||||||
|
void StateMachine::update_swingState()
|
||||||
|
{
|
||||||
|
mMaxSwingVelocity = std::max(mVelocity, mMaxSwingVelocity);
|
||||||
|
mStrength = std::min(1.f, (mMaxSwingVelocity - mMinVelocity) / mMaxVelocity);
|
||||||
|
|
||||||
|
// When velocity falls below minimum, transition to register the miss
|
||||||
|
if (!canSwing())
|
||||||
|
return transition_swingingToImpact();
|
||||||
|
// Call hit with simulated=true to check for hit without actually causing an impact
|
||||||
|
if (mPtr.getClass().hit(mPtr, mStrength, mSwingType, true))
|
||||||
|
return transition_swingingToImpact();
|
||||||
|
}
|
||||||
|
|
||||||
|
void StateMachine::transition_swingingToImpact()
|
||||||
|
{
|
||||||
|
mPtr.getClass().hit(mPtr, mStrength, mSwingType, false);
|
||||||
|
transition(SwingState_Impact);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StateMachine::update_impactState()
|
||||||
|
{
|
||||||
|
if (mVelocity < mMinVelocity)
|
||||||
|
return transition_impactToCooldown();
|
||||||
|
}
|
||||||
|
|
||||||
|
void StateMachine::transition_impactToCooldown()
|
||||||
|
{
|
||||||
|
transition(SwingState_Cooldown);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
115
apps/openmw/mwvr/realisticcombat.hpp
Normal file
115
apps/openmw/mwvr/realisticcombat.hpp
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
#ifndef MWVR_REALISTICCOMBAT_H
|
||||||
|
#define MWVR_REALISTICCOMBAT_H
|
||||||
|
|
||||||
|
#include <components/esm/loadweap.hpp>
|
||||||
|
|
||||||
|
#include "../mwbase/world.hpp"
|
||||||
|
#include "../mwworld/ptr.hpp"
|
||||||
|
#include "../mwworld/class.hpp"
|
||||||
|
|
||||||
|
#include "vrenvironment.hpp"
|
||||||
|
#include "vrsession.hpp"
|
||||||
|
#include "vrtracking.hpp"
|
||||||
|
|
||||||
|
namespace MWVR {
|
||||||
|
namespace RealisticCombat {
|
||||||
|
|
||||||
|
/// Enum describing the current state of the MWVR::RealisticCombat::StateMachine
|
||||||
|
enum SwingState
|
||||||
|
{
|
||||||
|
SwingState_Ready,
|
||||||
|
SwingState_Launch,
|
||||||
|
SwingState_Swing,
|
||||||
|
SwingState_Impact,
|
||||||
|
SwingState_Cooldown,
|
||||||
|
};
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
/// \brief State machine for "realistic" combat in openmw vr
|
||||||
|
///
|
||||||
|
/// \sa SwingState
|
||||||
|
///
|
||||||
|
/// Initial state: Ready.
|
||||||
|
///
|
||||||
|
/// State Ready: Ready to initiate a new attack.
|
||||||
|
/// State Launch: Player has begun swinging his weapon.
|
||||||
|
/// State Swing: Currently swinging weapon.
|
||||||
|
/// State Impact: Contact made, weapon still swinging.
|
||||||
|
/// State Cooldown: Swing completed, wait a minimum period before next.
|
||||||
|
///
|
||||||
|
/// Transition rules:
|
||||||
|
/// Ready -> Launch: When the minimum velocity of swing is achieved.
|
||||||
|
/// Launch -> Ready: When the minimum velocity of swing is lost before minimum distance was swung.
|
||||||
|
/// Launch -> Swing: When minimum distance is swung.
|
||||||
|
/// - Play Swish sound
|
||||||
|
/// Swing -> Impact: When minimum velocity is lost, or when a hit is detected.
|
||||||
|
/// - Register hit based on max velocity observed in swing state
|
||||||
|
/// Impact -> Cooldown: When velocity returns below minimum.
|
||||||
|
/// Cooldown -> Ready: When the minimum period has passed since entering Cooldown state
|
||||||
|
///
|
||||||
|
///
|
||||||
|
struct StateMachine : public VRTrackingListener
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
StateMachine(MWWorld::Ptr ptr, VRPath trackingPath);
|
||||||
|
void update(float dt, bool enabled);
|
||||||
|
MWWorld::Ptr ptr() { return mPtr; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void onTrackingUpdated(VRTrackingSource& source, DisplayTime predictedDisplayTime) override;
|
||||||
|
|
||||||
|
bool canSwing();
|
||||||
|
|
||||||
|
void playSwish();
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
void transition(SwingState newState);
|
||||||
|
|
||||||
|
void update_cooldownState();
|
||||||
|
void transition_cooldownToReady();
|
||||||
|
|
||||||
|
void update_readyState();
|
||||||
|
void transition_readyToLaunch();
|
||||||
|
|
||||||
|
void update_launchState();
|
||||||
|
void transition_launchToReady();
|
||||||
|
void transition_launchToSwing();
|
||||||
|
|
||||||
|
void update_swingState();
|
||||||
|
void transition_swingingToImpact();
|
||||||
|
|
||||||
|
void update_impactState();
|
||||||
|
void transition_impactToCooldown();
|
||||||
|
|
||||||
|
private:
|
||||||
|
MWWorld::Ptr mPtr;
|
||||||
|
const float mMinVelocity;
|
||||||
|
const float mMaxVelocity;
|
||||||
|
|
||||||
|
float mVelocity = 0.f;
|
||||||
|
float mMaxSwingVelocity = 0.f;
|
||||||
|
|
||||||
|
SwingState mState = SwingState_Ready;
|
||||||
|
int mSwingType = -1;
|
||||||
|
float mStrength = 0.f;
|
||||||
|
|
||||||
|
float mThrustVelocity{ 0.f };
|
||||||
|
float mSlashChopVelocity{ 0.f };
|
||||||
|
float mSideVelocity{ 0.f };
|
||||||
|
|
||||||
|
float mMinimumPeriod{ .25f };
|
||||||
|
|
||||||
|
float mTimeSinceEnteredState = { 0.f };
|
||||||
|
float mMovementSinceEnteredState = { 0.f };
|
||||||
|
|
||||||
|
bool mEnabled = false;
|
||||||
|
|
||||||
|
osg::Vec3 mPreviousPosition{ 0.f,0.f,0.f };
|
||||||
|
VRTrackingPose mTrackingInput = VRTrackingPose();
|
||||||
|
VRPath mTrackingPath = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue