#include "stdafx.h" #include "Configuration.h" #include #include #include #include #include #include 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) { picojson::array arr; for (int i = 0; i < numFloats; i++) arr.push_back(picojson::value(double(buf[i]))); return arr; } void LoadFloatArray(const picojson::value &obj, float *buf, int numFloats) { if (!obj.is()) throw std::runtime_error("expected array, got " + obj.to_str()); auto &arr = obj.get(); if (arr.size() != numFloats) throw std::runtime_error("wrong buffer size"); for (int i = 0; i < numFloats; i++) buf[i] = (float) arr[i].get(); } 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) { picojson::value v; std::string err = picojson::parse(v, stream); if (!err.empty()) throw std::runtime_error(err); auto arr = v.get(); if (arr.size() < 1) throw std::runtime_error("no profiles in file"); auto obj = arr[0].get(); ctx.referenceTrackingSystem = obj["reference_tracking_system"].get(); ctx.targetTrackingSystem = obj["target_tracking_system"].get(); ctx.calibratedRotation(0) = obj["roll"].get(); ctx.calibratedRotation(1) = obj["yaw"].get(); ctx.calibratedRotation(2) = obj["pitch"].get(); ctx.calibratedTranslation(0) = obj["x"].get(); ctx.calibratedTranslation(1) = obj["y"].get(); ctx.calibratedTranslation(2) = obj["z"].get(); if (obj["chaperone"].is()) { auto chaperone = obj["chaperone"].get(); ctx.chaperone.autoApply = chaperone["auto_apply"].get(); 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()) throw std::runtime_error("chaperone geometry is not an array"); auto &geometry = chaperone["geometry"].get(); 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) { std::cout << "Saving profile to " << ConfigFileName() << std::endl; std::ofstream file(ConfigFileName()); picojson::object profile; profile["reference_tracking_system"].set(ctx.referenceTrackingSystem); profile["target_tracking_system"].set(ctx.targetTrackingSystem); profile["roll"].set(ctx.calibratedRotation(0)); profile["yaw"].set(ctx.calibratedRotation(1)); profile["pitch"].set(ctx.calibratedRotation(2)); profile["x"].set(ctx.calibratedTranslation(0)); profile["y"].set(ctx.calibratedTranslation(1)); profile["z"].set(ctx.calibratedTranslation(2)); if (ctx.chaperone.valid) { picojson::object chaperone; chaperone["auto_apply"].set(ctx.chaperone.autoApply); chaperone["play_space_size"].set(FloatArray(ctx.chaperone.playSpaceSize.v, 2)); chaperone["standing_center"].set(FloatArray( (float *) ctx.chaperone.standingCenter.m, sizeof(ctx.chaperone.standingCenter.m) / sizeof(float) )); chaperone["geometry"].set(FloatArray( (float *) ctx.chaperone.geometry.data(), sizeof(ctx.chaperone.geometry[0]) / sizeof(float) * ctx.chaperone.geometry.size() )); profile["chaperone"].set(chaperone); } picojson::value profileV; profileV.set(profile); picojson::array profiles; profiles.push_back(profileV); picojson::value profilesV; profilesV.set(profiles); file << profilesV.serialize(true); ctx.validProfile = true; } static void UpgradeProfileV1(CalibrationContext &ctx) { std::ifstream file("openvr_space_calibration.txt"); ctx.validProfile = false; if (!file.good()) 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"); } void WriteActivateMultipleDriversToConfig() { std::string configPath = ConfigFileName(); 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()) throw std::runtime_error("steamvr.vrsettings is empty"); auto &root = v.get(); if (!root["steamvr"].is()) throw std::runtime_error("steamvr.vrsettings is missing \"steamvr\" key"); auto &steamvr = root["steamvr"].get(); const bool tru = true; // MSVC picks the wrong specialization when passing a literal... steamvr["activateMultipleDrivers"].set(tru); std::ofstream ofile(configPath); if (!ofile.good()) throw std::runtime_error("failed to write steamvr.vrsettings"); v.serialize(std::ostream_iterator(ofile), true); std::cout << "Successfully set activateMultipleDrivers to true" << std::endl; }