diff --git a/src/APILayer/VirtualTouchScreenSink.cpp b/src/APILayer/VirtualTouchScreenSink.cpp index 4237052..287981d 100644 --- a/src/APILayer/VirtualTouchScreenSink.cpp +++ b/src/APILayer/VirtualTouchScreenSink.cpp @@ -31,14 +31,33 @@ namespace HandTrackedCockpitClicking { VirtualTouchScreenSink::VirtualTouchScreenSink( - const std::shared_ptr& oxr, - XrSession session, - XrTime nextDisplayTime, - XrSpace viewSpace) { + Calibration calibration, + DWORD targetProcessID) + : mCalibration(calibration), mTargetProcessID(targetProcessID) { DebugPrint( "Initialized virtual touch screen - PointerSink: {}; ActionSink: {}", IsPointerSink(), IsActionSink()); + + UpdateMainWindow(); +} + +VirtualTouchScreenSink::VirtualTouchScreenSink( + const std::shared_ptr& oxr, + XrSession session, + XrTime nextDisplayTime, + XrSpace viewSpace) + : VirtualTouchScreenSink( + CalibrationFromOpenXR(oxr, session, nextDisplayTime, viewSpace).value(), + GetCurrentProcessId()) { +} + +std::optional +VirtualTouchScreenSink::CalibrationFromOpenXR( + const std::shared_ptr& oxr, + XrSession session, + XrTime nextDisplayTime, + XrSpace viewSpace) { XrViewLocateInfo viewLocateInfo { .type = XR_TYPE_VIEW_LOCATE_INFO, .viewConfigurationType = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO, @@ -57,42 +76,62 @@ VirtualTouchScreenSink::VirtualTouchScreenSink( &viewCount, views.data())) { DebugPrint("Failed to find FOV"); - return; + return {}; } const auto eyeFov = views[0].fov; + auto calibration = CalibrationFromOpenXRFOV(eyeFov); + + DebugPrint( + "Reported eye FOV: L {} R {} U {} D {} (input FOV {}x{}) - tracking origin " + "at ({}, " + "{})", + eyeFov.angleLeft, + eyeFov.angleRight, + eyeFov.angleUp, + eyeFov.angleDown, + calibration.mWindowInputFov.x, + calibration.mWindowInputFov.y, + calibration.mWindowInputFovOrigin0To1.x, + calibration.mWindowInputFovOrigin0To1.y); + return calibration; +} + +VirtualTouchScreenSink::Calibration +VirtualTouchScreenSink::CalibrationFromOpenXRFOV(const XrFovf& eyeFov) { // ideally this should be the sum of left+right and up+down FOVs - which // aren't symmetric - but this seems to match what DCS does - mWindowInputFov = { + XrVector2f windowInputFov { 2 * std::max(std::abs(eyeFov.angleRight), std::abs(eyeFov.angleLeft)), 2 * std::abs(eyeFov.angleUp), }; - mFovOrigin0To1 = { + XrVector2f fovOrigin0To1 { 0.5, 0.5, }; - DebugPrint( - "Reported eye FOV: L {} R {} U {} D {} (input FOV {}x{}) - tracking origin " - "at ({}, " - "{})", - eyeFov.angleLeft, - eyeFov.angleRight, - eyeFov.angleUp, - eyeFov.angleDown, - mWindowInputFov.x, - mWindowInputFov.y, - mFovOrigin0To1.x, - mFovOrigin0To1.y); + return {windowInputFov, fovOrigin0To1}; +} - UpdateMainWindow(); +std::optional +VirtualTouchScreenSink::CalibrationFromConfig() { + if (!Config::HaveSavedFOV) { + return {}; + } + + const XrFovf eyeFov { + .angleLeft = Config::LeftEyeFOVLeft, + .angleRight = Config::LeftEyeFOVRight, + .angleUp = Config::LeftEyeFOVUp, + .angleDown = Config::LeftEyeFOVDown, + }; + return CalibrationFromOpenXRFOV(eyeFov); } void VirtualTouchScreenSink::UpdateMainWindow() { - mThisProcess = GetCurrentProcessId(); mConsoleWindow = GetConsoleWindow(); EnumWindows( &VirtualTouchScreenSink::EnumWindowCallback, @@ -114,7 +153,7 @@ VirtualTouchScreenSink::EnumWindowCallback(HWND hwnd, LPARAM lparam) { DWORD processID {}; GetWindowThreadProcessId(hwnd, &processID); - if (processID != self->mThisProcess) { + if (processID != self->mTargetProcessID) { return TRUE; } @@ -187,9 +226,11 @@ bool VirtualTouchScreenSink::RotationToCartesian( // Flipped because screen X is left-to-right, which is a rotation around the Y // axis - const auto screenX = mFovOrigin0To1.x + (rotation.y / mWindowInputFov.x); + const auto screenX = mCalibration.mWindowInputFovOrigin0To1.x + + (rotation.y / mCalibration.mWindowInputFov.x); // OpenXR has Y origin in bottom left, screeen has it in top left - const auto screenY = mFovOrigin0To1.y - (rotation.x / mWindowInputFov.y); + const auto screenY = mCalibration.mWindowInputFovOrigin0To1.y + - (rotation.x / mCalibration.mWindowInputFov.y); if (screenX < 0 || screenX > 1 || screenY < 0 || screenY > 1) { return false; diff --git a/src/APILayer/VirtualTouchScreenSink.h b/src/APILayer/VirtualTouchScreenSink.h index 9cf6a31..a57d522 100644 --- a/src/APILayer/VirtualTouchScreenSink.h +++ b/src/APILayer/VirtualTouchScreenSink.h @@ -33,6 +33,12 @@ namespace HandTrackedCockpitClicking { class VirtualTouchScreenSink final { public: + struct Calibration { + XrVector2f mWindowInputFov {}; + XrVector2f mWindowInputFovOrigin0To1 {}; + }; + + VirtualTouchScreenSink(Calibration, DWORD targetProcessID); VirtualTouchScreenSink( const std::shared_ptr& oxr, XrSession session, @@ -44,21 +50,31 @@ class VirtualTouchScreenSink final { static bool IsActionSink(); static bool IsPointerSink(); + VirtualTouchScreenSink() = delete; + + static std::optional CalibrationFromOpenXR( + const std::shared_ptr& oxr, + XrSession session, + XrTime nextDisplayTime, + XrSpace viewSpace); + + static Calibration CalibrationFromOpenXRFOV(const XrFovf& eyeFov); + static std::optional CalibrationFromConfig(); + private: void Update(const InputState& hand); bool RotationToCartesian(const XrVector2f& rotation, XrVector2f* cartesian); void UpdateMainWindow(); static BOOL CALLBACK EnumWindowCallback(HWND hwnd, LPARAM lparam); - DWORD mThisProcess; + DWORD mTargetProcessID; HWND mWindow {}; HWND mConsoleWindow {}; XrVector2f mWindowSize {}; RECT mWindowRect {}; XrVector2f mScreenSize; - XrVector2f mWindowInputFov {}; - XrVector2f mFovOrigin0To1 {}; + Calibration mCalibration {}; bool mLeftClick {false}; bool mRightClick {false};