diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt new file mode 100644 index 0000000..3e80519 --- /dev/null +++ b/utils/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(driverlog) +add_subdirectory(vrmath) \ No newline at end of file diff --git a/utils/README.md b/utils/README.md new file mode 100644 index 0000000..c6598ad --- /dev/null +++ b/utils/README.md @@ -0,0 +1,7 @@ +`driverlog` - A wrapper around `IVRDriverLog` that provides a simple interface for logging messages to the console. +* `IVRDriverLog` + +`vrmath` - Operator overloads and extra functions for the included structs in the OpenVR interface +* `HmdQuaternion_t` +* `HmdVector3_t` +* `HmdMatrix34_t` diff --git a/utils/driverlog/CMakeLists.txt b/utils/driverlog/CMakeLists.txt new file mode 100644 index 0000000..748d50a --- /dev/null +++ b/utils/driverlog/CMakeLists.txt @@ -0,0 +1,5 @@ +add_library(util_driverlog STATIC driverlog.h driverlog.cpp) +target_include_directories(util_driverlog PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) + +target_link_libraries(util_driverlog PRIVATE ${OPENVR_LIBRARIES}) +target_include_directories(util_driverlog PUBLIC ${OPENVR_INCLUDE_DIR}) \ No newline at end of file diff --git a/utils/driverlog/driverlog.cpp b/utils/driverlog/driverlog.cpp new file mode 100644 index 0000000..9b6cfca --- /dev/null +++ b/utils/driverlog/driverlog.cpp @@ -0,0 +1,41 @@ +//============ Copyright (c) Valve Corporation, All rights reserved. ============ +#include "driverlog.h" + +#include +#include + +#if !defined( WIN32 ) +#define vsnprintf_s vsnprintf +#endif + +static void DriverLogVarArgs( const char *pMsgFormat, va_list args ) +{ + char buf[ 1024 ]; + vsnprintf_s( buf, sizeof( buf ), pMsgFormat, args ); + + vr::VRDriverLog()->Log( buf ); +} + + +void DriverLog( const char *pMsgFormat, ... ) +{ + va_list args; + va_start( args, pMsgFormat ); + + DriverLogVarArgs( pMsgFormat, args ); + + va_end( args ); +} + + +void DebugDriverLog( const char *pMsgFormat, ... ) +{ +#ifdef _DEBUG + va_list args; + va_start( args, pMsgFormat ); + + DriverLogVarArgs( pMsgFormat, args ); + + va_end( args ); +#endif +} diff --git a/utils/driverlog/driverlog.h b/utils/driverlog/driverlog.h new file mode 100644 index 0000000..ac4f82b --- /dev/null +++ b/utils/driverlog/driverlog.h @@ -0,0 +1,9 @@ +//============ Copyright (c) Valve Corporation, All rights reserved. ============ +#pragma once + +#include +#include + +extern void DriverLog( const char *pchFormat, ... ); + +extern void DebugDriverLog( const char *pchFormat, ... ); \ No newline at end of file diff --git a/utils/driverlog/util_driverlog.vcxproj b/utils/driverlog/util_driverlog.vcxproj new file mode 100644 index 0000000..6e4bfe2 --- /dev/null +++ b/utils/driverlog/util_driverlog.vcxproj @@ -0,0 +1,158 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {89689a91-fb38-4893-ba67-3d6f45eb2712} + utildriverlog + 10.0 + + + + StaticLibrary + true + v143 + Unicode + + + StaticLibrary + false + v143 + true + Unicode + + + StaticLibrary + true + v143 + Unicode + + + StaticLibrary + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)$(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + + + false + $(SolutionDir)$(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + + + true + + + false + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + ..\..\external\openvr\headers + + + Console + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + ..\..\external\openvr\headers + + + Console + true + true + true + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + ..\..\external\openvr\headers + + + Console + true + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + ..\..\external\openvr\headers + + + Console + true + true + true + + + + + + + + + + + + \ No newline at end of file diff --git a/utils/vrmath/CMakeLists.txt b/utils/vrmath/CMakeLists.txt new file mode 100644 index 0000000..45942fa --- /dev/null +++ b/utils/vrmath/CMakeLists.txt @@ -0,0 +1,5 @@ +add_library(util_vrmath INTERFACE vrmath.h) +target_include_directories(util_vrmath INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) + +target_link_libraries(util_vrmath INTERFACE ${OPENVR_LIBRARIES}) +target_include_directories(util_vrmath INTERFACE ${OPENVR_INCLUDE_DIR}) \ No newline at end of file diff --git a/utils/vrmath/util_vrmath.vcxproj b/utils/vrmath/util_vrmath.vcxproj new file mode 100644 index 0000000..3284896 --- /dev/null +++ b/utils/vrmath/util_vrmath.vcxproj @@ -0,0 +1,153 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {ac31972f-e424-4c19-86eb-7bcf1e9f8460} + utilvrmath + 10.0 + + + + StaticLibrary + true + v143 + Unicode + + + StaticLibrary + false + v143 + true + Unicode + + + StaticLibrary + true + v143 + Unicode + + + StaticLibrary + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)$(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + + + false + $(SolutionDir)$(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + + + true + + + false + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + ..\..\external\openvr\headers + + + Console + true + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + ..\..\external\openvr\headers + + + Console + true + true + true + + + + + + + + + \ No newline at end of file diff --git a/utils/vrmath/vrmath.h b/utils/vrmath/vrmath.h new file mode 100644 index 0000000..130596b --- /dev/null +++ b/utils/vrmath/vrmath.h @@ -0,0 +1,201 @@ +//============ Copyright (c) Valve Corporation, All rights reserved. ============ +#pragma once + +#include "openvr_driver.h" +#include + +#ifndef M_PI +#define M_PI 3.1415926535 +#endif + +#define DEG_TO_RAD( degrees ) ( ( degrees )*M_PI / 180.0 ) +#define RAD_TO_DEG( radians ) ( ( radians )*180.0 / M_PI ) + +static const vr::HmdQuaternion_t HmdQuaternion_Identity = { 1.f, 0.f, 0.f, 0.f }; + +// right hand coordinate system +static const vr::HmdVector3_t HmdVector3_Right = { 1.f, 0, 0 }; +static const vr::HmdVector3_t HmdVector3_Left = { -1.f, 0, 0 }; +static const vr::HmdVector3_t HmdVector3_Up = { 0, 1.f, 0 }; +static const vr::HmdVector3_t HmdVector3_Down = { 0, -1.f, 0 }; +static const vr::HmdVector3_t HmdVector3_Forward = { 0, 0, -1.f }; +static const vr::HmdVector3_t HmdVector3_Backward = { 0, 0, 1.f }; + +// 3x3 or 3x4 matrix +template < class T > +vr::HmdQuaternion_t HmdQuaternion_FromMatrix( const T &matrix ) +{ + vr::HmdQuaternion_t q{}; + + q.w = sqrt( fmax( 0, 1 + matrix.m[ 0 ][ 0 ] + matrix.m[ 1 ][ 1 ] + matrix.m[ 2 ][ 2 ] ) ) / 2; + q.x = sqrt( fmax( 0, 1 + matrix.m[ 0 ][ 0 ] - matrix.m[ 1 ][ 1 ] - matrix.m[ 2 ][ 2 ] ) ) / 2; + q.y = sqrt( fmax( 0, 1 - matrix.m[ 0 ][ 0 ] + matrix.m[ 1 ][ 1 ] - matrix.m[ 2 ][ 2 ] ) ) / 2; + q.z = sqrt( fmax( 0, 1 - matrix.m[ 0 ][ 0 ] - matrix.m[ 1 ][ 1 ] + matrix.m[ 2 ][ 2 ] ) ) / 2; + + q.x = copysign( q.x, matrix.m[ 2 ][ 1 ] - matrix.m[ 1 ][ 2 ] ); + q.y = copysign( q.y, matrix.m[ 0 ][ 2 ] - matrix.m[ 2 ][ 0 ] ); + q.z = copysign( q.z, matrix.m[ 1 ][ 0 ] - matrix.m[ 0 ][ 1 ] ); + + return q; +} + +static vr::HmdQuaternion_t HmdQuaternion_FromSwingTwist( const vr::HmdVector2_t &swing, const float twist ) +{ + vr::HmdQuaternion_t result{}; + + const float swing_squared = swing.v[ 0 ] * swing.v[ 0 ] + swing.v[ 1 ] * swing.v[ 1 ]; + + if ( swing_squared > 0.f ) + { + const float theta_swing = std::sqrt( swing_squared ); + + const float cos_half_theta_swing = std::cos( theta_swing * 0.5f ); + const float cos_half_theta_twist = std::cos( twist * 0.5f ); + + const float sin_half_theta_twist = std::sin( twist * 0.5f ); + + const float sin_half_theta_swing_over_theta = std::sin( theta_swing * 0.5f ) / theta_swing; + + result.w = cos_half_theta_swing * cos_half_theta_twist; + + result.x = cos_half_theta_swing * sin_half_theta_twist; + + result.y = ( swing.v[ 1 ] * cos_half_theta_twist * sin_half_theta_swing_over_theta ) - ( swing.v[ 0 ] * sin_half_theta_twist * sin_half_theta_swing_over_theta ); + + result.z = ( swing.v[ 0 ] * cos_half_theta_twist * sin_half_theta_swing_over_theta ) + ( swing.v[ 1 ] * sin_half_theta_twist * sin_half_theta_swing_over_theta ); + } + else + { + float half_twist = twist * 0.5f; + float cos_half_twist = cos( half_twist ); + float sin_half_twist = sin( half_twist ); + float sin_half_theta_over_theta = 0.5f; + + result.w = cos_half_twist; + result.x = sin_half_twist; + result.y = ( swing.v[ 1 ] * cos_half_twist * sin_half_theta_over_theta ) - ( swing.v[ 0 ] * sin_half_twist * sin_half_theta_over_theta ); + result.z = ( swing.v[ 0 ] * cos_half_twist * sin_half_theta_over_theta ) + ( swing.v[ 1 ] * sin_half_twist * sin_half_theta_over_theta ); + } + + return result; +} + +static vr::HmdQuaternion_t HmdQuaternion_Normalize( const vr::HmdQuaternion_t &q ) +{ + vr::HmdQuaternion_t result{}; + double n = sqrt( q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w ); + + result.w = q.w / n; + result.x = q.x / n; + result.y = q.y / n; + result.z = q.z / n; + + return result; +} + +static vr::HmdQuaternion_t HmdQuaternion_FromEulerAngles(double roll, double pitch, double yaw) { + double cr = cos(roll * 0.5); + double sr = sin(roll * 0.5); + double cp = cos(pitch * 0.5); + double sp = sin(pitch * 0.5); + double cy = cos(yaw * 0.5); + double sy = sin(yaw * 0.5); + + vr::HmdQuaternion_t q; + q.w = cr * cp * cy + sr * sp * sy; + q.x = cr * sp * cy + sr * cp * sy; + q.y = cr * cp * sy - sr * sp * cy; + q.z = sr * cp * cy - cr * sp * sy; + + return q; +} + +template < class T, class Q > +void HmdQuaternion_ConvertQuaternion( const T &in_quaternion, Q &out_quaternion ) +{ + out_quaternion.w = in_quaternion.w; + out_quaternion.x = in_quaternion.x; + out_quaternion.y = in_quaternion.y; + out_quaternion.z = in_quaternion.z; +} + +static vr::HmdQuaternion_t operator-( const vr::HmdQuaternion_t &q ) +{ + return { q.w, -q.x, -q.y, -q.z }; +} + +static vr::HmdQuaternion_t operator*( const vr::HmdQuaternion_t &lhs, const vr::HmdQuaternion_t &rhs ) +{ + return { + lhs.w * rhs.w - lhs.x * rhs.x - lhs.y * rhs.y - lhs.z * rhs.z, + lhs.w * rhs.x + lhs.x * rhs.w + lhs.y * rhs.z - lhs.z * rhs.y, + lhs.w * rhs.y - lhs.x * rhs.z + lhs.y * rhs.w + lhs.z * rhs.x, + lhs.w * rhs.z + lhs.x * rhs.y - lhs.y * rhs.x + lhs.z * rhs.w, + }; +} + +static vr::HmdVector3_t HmdVector3_From34Matrix( const vr::HmdMatrix34_t &matrix ) +{ + return { matrix.m[ 0 ][ 3 ], matrix.m[ 1 ][ 3 ], matrix.m[ 2 ][ 3 ] }; +} + + +static vr::HmdVector3_t operator+( const vr::HmdMatrix34_t &matrix, const vr::HmdVector3_t &vec ) +{ + vr::HmdVector3_t vector{}; + + vector.v[ 0 ] = matrix.m[ 0 ][ 3 ] + vec.v[ 0 ]; + vector.v[ 1 ] = matrix.m[ 1 ][ 3 ] + vec.v[ 1 ]; + vector.v[ 2 ] = matrix.m[ 2 ][ 3 ] + vec.v[ 2 ]; + + return vector; +} + +static vr::HmdVector3_t operator*( const vr::HmdMatrix33_t &matrix, const vr::HmdVector3_t &vec ) +{ + vr::HmdVector3_t result{}; + + result.v[ 0 ] = matrix.m[ 0 ][ 0 ] * vec.v[ 0 ] + matrix.m[ 0 ][ 1 ] * vec.v[ 1 ] + matrix.m[ 0 ][ 2 ] * vec.v[ 2 ]; + result.v[ 1 ] = matrix.m[ 1 ][ 0 ] * vec.v[ 0 ] + matrix.m[ 1 ][ 1 ] * vec.v[ 1 ] + matrix.m[ 1 ][ 2 ] * vec.v[ 2 ]; + result.v[ 2 ] = matrix.m[ 2 ][ 0 ] * vec.v[ 0 ] + matrix.m[ 2 ][ 1 ] * vec.v[ 1 ] + matrix.m[ 2 ][ 2 ] * vec.v[ 2 ]; + + return result; +} + +static vr::HmdVector3_t operator-( const vr::HmdVector3_t &vec, const vr::HmdMatrix34_t &matrix ) +{ + return { vec.v[ 0 ] - matrix.m[ 0 ][ 3 ], vec.v[ 1 ] - matrix.m[ 1 ][ 3 ], vec.v[ 2 ] - matrix.m[ 2 ][ 3 ] }; +} + +static vr::HmdVector3d_t operator+( const vr::HmdVector3d_t &vec1, const vr::HmdVector3d_t &vec2 ) +{ + return { vec1.v[ 0 ] + vec2.v[ 0 ], vec1.v[ 1 ] + vec2.v[ 1 ], vec1.v[ 2 ] + vec2.v[ 2 ] }; +} + + +static vr::HmdVector3_t operator+( const vr::HmdVector3_t &vec1, const vr::HmdVector3_t &vec2 ) +{ + return { vec1.v[ 0 ] + vec2.v[ 0 ], vec1.v[ 1 ] + vec2.v[ 1 ], vec1.v[ 2 ] + vec2.v[ 2 ] }; +} + +static vr::HmdVector3d_t operator-( const vr::HmdVector3d_t &vec1, const vr::HmdVector3d_t &vec2 ) +{ + return { vec1.v[ 0 ] - vec2.v[ 0 ], vec1.v[ 1 ] - vec2.v[ 1 ], vec1.v[ 2 ] - vec2.v[ 2 ] }; +} + +static vr::HmdVector3_t operator*( const vr::HmdVector3_t &vec, const vr::HmdQuaternion_t &q ) +{ + const vr::HmdQuaternion_t qvec = { 0.0, vec.v[ 0 ], vec.v[ 1 ], vec.v[ 2 ] }; + + const vr::HmdQuaternion_t qResult = (q * qvec) * (-q); + + return { static_cast< float >( qResult.x ), static_cast< float >( qResult.y ), static_cast< float >( qResult.z ) }; +} + +template < class T, class V > +void HmdVector3_CovertVector( const T &in_vector, V &out_vector ) +{ + out_vector.v[ 0 ] = in_vector.v[ 0 ]; + out_vector.v[ 1 ] = in_vector.v[ 1 ]; + out_vector.v[ 2 ] = in_vector.v[ 2 ]; +} \ No newline at end of file diff --git a/vrto3d.sln b/vrto3d.sln new file mode 100644 index 0000000..39ed209 --- /dev/null +++ b/vrto3d.sln @@ -0,0 +1,51 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.6.33829.357 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vrto3d", "vrto3d\vrto3d.vcxproj", "{1A008D17-0760-4179-8D42-995C5FF1C8D6}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "util_driverlog", "utils\driverlog\util_driverlog.vcxproj", "{89689A91-FB38-4893-BA67-3D6F45EB2712}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "util_vrmath", "utils\vrmath\util_vrmath.vcxproj", "{AC31972F-E424-4C19-86EB-7BCF1E9F8460}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1A008D17-0760-4179-8D42-995C5FF1C8D6}.Debug|x64.ActiveCfg = Debug|x64 + {1A008D17-0760-4179-8D42-995C5FF1C8D6}.Debug|x64.Build.0 = Debug|x64 + {1A008D17-0760-4179-8D42-995C5FF1C8D6}.Debug|x86.ActiveCfg = Debug|Win32 + {1A008D17-0760-4179-8D42-995C5FF1C8D6}.Debug|x86.Build.0 = Debug|Win32 + {1A008D17-0760-4179-8D42-995C5FF1C8D6}.Release|x64.ActiveCfg = Release|x64 + {1A008D17-0760-4179-8D42-995C5FF1C8D6}.Release|x64.Build.0 = Release|x64 + {1A008D17-0760-4179-8D42-995C5FF1C8D6}.Release|x86.ActiveCfg = Release|Win32 + {1A008D17-0760-4179-8D42-995C5FF1C8D6}.Release|x86.Build.0 = Release|Win32 + {89689A91-FB38-4893-BA67-3D6F45EB2712}.Debug|x64.ActiveCfg = Debug|x64 + {89689A91-FB38-4893-BA67-3D6F45EB2712}.Debug|x64.Build.0 = Debug|x64 + {89689A91-FB38-4893-BA67-3D6F45EB2712}.Debug|x86.ActiveCfg = Debug|Win32 + {89689A91-FB38-4893-BA67-3D6F45EB2712}.Debug|x86.Build.0 = Debug|Win32 + {89689A91-FB38-4893-BA67-3D6F45EB2712}.Release|x64.ActiveCfg = Release|x64 + {89689A91-FB38-4893-BA67-3D6F45EB2712}.Release|x64.Build.0 = Release|x64 + {89689A91-FB38-4893-BA67-3D6F45EB2712}.Release|x86.ActiveCfg = Release|Win32 + {89689A91-FB38-4893-BA67-3D6F45EB2712}.Release|x86.Build.0 = Release|Win32 + {AC31972F-E424-4C19-86EB-7BCF1E9F8460}.Debug|x64.ActiveCfg = Debug|x64 + {AC31972F-E424-4C19-86EB-7BCF1E9F8460}.Debug|x64.Build.0 = Debug|x64 + {AC31972F-E424-4C19-86EB-7BCF1E9F8460}.Debug|x86.ActiveCfg = Debug|Win32 + {AC31972F-E424-4C19-86EB-7BCF1E9F8460}.Debug|x86.Build.0 = Debug|Win32 + {AC31972F-E424-4C19-86EB-7BCF1E9F8460}.Release|x64.ActiveCfg = Release|x64 + {AC31972F-E424-4C19-86EB-7BCF1E9F8460}.Release|x64.Build.0 = Release|x64 + {AC31972F-E424-4C19-86EB-7BCF1E9F8460}.Release|x86.ActiveCfg = Release|Win32 + {AC31972F-E424-4C19-86EB-7BCF1E9F8460}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {86FCAF7E-50B5-4B9C-A2E5-C63B6B5B94F0} + EndGlobalSection +EndGlobal diff --git a/vrto3d/CMakeLists.txt b/vrto3d/CMakeLists.txt new file mode 100644 index 0000000..3e9aa6f --- /dev/null +++ b/vrto3d/CMakeLists.txt @@ -0,0 +1,29 @@ +set(TARGET_NAME simplehmd) + +# This is the name of the driver according to SteamVR +set(DRIVER_NAME "driver_${TARGET_NAME}") + +project(${TARGET_NAME}) + +add_library(${DRIVER_NAME} SHARED + src/hmd_driver_factory.cpp + src/device_provider.h + src/device_provider.cpp + src/hmd_device_driver.h + src/hmd_device_driver.cpp + ) + +# This is so we can build directly to "////." +set_target_properties(${DRIVER_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY $<1:${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TARGET_NAME}/bin/${ARCH_TARGET}>) + +target_link_libraries(${DRIVER_NAME} PRIVATE ${OPENVR_LIBRARIES} util_driverlog util_vrmath) +target_include_directories(${DRIVER_NAME} PRIVATE ${OPENVR_INCLUDE_DIR}) + +# Copy driver assets to output folder +add_custom_command( + TARGET ${DRIVER_NAME} + PRE_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + ${CMAKE_CURRENT_SOURCE_DIR}/${TARGET_NAME} + ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TARGET_NAME} +) \ No newline at end of file diff --git a/vrto3d/resources/settings/default.vrsettings b/vrto3d/resources/settings/default.vrsettings deleted file mode 100644 index 3d3b75b..0000000 --- a/vrto3d/resources/settings/default.vrsettings +++ /dev/null @@ -1,17 +0,0 @@ -{ - "driver_vrto3d": { - "enable": true, - "serial_number": "SbS3D-1234", - "model_number": "SbS3D-1" - }, - "vrto3d_display": { - "window_x": 0, - "window_y": 0, - "window_width": 2160, - "window_height": 1200, - "render_width": 1512, - "render_height": 1680, - "vsync_to_photons": 0.011, - "display_frequency": 0 - } -} diff --git a/vrto3d/src/device_provider.cpp b/vrto3d/src/device_provider.cpp new file mode 100644 index 0000000..be03c8f --- /dev/null +++ b/vrto3d/src/device_provider.cpp @@ -0,0 +1,89 @@ +//============ Copyright (c) Valve Corporation, All rights reserved. ============ +#include "device_provider.h" + +#include "driverlog.h" + +//----------------------------------------------------------------------------- +// Purpose: This is called by vrserver after it receives a pointer back from HmdDriverFactory. +// You should do your resources allocations here (**not** in the constructor). +//----------------------------------------------------------------------------- +vr::EVRInitError MyDeviceProvider::Init( vr::IVRDriverContext *pDriverContext ) +{ + // We need to initialise our driver context to make calls to the server. + // OpenVR provides a macro to do this for us. + VR_INIT_SERVER_DRIVER_CONTEXT( pDriverContext ); + + // First, initialize our hmd, which we'll later pass OpenVR a pointer to. + my_hmd_device_ = std::make_unique< MockControllerDeviceDriver >(); + + // TrackedDeviceAdded returning true means we have had our device added to SteamVR. + if ( !vr::VRServerDriverHost()->TrackedDeviceAdded( my_hmd_device_->MyGetSerialNumber().c_str(), vr::TrackedDeviceClass_HMD, my_hmd_device_.get() ) ) + { + DriverLog( "Failed to create hmd device!" ); + return vr::VRInitError_Driver_Unknown; + } + + return vr::VRInitError_None; +} + +//----------------------------------------------------------------------------- +// Purpose: Tells the runtime which version of the API we are targeting. +// Helper variables in the header you're using contain this information, which can be returned here. +//----------------------------------------------------------------------------- +const char *const *MyDeviceProvider::GetInterfaceVersions() +{ + return vr::k_InterfaceVersions; +} + +//----------------------------------------------------------------------------- +// Purpose: This function is deprecated and never called. But, it must still be defined, or we can't compile. +//----------------------------------------------------------------------------- +bool MyDeviceProvider::ShouldBlockStandbyMode() +{ + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: This is called in the main loop of vrserver. +// Drivers *can* do work here, but should ensure this work is relatively inexpensive. +// A good thing to do here is poll for events from the runtime or applications +//----------------------------------------------------------------------------- +void MyDeviceProvider::RunFrame() +{ + //Now, process events that were submitted for this frame. + vr::VREvent_t vrevent{}; + while ( vr::VRServerDriverHost()->PollNextEvent( &vrevent, sizeof( vr::VREvent_t ) ) ) + { + if ( my_hmd_device_ != nullptr ) + { + my_hmd_device_->MyProcessEvent( vrevent ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: This function is called when the system enters a period of inactivity. +// The devices might want to turn off their displays or go into a low power mode to preserve them. +//----------------------------------------------------------------------------- +void MyDeviceProvider::EnterStandby() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: This function is called after the system has been in a period of inactivity, and is waking up again. +// Turn back on the displays or devices here. +//----------------------------------------------------------------------------- +void MyDeviceProvider::LeaveStandby() +{ +} + +//----------------------------------------------------------------------------- +// Purpose: This function is called just before the driver is unloaded from vrserver. +// Drivers should free whatever resources they have acquired over the session here. +// Any calls to the server is guaranteed to be valid before this, but not after it has been called. +//----------------------------------------------------------------------------- +void MyDeviceProvider::Cleanup() +{ + // Our controller devices will have already deactivated. Let's now destroy them. + my_hmd_device_ = nullptr; +} \ No newline at end of file diff --git a/vrto3d/src/device_provider.h b/vrto3d/src/device_provider.h new file mode 100644 index 0000000..bbb90d1 --- /dev/null +++ b/vrto3d/src/device_provider.h @@ -0,0 +1,26 @@ +//============ Copyright (c) Valve Corporation, All rights reserved. ============ +#pragma once + +#include + +#include "hmd_device_driver.h" +#include "openvr_driver.h" + +// make sure your class is publicly inheriting vr::IServerTrackedDeviceProvider! +class MyDeviceProvider : public vr::IServerTrackedDeviceProvider +{ +public: + vr::EVRInitError Init( vr::IVRDriverContext *pDriverContext ) override; + const char *const *GetInterfaceVersions() override; + + void RunFrame() override; + + bool ShouldBlockStandbyMode() override; + void EnterStandby() override; + void LeaveStandby() override; + + void Cleanup() override; + +private: + std::unique_ptr my_hmd_device_; +}; \ No newline at end of file diff --git a/vrto3d/src/hmd_device_driver - Copy.cpp b/vrto3d/src/hmd_device_driver - Copy.cpp new file mode 100644 index 0000000..d5fd7f6 --- /dev/null +++ b/vrto3d/src/hmd_device_driver - Copy.cpp @@ -0,0 +1,335 @@ +//============ Copyright (c) Valve Corporation, All rights reserved. ============ +#include "hmd_device_driver.h" + +#include "driverlog.h" +#include "vrmath.h" +#include + +// Load settings from default.vrsettings +static const char *stereo_main_settings_section = "driver_vrto3d"; +static const char *stereo_display_settings_section = "vrto3d_display"; + +MockControllerDeviceDriver::MockControllerDeviceDriver() +{ + // Keep track of whether Activate() has been called + is_active_ = false; + + char model_number[ 1024 ]; + vr::VRSettings()->GetString( stereo_main_settings_section, "model_number", model_number, sizeof( model_number ) ); + stereo_model_number_ = model_number; + char serial_number[ 1024 ]; + vr::VRSettings()->GetString( stereo_main_settings_section, "serial_number", serial_number, sizeof( serial_number ) ); + stereo_serial_number_ = serial_number; + + DriverLog( "VRto3D Model Number: %s", stereo_model_number_.c_str() ); + DriverLog( "VRto3D Serial Number: %s", stereo_serial_number_.c_str() ); + + // Display settings + StereoDisplayDriverConfiguration display_configuration{}; + display_configuration.window_x = vr::VRSettings()->GetInt32( stereo_display_settings_section, "window_x" ); + display_configuration.window_y = vr::VRSettings()->GetInt32( stereo_display_settings_section, "window_y" ); + + display_configuration.window_width = vr::VRSettings()->GetInt32( stereo_display_settings_section, "window_width" ); + display_configuration.window_height = vr::VRSettings()->GetInt32( stereo_display_settings_section, "window_height" ); + + display_configuration.aspect_ratio = vr::VRSettings()->GetFloat(stereo_display_settings_section, "aspect_ratio"); + display_configuration.fov = vr::VRSettings()->GetFloat(stereo_display_settings_section, "fov"); + display_configuration.ipd = vr::VRSettings()->GetFloat(stereo_display_settings_section, "ipd"); + + display_configuration.tab_enable = vr::VRSettings()->GetBool(stereo_display_settings_section, "tab_enable"); + display_configuration.half_enable = vr::VRSettings()->GetBool(stereo_display_settings_section, "half_enable"); + display_configuration.hdr_enable = vr::VRSettings()->GetBool(stereo_display_settings_section, "hdr_enable"); + + display_configuration.display_latency = vr::VRSettings()->GetFloat(stereo_display_settings_section, "display_latency"); + display_configuration.display_frequency = vr::VRSettings()->GetFloat(stereo_display_settings_section, "display_frequency"); + + int32_t half_width = display_configuration.half_enable ? 1 : 2; + if (display_configuration.tab_enable) + { + display_configuration.render_width = display_configuration.window_width; + display_configuration.render_height = display_configuration.window_height / half_width; + } + else + { + display_configuration.render_width = display_configuration.window_width / half_width; + display_configuration.render_height = display_configuration.window_height; + } + + // Instantiate our display component + stereo_display_component_ = std::make_unique< StereoDisplayComponent >( display_configuration ); +} + +//----------------------------------------------------------------------------- +// Purpose: This is called by vrserver after our +// IServerTrackedDeviceProvider calls IVRServerDriverHost::TrackedDeviceAdded. +//----------------------------------------------------------------------------- +vr::EVRInitError MockControllerDeviceDriver::Activate( uint32_t unObjectId ) +{ + device_index_ = unObjectId; + is_active_ = true; + + // A list of properties available is contained in vr::ETrackedDeviceProperty. + vr::PropertyContainerHandle_t container = vr::VRProperties()->TrackedDeviceToPropertyContainer( device_index_ ); + vr::VRProperties()->SetStringProperty( container, vr::Prop_ModelNumber_String, stereo_model_number_.c_str() ); + vr::VRProperties()->SetStringProperty(container, vr::Prop_ManufacturerName_String, "VRto3D"); + vr::VRProperties()->SetStringProperty(container, vr::Prop_TrackingFirmwareVersion_String, "1.0"); + vr::VRProperties()->SetStringProperty(container, vr::Prop_HardwareRevision_String, "1.0"); + + // Display settings + vr::VRProperties()->SetFloatProperty( container, vr::Prop_UserIpdMeters_Float, stereo_display_component_.get()->GetConfig().ipd); + vr::VRProperties()->SetFloatProperty(container, vr::Prop_UserHeadToEyeDepthMeters_Float, 0.f); + vr::VRProperties()->SetFloatProperty( container, vr::Prop_DisplayFrequency_Float, stereo_display_component_.get()->GetConfig().display_frequency); + vr::VRProperties()->SetFloatProperty( container, vr::Prop_SecondsFromVsyncToPhotons_Float, stereo_display_component_.get()->GetConfig().display_latency); + + // Miscellaneous settings + vr::VRProperties()->SetBoolProperty( container, vr::Prop_IsOnDesktop_Bool, false ); + vr::VRProperties()->SetBoolProperty(container, vr::Prop_DisplayDebugMode_Bool, false); + vr::VRProperties()->SetBoolProperty(container, vr::Prop_WillDriftInYaw_Bool, false); + vr::VRProperties()->SetBoolProperty(container, vr::Prop_DeviceIsWireless_Bool, false); + vr::VRProperties()->SetBoolProperty(container, vr::Prop_DeviceIsCharging_Bool, false); + vr::VRProperties()->SetBoolProperty(container, vr::Prop_ContainsProximitySensor_Bool, false); + vr::VRProperties()->SetBoolProperty(container, vr::Prop_DeviceCanPowerOff_Bool, false); + + // Now let's set up our inputs + // This tells the UI what to show the user for bindings for this controller, + // As well as what default bindings should be for legacy apps. + // Note, we can use the wildcard {} to match the root folder location + // of our driver. + vr::VRProperties()->SetStringProperty( container, vr::Prop_InputProfilePath_String, "{vrto3d}/input/vrto3d_profile.json" ); + + // Let's set up handles for all of our components. + // Even though these are also defined in our input profile, + // We need to get handles to them to update the inputs. + vr::VRDriverInput()->CreateBooleanComponent( container, "/input/system/touch", &my_input_handles_[ MyComponent_system_touch ] ); + vr::VRDriverInput()->CreateBooleanComponent( container, "/input/system/click", &my_input_handles_[ MyComponent_system_click ] ); + + pose_update_thread_ = std::thread( &MockControllerDeviceDriver::PoseUpdateThread, this ); + + return vr::VRInitError_None; +} + +//----------------------------------------------------------------------------- +// Purpose: If you're an HMD, this is where you would return an implementation +// of vr::IVRDisplayComponent, vr::IVRVirtualDisplay or vr::IVRDirectModeComponent. +//----------------------------------------------------------------------------- +void *MockControllerDeviceDriver::GetComponent( const char *pchComponentNameAndVersion ) +{ + if ( strcmp( pchComponentNameAndVersion, vr::IVRDisplayComponent_Version ) == 0 ) + { + return stereo_display_component_.get(); + } + + return nullptr; +} + +//----------------------------------------------------------------------------- +// Purpose: This is called by vrserver when a debug request has been made from an application to the driver. +// What is in the response and request is up to the application and driver to figure out themselves. +//----------------------------------------------------------------------------- +void MockControllerDeviceDriver::DebugRequest( const char *pchRequest, char *pchResponseBuffer, uint32_t unResponseBufferSize ) +{ + if ( unResponseBufferSize >= 1 ) + pchResponseBuffer[ 0 ] = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: This is never called by vrserver in recent OpenVR versions, +// but is useful for giving data to vr::VRServerDriverHost::TrackedDevicePoseUpdated. +//----------------------------------------------------------------------------- +vr::DriverPose_t MockControllerDeviceDriver::GetPose() +{ + vr::DriverPose_t pose = { 0 }; + + pose.qWorldFromDriverRotation.w = 1.f; + pose.qDriverFromHeadRotation.w = 1.f; + + pose.qRotation.w = 1.f; + + pose.vecPosition[ 0 ] = 0.0f; + pose.vecPosition[ 1 ] = 1.0f; + pose.vecPosition[ 2 ] = 0.0f; + + pose.poseIsValid = true; + pose.deviceIsConnected = true; + pose.result = vr::TrackingResult_Running_OK; + + // For HMDs we want to apply rotation/motion prediction + pose.shouldApplyHeadModel = false; + + return pose; +} + +void MockControllerDeviceDriver::PoseUpdateThread() +{ + while ( is_active_ ) + { + // Inform the vrserver that our tracked device's pose has updated, giving it the pose returned by our GetPose(). + vr::VRServerDriverHost()->TrackedDevicePoseUpdated( device_index_, GetPose(), sizeof( vr::DriverPose_t ) ); + + // Update our pose every five milliseconds. + // In reality, you should update the pose whenever you have new data from your device. + std::this_thread::sleep_for( std::chrono::milliseconds( 5 ) ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: This is called by vrserver when the device should enter standby mode. +// The device should be put into whatever low power mode it has. +// We don't really have anything to do here, so let's just log something. +//----------------------------------------------------------------------------- +void MockControllerDeviceDriver::EnterStandby() +{ + DriverLog( "HMD has been put into standby." ); +} + +//----------------------------------------------------------------------------- +// Purpose: This is called by vrserver when the device should deactivate. +// This is typically at the end of a session +// The device should free any resources it has allocated here. +//----------------------------------------------------------------------------- +void MockControllerDeviceDriver::Deactivate() +{ + // Let's join our pose thread that's running + // by first checking then setting is_active_ to false to break out + // of the while loop, if it's running, then call .join() on the thread + if ( is_active_.exchange( false ) ) + { + pose_update_thread_.join(); + } + + // unassign our controller index (we don't want to be calling vrserver anymore after Deactivate() has been called + device_index_ = vr::k_unTrackedDeviceIndexInvalid; +} + + +//----------------------------------------------------------------------------- +// Purpose: This is called by our IServerTrackedDeviceProvider when it pops an event off the event queue. +// It's not part of the ITrackedDeviceServerDriver interface, we created it ourselves. +//----------------------------------------------------------------------------- +void MockControllerDeviceDriver::MyProcessEvent( const vr::VREvent_t &vrevent ) +{ +} + + +//----------------------------------------------------------------------------- +// Purpose: Our IServerTrackedDeviceProvider needs our serial number to add us to vrserver. +// It's not part of the ITrackedDeviceServerDriver interface, we created it ourselves. +//----------------------------------------------------------------------------- +const std::string &MockControllerDeviceDriver::MyGetSerialNumber() +{ + return stereo_serial_number_; +} + +//----------------------------------------------------------------------------- +// DISPLAY DRIVER METHOD DEFINITIONS +//----------------------------------------------------------------------------- + +StereoDisplayComponent::StereoDisplayComponent( const StereoDisplayDriverConfiguration &config ) + : config_( config ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: To inform vrcompositor if this display is considered an on-desktop display. +//----------------------------------------------------------------------------- +bool StereoDisplayComponent::IsDisplayOnDesktop() +{ + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: To as vrcompositor to search for this display. +//----------------------------------------------------------------------------- +bool StereoDisplayComponent::IsDisplayRealDisplay() +{ + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: To inform the rest of the vr system what the recommended target size should be +//----------------------------------------------------------------------------- +void StereoDisplayComponent::GetRecommendedRenderTargetSize( uint32_t *pnWidth, uint32_t *pnHeight ) +{ + *pnWidth = config_.render_width; + *pnHeight = config_.render_height; +} + +//----------------------------------------------------------------------------- +// Purpose: To inform vrcompositor how the screens should be organized. +//----------------------------------------------------------------------------- +void StereoDisplayComponent::GetEyeOutputViewport( vr::EVREye eEye, uint32_t *pnX, uint32_t *pnY, uint32_t *pnWidth, uint32_t *pnHeight ) +{ + *pnY = 0; + //*pnX = 0; + + // Each eye will have half the window + *pnWidth = config_.window_width / 2; + //*pnWidth = config_.window_width; + + // Each eye will have the full height + *pnHeight = config_.window_height; + //*pnHeight = config_.window_height / 2; + + if ( eEye == vr::Eye_Left ) + { + // Left eye viewport on the left half of the window + *pnX = 0; + //*pnY = 0; + } + else + { + // Right eye viewport on the right half of the window + *pnX = config_.window_width / 2; + //*pnY = config_.window_height / 2; + } +} + +//----------------------------------------------------------------------------- +// Purpose: To inform the compositor what the projection parameters are for this HMD. +//----------------------------------------------------------------------------- +void StereoDisplayComponent::GetProjectionRaw( vr::EVREye eEye, float *pfLeft, float *pfRight, float *pfTop, float *pfBottom ) +{ + *pfLeft = -1.0; + *pfRight = 1.0; + *pfTop = -0.5; + *pfBottom = 0.5; +} + +//----------------------------------------------------------------------------- +// Purpose: To compute the distortion properties for a given uv in an image. +//----------------------------------------------------------------------------- +vr::DistortionCoordinates_t StereoDisplayComponent::ComputeDistortion( vr::EVREye eEye, float fU, float fV ) +{ + vr::DistortionCoordinates_t coordinates{}; + coordinates.rfBlue[ 0 ] = fU; + coordinates.rfBlue[ 1 ] = fV; + coordinates.rfGreen[ 0 ] = fU; + coordinates.rfGreen[ 1 ] = fV; + coordinates.rfRed[ 0 ] = fU; + coordinates.rfRed[ 1 ] = fV; + return coordinates; +} + +bool StereoDisplayComponent::ComputeInverseDistortion(vr::HmdVector2_t* pResult, vr::EVREye eEye, uint32_t unChannel, float fU, float fV) +{ + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: To inform vrcompositor what the window bounds for this virtual HMD are. +//----------------------------------------------------------------------------- +void StereoDisplayComponent::GetWindowBounds( int32_t *pnX, int32_t *pnY, uint32_t *pnWidth, uint32_t *pnHeight ) +{ + *pnX = config_.window_x; + *pnY = config_.window_y; + *pnWidth = config_.window_width; + *pnHeight = config_.window_height; +} + +//----------------------------------------------------------------------------- +// Purpose: To provide access to settings +//----------------------------------------------------------------------------- +StereoDisplayDriverConfiguration StereoDisplayComponent::GetConfig() +{ + return config_; +} diff --git a/vrto3d/src/hmd_device_driver.cpp b/vrto3d/src/hmd_device_driver.cpp new file mode 100644 index 0000000..e460987 --- /dev/null +++ b/vrto3d/src/hmd_device_driver.cpp @@ -0,0 +1,371 @@ +//============ Copyright (c) Valve Corporation, All rights reserved. ============ +#include "hmd_device_driver.h" + +#include "driverlog.h" +#include "vrmath.h" +#include + +// Load settings from default.vrsettings +static const char *stereo_main_settings_section = "driver_vrto3d"; +static const char *stereo_display_settings_section = "vrto3d_display"; + +MockControllerDeviceDriver::MockControllerDeviceDriver() +{ + // Keep track of whether Activate() has been called + is_active_ = false; + + char model_number[ 1024 ]; + vr::VRSettings()->GetString( stereo_main_settings_section, "model_number", model_number, sizeof( model_number ) ); + stereo_model_number_ = model_number; + char serial_number[ 1024 ]; + vr::VRSettings()->GetString( stereo_main_settings_section, "serial_number", serial_number, sizeof( serial_number ) ); + stereo_serial_number_ = serial_number; + + DriverLog( "VRto3D Model Number: %s", stereo_model_number_.c_str() ); + DriverLog( "VRto3D Serial Number: %s", stereo_serial_number_.c_str() ); + + // Display settings + StereoDisplayDriverConfiguration display_configuration{}; + display_configuration.window_x = vr::VRSettings()->GetInt32( stereo_display_settings_section, "window_x" ); + display_configuration.window_y = vr::VRSettings()->GetInt32( stereo_display_settings_section, "window_y" ); + + display_configuration.window_width = vr::VRSettings()->GetInt32( stereo_display_settings_section, "window_width" ); + display_configuration.window_height = vr::VRSettings()->GetInt32( stereo_display_settings_section, "window_height" ); + + display_configuration.aspect_ratio = vr::VRSettings()->GetFloat(stereo_display_settings_section, "aspect_ratio"); + display_configuration.fov = vr::VRSettings()->GetFloat(stereo_display_settings_section, "fov"); + display_configuration.ipd = vr::VRSettings()->GetFloat(stereo_display_settings_section, "ipd"); + + display_configuration.tab_enable = vr::VRSettings()->GetBool(stereo_display_settings_section, "tab_enable"); + display_configuration.half_enable = vr::VRSettings()->GetBool(stereo_display_settings_section, "half_enable"); + display_configuration.super_sample = vr::VRSettings()->GetBool(stereo_display_settings_section, "super_sample"); + display_configuration.hdr_enable = vr::VRSettings()->GetBool(stereo_display_settings_section, "hdr_enable"); + + display_configuration.display_latency = vr::VRSettings()->GetFloat(stereo_display_settings_section, "display_latency"); + display_configuration.display_frequency = vr::VRSettings()->GetFloat(stereo_display_settings_section, "display_frequency"); + + int32_t half_width = display_configuration.half_enable ? 1 : 2; + if (display_configuration.tab_enable) + { + display_configuration.render_width = display_configuration.window_width; + display_configuration.render_height = display_configuration.window_height / half_width; + } + else + { + display_configuration.render_width = display_configuration.window_width / half_width; + display_configuration.render_height = display_configuration.window_height; + } + + // Instantiate our display component + stereo_display_component_ = std::make_unique< StereoDisplayComponent >( display_configuration ); +} + +//----------------------------------------------------------------------------- +// Purpose: This is called by vrserver after our +// IServerTrackedDeviceProvider calls IVRServerDriverHost::TrackedDeviceAdded. +//----------------------------------------------------------------------------- +vr::EVRInitError MockControllerDeviceDriver::Activate( uint32_t unObjectId ) +{ + device_index_ = unObjectId; + is_active_ = true; + + // A list of properties available is contained in vr::ETrackedDeviceProperty. + vr::PropertyContainerHandle_t container = vr::VRProperties()->TrackedDeviceToPropertyContainer( device_index_ ); + vr::VRProperties()->SetStringProperty( container, vr::Prop_ModelNumber_String, stereo_model_number_.c_str() ); + vr::VRProperties()->SetStringProperty( container, vr::Prop_ManufacturerName_String, "VRto3D"); + vr::VRProperties()->SetStringProperty( container, vr::Prop_TrackingFirmwareVersion_String, "1.0"); + vr::VRProperties()->SetStringProperty( container, vr::Prop_HardwareRevision_String, "1.0"); + + // Display settings + vr::VRProperties()->SetFloatProperty( container, vr::Prop_UserIpdMeters_Float, stereo_display_component_.get()->GetConfig().ipd); + vr::VRProperties()->SetFloatProperty( container, vr::Prop_UserHeadToEyeDepthMeters_Float, 0.f); + vr::VRProperties()->SetFloatProperty( container, vr::Prop_DisplayFrequency_Float, stereo_display_component_.get()->GetConfig().display_frequency); + vr::VRProperties()->SetFloatProperty( container, vr::Prop_SecondsFromVsyncToPhotons_Float, stereo_display_component_.get()->GetConfig().display_latency); + vr::VRProperties()->SetBoolProperty( container, vr::Prop_IsOnDesktop_Bool, false); + vr::VRProperties()->SetBoolProperty( container, vr::Prop_DisplayDebugMode_Bool, true); + vr::VRProperties()->SetBoolProperty( container, vr::Prop_Hmd_SupportsHDR10_Bool, stereo_display_component_.get()->GetConfig().hdr_enable); + vr::VRProperties()->SetBoolProperty( container, vr::Prop_Hmd_AllowSupersampleFiltering_Bool, stereo_display_component_.get()->GetConfig().super_sample); + + //Prop_FieldOfViewLeftDegrees_Float + //Prop_FieldOfViewRightDegrees_Float + //Prop_FieldOfViewTopDegrees_Float + //Prop_FieldOfViewBottomDegrees_Float + + // Miscellaneous settings + vr::VRProperties()->SetBoolProperty( container, vr::Prop_WillDriftInYaw_Bool, false); + vr::VRProperties()->SetBoolProperty( container, vr::Prop_DeviceIsWireless_Bool, false); + vr::VRProperties()->SetBoolProperty( container, vr::Prop_DeviceIsCharging_Bool, false); + vr::VRProperties()->SetBoolProperty( container, vr::Prop_ContainsProximitySensor_Bool, false); + vr::VRProperties()->SetBoolProperty( container, vr::Prop_DeviceCanPowerOff_Bool, false); + + // Now let's set up our inputs + // This tells the UI what to show the user for bindings for this controller, + // As well as what default bindings should be for legacy apps. + // Note, we can use the wildcard {} to match the root folder location + // of our driver. + vr::VRProperties()->SetStringProperty( container, vr::Prop_InputProfilePath_String, "{vrto3d}/input/vrto3d_profile.json" ); + + // Let's set up handles for all of our components. + // Even though these are also defined in our input profile, + // We need to get handles to them to update the inputs. + vr::VRDriverInput()->CreateBooleanComponent( container, "/input/system/touch", &my_input_handles_[ MyComponent_system_touch ] ); + vr::VRDriverInput()->CreateBooleanComponent( container, "/input/system/click", &my_input_handles_[ MyComponent_system_click ] ); + + pose_update_thread_ = std::thread( &MockControllerDeviceDriver::PoseUpdateThread, this ); + + return vr::VRInitError_None; +} + +//----------------------------------------------------------------------------- +// Purpose: If you're an HMD, this is where you would return an implementation +// of vr::IVRDisplayComponent, vr::IVRVirtualDisplay or vr::IVRDirectModeComponent. +//----------------------------------------------------------------------------- +void *MockControllerDeviceDriver::GetComponent( const char *pchComponentNameAndVersion ) +{ + if ( strcmp( pchComponentNameAndVersion, vr::IVRDisplayComponent_Version ) == 0 ) + { + return stereo_display_component_.get(); + } + + return nullptr; +} + +//----------------------------------------------------------------------------- +// Purpose: This is called by vrserver when a debug request has been made from an application to the driver. +// What is in the response and request is up to the application and driver to figure out themselves. +//----------------------------------------------------------------------------- +void MockControllerDeviceDriver::DebugRequest( const char *pchRequest, char *pchResponseBuffer, uint32_t unResponseBufferSize ) +{ + if ( unResponseBufferSize >= 1 ) + pchResponseBuffer[ 0 ] = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: This is never called by vrserver in recent OpenVR versions, +// but is useful for giving data to vr::VRServerDriverHost::TrackedDevicePoseUpdated. +//----------------------------------------------------------------------------- +vr::DriverPose_t MockControllerDeviceDriver::GetPose() +{ + vr::DriverPose_t pose = { 0 }; + + pose.qWorldFromDriverRotation.w = 1.f; + pose.qDriverFromHeadRotation.w = 1.f; + + pose.qRotation.w = 1.f; + + pose.vecPosition[ 0 ] = 0.0f; + pose.vecPosition[ 1 ] = 1.0f; + pose.vecPosition[ 2 ] = 0.0f; + + pose.poseIsValid = true; + pose.deviceIsConnected = true; + pose.result = vr::TrackingResult_Running_OK; + + // For HMDs we want to apply rotation/motion prediction + pose.shouldApplyHeadModel = false; + + return pose; +} + +void MockControllerDeviceDriver::PoseUpdateThread() +{ + while ( is_active_ ) + { + // Inform the vrserver that our tracked device's pose has updated, giving it the pose returned by our GetPose(). + vr::VRServerDriverHost()->TrackedDevicePoseUpdated( device_index_, GetPose(), sizeof( vr::DriverPose_t ) ); + + // Update our pose every five milliseconds. + // In reality, you should update the pose whenever you have new data from your device. + std::this_thread::sleep_for( std::chrono::milliseconds( 5 ) ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: This is called by vrserver when the device should enter standby mode. +// The device should be put into whatever low power mode it has. +// We don't really have anything to do here, so let's just log something. +//----------------------------------------------------------------------------- +void MockControllerDeviceDriver::EnterStandby() +{ + DriverLog( "HMD has been put into standby." ); +} + +//----------------------------------------------------------------------------- +// Purpose: This is called by vrserver when the device should deactivate. +// This is typically at the end of a session +// The device should free any resources it has allocated here. +//----------------------------------------------------------------------------- +void MockControllerDeviceDriver::Deactivate() +{ + // Let's join our pose thread that's running + // by first checking then setting is_active_ to false to break out + // of the while loop, if it's running, then call .join() on the thread + if ( is_active_.exchange( false ) ) + { + pose_update_thread_.join(); + } + + // unassign our controller index (we don't want to be calling vrserver anymore after Deactivate() has been called + device_index_ = vr::k_unTrackedDeviceIndexInvalid; +} + + +//----------------------------------------------------------------------------- +// Purpose: This is called by our IServerTrackedDeviceProvider when it pops an event off the event queue. +// It's not part of the ITrackedDeviceServerDriver interface, we created it ourselves. +//----------------------------------------------------------------------------- +void MockControllerDeviceDriver::MyProcessEvent( const vr::VREvent_t &vrevent ) +{ +} + + +//----------------------------------------------------------------------------- +// Purpose: Our IServerTrackedDeviceProvider needs our serial number to add us to vrserver. +// It's not part of the ITrackedDeviceServerDriver interface, we created it ourselves. +//----------------------------------------------------------------------------- +const std::string &MockControllerDeviceDriver::MyGetSerialNumber() +{ + return stereo_serial_number_; +} + +//----------------------------------------------------------------------------- +// DISPLAY DRIVER METHOD DEFINITIONS +//----------------------------------------------------------------------------- + +StereoDisplayComponent::StereoDisplayComponent( const StereoDisplayDriverConfiguration &config ) + : config_( config ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: To inform vrcompositor if this display is considered an on-desktop display. +//----------------------------------------------------------------------------- +bool StereoDisplayComponent::IsDisplayOnDesktop() +{ + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: To as vrcompositor to search for this display. +//----------------------------------------------------------------------------- +bool StereoDisplayComponent::IsDisplayRealDisplay() +{ + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: To inform the rest of the vr system what the recommended target size should be +//----------------------------------------------------------------------------- +void StereoDisplayComponent::GetRecommendedRenderTargetSize( uint32_t *pnWidth, uint32_t *pnHeight ) +{ + *pnWidth = config_.render_width; + *pnHeight = config_.render_height; +} + +//----------------------------------------------------------------------------- +// Purpose: Render in SbS or TaB Stereo3D +//----------------------------------------------------------------------------- +void StereoDisplayComponent::GetEyeOutputViewport( vr::EVREye eEye, uint32_t *pnX, uint32_t *pnY, uint32_t *pnWidth, uint32_t *pnHeight ) +{ + // Use Top and Bottom Rendering + if (config_.tab_enable) + { + *pnX = 0; + + // Each eye will have full width + *pnWidth = config_.window_width; + + // Each eye will have half height + *pnHeight = config_.window_height / 2; + + if (eEye == vr::Eye_Left) + { + // Left eye viewport on the top half of the window + *pnY = 0; + } + else + { + // Right eye viewport on the bottom half of the window + *pnY = config_.window_height / 2; + } + } + + // Use Side by Side Rendering + else + { + *pnY = 0; + + // Each eye will have half width + *pnWidth = config_.window_width / 2; + + // Each eye will have full height + *pnHeight = config_.window_height; + + if (eEye == vr::Eye_Left) + { + // Left eye viewport on the left half of the window + *pnX = 0; + } + else + { + // Right eye viewport on the right half of the window + *pnX = config_.window_width / 2; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: To inform the compositor what the projection parameters are for this HMD. +//----------------------------------------------------------------------------- +void StereoDisplayComponent::GetProjectionRaw( vr::EVREye eEye, float *pfLeft, float *pfRight, float *pfTop, float *pfBottom ) +{ + // Convert horizontal FOV from degrees to radians + float horFovRadians = config_.fov * (M_PI / 180.0f); + + // Calculate the vertical FOV in radians + float verFovRadians = 2 * atan(tan(horFovRadians / 2) / config_.aspect_ratio); + + // Calculate the raw projection values + *pfLeft = -tan(horFovRadians / 2); + *pfRight = tan(horFovRadians / 2); + *pfTop = -tan(verFovRadians / 2); + *pfBottom = tan(verFovRadians / 2); +} + +//----------------------------------------------------------------------------- +// Purpose: Don't distort any coordinates for Stereo3D +//----------------------------------------------------------------------------- +vr::DistortionCoordinates_t StereoDisplayComponent::ComputeDistortion( vr::EVREye eEye, float fU, float fV ) +{ + vr::DistortionCoordinates_t coordinates{}; + coordinates.rfBlue[ 0 ] = fU; + coordinates.rfBlue[ 1 ] = fV; + coordinates.rfGreen[ 0 ] = fU; + coordinates.rfGreen[ 1 ] = fV; + coordinates.rfRed[ 0 ] = fU; + coordinates.rfRed[ 1 ] = fV; + return coordinates; +} +bool StereoDisplayComponent::ComputeInverseDistortion(vr::HmdVector2_t* pResult, vr::EVREye eEye, uint32_t unChannel, float fU, float fV) +{ + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: To inform vrcompositor what the window bounds for this virtual HMD are. +//----------------------------------------------------------------------------- +void StereoDisplayComponent::GetWindowBounds( int32_t *pnX, int32_t *pnY, uint32_t *pnWidth, uint32_t *pnHeight ) +{ + *pnX = config_.window_x; + *pnY = config_.window_y; + *pnWidth = config_.window_width; + *pnHeight = config_.window_height; +} + +//----------------------------------------------------------------------------- +// Purpose: To provide access to settings +//----------------------------------------------------------------------------- +StereoDisplayDriverConfiguration StereoDisplayComponent::GetConfig() +{ + return config_; +} diff --git a/vrto3d/src/hmd_device_driver.h b/vrto3d/src/hmd_device_driver.h new file mode 100644 index 0000000..89aa16a --- /dev/null +++ b/vrto3d/src/hmd_device_driver.h @@ -0,0 +1,96 @@ +//============ Copyright (c) Valve Corporation, All rights reserved. ============ +#pragma once + +#include +#include + +#include "openvr_driver.h" +#include +#include + +enum MyComponent +{ + MyComponent_system_touch, + MyComponent_system_click, + + MyComponent_MAX +}; + +struct StereoDisplayDriverConfiguration +{ + int32_t window_x; + int32_t window_y; + + int32_t window_width; + int32_t window_height; + + int32_t render_width; + int32_t render_height; + + float aspect_ratio; + float fov; + float ipd; + + bool tab_enable; + bool half_enable; + bool super_sample; + bool hdr_enable; + + float display_latency; + float display_frequency; +}; + +class StereoDisplayComponent : public vr::IVRDisplayComponent +{ +public: + explicit StereoDisplayComponent( const StereoDisplayDriverConfiguration &config ); + + // ----- Functions to override vr::IVRDisplayComponent ----- + bool IsDisplayOnDesktop() override; + bool IsDisplayRealDisplay() override; + void GetRecommendedRenderTargetSize( uint32_t *pnWidth, uint32_t *pnHeight ) override; + void GetEyeOutputViewport( vr::EVREye eEye, uint32_t *pnX, uint32_t *pnY, uint32_t *pnWidth, uint32_t *pnHeight ) override; + void GetProjectionRaw( vr::EVREye eEye, float *pfLeft, float *pfRight, float *pfTop, float *pfBottom ) override; + vr::DistortionCoordinates_t ComputeDistortion( vr::EVREye eEye, float fU, float fV ) override; + bool ComputeInverseDistortion(vr::HmdVector2_t* pResult, vr::EVREye eEye, uint32_t unChannel, float fU, float fV) override; + void GetWindowBounds( int32_t *pnX, int32_t *pnY, uint32_t *pnWidth, uint32_t *pnHeight ) override; + StereoDisplayDriverConfiguration GetConfig(); + +private: + StereoDisplayDriverConfiguration config_; +}; + +//----------------------------------------------------------------------------- +// Purpose: Represents a single tracked device in the system. +// What this device actually is (controller, hmd) depends on what the +// IServerTrackedDeviceProvider calls to TrackedDeviceAdded and the +// properties within Activate() of the ITrackedDeviceServerDriver class. +//----------------------------------------------------------------------------- +class MockControllerDeviceDriver : public vr::ITrackedDeviceServerDriver +{ +public: + MockControllerDeviceDriver(); + vr::EVRInitError Activate( uint32_t unObjectId ) override; + void EnterStandby() override; + void *GetComponent( const char *pchComponentNameAndVersion ) override; + void DebugRequest( const char *pchRequest, char *pchResponseBuffer, uint32_t unResponseBufferSize ) override; + vr::DriverPose_t GetPose() override; + void Deactivate() override; + + // ----- Functions we declare ourselves below ----- + const std::string &MyGetSerialNumber(); + void MyProcessEvent( const vr::VREvent_t &vrevent ); + void PoseUpdateThread(); + +private: + std::unique_ptr< StereoDisplayComponent > stereo_display_component_; + + std::string stereo_model_number_; + std::string stereo_serial_number_; + + std::array< vr::VRInputComponentHandle_t, MyComponent_MAX > my_input_handles_{}; + std::atomic< bool > is_active_; + std::atomic< uint32_t > device_index_; + + std::thread pose_update_thread_; +}; diff --git a/vrto3d/src/hmd_driver_factory.cpp b/vrto3d/src/hmd_driver_factory.cpp new file mode 100644 index 0000000..4266144 --- /dev/null +++ b/vrto3d/src/hmd_driver_factory.cpp @@ -0,0 +1,36 @@ +//============ Copyright (c) Valve Corporation, All rights reserved. ============ +#include "device_provider.h" +#include "openvr_driver.h" +#include + +#if defined( _WIN32 ) +#define HMD_DLL_EXPORT extern "C" __declspec( dllexport ) +#define HMD_DLL_IMPORT extern "C" __declspec( dllimport ) +#elif defined( __GNUC__ ) || defined( COMPILER_GCC ) || defined( __APPLE__ ) +#define HMD_DLL_EXPORT extern "C" __attribute__( ( visibility( "default" ) ) ) +#define HMD_DLL_IMPORT extern "C" +#else +#error "Unsupported Platform." +#endif + +MyDeviceProvider device_provider; + +//----------------------------------------------------------------------------- +// Purpose: This is exported from the shared library to be called as the entry point into the driver by vrserver. +// You should return a point to your IServerTrackedDeviceProvider here, as well as optionally a watchdog (see other +// samples). +//----------------------------------------------------------------------------- +HMD_DLL_EXPORT void *HmdDriverFactory( const char *pInterfaceName, int *pReturnCode ) +{ + // This is where we return our device provider, if the HmdDriverFactory call asks for it. + if ( 0 == strcmp( vr::IServerTrackedDeviceProvider_Version, pInterfaceName ) ) + { + return &device_provider; + } + + // Otherwise tell the runtime that we don't have this interface. + if ( pReturnCode ) + *pReturnCode = vr::VRInitError_Init_InterfaceNotFound; + + return NULL; +} \ No newline at end of file diff --git a/vrto3d/vrto3d.vcxproj b/vrto3d/vrto3d.vcxproj new file mode 100644 index 0000000..b61a54b --- /dev/null +++ b/vrto3d/vrto3d.vcxproj @@ -0,0 +1,192 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {1A008D17-0760-4179-8D42-995C5FF1C8D6} + simplehmd + 10.0 + + + + DynamicLibrary + true + v143 + Unicode + + + DynamicLibrary + false + v143 + true + Unicode + + + DynamicLibrary + true + v143 + Unicode + + + DynamicLibrary + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)output\drivers\$(ProjectName)\bin\win$(PlatformArchitecture) + $(Platform)\$(Configuration)\ + driver_$(ProjectName) + + + false + $(SolutionDir)output\drivers\$(ProjectName)\bin\win$(PlatformArchitecture) + $(Platform)\$(Configuration)\ + driver_$(ProjectName) + + + true + $(SolutionDir)output\drivers\$(ProjectName)\bin\win$(PlatformArchitecture) + driver_$(ProjectName) + + + false + $(SolutionDir)output\drivers\$(ProjectName)\bin\win$(PlatformArchitecture) + driver_$(ProjectName) + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + ..\external\openvr\headers;..\utils\driverlog;..\utils\vrmath + + + Console + true + ..\external\openvr\lib\win$(PlatformArchitecture) + openvr_api.lib;%(AdditionalDependencies) + + + XCOPY "$(ProjectDir)$(ProjectName)" "$(SolutionDir)output\drivers\$(ProjectName)" /S /Y /I + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + ..\external\openvr\headers;..\utils\driverlog;..\utils\vrmath + + + Console + true + true + true + ..\external\openvr\lib\win$(PlatformArchitecture) + openvr_api.lib;%(AdditionalDependencies) + + + XCOPY "$(ProjectDir)$(ProjectName)" "$(SolutionDir)output\drivers\$(ProjectName)" /S /Y /I + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + ..\external\openvr\headers;..\utils\driverlog;..\utils\vrmath + + + Console + true + openvr_api.lib;%(AdditionalDependencies) + ..\external\openvr\lib\win$(PlatformArchitecture) + + + XCOPY "$(ProjectDir)$(ProjectName)" "$(SolutionDir)output\drivers\$(ProjectName)" /S /Y /I + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + ..\external\openvr\headers;..\utils\driverlog;..\utils\vrmath + + + Console + true + true + true + openvr_api.lib;%(AdditionalDependencies) + ..\external\openvr\lib\win$(PlatformArchitecture) + + + XCOPY "$(ProjectDir)$(ProjectName)" "$(SolutionDir)output\drivers\$(ProjectName)" /S /Y /I + + + + + + + + + + + + + + {89689a91-fb38-4893-ba67-3d6f45eb2712} + + + + + + \ No newline at end of file diff --git a/vrto3d/driver.vrdrivermanifest b/vrto3d/vrto3d/driver.vrdrivermanifest similarity index 100% rename from vrto3d/driver.vrdrivermanifest rename to vrto3d/vrto3d/driver.vrdrivermanifest diff --git a/vrto3d/resources/driver.vrresources b/vrto3d/vrto3d/resources/driver.vrresources similarity index 100% rename from vrto3d/resources/driver.vrresources rename to vrto3d/vrto3d/resources/driver.vrresources diff --git a/vrto3d/resources/input/vrto3d_profile.json b/vrto3d/vrto3d/resources/input/vrto3d_profile.json similarity index 100% rename from vrto3d/resources/input/vrto3d_profile.json rename to vrto3d/vrto3d/resources/input/vrto3d_profile.json diff --git a/vrto3d/resources/localization/localization.json b/vrto3d/vrto3d/resources/localization/localization.json similarity index 100% rename from vrto3d/resources/localization/localization.json rename to vrto3d/vrto3d/resources/localization/localization.json diff --git a/vrto3d/vrto3d/resources/settings/default.vrsettings b/vrto3d/vrto3d/resources/settings/default.vrsettings new file mode 100644 index 0000000..81da380 --- /dev/null +++ b/vrto3d/vrto3d/resources/settings/default.vrsettings @@ -0,0 +1,22 @@ +{ + "driver_vrto3d": { + "enable": true, + "serial_number": "Stereo3D-1234", + "model_number": "Stereo3D-1" + }, + "vrto3d_display": { + "window_x": 0, + "window_y": 0, + "window_width": 1920, + "window_height": 1080, + "aspect_ratio": 1.77778, + "fov": 90.0, + "ipd": 0.065, + "tab_enable": false, + "half_enable": true, + "super_sample": false, + "hdr_enable": false, + "display_latency": 0.011, + "display_frequency": 60.0 + } +}