diff --git a/Readme.md b/Readme.md index 705b9f48..208dbabd 100644 --- a/Readme.md +++ b/Readme.md @@ -77,7 +77,11 @@ Redirect mode can be temporarily suspended by pressing the system button on eith ![Motion Compensation Settings Page](https://raw.githubusercontent.com/matzman666/OpenVR-InputEmulator/master/docs/screenshots/MotionCompensationPage.png) -- **Center Point**: Set the center point of the motion platform. +- **Vel/Acc Compensation Mode**: How should reported velocities and acceleration values be adjusted. The problem with only adjusting the headset position is that pose prediction also takes velocity and acceleration into accound. As long as the reported values to not differ too much from the real values, pose prediction errors are hardly noticeable. But with fast movements of the motion platform the pose prediction error can be noticeable. Available modes are: + - **Disabled**: Do not adjust velocity/acceration values. + - **Set Zero**: Set all velocity/acceleration values to zero. Most simple form of velocity/acceleration compensation. + - **Use Reference Tracker**: Substract the velocity/acceleration values of the motion compensation reference tracker/controller from the values reported from the headset. Most accurate form of velocity/acceleration compensation. However, it requires that the reference tracker/controller is as closely mounted to the head position as possible. The further away it is from the head position the larger the error. + - **Linear Approximation (Experimental)**: Uses linear approximation to estimate the velocity/acceleration values. The used formula is: (current_position - last_position) / time_difference, however the resulting values do cause a lot of jitter and therefore they are divided by four to reduce jitter to an acceptable level. ## client_commandline commands: diff --git a/client_overlay/bin/win64/openvr_api.dll b/client_overlay/bin/win64/openvr_api.dll index 5767d725..ddda8970 100644 Binary files a/client_overlay/bin/win64/openvr_api.dll and b/client_overlay/bin/win64/openvr_api.dll differ diff --git a/client_overlay/bin/win64/res/qml/DeviceManipulationPage.qml b/client_overlay/bin/win64/res/qml/DeviceManipulationPage.qml index 20277512..3729b66d 100644 --- a/client_overlay/bin/win64/res/qml/DeviceManipulationPage.qml +++ b/client_overlay/bin/win64/res/qml/DeviceManipulationPage.qml @@ -60,8 +60,7 @@ MyStackViewPage { Layout.preferredWidth: 555 Layout.fillWidth: true model: [ - "Device Offsets", - "Motion Compensation Settings", + "Device Offsets" ] currentIndex: 0 } @@ -451,7 +450,7 @@ MyStackViewPage { deviceSelectionComboBox.currentIndex = -1 deviceModeSelectionComboBox.enabled = false deviceModeApplyButton.enabled = false - //motionCompensationButton.enabled = false + motionCompensationButton.enabled = false deviceManipulationOffsetButton.enabled = false deviceIdentifyButton.enabled = false deviceRenderModelComboBox.enabled = false @@ -461,7 +460,7 @@ MyStackViewPage { } else { deviceModeSelectionComboBox.enabled = true deviceManipulationOffsetButton.enabled = true - //motionCompensationButton.enabled = true + motionCompensationButton.enabled = true deviceModeApplyButton.enabled = true deviceIdentifyButton.enabled = true deviceRenderModelComboBox.enabled = true diff --git a/client_overlay/bin/win64/res/qml/MotionCompensationPage.qml b/client_overlay/bin/win64/res/qml/MotionCompensationPage.qml index 2a24096f..fb4f8a65 100644 --- a/client_overlay/bin/win64/res/qml/MotionCompensationPage.qml +++ b/client_overlay/bin/win64/res/qml/MotionCompensationPage.qml @@ -9,17 +9,54 @@ MyStackViewPage { headerText: "Motion Compensation Settings" + property bool setupFinished: false + content: ColumnLayout { spacing: 18 + RowLayout { + spacing: 18 + Layout.bottomMargin: 64 + + MyText { + text: "Vel/Acc Compensation Mode:" + } + + MyComboBox { + id: deviceSelectionComboBox + Layout.maximumWidth: 750 + Layout.minimumWidth: 750 + Layout.preferredWidth: 750 + Layout.fillWidth: true + model: [ + "Disabled", + "Set Zero", + "Use Reference Tracker", + "Linear Approximation (Experimental)" + ] + onCurrentIndexChanged: { + if (setupFinished) { + DeviceManipulationTabController.setMotionCompensationVelAccMode(currentIndex) + } + } + } + } + Item { Layout.fillWidth: true Layout.fillHeight: true } + + Component.onCompleted: { + deviceSelectionComboBox.currentIndex = DeviceManipulationTabController.getMotionCompensationVelAccMode() + setupFinished = true + } + Connections { target: DeviceManipulationTabController - onDeviceInfoChanged: { + onMotionCompensationVelAccModeChanged: { + deviceSelectionComboBox.currentIndex = mode } } diff --git a/client_overlay/src/overlaycontroller.h b/client_overlay/src/overlaycontroller.h index bcf8c1ed..4099a2b4 100644 --- a/client_overlay/src/overlaycontroller.h +++ b/client_overlay/src/overlaycontroller.h @@ -36,7 +36,7 @@ class OverlayController : public QObject { public: static constexpr const char* applicationKey = "matzman666.VRInputEmulator"; static constexpr const char* applicationName = "VR Input Emulator"; - static constexpr const char* applicationVersionString = "v1.0"; + static constexpr const char* applicationVersionString = "v1.0.3"; private: vr::VROverlayHandle_t m_ulOverlayHandle = vr::k_ulOverlayHandleInvalid; diff --git a/client_overlay/src/tabcontrollers/DeviceManipulationTabController.cpp b/client_overlay/src/tabcontrollers/DeviceManipulationTabController.cpp index 2503e58c..973c4b9c 100644 --- a/client_overlay/src/tabcontrollers/DeviceManipulationTabController.cpp +++ b/client_overlay/src/tabcontrollers/DeviceManipulationTabController.cpp @@ -239,6 +239,10 @@ double DeviceManipulationTabController::getDriverTranslationOffset(unsigned inde } } +unsigned DeviceManipulationTabController::getMotionCompensationVelAccMode() { + return motionCompensationVelAccMode; +} + #define DEVICEMANIPULATIONSETTINGS_GETTRANSLATIONVECTOR(name) { \ double valueX = settings->value(#name ## "_x", 0.0).toDouble(); \ @@ -255,9 +259,10 @@ double DeviceManipulationTabController::getDriverTranslationOffset(unsigned inde } void DeviceManipulationTabController::reloadDeviceManipulationSettings() { - /*auto settings = OverlayController::appSettings(); + auto settings = OverlayController::appSettings(); settings->beginGroup("deviceManipulationSettings"); - settings->endGroup();*/ + motionCompensationVelAccMode = settings->value("motionCompensationVelAccMode").toUInt(); + settings->endGroup(); } void DeviceManipulationTabController::reloadDeviceManipulationProfiles() { @@ -286,10 +291,11 @@ void DeviceManipulationTabController::reloadDeviceManipulationProfiles() { } void DeviceManipulationTabController::saveDeviceManipulationSettings() { - /*auto settings = OverlayController::appSettings(); + auto settings = OverlayController::appSettings(); settings->beginGroup("deviceManipulationSettings"); + settings->setValue("motionCompensationVelAccMode", motionCompensationVelAccMode); settings->endGroup(); - settings->sync();*/ + settings->sync(); } @@ -410,6 +416,17 @@ void DeviceManipulationTabController::deleteDeviceManipulationProfile(unsigned i } } +void DeviceManipulationTabController::setMotionCompensationVelAccMode(unsigned mode, bool notify) { + if (motionCompensationVelAccMode != mode) { + motionCompensationVelAccMode = mode; + vrInputEmulator.setMotionVelAccCompensationMode(mode); + saveDeviceManipulationSettings(); + if (notify) { + emit motionCompensationVelAccModeChanged(mode); + } + } +} + unsigned DeviceManipulationTabController::getRenderModelCount() { return (unsigned)vr::VRRenderModels()->GetRenderModelCount(); } @@ -555,7 +572,7 @@ void DeviceManipulationTabController::setDeviceMode(unsigned index, unsigned mod vrInputEmulator.setDeviceSwapMode(deviceInfos[index]->openvrId, deviceInfos[targedIndex]->openvrId); break; case 4: - vrInputEmulator.setDeviceMotionCompensationMode(deviceInfos[index]->openvrId); + vrInputEmulator.setDeviceMotionCompensationMode(deviceInfos[index]->openvrId, motionCompensationVelAccMode); break; default: LOG(ERROR) << "Unkown device mode"; diff --git a/client_overlay/src/tabcontrollers/DeviceManipulationTabController.h b/client_overlay/src/tabcontrollers/DeviceManipulationTabController.h index 133dd055..727ccd46 100644 --- a/client_overlay/src/tabcontrollers/DeviceManipulationTabController.h +++ b/client_overlay/src/tabcontrollers/DeviceManipulationTabController.h @@ -60,6 +60,8 @@ class DeviceManipulationTabController : public QObject { std::vector deviceManipulationProfiles; + uint32_t motionCompensationVelAccMode = 0; + unsigned settingsUpdateCounter = 0; std::thread identifyThread; @@ -85,6 +87,7 @@ class DeviceManipulationTabController : public QObject { Q_INVOKABLE double getDriverFromHeadTranslationOffset(unsigned index, unsigned axis); Q_INVOKABLE double getDriverRotationOffset(unsigned index, unsigned axis); Q_INVOKABLE double getDriverTranslationOffset(unsigned index, unsigned axis); + Q_INVOKABLE unsigned getMotionCompensationVelAccMode(); void reloadDeviceManipulationSettings(); void reloadDeviceManipulationProfiles(); @@ -116,11 +119,14 @@ public slots: void applyDeviceManipulationProfile(unsigned index, unsigned deviceIndex); void deleteDeviceManipulationProfile(unsigned index); + void setMotionCompensationVelAccMode(unsigned mode, bool notify = true); + signals: void deviceCountChanged(unsigned deviceCount); void deviceInfoChanged(unsigned index); void motionCompensationSettingsChanged(); void deviceManipulationProfilesChanged(); + void motionCompensationVelAccModeChanged(unsigned mode); }; } // namespace inputemulator diff --git a/docs/screenshots/MotionCompensationPage.png b/docs/screenshots/MotionCompensationPage.png index d9307356..d727126f 100644 Binary files a/docs/screenshots/MotionCompensationPage.png and b/docs/screenshots/MotionCompensationPage.png differ diff --git a/driver_vrinputemulator/src/com/shm/driver_ipc_shm.cpp b/driver_vrinputemulator/src/com/shm/driver_ipc_shm.cpp index f55431b0..9ff4a895 100644 --- a/driver_vrinputemulator/src/com/shm/driver_ipc_shm.cpp +++ b/driver_vrinputemulator/src/com/shm/driver_ipc_shm.cpp @@ -812,6 +812,7 @@ void IpcShmCommunicator::_ipcThreadFunc(IpcShmCommunicator* _this, CServerDriver } else { auto serverDriver = CServerDriver::getInstance(); if (serverDriver) { + serverDriver->setMotionCompensationVelAccMode(message.msg.dm_MotionCompensationMode.velAccCompensationMode); info->setMotionCompensationMode(); resp.status = ipc::ReplyStatus::Ok; } else { @@ -897,6 +898,7 @@ void IpcShmCommunicator::_ipcThreadFunc(IpcShmCommunicator* _this, CServerDriver resp.messageId = message.msg.dm_SetMotionCompensationProperties.messageId; auto serverDriver = CServerDriver::getInstance(); if (serverDriver) { + serverDriver->setMotionCompensationVelAccMode(message.msg.dm_SetMotionCompensationProperties.velAccCompensationMode); resp.status = ipc::ReplyStatus::Ok; } else { resp.status = ipc::ReplyStatus::UnknownError; diff --git a/driver_vrinputemulator/src/driver_deviceinfo.cpp b/driver_vrinputemulator/src/driver_deviceinfo.cpp index 89a576ab..b0cc8e03 100644 --- a/driver_vrinputemulator/src/driver_deviceinfo.cpp +++ b/driver_vrinputemulator/src/driver_deviceinfo.cpp @@ -73,23 +73,7 @@ void OpenvrDeviceManipulationInfo::handleNewDevicePose(vr::IVRServerDriverHost* } auto serverDriver = CServerDriver::getInstance(); if (serverDriver) { - if (serverDriver->_applyMotionCompensation(newPose, this)) { - /*auto now = std::chrono::duration_cast (std::chrono::system_clock::now().time_since_epoch()).count(); - if (m_lastDriverPoseValid) { - double tdiff = (double)(now - m_lastDriverPoseTime) / 500000.0; - newPose.vecVelocity[0] = (newPose.vecPosition[0] - m_lastDriverPose.vecPosition[0]) / tdiff; - newPose.vecVelocity[1] = (newPose.vecPosition[1] - m_lastDriverPose.vecPosition[1]) / tdiff; - newPose.vecVelocity[2] = (newPose.vecPosition[2] - m_lastDriverPose.vecPosition[2]) / tdiff; - } - m_lastDriverPose = newPose; - m_lastDriverPoseTime = now; - m_lastDriverPoseValid = true; - newPose.vecAcceleration[0] = 0.0; - newPose.vecAcceleration[1] = 0.0; - newPose.vecAcceleration[2] = 0.0;*/ - } else { - m_lastDriverPoseValid = false; - } + serverDriver->_applyMotionCompensation(newPose, this); } if (m_deviceMode == 2 && !m_redirectSuspended) { // redirect source if (!_disconnectedMsgSend) { @@ -227,7 +211,7 @@ int OpenvrDeviceManipulationInfo::setMotionCompensationMode() { auto serverDriver = CServerDriver::getInstance(); if (res == 0 && serverDriver) { _disconnectedMsgSend = false; - serverDriver->_enableMotionCompensation(true); + serverDriver->enableMotionCompensation(true); m_deviceMode = 5; } return 0; @@ -248,11 +232,18 @@ int OpenvrDeviceManipulationInfo::_disableOldMode(int newMode) { if (m_deviceMode == 5) { auto serverDriver = CServerDriver::getInstance(); if (serverDriver) { - serverDriver->_enableMotionCompensation(false); + serverDriver->enableMotionCompensation(false); } } else if (m_deviceMode == 3 || m_deviceMode == 2 || m_deviceMode == 4) { m_redirectRef->m_deviceMode = 0; } + if (newMode == 5) { + auto serverDriver = CServerDriver::getInstance(); + if (serverDriver) { + serverDriver->disableMotionCompensationOnAllDevices(); + serverDriver->enableMotionCompensation(false); + } + } } return 0; } diff --git a/driver_vrinputemulator/src/driver_server.cpp b/driver_vrinputemulator/src/driver_server.cpp index 3de39eae..1c0f9d82 100644 --- a/driver_vrinputemulator/src/driver_server.cpp +++ b/driver_vrinputemulator/src/driver_server.cpp @@ -72,7 +72,6 @@ CServerDriver::~CServerDriver() { // Cleanup(); } - void CServerDriver::_poseUpatedDetourFunc(vr::IVRServerDriverHost* _this, uint32_t unWhichDevice, const vr::DriverPose_t& newPose, uint32_t unPoseStructSize) { // Call rates: // @@ -96,7 +95,6 @@ void CServerDriver::_poseUpatedDetourFunc(vr::IVRServerDriverHost* _this, uint32 callcount = 0; } }*/ - if (_openvrIdToDeviceInfoMap[unWhichDevice] && _openvrIdToDeviceInfoMap[unWhichDevice]->isValid()) { _openvrIdToDeviceInfoMap[unWhichDevice]->handleNewDevicePose(_this, _poseUpatedDetour.origFunc, unWhichDevice, newPose); } else { @@ -469,12 +467,30 @@ OpenvrDeviceManipulationInfo* CServerDriver::deviceManipulation_getInfo(uint32_t return nullptr; } -void CServerDriver::_enableMotionCompensation(bool enable) { +void CServerDriver::enableMotionCompensation(bool enable) { _motionCompensationZeroPoseValid = false; _motionCompensationRefPoseValid = false; _motionCompensationEnabled = enable; } +void CServerDriver::setMotionCompensationVelAccMode(uint32_t velAccMode) { + if (_motionCompensationVelAccMode != velAccMode) { + _motionCompensationRefVelAccValid = false; + for (auto d : _openvrDeviceInfos) { + d.second->setLastDriverPoseValid(false); + } + _motionCompensationVelAccMode = velAccMode; + } +} + +void CServerDriver::disableMotionCompensationOnAllDevices() { + for (auto d : _openvrDeviceInfos) { + if (d.second->deviceMode() == 5) { + d.second->setDefaultMode(); + } + } +} + bool CServerDriver::_isMotionCompensationZeroPoseValid() { return _motionCompensationZeroPoseValid; } @@ -498,6 +514,17 @@ void CServerDriver::_updateMotionCompensationRefPose(const vr::DriverPose_t& pos _motionCompensationRotDiff = poseWorldRot * vrmath::quaternionConjugate(_motionCompensationZeroRot); _motionCompensationRotDiffInv = vrmath::quaternionConjugate(_motionCompensationRotDiff); + // Convert velocity and acceleration values into app space and undo device rotation + if (_motionCompensationVelAccMode == 2) { + auto tmpRot = tmpConj * vrmath::quaternionConjugate(pose.qRotation); + auto tmpRotInv = vrmath::quaternionConjugate(tmpRot); + _motionCompensationRefPosVel = vrmath::quaternionRotateVector(tmpRot, tmpRotInv, { pose.vecVelocity[0], pose.vecVelocity[1], pose.vecVelocity[2] }); + _motionCompensationRefPosAcc = vrmath::quaternionRotateVector(tmpRot, tmpRotInv, { pose.vecAcceleration[0], pose.vecAcceleration[1], pose.vecAcceleration[2] }); + _motionCompensationRefRotVel = vrmath::quaternionRotateVector(tmpRot, tmpRotInv, { pose.vecAngularVelocity[0], pose.vecAngularVelocity[1], pose.vecAngularVelocity[2] }); + _motionCompensationRefRotAcc = vrmath::quaternionRotateVector(tmpRot, tmpRotInv, { pose.vecAngularAcceleration[0], pose.vecAngularAcceleration[1], pose.vecAngularAcceleration[2] }); + _motionCompensationRefVelAccValid = true; + } + _motionCompensationRefPoseValid = true; } @@ -518,6 +545,89 @@ bool CServerDriver::_applyMotionCompensation(vr::DriverPose_t& pose, OpenvrDevic pose.vecPosition[0] = adjPoseDriverPos.v[0]; pose.vecPosition[1] = adjPoseDriverPos.v[1]; pose.vecPosition[2] = adjPoseDriverPos.v[2]; + + // Velocity / Acceleration Compensation + if (_motionCompensationVelAccMode == 1) { // Set Zero + pose.vecVelocity[0] = 0.0; + pose.vecVelocity[1] = 0.0; + pose.vecVelocity[2] = 0.0; + pose.vecAcceleration[0] = 0.0; + pose.vecAcceleration[1] = 0.0; + pose.vecAcceleration[2] = 0.0; + pose.vecAngularVelocity[0] = 0.0; + pose.vecAngularVelocity[1] = 0.0; + pose.vecAngularVelocity[2] = 0.0; + pose.vecAngularAcceleration[0] = 0.0; + pose.vecAngularAcceleration[1] = 0.0; + pose.vecAngularAcceleration[2] = 0.0; + } else if (_motionCompensationVelAccMode == 2) { // Substract Motion Ref + if (_motionCompensationRefVelAccValid) { + auto tmpRot = pose.qWorldFromDriverRotation * pose.qRotation; + auto tmpRotInv = vrmath::quaternionConjugate(tmpRot); + auto tmpPosVel = vrmath::quaternionRotateVector(tmpRot, tmpRotInv, _motionCompensationRefPosVel); + pose.vecVelocity[0] -= tmpPosVel.v[0]; + pose.vecVelocity[1] -= tmpPosVel.v[1]; + pose.vecVelocity[2] -= tmpPosVel.v[2]; + auto tmpPosAcc = vrmath::quaternionRotateVector(tmpRot, tmpRotInv, _motionCompensationRefPosAcc); + pose.vecAcceleration[0] -= tmpPosAcc.v[0]; + pose.vecAcceleration[1] -= tmpPosAcc.v[1]; + pose.vecAcceleration[2] -= tmpPosAcc.v[2]; + auto tmpRotVel = vrmath::quaternionRotateVector(tmpRot, tmpRotInv, _motionCompensationRefRotVel); + pose.vecAngularVelocity[0] -= tmpRotVel.v[0]; + pose.vecAngularVelocity[1] -= tmpRotVel.v[1]; + pose.vecAngularVelocity[2] -= tmpRotVel.v[2]; + auto tmpRotAcc = vrmath::quaternionRotateVector(tmpRot, tmpRotInv, _motionCompensationRefRotAcc); + pose.vecAngularAcceleration[0] -= tmpRotAcc.v[0]; + pose.vecAngularAcceleration[1] -= tmpRotAcc.v[1]; + pose.vecAngularAcceleration[2] -= tmpRotAcc.v[2]; + } + } else if (_motionCompensationVelAccMode == 3) { // Linear Approximation + auto now = std::chrono::duration_cast (std::chrono::system_clock::now().time_since_epoch()).count(); + if (deviceInfo->lastDriverPoseValid()) { + auto& lastPose = deviceInfo->lastDriverPose(); + double tdiff = ((double)(now - deviceInfo->lastDriverPoseTime()) / 1.0E6) + (pose.poseTimeOffset - lastPose.poseTimeOffset); + tdiff *= 4; // To reduce jitter + if (tdiff < 0.0001) { // Sometimes we get a very small or even negative time difference between current and last pose + // In this case we just take the velocities and accelerations from last time + pose.vecVelocity[0] = lastPose.vecVelocity[0]; + pose.vecVelocity[1] = lastPose.vecVelocity[1]; + pose.vecVelocity[2] = lastPose.vecVelocity[2]; + pose.vecAcceleration[0] = lastPose.vecAcceleration[0]; + pose.vecAcceleration[1] = lastPose.vecAcceleration[1]; + pose.vecAcceleration[2] = lastPose.vecAcceleration[2]; + } else { + pose.vecVelocity[0] = (pose.vecPosition[0] - lastPose.vecPosition[0]) / tdiff; + // Set very small values to zero to avoid jitter + if (pose.vecVelocity[0] > -0.01 && pose.vecVelocity[0] < 0.01) { + pose.vecVelocity[0] = 0.0; + } + pose.vecVelocity[1] = (pose.vecPosition[1] - lastPose.vecPosition[1]) / tdiff; + if (pose.vecVelocity[1] > -0.01 && pose.vecVelocity[1] < 0.01) { + pose.vecVelocity[1] = 0.0; + } + pose.vecVelocity[2] = (pose.vecPosition[2] - lastPose.vecPosition[2]) / tdiff; + if (pose.vecVelocity[2] > -0.01 && pose.vecVelocity[2] < 0.01) { + pose.vecVelocity[2] = 0.0; + } + + // Predicting acceleration values leads to a very jittery experience, + // furthermore the pose updates coming from the lighthouse driver do no have acceleration set in the first place. + /*pose.vecAcceleration[0] = (pose.vecVelocity[0] - lastPose.vecVelocity[0]) / tdiff; + if (pose.vecAcceleration[0] > -0.01 && pose.vecAcceleration[0] < 0.01) { + pose.vecAcceleration[0] = 0.0; + } + pose.vecAcceleration[1] = (pose.vecVelocity[1] - lastPose.vecVelocity[1]) / tdiff; + if (pose.vecAcceleration[1] > -0.01 && pose.vecAcceleration[1] < 0.01) { + pose.vecAcceleration[1] = 0.0; + } + pose.vecAcceleration[2] = (pose.vecVelocity[2] - lastPose.vecVelocity[2]) / tdiff; + if (pose.vecAcceleration[2] > -0.01 && pose.vecAcceleration[2] < 0.01) { + pose.vecAcceleration[2] = 0.0; + }*/ + } + } + deviceInfo->setLastDriverPose(pose, now); + } return true; } else { return false; diff --git a/driver_vrinputemulator/src/driver_vrinputemulator.h b/driver_vrinputemulator/src/driver_vrinputemulator.h index a4810c94..eb7540a5 100644 --- a/driver_vrinputemulator/src/driver_vrinputemulator.h +++ b/driver_vrinputemulator/src/driver_vrinputemulator.h @@ -91,6 +91,7 @@ class OpenvrDeviceManipulationInfo { long long m_lastDriverPoseTime = 0; + public: OpenvrDeviceManipulationInfo() {} OpenvrDeviceManipulationInfo(vr::ITrackedDeviceServerDriver* driver, vr::ETrackedDeviceClass eDeviceClass, uint32_t openvrId, vr::IVRServerDriverHost* driverHost) @@ -147,6 +148,16 @@ class OpenvrDeviceManipulationInfo { void handleAxisEvent(vr::IVRServerDriverHost* driver, _DetourTrackedDeviceAxisUpdated_t origFunc, uint32_t& unWhichDevice, uint32_t unWhichAxis, const vr::VRControllerAxis_t& axisState); bool triggerHapticPulse(uint32_t unAxisId, uint16_t usPulseDurationMicroseconds, bool directMode = false); + + bool lastDriverPoseValid() { return m_lastDriverPoseValid; } + vr::DriverPose_t& lastDriverPose() { return m_lastDriverPose; } + long long lastDriverPoseTime() { return m_lastDriverPoseTime; } + void setLastDriverPoseValid(bool valid) { m_lastDriverPoseValid = valid; } + void setLastDriverPose(const vr::DriverPose_t& pose, long long time) { + m_lastDriverPose = pose; + m_lastDriverPoseTime = time; + m_lastDriverPoseValid = true; + } }; @@ -223,7 +234,10 @@ class CServerDriver : public vr::IServerTrackedDeviceProvider { /** Called by virtual devices when they are deactivated */ void _trackedDeviceDeactivated(uint32_t deviceId); - void _enableMotionCompensation(bool enable); + /* Motion Compensation API */ + void enableMotionCompensation(bool enable); + void setMotionCompensationVelAccMode(uint32_t velAccMode); + void disableMotionCompensationOnAllDevices(); bool _isMotionCompensationZeroPoseValid(); void _setMotionCompensationZeroPose(const vr::DriverPose_t& pose); void _updateMotionCompensationRefPose(const vr::DriverPose_t& pose); @@ -250,6 +264,7 @@ class CServerDriver : public vr::IServerTrackedDeviceProvider { //// motion compensation related //// bool _motionCompensationEnabled = false; + int _motionCompensationVelAccMode = 0; // 0 .. Disabled, 1 .. Set Zero, 2 .. Substract Motion Ref, 3 .. Linear Approximation bool _motionCompensationZeroPoseValid = false; vr::HmdVector3d_t _motionCompensationZeroPos; @@ -260,6 +275,12 @@ class CServerDriver : public vr::IServerTrackedDeviceProvider { vr::HmdQuaternion_t _motionCompensationRotDiff; vr::HmdQuaternion_t _motionCompensationRotDiffInv; + bool _motionCompensationRefVelAccValid = false; + vr::HmdVector3d_t _motionCompensationRefPosVel; + vr::HmdVector3d_t _motionCompensationRefPosAcc; + vr::HmdVector3d_t _motionCompensationRefRotVel; + vr::HmdVector3d_t _motionCompensationRefRotAcc; + //// function hooks related //// template diff --git a/lib_vrinputemulator/include/ipc_protocol.h b/lib_vrinputemulator/include/ipc_protocol.h index c18da643..280698bc 100644 --- a/lib_vrinputemulator/include/ipc_protocol.h +++ b/lib_vrinputemulator/include/ipc_protocol.h @@ -264,6 +264,7 @@ struct Request_DeviceManipulation_MotionCompensationMode { uint32_t clientId; uint32_t messageId; // Used to associate with Reply uint32_t deviceId; + uint32_t velAccCompensationMode; }; struct Request_DeviceManipulation_TriggerHapticPulse { @@ -278,6 +279,7 @@ struct Request_DeviceManipulation_TriggerHapticPulse { struct Request_DeviceManipulation_SetMotionCompensationProperties { uint32_t clientId; uint32_t messageId; // Used to associate with Reply + uint32_t velAccCompensationMode; }; diff --git a/lib_vrinputemulator/include/vrinputemulator.h b/lib_vrinputemulator/include/vrinputemulator.h index 6ac34172..d565417b 100644 --- a/lib_vrinputemulator/include/vrinputemulator.h +++ b/lib_vrinputemulator/include/vrinputemulator.h @@ -181,7 +181,9 @@ class VRInputEmulator { void setDeviceFakeDisconnectedMode(uint32_t deviceId, bool modal = true); void setDeviceRedictMode(uint32_t deviceId, uint32_t target, bool modal = true); void setDeviceSwapMode(uint32_t deviceId, uint32_t target, bool modal = true); - void setDeviceMotionCompensationMode(uint32_t deviceId, bool modal = true); + void setDeviceMotionCompensationMode(uint32_t deviceId, uint32_t velAccMode = 0, bool modal = true); + + void setMotionVelAccCompensationMode(uint32_t velAccMode, bool modal = true); void triggerHapticPulse(uint32_t deviceId, uint32_t axisId, uint16_t durationMicroseconds, bool directMode, bool modal = true); diff --git a/lib_vrinputemulator/src/vrinputemulator.cpp b/lib_vrinputemulator/src/vrinputemulator.cpp index a8f2f472..df6338b3 100644 --- a/lib_vrinputemulator/src/vrinputemulator.cpp +++ b/lib_vrinputemulator/src/vrinputemulator.cpp @@ -1283,7 +1283,7 @@ void VRInputEmulator::getDeviceInfo(uint32_t deviceId, DeviceInfo & info) { _ipcPromiseMap.erase(messageId); } std::stringstream ss; - ss << "Error while enabling device offsets: "; + ss << "Error while getting device info: "; if (resp.status == ipc::ReplyStatus::Ok) { info.deviceId = resp.msg.dm_deviceInfo.deviceId; info.deviceClass = resp.msg.dm_deviceInfo.deviceClass; @@ -1329,7 +1329,7 @@ void VRInputEmulator::setDeviceNormalMode(uint32_t deviceId, bool modal) { _ipcPromiseMap.erase(messageId); } std::stringstream ss; - ss << "Error while enabling device offsets: "; + ss << "Error while setting normal mode: "; if (resp.status == ipc::ReplyStatus::InvalidId) { ss << "Invalid device id"; throw vrinputemulator_invalidid(ss.str()); @@ -1371,7 +1371,7 @@ void VRInputEmulator::setDeviceFakeDisconnectedMode(uint32_t deviceId, bool moda _ipcPromiseMap.erase(messageId); } std::stringstream ss; - ss << "Error while enabling device offsets: "; + ss << "Error while setting fake disconnection mode: "; if (resp.status == ipc::ReplyStatus::InvalidId) { ss << "Invalid device id"; throw vrinputemulator_invalidid(ss.str()); @@ -1414,7 +1414,7 @@ void VRInputEmulator::setDeviceRedictMode(uint32_t deviceId, uint32_t target, bo _ipcPromiseMap.erase(messageId); } std::stringstream ss; - ss << "Error while enabling device offsets: "; + ss << "Error while setting redirect mode: "; if (resp.status == ipc::ReplyStatus::InvalidId) { ss << "Invalid device id"; throw vrinputemulator_invalidid(ss.str()); @@ -1457,7 +1457,7 @@ void VRInputEmulator::setDeviceSwapMode(uint32_t deviceId, uint32_t target, bool _ipcPromiseMap.erase(messageId); } std::stringstream ss; - ss << "Error while enabling device offsets: "; + ss << "Error while setting swap mode: "; if (resp.status == ipc::ReplyStatus::InvalidId) { ss << "Invalid device id"; throw vrinputemulator_invalidid(ss.str()); @@ -1477,13 +1477,14 @@ void VRInputEmulator::setDeviceSwapMode(uint32_t deviceId, uint32_t target, bool } -void VRInputEmulator::setDeviceMotionCompensationMode(uint32_t deviceId, bool modal) { +void VRInputEmulator::setDeviceMotionCompensationMode(uint32_t deviceId, uint32_t velAccMode, bool modal) { if (_ipcServerQueue) { ipc::Request message(ipc::RequestType::DeviceManipulation_MotionCompensationMode); memset(&message.msg, 0, sizeof(message.msg)); message.msg.dm_MotionCompensationMode.clientId = m_clientId; message.msg.dm_MotionCompensationMode.messageId = 0; message.msg.dm_MotionCompensationMode.deviceId = deviceId; + message.msg.dm_MotionCompensationMode.velAccCompensationMode = velAccMode; if (modal) { uint32_t messageId = _ipcRandomDist(_ipcRandomDevice); message.msg.dm_MotionCompensationMode.messageId = messageId; @@ -1500,7 +1501,50 @@ void VRInputEmulator::setDeviceMotionCompensationMode(uint32_t deviceId, bool mo _ipcPromiseMap.erase(messageId); } std::stringstream ss; - ss << "Error while enabling device offsets: "; + ss << "Error while setting motion compensation mode: "; + if (resp.status == ipc::ReplyStatus::InvalidId) { + ss << "Invalid device id"; + throw vrinputemulator_invalidid(ss.str()); + } else if (resp.status == ipc::ReplyStatus::NotFound) { + ss << "Device not found"; + throw vrinputemulator_notfound(ss.str()); + } else if (resp.status != ipc::ReplyStatus::Ok) { + ss << "Error code " << (int)resp.status; + throw vrinputemulator_exception(ss.str()); + } + } else { + _ipcServerQueue->send(&message, sizeof(ipc::Request), 0); + } + } else { + throw vrinputemulator_connectionerror("No active connection."); + } +} + + +void VRInputEmulator::setMotionVelAccCompensationMode(uint32_t velAccMode, bool modal) { + if (_ipcServerQueue) { + ipc::Request message(ipc::RequestType::DeviceManipulation_SetMotionCompensationProperties); + memset(&message.msg, 0, sizeof(message.msg)); + message.msg.dm_SetMotionCompensationProperties.clientId = m_clientId; + message.msg.dm_SetMotionCompensationProperties.messageId = 0; + message.msg.dm_SetMotionCompensationProperties.velAccCompensationMode = velAccMode; + if (modal) { + uint32_t messageId = _ipcRandomDist(_ipcRandomDevice); + message.msg.dm_SetMotionCompensationProperties.messageId = messageId; + std::promise respPromise; + auto respFuture = respPromise.get_future(); + { + std::lock_guard lock(_mutex); + _ipcPromiseMap.insert({ messageId, std::move(respPromise) }); + } + _ipcServerQueue->send(&message, sizeof(ipc::Request), 0); + auto resp = respFuture.get(); + { + std::lock_guard lock(_mutex); + _ipcPromiseMap.erase(messageId); + } + std::stringstream ss; + ss << "Error while setting motion compensation properties: "; if (resp.status == ipc::ReplyStatus::InvalidId) { ss << "Invalid device id"; throw vrinputemulator_invalidid(ss.str()); @@ -1519,6 +1563,7 @@ void VRInputEmulator::setDeviceMotionCompensationMode(uint32_t deviceId, bool mo } } + void VRInputEmulator::triggerHapticPulse(uint32_t deviceId, uint32_t axisId, uint16_t durationMicroseconds, bool directMode, bool modal) { if (_ipcServerQueue) { ipc::Request message(ipc::RequestType::DeviceManipulation_TriggerHapticPulse);