mirror of
https://github.com/hyblocker/OpenVR-SpaceCalibrator.git
synced 2026-10-08 19:00:22 +02:00
256 lines
8.7 KiB
C++
256 lines
8.7 KiB
C++
#include "ServerTrackedDeviceProvider.h"
|
|
#include "Logging.h"
|
|
#include "InterfaceHookInjector.h"
|
|
#include "IsometryTransform.h"
|
|
|
|
#include <random>
|
|
|
|
vr::EVRInitError ServerTrackedDeviceProvider::Init(vr::IVRDriverContext *pDriverContext)
|
|
{
|
|
TRACE("ServerTrackedDeviceProvider::Init()");
|
|
VR_INIT_SERVER_DRIVER_CONTEXT(pDriverContext);
|
|
|
|
memset(transforms, 0, vr::k_unMaxTrackedDeviceCount * sizeof DeviceTransform);
|
|
memset(&alignmentSpeedParams, 0, sizeof alignmentSpeedParams);
|
|
|
|
alignmentSpeedParams.thr_rot_tiny = 0.1f * (EIGEN_PI / 180.0f);
|
|
alignmentSpeedParams.thr_rot_small = 1.0f * (EIGEN_PI / 180.0f);
|
|
alignmentSpeedParams.thr_rot_large = 5.0f * (EIGEN_PI / 180.0f);
|
|
|
|
alignmentSpeedParams.thr_trans_tiny = 0.1f / 1000.0; // mm
|
|
alignmentSpeedParams.thr_trans_small = 1.0f / 1000.0; // mm
|
|
alignmentSpeedParams.thr_trans_large = 20.0f / 1000.0; // mm
|
|
|
|
alignmentSpeedParams.align_speed_tiny = 0.05f;
|
|
alignmentSpeedParams.align_speed_small = 0.2f;
|
|
alignmentSpeedParams.align_speed_large = 2.0f;
|
|
|
|
InjectHooks(this, pDriverContext);
|
|
server.Run();
|
|
shmem.Create(OPENVR_SPACECALIBRATOR_SHMEM_NAME);
|
|
|
|
debugTransform = Eigen::Vector3d::Zero();
|
|
debugRotation = Eigen::Quaterniond::Identity();
|
|
|
|
return vr::VRInitError_None;
|
|
}
|
|
|
|
void ServerTrackedDeviceProvider::Cleanup()
|
|
{
|
|
TRACE("ServerTrackedDeviceProvider::Cleanup()");
|
|
server.Stop();
|
|
shmem.Close();
|
|
DisableHooks();
|
|
VR_CLEANUP_SERVER_DRIVER_CONTEXT();
|
|
}
|
|
|
|
namespace {
|
|
|
|
|
|
vr::HmdQuaternion_t convert(const Eigen::Quaterniond& q) {
|
|
vr::HmdQuaternion_t result;
|
|
result.w = q.w();
|
|
result.x = q.x();
|
|
result.y = q.y();
|
|
result.z = q.z();
|
|
return result;
|
|
}
|
|
|
|
vr::HmdVector3_t convert(const Eigen::Vector3d& v) {
|
|
vr::HmdVector3_t result;
|
|
result.v[0] = (float) v.x();
|
|
result.v[1] = (float) v.y();
|
|
result.v[2] = (float) v.z();
|
|
return result;
|
|
}
|
|
|
|
Eigen::Quaterniond convert(const vr::HmdQuaternion_t& q) {
|
|
return Eigen::Quaterniond(q.w, q.x, q.y, q.z);
|
|
}
|
|
|
|
Eigen::Vector3d convert(const vr::HmdVector3d_t& v) {
|
|
return Eigen::Vector3d(v.v[0], v.v[1], v.v[2]);
|
|
}
|
|
|
|
Eigen::Vector3d convert(const double* arr) {
|
|
return Eigen::Vector3d(arr[0], arr[1], arr[2]);
|
|
}
|
|
|
|
IsoTransform toIsoWorldTransform(const vr::DriverPose_t& pose) {
|
|
Eigen::Quaterniond rot(pose.qWorldFromDriverRotation.w, pose.qWorldFromDriverRotation.x, pose.qWorldFromDriverRotation.y, pose.qWorldFromDriverRotation.z);
|
|
Eigen::Vector3d trans(pose.vecWorldFromDriverTranslation[0], pose.vecWorldFromDriverTranslation[1], pose.vecWorldFromDriverTranslation[2]);
|
|
|
|
return IsoTransform(rot, trans);
|
|
}
|
|
|
|
IsoTransform toIsoPose(const vr::DriverPose_t& pose) {
|
|
auto worldXform = toIsoWorldTransform(pose);
|
|
|
|
Eigen::Quaterniond rot(pose.qRotation.w, pose.qRotation.x, pose.qRotation.y, pose.qRotation.z);
|
|
Eigen::Vector3d trans(pose.vecPosition[0], pose.vecPosition[1], pose.vecPosition[2]);
|
|
|
|
return worldXform * IsoTransform(rot, trans);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* This function heuristically evaluates the amount of drift between the src and target playspace transforms,
|
|
* evaluated centered on the `pose` device transform. This is then used to control the speed of realignment.
|
|
*/
|
|
ServerTrackedDeviceProvider::DeltaSize ServerTrackedDeviceProvider::GetTransformDeltaSize(
|
|
DeltaSize prior_delta,
|
|
const IsoTransform& deviceWorldPose,
|
|
const IsoTransform& src,
|
|
const IsoTransform& target
|
|
) const {
|
|
const auto src_pose = src * deviceWorldPose;
|
|
const auto target_pose = target * deviceWorldPose;
|
|
|
|
const auto trans_delta = (src_pose.translation - target_pose.translation).squaredNorm();
|
|
const auto rot_delta = src_pose.rotation.angularDistance(target_pose.rotation);
|
|
|
|
DeltaSize trans_level, rot_level;
|
|
|
|
if (trans_delta > alignmentSpeedParams.thr_trans_large) trans_level = DeltaSize::LARGE;
|
|
else if (trans_delta > alignmentSpeedParams.thr_trans_small) trans_level = DeltaSize::SMALL;
|
|
else trans_level = DeltaSize::TINY;
|
|
|
|
if (rot_delta > alignmentSpeedParams.thr_rot_large) rot_level = DeltaSize::LARGE;
|
|
else if (rot_delta > alignmentSpeedParams.thr_rot_small) rot_level = DeltaSize::SMALL;
|
|
else rot_level = DeltaSize::TINY;
|
|
|
|
if (trans_level == DeltaSize::TINY && rot_level == DeltaSize::TINY) return DeltaSize::TINY;
|
|
else return max(prior_delta, max(trans_level, rot_level));
|
|
}
|
|
|
|
double ServerTrackedDeviceProvider::GetTransformRate(DeltaSize delta) const {
|
|
switch (delta) {
|
|
case DeltaSize::TINY: return alignmentSpeedParams.align_speed_tiny;
|
|
case DeltaSize::SMALL: return alignmentSpeedParams.align_speed_small;
|
|
default: return alignmentSpeedParams.align_speed_large;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Smoothly interpolates the device active transform towards the target transform.
|
|
*/
|
|
void ServerTrackedDeviceProvider::BlendTransform(DeviceTransform& device, const IsoTransform &deviceWorldPose) const {
|
|
LARGE_INTEGER timestamp, freq;
|
|
QueryPerformanceCounter(×tamp);
|
|
QueryPerformanceFrequency(&freq);
|
|
|
|
double lerp = (timestamp.QuadPart - device.lastPoll.QuadPart) / (double)freq.QuadPart;
|
|
device.lastPoll = timestamp;
|
|
|
|
lerp *= GetTransformRate(device.currentRate);
|
|
if (lerp > 1.0)
|
|
lerp = 1.0;
|
|
if (lerp < 0 || isnan(lerp))
|
|
lerp = 0;
|
|
|
|
device.transform = device.transform.interpolateAround(lerp, device.targetTransform, deviceWorldPose.translation);
|
|
}
|
|
|
|
void ServerTrackedDeviceProvider::ApplyTransform(DeviceTransform& device, vr::DriverPose_t& devicePose) const {
|
|
auto deviceWorldTransform = toIsoWorldTransform(devicePose);
|
|
deviceWorldTransform = device.transform * deviceWorldTransform;
|
|
devicePose.vecWorldFromDriverTranslation[0] = deviceWorldTransform.translation(0);
|
|
devicePose.vecWorldFromDriverTranslation[1] = deviceWorldTransform.translation(1);
|
|
devicePose.vecWorldFromDriverTranslation[2] = deviceWorldTransform.translation(2);
|
|
devicePose.qWorldFromDriverRotation = convert(deviceWorldTransform.rotation);
|
|
}
|
|
|
|
|
|
inline 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.y * rhs.w) + (lhs.z * rhs.x) - (lhs.x * rhs.z),
|
|
(lhs.w * rhs.z) + (lhs.z * rhs.w) + (lhs.x * rhs.y) - (lhs.y * rhs.x)
|
|
};
|
|
}
|
|
|
|
inline vr::HmdVector3d_t quaternionRotateVector(const vr::HmdQuaternion_t& quat, const double(&vector)[3]) {
|
|
vr::HmdQuaternion_t vectorQuat = { 0.0, vector[0], vector[1] , vector[2] };
|
|
vr::HmdQuaternion_t conjugate = { quat.w, -quat.x, -quat.y, -quat.z };
|
|
auto rotatedVectorQuat = quat * vectorQuat * conjugate;
|
|
return { rotatedVectorQuat.x, rotatedVectorQuat.y, rotatedVectorQuat.z };
|
|
}
|
|
|
|
void ServerTrackedDeviceProvider::SetDeviceTransform(const protocol::SetDeviceTransform& newTransform)
|
|
{
|
|
auto &tf = transforms[newTransform.openVRID];
|
|
tf.enabled = newTransform.enabled;
|
|
|
|
if (newTransform.updateTranslation) {
|
|
tf.targetTransform.translation = convert(newTransform.translation);
|
|
if (!newTransform.lerp) {
|
|
tf.transform.translation = tf.targetTransform.translation;
|
|
}
|
|
}
|
|
|
|
if (newTransform.updateRotation) {
|
|
tf.targetTransform.rotation = convert(newTransform.rotation);
|
|
|
|
if (!newTransform.lerp) {
|
|
tf.transform.rotation = tf.targetTransform.rotation;
|
|
}
|
|
}
|
|
|
|
if (newTransform.updateScale)
|
|
tf.scale = newTransform.scale;
|
|
|
|
tf.quash = newTransform.quash;
|
|
}
|
|
|
|
bool ServerTrackedDeviceProvider::HandleDevicePoseUpdated(uint32_t openVRID, vr::DriverPose_t &pose)
|
|
{
|
|
// Apply debug pose before anything else
|
|
if (openVRID > 0) {
|
|
auto dbgPos = convert(pose.vecPosition) + debugTransform;
|
|
auto dbgRot = convert(pose.qRotation) * debugRotation;
|
|
pose.qRotation = convert(dbgRot);
|
|
pose.vecPosition[0] = dbgPos(0);
|
|
pose.vecPosition[1] = dbgPos(1);
|
|
pose.vecPosition[2] = dbgPos(2);
|
|
}
|
|
|
|
shmem.SetPose(openVRID, pose);
|
|
|
|
auto& tf = transforms[openVRID];
|
|
|
|
if (tf.quash) {
|
|
pose.vecPosition[0] = -pose.vecWorldFromDriverTranslation[0];
|
|
pose.vecPosition[1] = -pose.vecWorldFromDriverTranslation[1] + 9001; // put it 9001m above the origin
|
|
pose.vecPosition[2] = -pose.vecWorldFromDriverTranslation[2];
|
|
} else if (tf.enabled)
|
|
{
|
|
pose.vecPosition[0] *= tf.scale;
|
|
pose.vecPosition[1] *= tf.scale;
|
|
pose.vecPosition[2] *= tf.scale;
|
|
|
|
auto deviceWorldPose = toIsoPose(pose);
|
|
tf.currentRate = GetTransformDeltaSize(tf.currentRate, deviceWorldPose, tf.transform, tf.targetTransform);
|
|
double lerp = GetTransformRate(tf.currentRate);
|
|
|
|
BlendTransform(tf, deviceWorldPose);
|
|
ApplyTransform(tf, pose);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void ServerTrackedDeviceProvider::HandleApplyRandomOffset() {
|
|
std::random_device gen;
|
|
std::uniform_real_distribution<double> d(-1, 1);
|
|
auto init = Eigen::Vector3d(d(gen), d(gen), d(gen));
|
|
auto posOffset = init * 0.25f;
|
|
|
|
debugTransform = posOffset;
|
|
debugRotation = Eigen::Quaterniond::Identity();
|
|
|
|
std::ostringstream oss;
|
|
oss << "Applied random offset: " << posOffset << " from init " << init << std::endl;
|
|
LOG("%s", oss.str().c_str());
|
|
} |