Compare commits

..
20 Commits
Author SHA1 Message Date
Justin Li 8e6796a9b5 Log device info when starting calibration 2020-10-25 15:05:02 -04:00
Justin Li 6389d745ae Update issue templates 2020-10-12 17:15:03 -04:00
Justin Li 326b54ba74 Update issue templates 2020-10-12 17:13:51 -04:00
Justin Li 98b23ad5bb Update README.md 2020-09-10 22:20:41 -04:00
Justin Li 7788c2af38 Update README.md 2020-09-08 20:15:28 -04:00
Justin Li 5334084508 Update README.md 2020-09-08 20:12:20 -04:00
Justin Li 9752a3be05 Update README for v1.1 2020-09-08 20:04:09 -04:00
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
Justin Li ab5e80a8ef v0.9 2020-04-06 00:25:54 -04:00
Justin Li ffb301b38c Disable profile when HMD is not the calibration reference, Clear button saves profile 2020-04-06 00:25:18 -04:00
Justin Li 9744585cf0 Merge pull request #17 from direct-configuration 2020-04-05 23:06:43 -04:00
Justin Li 7cf363e315 Reduce duplication and remove dead code 2020-04-05 23:04:45 -04:00
Dj lukis.LT 9c4a906ec0 Also apply calibration to TrackingReference devices
Been running Rift CV1 with Steamvr 2.0 basestations for a while and did not run into any problems so far even when purely on Index. The lighthouse driver station poses update on their (re)initialisation or some speciffic(dynamic recalibration?) cases and not immediately.
2020-04-05 21:07:07 -04:00
Dj lukis.LT d6030195f9 Set ActivateMultipleDrivers flag via API, Increment version
Left old config file method as a renamed launch command
2020-04-05 21:07:07 -04:00
Dj lukis.LT c607fa30c4 Update to OpenVR v1.10.30 2020-04-05 21:06:51 -04:00
Justin Li eebf2877f3 Update README for 0.8 2019-03-03 22:27:23 -05:00
Justin Li 7f60358eac Release v0.8 2019-03-03 15:42:55 -05:00
Justin Li 85c6b3cea4 Add chaperone bounds tool, fix rendering when launched minimized 2019-03-03 15:37:23 -05:00
16 changed files with 4058 additions and 753 deletions

No files matched your search

+14
View File
@@ -0,0 +1,14 @@
---
name: Bug report
about: Something is broken
title: ''
labels: bug
assignees: ''
---
<!--
If you are having trouble setting this up with SteamVR and would like help, the fastest way is to ask in the Discord group. https://discord.com/invite/m7g2Wyj
If you are having issues after updating SteamVR, please check for a new release at https://github.com/pushrax/OpenVR-SpaceCalibrator/releases before posting an issue.
-->
+10
View File
@@ -0,0 +1,10 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: ''
---
+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
+84 -29
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();
}
@@ -273,9 +273,12 @@ void ResetAndDisableOffsets(uint32_t id)
Driver.SendBlocking(req);
}
static_assert(vr::k_unTrackedDeviceIndex_Hmd == 0, "HMD index expected to be 0");
void ScanAndApplyProfile(CalibrationContext &ctx)
{
char buffer[vr::k_unMaxPropertyStringSize];
ctx.enabled = ctx.validProfile;
for (uint32_t id = 0; id < vr::k_unMaxTrackedDeviceCount; ++id)
{
@@ -292,20 +295,12 @@ void ScanAndApplyProfile(CalibrationContext &ctx)
continue;
}*/
if (!ctx.validProfile)
if (!ctx.enabled)
{
ResetAndDisableOffsets(id);
continue;
}
if (deviceClass == vr::TrackedDeviceClass_TrackingReference || deviceClass == vr::TrackedDeviceClass_HMD)
{
//auto p = ctx.devicePoses[id].mDeviceToAbsoluteTracking.m;
//printf("REF %d: %f %f %f\n", id, p[0][3], p[1][3], p[2][3]);
ResetAndDisableOffsets(id);
continue;
}
vr::ETrackedPropertyError err = vr::TrackedProp_Success;
vr::VRSystem()->GetStringTrackedDeviceProperty(id, vr::Prop_TrackingSystemName_String, buffer, vr::k_unMaxPropertyStringSize, &err);
@@ -317,6 +312,21 @@ void ScanAndApplyProfile(CalibrationContext &ctx)
std::string trackingSystem(buffer);
if (id == vr::k_unTrackedDeviceIndex_Hmd)
{
//auto p = ctx.devicePoses[id].mDeviceToAbsoluteTracking.m;
//printf("HMD %d: %f %f %f\n", id, p[0][3], p[1][3], p[2][3]);
if (trackingSystem != ctx.referenceTrackingSystem)
{
// Currently using an HMD with a different tracking system than the calibration.
ctx.enabled = false;
}
ResetAndDisableOffsets(id);
continue;
}
if (trackingSystem != ctx.targetTrackingSystem)
{
ResetAndDisableOffsets(id);
@@ -332,13 +342,26 @@ void ScanAndApplyProfile(CalibrationContext &ctx)
};
Driver.SendBlocking(req);
}
if (ctx.enabled && ctx.chaperone.valid && ctx.chaperone.autoApply)
{
uint32_t quadCount = 0;
vr::VRChaperoneSetup()->GetLiveCollisionBoundsInfo(nullptr, &quadCount);
// Heuristic: when SteamVR resets to a blank-ish chaperone, it uses empty geometry,
// but manual adjustments (e.g. via a play space mover) will not touch geometry.
if (quadCount != ctx.chaperone.geometry.size())
{
ApplyChaperoneBounds();
}
}
}
void StartCalibration()
{
CalCtx.state = CalibrationState::Begin;
CalCtx.wantedUpdateInterval = 0.0;
CalCtx.messages = "";
CalCtx.messages.clear();
}
void CalibrationTick(double time)
@@ -381,28 +404,38 @@ void CalibrationTick(double time)
{
bool ok = true;
char referenceSerial[256], targetSerial[256];
vr::VRSystem()->GetStringTrackedDeviceProperty(ctx.referenceID, vr::Prop_SerialNumber_String, referenceSerial, 256);
vr::VRSystem()->GetStringTrackedDeviceProperty(ctx.targetID, vr::Prop_SerialNumber_String, targetSerial, 256);
char buf[256];
snprintf(buf, sizeof buf, "Reference device ID: %d, serial: %s\n", ctx.referenceID, referenceSerial);
CalCtx.Log(buf);
snprintf(buf, sizeof buf, "Target device ID: %d, serial %s\n", ctx.targetID, targetSerial);
CalCtx.Log(buf);
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;
}
@@ -410,9 +443,7 @@ void CalibrationTick(double time)
ctx.state = CalibrationState::Rotation;
ctx.wantedUpdateInterval = 0.0;
char buf[256];
snprintf(buf, sizeof buf, "Starting calibration, referenceID=%d targetID=%d\n", ctx.referenceID, ctx.targetID);
CalCtx.Message(buf);
CalCtx.Log("Starting calibration...\n");
return;
}
@@ -421,15 +452,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);
@@ -452,8 +483,9 @@ void CalibrationTick(double time)
req.setDeviceTransform = { ctx.targetID, true, vrTrans };
Driver.SendBlocking(req);
ctx.validProfile = true;
SaveProfile(ctx);
CalCtx.Message("Finished calibration, profile saved\n");
CalCtx.Log("Finished calibration, profile saved\n");
ctx.state = CalibrationState::None;
}
@@ -461,3 +493,26 @@ void CalibrationTick(double time)
samples.clear();
}
}
void LoadChaperoneBounds()
{
vr::VRChaperoneSetup()->RevertWorkingCopy();
uint32_t quadCount = 0;
vr::VRChaperoneSetup()->GetLiveCollisionBoundsInfo(nullptr, &quadCount);
CalCtx.chaperone.geometry.resize(quadCount);
vr::VRChaperoneSetup()->GetLiveCollisionBoundsInfo(&CalCtx.chaperone.geometry[0], &quadCount);
vr::VRChaperoneSetup()->GetWorkingStandingZeroPoseToRawTrackingPose(&CalCtx.chaperone.standingCenter);
vr::VRChaperoneSetup()->GetWorkingPlayAreaSize(&CalCtx.chaperone.playSpaceSize.v[0], &CalCtx.chaperone.playSpaceSize.v[1]);
CalCtx.chaperone.valid = true;
}
void ApplyChaperoneBounds()
{
vr::VRChaperoneSetup()->RevertWorkingCopy();
vr::VRChaperoneSetup()->SetWorkingCollisionBoundsInfo(&CalCtx.chaperone.geometry[0], CalCtx.chaperone.geometry.size());
vr::VRChaperoneSetup()->SetWorkingStandingZeroPoseToRawTrackingPose(&CalCtx.chaperone.standingCenter);
vr::VRChaperoneSetup()->SetWorkingPlayAreaSize(CalCtx.chaperone.playSpaceSize.v[0], CalCtx.chaperone.playSpaceSize.v[1]);
vr::VRChaperoneSetup()->CommitWorkingCopy(vr::EChaperoneConfigFile_Live);
}
+81 -5
View File
@@ -2,6 +2,7 @@
#include <Eigen/Core>
#include <openvr.h>
#include <vector>
enum class CalibrationState
{
@@ -23,23 +24,98 @@ struct CalibrationContext
std::string referenceTrackingSystem;
std::string targetTrackingSystem;
bool enabled = false;
bool validProfile = false;
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];
std::string messages;
void Message(const std::string &msg)
struct Chaperone
{
messages += msg;
bool valid = false;
bool autoApply = true;
std::vector<vr::HmdQuad_t> geometry;
vr::HmdMatrix34_t standingCenter;
vr::HmdVector2_t playSpaceSize;
} chaperone;
void Clear()
{
chaperone.geometry.clear();
chaperone.standingCenter = vr::HmdMatrix34_t();
chaperone.playSpaceSize = vr::HmdVector2_t();
chaperone.valid = false;
calibratedRotation = Eigen::Vector3d();
calibratedTranslation = Eigen::Vector3d();
referenceTrackingSystem = "";
targetTrackingSystem = "";
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;
void InitCalibrator();
void CalibrationTick(double time);
void StartCalibration();
void StartCalibration();
void LoadChaperoneBounds();
void ApplyChaperoneBounds();
+145 -92
View File
@@ -9,47 +9,30 @@
#include <iomanip>
#include <limits>
static void UpgradeProfileV1(CalibrationContext &ctx);
static void ParseProfileV2(CalibrationContext &ctx, std::istream &stream);
static std::string ConfigFileName()
static picojson::array FloatArray(const float *buf, int numFloats)
{
std::string vrRuntimeConfigName = vr::VR_RuntimePath();
return vrRuntimeConfigName + "\\..\\..\\..\\config\\01spacecalibrator\\calibration.json";
picojson::array arr;
for (int i = 0; i < numFloats; i++)
arr.push_back(picojson::value(double(buf[i])));
return arr;
}
void LoadProfile(CalibrationContext &ctx)
static void LoadFloatArray(const picojson::value &obj, float *buf, int numFloats)
{
ctx.validProfile = false;
if (!obj.is<picojson::array>())
throw std::runtime_error("expected array, got " + obj.to_str());
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");
}
auto &arr = obj.get<picojson::array>();
if (arr.size() != numFloats)
throw std::runtime_error("wrong buffer size");
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;
}
for (int i = 0; i < numFloats; i++)
buf[i] = (float) arr[i].get<double>();
}
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);
@@ -71,14 +54,43 @@ 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>();
ctx.chaperone.autoApply = chaperone["auto_apply"].get<bool>();
LoadFloatArray(chaperone["play_space_size"], ctx.chaperone.playSpaceSize.v, 2);
LoadFloatArray(
chaperone["standing_center"],
(float *) ctx.chaperone.standingCenter.m,
sizeof(ctx.chaperone.standingCenter.m) / sizeof(float)
);
if (!chaperone["geometry"].is<picojson::array>())
throw std::runtime_error("chaperone geometry is not an array");
auto &geometry = chaperone["geometry"].get<picojson::array>();
if (geometry.size() > 0)
{
ctx.chaperone.geometry.resize(geometry.size() * sizeof(float) / sizeof(ctx.chaperone.geometry[0]));
LoadFloatArray(chaperone["geometry"], (float *) ctx.chaperone.geometry.data(), geometry.size());
ctx.chaperone.valid = true;
}
}
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)
return;
picojson::object profile;
profile["reference_tracking_system"].set<std::string>(ctx.referenceTrackingSystem);
@@ -90,6 +102,28 @@ 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;
chaperone["auto_apply"].set<bool>(ctx.chaperone.autoApply);
chaperone["play_space_size"].set<picojson::array>(FloatArray(ctx.chaperone.playSpaceSize.v, 2));
chaperone["standing_center"].set<picojson::array>(FloatArray(
(float *) ctx.chaperone.standingCenter.m,
sizeof(ctx.chaperone.standingCenter.m) / sizeof(float)
));
chaperone["geometry"].set<picojson::array>(FloatArray(
(float *) ctx.chaperone.geometry.data(),
sizeof(ctx.chaperone.geometry[0]) / sizeof(float) * ctx.chaperone.geometry.size()
));
profile["chaperone"].set<picojson::object>(chaperone);
}
picojson::value profileV;
profileV.set<picojson::object>(profile);
@@ -99,70 +133,89 @@ void SaveProfile(CalibrationContext &ctx)
picojson::value profilesV;
profilesV.set<picojson::array>(profiles);
file << profilesV.serialize(true);
ctx.validProfile = 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
ctx.validProfile = true;
SaveProfile(ctx);
file.close();
std::remove("openvr_space_calibration.txt");
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;
}
}
void WriteActivateMultipleDriversToConfig()
void SaveProfile(CalibrationContext &ctx)
{
std::string configPath = vr::VR_RuntimePath();
configPath += "\\..\\..\\..\\config\\steamvr.vrsettings";
std::cout << "Saving profile to registry" << std::endl;
std::ifstream ifile(configPath);
if (!ifile.good())
throw std::runtime_error("failed to read steamvr.vrsettings");
picojson::value v;
std::string err = picojson::parse(v, ifile);
if (!err.empty())
throw std::runtime_error(err);
ifile.close();
if (!v.is<picojson::object>())
throw std::runtime_error("steamvr.vrsettings is empty");
auto &root = v.get<picojson::object>();
if (!root["steamvr"].is<picojson::object>())
throw std::runtime_error("steamvr.vrsettings is missing \"steamvr\" key");
auto &steamvr = root["steamvr"].get<picojson::object>();
const bool tru = true; // MSVC picks the wrong specialization when passing a literal...
steamvr["activateMultipleDrivers"].set<bool>(tru);
std::ofstream ofile(configPath);
if (!ofile.good())
throw std::runtime_error("failed to write steamvr.vrsettings");
v.serialize(std::ostream_iterator<char>(ofile), true);
std::cout << "Successfully set activateMultipleDrivers to true" << std::endl;
std::stringstream io;
WriteProfile(ctx, io);
WriteRegistryKey(io.str());
}
-1
View File
@@ -4,4 +4,3 @@
void LoadProfile(CalibrationContext &ctx);
void SaveProfile(CalibrationContext &ctx);
void WriteActivateMultipleDriversToConfig();
@@ -119,13 +119,45 @@ void TryCreateVROverlay()
vr::VROverlay()->SetOverlayWidthInMeters(overlayMainHandle, 3.0f);
vr::VROverlay()->SetOverlayInputMethod(overlayMainHandle, vr::VROverlayInputMethod_Mouse);
vr::VROverlay()->SetOverlayFlag(overlayMainHandle, vr::VROverlayFlags_SendVRScrollEvents, true);
vr::VROverlay()->SetOverlayFlag(overlayMainHandle, vr::VROverlayFlags_SendVRDiscreteScrollEvents, true);
std::string iconPath = cwd;
iconPath += "\\icon.png";
vr::VROverlay()->SetOverlayFromFile(overlayThumbnailHandle, iconPath.c_str());
}
void ActivateMultipleDrivers()
{
vr::EVRSettingsError vrSettingsError;
bool enabled = vr::VRSettings()->GetBool(vr::k_pch_SteamVR_Section, vr::k_pch_SteamVR_ActivateMultipleDrivers_Bool, &vrSettingsError);
if (vrSettingsError != vr::VRSettingsError_None)
{
std::string err = "Could not read \"" + std::string(vr::k_pch_SteamVR_ActivateMultipleDrivers_Bool) + "\" setting: "
+ vr::VRSettings()->GetSettingsErrorNameFromEnum(vrSettingsError);
throw std::runtime_error(err);
}
if (!enabled)
{
vr::VRSettings()->SetBool(vr::k_pch_SteamVR_Section, vr::k_pch_SteamVR_ActivateMultipleDrivers_Bool, true, &vrSettingsError);
if (vrSettingsError != vr::VRSettingsError_None)
{
std::string err = "Could not set \"" + std::string(vr::k_pch_SteamVR_ActivateMultipleDrivers_Bool) + "\" setting: "
+ vr::VRSettings()->GetSettingsErrorNameFromEnum(vrSettingsError);
throw std::runtime_error(err);
}
std::cerr << "Enabled \"" << vr::k_pch_SteamVR_ActivateMultipleDrivers_Bool << "\" setting" << std::endl;
}
else
{
std::cerr << "\"" << vr::k_pch_SteamVR_ActivateMultipleDrivers_Bool << "\" setting previously enabled" << std::endl;
}
}
void InitVR()
{
auto initError = vr::VRInitError_None;
@@ -148,6 +180,8 @@ void InitVR()
{
throw std::runtime_error("OpenVR error: Outdated IVROverlay_Version");
}
ActivateMultipleDrivers();
}
void RunLoop()
@@ -192,10 +226,11 @@ void RunLoop()
char buf[0x400];
ImGui::GetActiveText(buf, sizeof buf);
buf[0x3ff] = 0;
uint32_t unFlags = 0; // EKeyboardFlags
vr::VROverlay()->ShowKeyboardForOverlay(
overlayMainHandle, vr::k_EGamepadTextInputModeNormal, vr::k_EGamepadTextInputLineModeSingleLine,
"Space Calibrator Overlay", sizeof buf, buf, false, 0
unFlags, "Space Calibrator Overlay", sizeof buf, buf, 0
);
keyboardOpen = true;
}
@@ -214,7 +249,7 @@ void RunLoop()
case vr::VREvent_MouseButtonUp:
io.MouseDown[vrEvent.data.mouse.button == vr::VRMouseButton_Left ? 0 : 1] = false;
break;
case vr::VREvent_Scroll:
case vr::VREvent_ScrollDiscrete:
io.MouseWheelH += vrEvent.data.scroll.xdelta * 360.0f * 8.0f;
io.MouseWheel += vrEvent.data.scroll.ydelta * 360.0f * 8.0f;
break;
@@ -231,6 +266,8 @@ void RunLoop()
}
}
ImGui::GetIO().DisplaySize = ImVec2((float) fboTextureWidth, (float) fboTextureHeight);
ImGui_ImplGlfw_SetReadMouseFromGlfw(!dashboardVisible);
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplGlfw_NewFrame();
@@ -241,16 +278,20 @@ void RunLoop()
ImGui::Render();
glBindFramebuffer(GL_FRAMEBUFFER, fboHandle);
glViewport(0, 0, width, height);
glViewport(0, 0, fboTextureWidth, fboTextureHeight);
glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glBindFramebuffer(GL_READ_FRAMEBUFFER, fboHandle);
glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
glfwSwapBuffers(glfwWindow);
if (width && height)
{
glBindFramebuffer(GL_READ_FRAMEBUFFER, fboHandle);
glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
glfwSwapBuffers(glfwWindow);
}
if (dashboardVisible)
{
@@ -264,21 +305,27 @@ void RunLoop()
#endif
fboTextureHandle;
vr::HmdVector2_t mouseScale = { (float) width, (float) height };
vr::HmdVector2_t mouseScale = { (float) fboTextureWidth, (float) fboTextureHeight };
vr::VROverlay()->SetOverlayTexture(overlayMainHandle, &vrTex);
vr::VROverlay()->SetOverlayMouseScale(overlayMainHandle, &mouseScale);
}
glfwWaitEventsTimeout(dashboardVisible ? 0.0 : CalCtx.wantedUpdateInterval);
const double dashboardInterval = 1.0 / 90.0; // fps
double waitEventsTimeout = CalCtx.wantedUpdateInterval;
if (dashboardVisible && waitEventsTimeout > dashboardInterval)
waitEventsTimeout = dashboardInterval;
glfwWaitEventsTimeout(waitEventsTimeout);
}
}
int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow)
{
_getcwd(cwd, MAX_PATH);
//CreateConsole();
HandleCommandLine(lpCmdLine);
//CreateConsole();
if (!glfwInit())
{
@@ -330,7 +377,11 @@ static void HandleCommandLine(LPWSTR lpCmdLine)
vr::VR_Init(&vrErr, vr::VRApplication_Utility);
if (vrErr == vr::VRInitError_None)
{
printf("%s", vr::VR_RuntimePath());
char cruntimePath[MAX_PATH] = { 0 };
unsigned int pathLen;
vr::VR_GetRuntimePath(cruntimePath, MAX_PATH, &pathLen);
printf("%s", cruntimePath);
vr::VR_Shutdown();
exit(0);
}
@@ -409,12 +460,12 @@ static void HandleCommandLine(LPWSTR lpCmdLine)
{
try
{
WriteActivateMultipleDriversToConfig();
ActivateMultipleDrivers();
ret = 0;
}
catch (std::runtime_error &e)
{
std::cerr << "Failed to set activateMultipleDrivers: " << e.what() << std::endl;
std::cerr << e.what() << std::endl;
}
}
else
+111 -35
View File
@@ -9,7 +9,7 @@
#include <algorithm>
#include <imgui/imgui.h>
#define VERSION_STRING "0.7"
#define VERSION_STRING "1.2"
struct VRDevice
{
@@ -33,35 +33,51 @@ 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)
{
if (CalCtx.validProfile && !CalCtx.enabled)
{
ImGui::TextColored(ImColor(0.8f, 0.2f, 0.2f), "Reference (%s) HMD not detected, profile disabled", CalCtx.referenceTrackingSystem.c_str());
ImGui::Text("");
}
float width = ImGui::GetWindowContentRegionWidth(), scale = 1.0f;
if (CalCtx.validProfile)
{
@@ -86,11 +102,59 @@ void BuildMainWindow(bool runningInOverlay)
ImGui::SameLine();
if (ImGui::Button("Clear Calibration", ImVec2(width * scale, ImGui::GetTextLineHeight() * 2)))
{
CalCtx.calibratedRotation = Eigen::Vector3d();
CalCtx.calibratedTranslation = Eigen::Vector3d();
CalCtx.validProfile = false;
CalCtx.Clear();
SaveProfile(CalCtx);
}
}
width = ImGui::GetWindowContentRegionWidth();
scale = 1.0f;
if (CalCtx.chaperone.valid)
{
width -= style.FramePadding.x * 2.0f;
scale = 0.5;
}
ImGui::Text("");
if (ImGui::Button("Copy Chaperone Bounds to profile", ImVec2(width * scale, ImGui::GetTextLineHeight() * 2)))
{
LoadChaperoneBounds();
SaveProfile(CalCtx);
}
if (CalCtx.chaperone.valid)
{
ImGui::SameLine();
if (ImGui::Button("Paste Chaperone Bounds", ImVec2(width * scale, ImGui::GetTextLineHeight() * 2)))
{
ApplyChaperoneBounds();
}
if (ImGui::Checkbox(" Paste Chaperone Bounds automatically when geometry resets", &CalCtx.chaperone.autoApply))
{
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)
{
@@ -107,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");
@@ -133,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)
@@ -199,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)
@@ -394,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);
@@ -406,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);
}
}
+37 -30
View File
@@ -1,47 +1,54 @@
# OpenVR-SpaceCalibrator
# OpenVR Space Calibrator
Use VR devices from one company with any other.
This helps you use tracked VR devices from one company with any other. It aligns multiple tracking systems with a quick calibration step. It may not work for your setup, but there are many cases that work to a degree, and some work very well.
This is **beta software** and may not work for you. A quick video walkthrough of the calibration process is available at https://www.youtube.com/watch?v=W3TnQd9JMl4
- Rift CV1 x Vive devices: works very well with the v2 (blue logo) trackers, v1 trackers (grey logo, not in production) have major interference issues in the IR spectrum, controller wands (both gen) and Index controllers work very well.
- Rift S, Quest, Windows MR, other SLAM inside-out tracked HMDs x Vive devices: works very well when you aren't moving around the room far (e.g. Beat Saber) but a lot of walking around causes a nontrivial amount of drift between systems. Your results may vary depending on your space. It's possible some of this can be fixed in software with a better calibration algorithm.
- Quest wireless streaming is particularly bad right now and requires frequent recalibration, but it does work for a short time until one of many factors causes it to drift. With wireless devices, moving slowly when calibrating and using the Slow or Very Slow calibration modes is effective at reducing the initial error.
- Any non-Rift HMD x Touch controllers: does not work, the Oculus driver requires the HMD is a Rift. It's theoretically possible to work around this in software but as far as I know it hasn't been done as it would require a fair amount of reverse engineering effort.
## Usage
There is a community of a few thousand users on [Discord][].
A quick video of how this works using an old version (~v0.3) is available at https://www.youtube.com/watch?v=W3TnQd9JMl4. The user interface has been upgraded since then; the calibration is now done via a SteamVR dashboard menu, and there's much more configurability.
[Discord]: https://discord.gg/m7g2Wyj
### Install
Before following the directions below, download and run the installer for the [latest release](https://github.com/pushrax/OpenVR-SpaceCalibrator/releases). This will automatically set up SteamVR for use with multiple tracking systems (`activateMultipleDrivers: true`). There are many guides that say you need to edit the SteamVR config manually. You do not.
### Usage
Once Space Calibrator has a calibration, it works in the background to keep your devices configured correctly. Since v0.8, everything aside from creating the calibration is automated.
### Calibration
If you don't already run a setup with multiple device types, see [Setting up SteamVR](#setting-up-steamvr).
As part of first time setup, or when you make a change to your space (e.g. move a sensor), and occasionally as the calibration drifts over time (consumer VR tracking isn't perfectly stable), you'll need to run a calibration:
1. Download and unzip the [latest release](https://github.com/pushrax/OpenVR-SpaceCalibrator/releases) of OpenVR-SpaceCalibrator.
2. Run SteamVR, and turn on your Touch controllers and only one Vive device to use for calibration.
3. Run OpenVR-SpaceCalibrator.
4. Hold the left Touch controller and your Vive device in your left hand securely, like they're glued together.
5. Click "Start Calibration"
6. Wave your left hand around, like you're calibrating the compass on your phone. You want to get as many orientations as possible.
7. Done! A profile will be saved automatically. You can turn on the rest of your Vive devices now.
1. Copy the chaperone/guardian bounds from your HMD's play space. This doesn't need to be run if your HMD's play space hasn't changed since last time you copied it. __Example:__ if you're using the Rift with Vive trackers and you bump a Vive lighthouse, or if the calibration has just drifted a little, you likely don't need to run this step, but if you bump an Oculus sensor you will (after running Oculus guardian setup again).
1. Run SteamVR, with only devices from your HMD's tracking system powered on. __Example:__ for Rift with Vive trackers, don't turn on the trackers yet.
2. Confirm your chaperone/guardian is set up with the walls in the right place. If you change it later, you need to run step again.
3. Open SPACE CAL in the SteamVR dashboard overlay.
4. Click `Copy Chaperone Bounds to profile`
Next time you run SteamVR, open OpenVR-SpaceCalibrator and it will load the calibration automatically and apply it devices you turn on.
2. Calibrate devices.
1. Open SteamVR if you haven't already. Turn on some or all your devices.
2. Open SPACE CAL in the SteamVR dashboard overlay.
3. Select one device from the reference space on the left and one device from the target space on the right. If you turned on multiple devices from one space and can't tell which one is selected, click "Identify selected devices" to blink an LED or vibrate it. __Example:__ for Rift with Vive trackers, you'll see the Touch controllers on the left, and Vive trackers on the right. __Pro tip:__ if you turn on just one Vive tracker, you don't have to figure out which one is selected.
4. Hold these two devices in one hand, like they're glued together. If they slip, calibration won't work as well.
5. Click `Start Calibration`
6. Move and rotate your hand around slowly a few times, like you're calibrating the compass on your phone. You want to sample as many orientations as possible.
7. Done! A profile will be saved automatically. If you haven't already, turn on all your devices. Space Calibrator will automatically apply the calibration to devices as they turn on.
### Calibration outside VR
### Setting up SteamVR
Every hardware designer with their own tracking system needs to make a "driver"
so SteamVR can see their tracking data. SteamVR by default will load only one driver at a time,
but we can tell it to load all of them by editing the vrsettings config file.
Open `Steam\steamapps\common\SteamVR\resources\settings\default.vrsettings` in a text editor,
look for the line that has `"activateMultipleDrivers": false` and change the `false` to `true`.
In some guides online you may also see references to `requireHmd`, this does _not_ need to be changed
unless you truly want to be able to run without any kind of HMD.
Sometimes when SteamVR updates this file seems to get wiped, if your setup stops working you might
have to edit the config again.
You can calibrate without using the dashboard overlay by unminimizing Space Calibrator after opening SteamVR (it starts minimized). This is required if you're calibrating for a lone HMD without any devices in its tracking system.
### Compiling your own build
1. Open `OpenVR-SpaceCalibrator.sln` in Visual Studio 2015 and build.
Open `OpenVR-SpaceCalibrator.sln` in Visual Studio 2015 and build. There are no external dependencies.
## The math
### The math
See [math.pdf](https://github.com/pushrax/OpenVR-SpaceCalibrator/blob/master/math.pdf) for details.
If you have some ideas for how to improve the calibration process, let me know!
+2
View File
@@ -247,6 +247,7 @@ void ImGui_ImplGlfw_NewFrame()
ImGuiIO& io = ImGui::GetIO();
IM_ASSERT(io.Fonts->IsBuilt()); // Font atlas needs to be built, call renderer _NewFrame() function e.g. ImGui_ImplOpenGL3_NewFrame()
#if false // Rendering to our own framebuffer
// Setup display size
int w, h;
int display_w, display_h;
@@ -254,6 +255,7 @@ void ImGui_ImplGlfw_NewFrame()
glfwGetFramebufferSize(g_Window, &display_w, &display_h);
io.DisplaySize = ImVec2((float)w, (float)h);
io.DisplayFramebufferScale = ImVec2(w > 0 ? ((float)display_w / w) : 0, h > 0 ? ((float)display_h / h) : 0);
#endif
// Setup time step
double current_time = glfwGetTime();
Binary file not shown.
Binary file not shown.
+1981 -351
View File
File diff suppressed because it is too large. Load diff
+1501 -190
View File
File diff suppressed because it is too large. Load diff