Compare commits

...
3 Commits
Author SHA1 Message Date
Justin Li 8323d64b2d Support IVRServerDriverHost_006 2020-09-02 11:54:55 -04:00
Justin Li b04615dcb6 Store config in the registry 2020-04-08 00:25:01 -04:00
Justin Li 9d1fa11f98 Calibration speed setting, improve UI 2020-04-07 22:48:36 -04:00
6 changed files with 262 additions and 136 deletions

No files matched your search

+1 -1
View File
@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2018 Justin Li
Copyright (c) 2020 Justin Li
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+18 -18
View File
@@ -113,7 +113,7 @@ Eigen::Vector3d CalibrateRotation(const std::vector<Sample> &samples)
}
char buf[256];
snprintf(buf, sizeof buf, "Got %zd samples with %zd delta samples\n", samples.size(), deltas.size());
CalCtx.Message(buf);
CalCtx.Log(buf);
// Kabsch algorithm
@@ -155,7 +155,7 @@ Eigen::Vector3d CalibrateRotation(const std::vector<Sample> &samples)
Eigen::Vector3d euler = rot.eulerAngles(2, 1, 0) * 180.0 / EIGEN_PI;
snprintf(buf, sizeof buf, "Calibrated rotation: yaw=%.2f pitch=%.2f roll=%.2f\n", euler[1], euler[2], euler[0]);
CalCtx.Message(buf);
CalCtx.Log(buf);
return euler;
}
@@ -198,7 +198,7 @@ Eigen::Vector3d CalibrateTranslation(const std::vector<Sample> &samples)
char buf[256];
snprintf(buf, sizeof buf, "Calibrated translation x=%.2f y=%.2f z=%.2f\n", transcm[0], transcm[1], transcm[2]);
CalCtx.Message(buf);
CalCtx.Log(buf);
return transcm;
}
@@ -214,15 +214,15 @@ Sample CollectSample(const CalibrationContext &ctx)
bool ok = true;
if (!reference.bPoseIsValid)
{
CalCtx.Message("Reference device is not tracking\n"); ok = false;
CalCtx.Log("Reference device is not tracking\n"); ok = false;
}
if (!target.bPoseIsValid)
{
CalCtx.Message("Target device is not tracking\n"); ok = false;
CalCtx.Log("Target device is not tracking\n"); ok = false;
}
if (!ok)
{
CalCtx.Message("Aborting calibration!\n");
CalCtx.Log("Aborting calibration!\n");
CalCtx.state = CalibrationState::None;
return Sample();
}
@@ -361,7 +361,7 @@ void StartCalibration()
{
CalCtx.state = CalibrationState::Begin;
CalCtx.wantedUpdateInterval = 0.0;
CalCtx.messages = "";
CalCtx.messages.clear();
}
void CalibrationTick(double time)
@@ -406,26 +406,26 @@ void CalibrationTick(double time)
if (ctx.referenceID == -1)
{
CalCtx.Message("Missing reference device\n"); ok = false;
CalCtx.Log("Missing reference device\n"); ok = false;
}
else if (!ctx.devicePoses[ctx.referenceID].bPoseIsValid)
{
CalCtx.Message("Reference device is not tracking\n"); ok = false;
CalCtx.Log("Reference device is not tracking\n"); ok = false;
}
if (ctx.targetID == -1)
{
CalCtx.Message("Missing target device\n"); ok = false;
CalCtx.Log("Missing target device\n"); ok = false;
}
else if (!ctx.devicePoses[ctx.targetID].bPoseIsValid)
{
CalCtx.Message("Target device is not tracking\n"); ok = false;
CalCtx.Log("Target device is not tracking\n"); ok = false;
}
if (!ok)
{
ctx.state = CalibrationState::None;
CalCtx.Message("Aborting calibration!\n");
CalCtx.Log("Aborting calibration!\n");
return;
}
@@ -435,7 +435,7 @@ void CalibrationTick(double time)
char buf[256];
snprintf(buf, sizeof buf, "Starting calibration, referenceID=%d targetID=%d\n", ctx.referenceID, ctx.targetID);
CalCtx.Message(buf);
CalCtx.Log(buf);
return;
}
@@ -444,15 +444,15 @@ void CalibrationTick(double time)
{
return;
}
CalCtx.Message(".");
const int totalSamples = 100;
static std::vector<Sample> samples;
samples.push_back(sample);
if (samples.size() == totalSamples)
CalCtx.Progress(samples.size(), CalCtx.SampleCount());
if (samples.size() == CalCtx.SampleCount())
{
CalCtx.Message("\n");
CalCtx.Log("\n");
if (ctx.state == CalibrationState::Rotation)
{
ctx.calibratedRotation = CalibrateRotation(samples);
@@ -477,7 +477,7 @@ void CalibrationTick(double time)
ctx.validProfile = true;
SaveProfile(ctx);
CalCtx.Message("Finished calibration, profile saved\n");
CalCtx.Log("Finished calibration, profile saved\n");
ctx.state = CalibrationState::None;
}
+56 -8
View File
@@ -29,6 +29,14 @@ struct CalibrationContext
double timeLastTick = 0, timeLastScan = 0;
double wantedUpdateInterval = 1.0;
enum Speed
{
FAST = 0,
SLOW = 1,
VERY_SLOW = 2
};
Speed calibrationSpeed = FAST;
vr::TrackedDevicePose_t devicePoses[vr::k_unMaxTrackedDeviceCount];
struct Chaperone
@@ -40,14 +48,6 @@ struct CalibrationContext
vr::HmdVector2_t playSpaceSize;
} chaperone;
std::string messages;
void Message(const std::string &msg)
{
messages += msg;
std::cerr << msg;
}
void Clear()
{
chaperone.geometry.clear();
@@ -62,6 +62,54 @@ struct CalibrationContext
enabled = false;
validProfile = false;
}
size_t SampleCount()
{
switch (calibrationSpeed)
{
case FAST:
return 100;
case SLOW:
return 250;
case VERY_SLOW:
return 500;
}
return 100;
}
struct Message
{
enum Type
{
String,
Progress
} type = String;
Message(Type type) : type(type) { }
std::string str;
int progress, target;
};
std::vector<Message> messages;
void Log(const std::string &msg)
{
if (messages.empty() || messages.back().type == Message::Progress)
messages.push_back(Message(Message::String));
messages.back().str += msg;
std::cerr << msg;
}
void Progress(int current, int target)
{
if (messages.empty() || messages.back().type == Message::String)
messages.push_back(Message(Message::Progress));
messages.back().progress = current;
messages.back().target = target;
}
};
extern CalibrationContext CalCtx;
+86 -71
View File
@@ -9,19 +9,7 @@
#include <iomanip>
#include <limits>
static void UpgradeProfileV1(CalibrationContext &ctx);
static void ParseProfileV2(CalibrationContext &ctx, std::istream &stream);
static std::string ConfigFileName()
{
char cruntimePath[MAX_PATH] = { 0 };
unsigned int pathLen;
vr::VR_GetRuntimePath(cruntimePath, MAX_PATH, &pathLen);
std::string vrRuntimeConfigName(cruntimePath);
return vrRuntimeConfigName + "\\..\\..\\..\\config\\01spacecalibrator\\calibration.json";
}
picojson::array FloatArray(const float *buf, int numFloats)
static picojson::array FloatArray(const float *buf, int numFloats)
{
picojson::array arr;
@@ -31,7 +19,7 @@ picojson::array FloatArray(const float *buf, int numFloats)
return arr;
}
void LoadFloatArray(const picojson::value &obj, float *buf, int numFloats)
static void LoadFloatArray(const picojson::value &obj, float *buf, int numFloats)
{
if (!obj.is<picojson::array>())
throw std::runtime_error("expected array, got " + obj.to_str());
@@ -44,38 +32,7 @@ void LoadFloatArray(const picojson::value &obj, float *buf, int numFloats)
buf[i] = (float) arr[i].get<double>();
}
void LoadProfile(CalibrationContext &ctx)
{
ctx.validProfile = false;
std::ifstream file(ConfigFileName());
if (!file.good())
{
std::cout << "Profile missing, trying fallback path" << std::endl;
// Fallback to working directory, which was the default for a long time.
file = std::ifstream("openvr_space_calibration.json");
}
if (!file.good())
{
std::cout << "Fallback profile missing, trying V1 path" << std::endl;
UpgradeProfileV1(ctx);
return;
}
try
{
ParseProfileV2(ctx, file);
std::cout << "Loaded profile" << std::endl;
}
catch (const std::runtime_error &e)
{
std::cerr << "Error loading profile: " << e.what() << std::endl;
}
}
static void ParseProfileV2(CalibrationContext &ctx, std::istream &stream)
static void ParseProfile(CalibrationContext &ctx, std::istream &stream)
{
picojson::value v;
std::string err = picojson::parse(v, stream);
@@ -97,6 +54,9 @@ static void ParseProfileV2(CalibrationContext &ctx, std::istream &stream)
ctx.calibratedTranslation(1) = obj["y"].get<double>();
ctx.calibratedTranslation(2) = obj["z"].get<double>();
if (obj["calibration_speed"].is<double>())
ctx.calibrationSpeed = (CalibrationContext::Speed)(int) obj["calibration_speed"].get<double>();
if (obj["chaperone"].is<picojson::object>())
{
auto chaperone = obj["chaperone"].get<picojson::object>();
@@ -127,17 +87,10 @@ static void ParseProfileV2(CalibrationContext &ctx, std::istream &stream)
ctx.validProfile = true;
}
void SaveProfile(CalibrationContext &ctx)
static void WriteProfile(CalibrationContext &ctx, std::ostream &out)
{
std::cout << "Saving profile to " << ConfigFileName() << std::endl;
std::ofstream file(ConfigFileName());
if (!ctx.validProfile)
{
file << "[]";
return;
}
picojson::object profile;
profile["reference_tracking_system"].set<std::string>(ctx.referenceTrackingSystem);
@@ -149,6 +102,9 @@ void SaveProfile(CalibrationContext &ctx)
profile["y"].set<double>(ctx.calibratedTranslation(1));
profile["z"].set<double>(ctx.calibratedTranslation(2));
double speed = (int) ctx.calibrationSpeed;
profile["calibration_speed"].set<double>(speed);
if (ctx.chaperone.valid)
{
picojson::object chaperone;
@@ -177,30 +133,89 @@ void SaveProfile(CalibrationContext &ctx)
picojson::value profilesV;
profilesV.set<picojson::array>(profiles);
file << profilesV.serialize(true);
out << profilesV.serialize(true);
}
static void UpgradeProfileV1(CalibrationContext &ctx)
static void LogRegistryResult(LSTATUS result)
{
char *message;
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, 0, result, LANG_USER_DEFAULT, (LPSTR)&message, 0, NULL);
std::cerr << "Opening registry key: " << message << std::endl;
}
static const char *RegistryKey = "Software\\OpenVR-SpaceCalibrator";
static std::string ReadRegistryKey()
{
DWORD size = 0;
auto result = RegGetValueA(HKEY_CURRENT_USER_LOCAL_SETTINGS, RegistryKey, "Config", RRF_RT_REG_SZ, 0, 0, &size);
if (result != ERROR_SUCCESS)
{
LogRegistryResult(result);
return "";
}
std::string str;
str.resize(size);
result = RegGetValueA(HKEY_CURRENT_USER_LOCAL_SETTINGS, RegistryKey, "Config", RRF_RT_REG_SZ, 0, &str[0], &size);
if (result != ERROR_SUCCESS)
{
LogRegistryResult(result);
return "";
}
str.resize(size - 1);
return str;
}
static void WriteRegistryKey(std::string str)
{
HKEY hkey;
auto result = RegCreateKeyExA(HKEY_CURRENT_USER_LOCAL_SETTINGS, RegistryKey, 0, REG_NONE, 0, KEY_ALL_ACCESS, 0, &hkey, 0);
if (result != ERROR_SUCCESS)
{
LogRegistryResult(result);
return;
}
DWORD size = str.size() + 1;
result = RegSetValueExA(hkey, "Config", 0, REG_SZ, reinterpret_cast<const BYTE*>(str.c_str()), size);
if (result != ERROR_SUCCESS)
LogRegistryResult(result);
RegCloseKey(hkey);
}
void LoadProfile(CalibrationContext &ctx)
{
std::ifstream file("openvr_space_calibration.txt");
ctx.validProfile = false;
if (!file.good())
auto str = ReadRegistryKey();
if (str == "")
{
std::cout << "Profile is empty" << std::endl;
return;
}
file
>> ctx.targetTrackingSystem
>> ctx.calibratedRotation(1) // yaw
>> ctx.calibratedRotation(2) // pitch
>> ctx.calibratedRotation(0) // roll
>> ctx.calibratedTranslation(0) // x
>> ctx.calibratedTranslation(1) // y
>> ctx.calibratedTranslation(2); // z
try
{
std::stringstream io(str);
ParseProfile(ctx, io);
std::cout << "Loaded profile" << std::endl;
}
catch (const std::runtime_error &e)
{
std::cerr << "Error loading profile: " << e.what() << std::endl;
}
}
ctx.validProfile = true;
void SaveProfile(CalibrationContext &ctx)
{
std::cout << "Saving profile to registry" << std::endl;
SaveProfile(ctx);
file.close();
std::remove("openvr_space_calibration.txt");
std::stringstream io;
WriteProfile(ctx, io);
WriteRegistryKey(io.str());
}
+74 -32
View File
@@ -9,7 +9,7 @@
#include <algorithm>
#include <imgui/imgui.h>
#define VERSION_STRING "0.9.0"
#define VERSION_STRING "1.1"
struct VRDevice
{
@@ -33,31 +33,41 @@ VRState LoadVRState();
void BuildSystemSelection(const VRState &state);
void BuildDeviceSelections(const VRState &state);
void BuildProfileEditor();
void BuildMenu(bool runningInOverlay);
static const ImGuiWindowFlags bareWindowFlags =
ImGuiWindowFlags_NoTitleBar |
ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_NoMove;
void BuildMainWindow(bool runningInOverlay)
{
ImGuiWindowFlags windowFlags =
ImGuiWindowFlags_NoTitleBar |
ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_NoMove;
auto &io = ImGui::GetIO();
ImGui::SetNextWindowPos(ImVec2(0.0f, 0.0f), ImGuiSetCond_Always);
ImGui::SetNextWindowSize(io.DisplaySize, ImGuiSetCond_Always);
if (!ImGui::Begin("MainWindow", nullptr, windowFlags))
if (!ImGui::Begin("MainWindow", nullptr, bareWindowFlags))
{
ImGui::End();
ImGui::End();
return;
}
ImGuiStyle &style = ImGui::GetStyle();
ImGui::PushStyleColor(ImGuiCol_PlotHistogram, ImGui::GetStyleColorVec4(ImGuiCol_Button));
auto state = LoadVRState();
BuildSystemSelection(state);
BuildDeviceSelections(state);
BuildMenu(runningInOverlay);
ImGui::PopStyleColor();
ImGui::End();
}
void BuildMenu(bool runningInOverlay)
{
auto &io = ImGui::GetIO();
ImGuiStyle &style = ImGui::GetStyle();
ImGui::Text("");
if (CalCtx.state == CalibrationState::None)
@@ -125,6 +135,26 @@ void BuildMainWindow(bool runningInOverlay)
SaveProfile(CalCtx);
}
}
ImGui::Text("");
auto speed = CalCtx.calibrationSpeed;
ImGui::Columns(4, NULL, false);
ImGui::Text("Calibration Speed");
ImGui::NextColumn();
if (ImGui::RadioButton(" Fast ", speed == CalibrationContext::FAST))
CalCtx.calibrationSpeed = CalibrationContext::FAST;
ImGui::NextColumn();
if (ImGui::RadioButton(" Slow ", speed == CalibrationContext::SLOW))
CalCtx.calibrationSpeed = CalibrationContext::SLOW;
ImGui::NextColumn();
if (ImGui::RadioButton(" Very Slow ", speed == CalibrationContext::VERY_SLOW))
CalCtx.calibrationSpeed = CalibrationContext::VERY_SLOW;
ImGui::Columns(1);
}
else if (CalCtx.state == CalibrationState::Editing)
{
@@ -141,22 +171,6 @@ void BuildMainWindow(bool runningInOverlay)
ImGui::Button("Calibration in progress...", ImVec2(ImGui::GetWindowContentRegionWidth(), ImGui::GetTextLineHeight() * 2));
}
ImGui::SetNextWindowPos(ImVec2(20.0f, 20.0f), ImGuiSetCond_Always);
ImGui::SetNextWindowSize(ImVec2(io.DisplaySize.x - 40.0f, io.DisplaySize.y - 40.0f), ImGuiSetCond_Always);
if (ImGui::BeginPopupModal("Calibration Progress", nullptr, windowFlags))
{
ImGui::TextWrapped(CalCtx.messages.c_str());
if (CalCtx.state == CalibrationState::None)
{
ImGui::Text("");
if (ImGui::Button("Close", ImVec2(ImGui::GetWindowContentRegionWidth(), ImGui::GetTextLineHeight() * 2)))
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
ImGui::SetNextWindowPos(ImVec2(10.0f, ImGui::GetWindowHeight() - ImGui::GetItemsLineHeightWithSpacing()));
ImGui::BeginChild("bottom line", ImVec2(ImGui::GetWindowWidth() - 20.0f, ImGui::GetItemsLineHeightWithSpacing() * 2), false);
ImGui::Text("OpenVR Space Calibrator v" VERSION_STRING " - by tach/pushrax");
@@ -167,7 +181,38 @@ void BuildMainWindow(bool runningInOverlay)
}
ImGui::EndChild();
ImGui::End();
ImGui::SetNextWindowPos(ImVec2(20.0f, 20.0f), ImGuiSetCond_Always);
ImGui::SetNextWindowSize(ImVec2(io.DisplaySize.x - 40.0f, io.DisplaySize.y - 40.0f), ImGuiSetCond_Always);
if (ImGui::BeginPopupModal("Calibration Progress", nullptr, bareWindowFlags))
{
ImGui::PushStyleColor(ImGuiCol_FrameBg, (ImVec4)ImColor(0, 0, 0));
for (auto &message : CalCtx.messages)
{
switch (message.type)
{
case CalibrationContext::Message::String:
ImGui::TextWrapped(message.str.c_str());
break;
case CalibrationContext::Message::Progress:
float fraction = (float)message.progress / (float)message.target;
ImGui::Text("");
ImGui::ProgressBar(fraction, ImVec2(-1.0f, 0.0f), "");
ImGui::SetCursorPosY(ImGui::GetCursorPosY() - ImGui::GetFontSize() - style.FramePadding.y * 2);
ImGui::Text(" %d%%", (int)(fraction * 100));
break;
}
}
ImGui::PopStyleColor();
if (CalCtx.state == CalibrationState::None)
{
ImGui::Text("");
if (ImGui::Button("Close", ImVec2(ImGui::GetWindowContentRegionWidth(), ImGui::GetTextLineHeight() * 2)))
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
}
void BuildSystemSelection(const VRState &state)
@@ -233,13 +278,14 @@ void BuildSystemSelection(const VRState &state)
}
ImGui::SameLine();
ImGui::PushItemWidth(paneWidth);
ImGui::Combo("##TargetTrackingSystem", &currentTargetSystem, &targetSystems[0], (int) targetSystems.size());
if (currentTargetSystem != -1 && currentTargetSystem < targetSystems.size())
{
CalCtx.targetTrackingSystem = std::string(targetSystems[currentTargetSystem]);
}
ImGui::PopItemWidth();
}
void AppendSeparated(std::string &buffer, const std::string &suffix)
@@ -428,10 +474,8 @@ void BuildProfileEditor()
ImGui::PushItemWidth(widthF);
ImGui::InputDouble("##Yaw", &CalCtx.calibratedRotation(1), 0.1, 1.0, "%.8f");
ImGui::SameLine();
ImGui::PushItemWidth(widthF);
ImGui::InputDouble("##Pitch", &CalCtx.calibratedRotation(2), 0.1, 1.0, "%.8f");
ImGui::SameLine();
ImGui::PushItemWidth(widthF);
ImGui::InputDouble("##Roll", &CalCtx.calibratedRotation(0), 0.1, 1.0, "%.8f");
TextWithWidth("XLabel", "X", width);
@@ -440,14 +484,12 @@ void BuildProfileEditor()
ImGui::SameLine();
TextWithWidth("ZLabel", "Z", width);
ImGui::PushItemWidth(widthF);
ImGui::InputDouble("##X", &CalCtx.calibratedTranslation(0), 1.0, 10.0, "%.8f");
ImGui::SameLine();
ImGui::PushItemWidth(widthF);
ImGui::InputDouble("##Y", &CalCtx.calibratedTranslation(1), 1.0, 10.0, "%.8f");
ImGui::SameLine();
ImGui::PushItemWidth(widthF);
ImGui::InputDouble("##Z", &CalCtx.calibratedTranslation(2), 1.0, 10.0, "%.8f");
ImGui::PopItemWidth();
}
void TextWithWidth(const char *label, const char *text, float width)
@@ -9,15 +9,28 @@ static Hook<void*(*)(vr::IVRDriverContext *, const char *, vr::EVRInitError *)>
GetGenericInterfaceHook("IVRDriverContext::GetGenericInterface");
static Hook<void(*)(vr::IVRServerDriverHost *, uint32_t, const vr::DriverPose_t &, uint32_t)>
TrackedDevicePoseUpdatedHook("IVRServerDriverHost005::TrackedDevicePoseUpdated");
TrackedDevicePoseUpdatedHook005("IVRServerDriverHost005::TrackedDevicePoseUpdated");
static void DetourTrackedDevicePoseUpdated(vr::IVRServerDriverHost *_this, uint32_t unWhichDevice, const vr::DriverPose_t &newPose, uint32_t unPoseStructSize)
static Hook<void(*)(vr::IVRServerDriverHost *, uint32_t, const vr::DriverPose_t &, uint32_t)>
TrackedDevicePoseUpdatedHook006("IVRServerDriverHost006::TrackedDevicePoseUpdated");
static void DetourTrackedDevicePoseUpdated005(vr::IVRServerDriverHost *_this, uint32_t unWhichDevice, const vr::DriverPose_t &newPose, uint32_t unPoseStructSize)
{
//TRACE("ServerTrackedDeviceProvider::DetourTrackedDevicePoseUpdated(%d)", unWhichDevice);
auto pose = newPose;
if (Driver->HandleDevicePoseUpdated(unWhichDevice, pose))
{
TrackedDevicePoseUpdatedHook.originalFunc(_this, unWhichDevice, pose, unPoseStructSize);
TrackedDevicePoseUpdatedHook005.originalFunc(_this, unWhichDevice, pose, unPoseStructSize);
}
}
static void DetourTrackedDevicePoseUpdated006(vr::IVRServerDriverHost *_this, uint32_t unWhichDevice, const vr::DriverPose_t &newPose, uint32_t unPoseStructSize)
{
//TRACE("ServerTrackedDeviceProvider::DetourTrackedDevicePoseUpdated(%d)", unWhichDevice);
auto pose = newPose;
if (Driver->HandleDevicePoseUpdated(unWhichDevice, pose))
{
TrackedDevicePoseUpdatedHook006.originalFunc(_this, unWhichDevice, pose, unPoseStructSize);
}
}
@@ -29,10 +42,18 @@ static void *DetourGetGenericInterface(vr::IVRDriverContext *_this, const char *
std::string iface(pchInterfaceVersion);
if (iface == "IVRServerDriverHost_005")
{
if (!IHook::Exists(TrackedDevicePoseUpdatedHook.name))
if (!IHook::Exists(TrackedDevicePoseUpdatedHook005.name))
{
TrackedDevicePoseUpdatedHook.CreateHookInObjectVTable(originalInterface, 1, &DetourTrackedDevicePoseUpdated);
IHook::Register(&TrackedDevicePoseUpdatedHook);
TrackedDevicePoseUpdatedHook005.CreateHookInObjectVTable(originalInterface, 1, &DetourTrackedDevicePoseUpdated005);
IHook::Register(&TrackedDevicePoseUpdatedHook005);
}
}
else if (iface == "IVRServerDriverHost_006")
{
if (!IHook::Exists(TrackedDevicePoseUpdatedHook006.name))
{
TrackedDevicePoseUpdatedHook006.CreateHookInObjectVTable(originalInterface, 1, &DetourTrackedDevicePoseUpdated006);
IHook::Register(&TrackedDevicePoseUpdatedHook006);
}
}