mirror of
https://github.com/hyblocker/OpenVR-SpaceCalibrator.git
synced 2026-10-08 14:00:22 +02:00
767 lines
22 KiB
C++
767 lines
22 KiB
C++
#include "stdafx.h"
|
|
#include "UserInterface.h"
|
|
#include "Calibration.h"
|
|
#include "Configuration.h"
|
|
#include "VRState.h"
|
|
#include "CalibrationMetrics.h"
|
|
#include "../Version.h"
|
|
|
|
#include <thread>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <algorithm>
|
|
#include <imgui/imgui.h>
|
|
|
|
void TextWithWidth(const char *label, const char *text, float width);
|
|
|
|
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 |
|
|
ImGuiWindowFlags_NoScrollbar |
|
|
ImGuiWindowFlags_NoScrollWithMouse |
|
|
ImGuiWindowFlags_NoCollapse;
|
|
|
|
void BuildContinuousCalDisplay();
|
|
void ShowVersionLine();
|
|
|
|
static bool runningInOverlay;
|
|
|
|
void BuildMainWindow(bool runningInOverlay_)
|
|
{
|
|
runningInOverlay = runningInOverlay_;
|
|
bool continuousCalibration = CalCtx.state == CalibrationState::Continuous || CalCtx.state == CalibrationState::ContinuousStandby;
|
|
|
|
auto &io = ImGui::GetIO();
|
|
|
|
ImGui::SetNextWindowPos(ImVec2(0.0f, 0.0f), ImGuiCond_Always);
|
|
ImGui::SetNextWindowSize(io.DisplaySize, ImGuiCond_Always);
|
|
|
|
if (!ImGui::Begin("OpenVRSpaceCalibrator", nullptr, bareWindowFlags))
|
|
{
|
|
ImGui::End();
|
|
return;
|
|
}
|
|
|
|
ImGui::PushStyleColor(ImGuiCol_PlotHistogram, ImGui::GetStyleColorVec4(ImGuiCol_Button));
|
|
|
|
if (continuousCalibration) {
|
|
BuildContinuousCalDisplay();
|
|
}
|
|
else {
|
|
auto state = LoadVRState();
|
|
|
|
ImGui::BeginDisabled(CalCtx.state == CalibrationState::Continuous);
|
|
BuildSystemSelection(state);
|
|
BuildDeviceSelections(state);
|
|
ImGui::EndDisabled();
|
|
BuildMenu(runningInOverlay);
|
|
}
|
|
|
|
ShowVersionLine();
|
|
|
|
ImGui::PopStyleColor();
|
|
ImGui::End();
|
|
}
|
|
|
|
void ShowVersionLine() {
|
|
ImGui::SetNextWindowPos(ImVec2(10.0f, ImGui::GetWindowHeight() - ImGui::GetFrameHeightWithSpacing()));
|
|
if (!ImGui::BeginChild("bottom line", ImVec2(ImGui::GetWindowWidth() - 20.0f, ImGui::GetFrameHeightWithSpacing() * 2), false)) {
|
|
ImGui::EndChild();
|
|
return;
|
|
}
|
|
ImGui::Text("OpenVR Space Calibrator v" SPACECAL_VERSION_STRING " - by tach/pushrax/bd_");
|
|
if (runningInOverlay)
|
|
{
|
|
ImGui::SameLine();
|
|
ImGui::Text("- close VR overlay to use mouse");
|
|
}
|
|
ImGui::EndChild();
|
|
}
|
|
|
|
void CCal_BasicInfo();
|
|
void CCal_AlignParams();
|
|
|
|
void BuildContinuousCalDisplay() {
|
|
ImGui::SetNextWindowPos(ImVec2(0, 0));
|
|
ImGui::SetNextWindowSize(ImGui::GetWindowSize());
|
|
ImGui::SetNextWindowBgAlpha(1);
|
|
if (!ImGui::Begin("Continuous Calibration", nullptr,
|
|
bareWindowFlags & ~ImGuiWindowFlags_NoTitleBar
|
|
)) {
|
|
ImGui::End();
|
|
return;
|
|
}
|
|
|
|
ImVec2 contentRegion;
|
|
contentRegion.x = ImGui::GetWindowContentRegionWidth();
|
|
contentRegion.y = ImGui::GetWindowHeight() - ImGui::GetFrameHeightWithSpacing() * 2.1;
|
|
|
|
if (!ImGui::BeginChild("CCalDisplayFrame", contentRegion, false)) {
|
|
ImGui::EndChild();
|
|
return;
|
|
}
|
|
|
|
if (ImGui::BeginTabBar("CCalTabs", 0)) {
|
|
if (ImGui::BeginTabItem("Status")) {
|
|
CCal_BasicInfo();
|
|
ImGui::EndTabItem();
|
|
}
|
|
|
|
if (ImGui::BeginTabItem("More Graphs")) {
|
|
ShowCalibrationDebug(2, 3);
|
|
ImGui::EndTabItem();
|
|
}
|
|
|
|
if (ImGui::BeginTabItem("Alignment speeds")) {
|
|
CCal_AlignParams();
|
|
ImGui::EndTabItem();
|
|
}
|
|
|
|
ImGui::EndTabBar();
|
|
}
|
|
|
|
ImGui::EndChild();
|
|
|
|
ShowVersionLine();
|
|
|
|
ImGui::End();
|
|
}
|
|
|
|
static void ScaledDragFloat(const char* label, double& f, double scale, double min, double max, int flags = ImGuiSliderFlags_AlwaysClamp) {
|
|
float v = (float) (f * scale);
|
|
|
|
ImGui::SliderFloat(label, &v, (float)min, (float)max, "%1.2f", flags);
|
|
|
|
f = v / scale;
|
|
}
|
|
|
|
void CCal_AlignParams() {
|
|
if (ImGui::Button("Reset settings")) {
|
|
CalCtx.ResetConfig();
|
|
}
|
|
|
|
ImGui::SliderFloat("Recalibration threshold", &CalCtx.continuousCalibrationThreshold, 1.01f, 10.0f, "%1.1f", 0);
|
|
if (ImGui::IsItemHovered(0)) {
|
|
ImGui::SetTooltip("Controls how confident SpaceCalibrator must be in the new calibration before updating the calibration.\n"
|
|
"Higher values calibrate less frequently, and may be useful on systems with lots of tracker drift."
|
|
);
|
|
}
|
|
|
|
ImGui::Separator();
|
|
ImGui::TextWrapped(
|
|
"Calibration speeds: SpaceCalibrator uses up to three different speeds at which it drags the calibration back into "
|
|
"position when drift occurs. These settings control how far off the calibration should be before going back to low speed (for "
|
|
"Decel) or going to higher speeds (for Slow and Fast)."
|
|
);
|
|
if (ImGui::BeginTable("SpeedThresholds", 3, 0)) {
|
|
ImGui::TableNextRow();
|
|
ImGui::TableSetColumnIndex(1);
|
|
ImGui::Text("Translation (mm)");
|
|
ImGui::TableSetColumnIndex(2);
|
|
ImGui::Text("Rotation (degrees)");
|
|
|
|
|
|
ImGui::TableNextRow();
|
|
ImGui::TableSetColumnIndex(0);
|
|
ImGui::Text("Decel");
|
|
ImGui::TableSetColumnIndex(1);
|
|
ScaledDragFloat("##TransDecel", CalCtx.alignmentSpeedParams.thr_trans_tiny, 1000.0, 0, 20.0);
|
|
ImGui::TableSetColumnIndex(2);
|
|
ScaledDragFloat("##RotDecel", CalCtx.alignmentSpeedParams.thr_rot_tiny, 180.0 / EIGEN_PI, 0, 5.0);
|
|
|
|
ImGui::TableNextRow();
|
|
ImGui::TableSetColumnIndex(0);
|
|
ImGui::Text("Slow");
|
|
ImGui::TableSetColumnIndex(1);
|
|
ScaledDragFloat("##TransSlow", CalCtx.alignmentSpeedParams.thr_trans_small, 1000.0,
|
|
CalCtx.alignmentSpeedParams.thr_trans_tiny * 1000.0, 20.0);
|
|
ImGui::TableSetColumnIndex(2);
|
|
ScaledDragFloat("##RotSlow", CalCtx.alignmentSpeedParams.thr_rot_small, 180.0 / EIGEN_PI,
|
|
CalCtx.alignmentSpeedParams.thr_rot_tiny * (180.0 / EIGEN_PI), 10.0);
|
|
|
|
ImGui::TableNextRow();
|
|
ImGui::TableSetColumnIndex(0);
|
|
ImGui::Text("Fast");
|
|
ImGui::TableSetColumnIndex(1);
|
|
ScaledDragFloat("##TransFast", CalCtx.alignmentSpeedParams.thr_trans_large, 1000.0,
|
|
CalCtx.alignmentSpeedParams.thr_trans_small * 1000.0, 50.0);
|
|
ImGui::TableSetColumnIndex(2);
|
|
ScaledDragFloat("##RotFast", CalCtx.alignmentSpeedParams.thr_rot_large, 180.0 / EIGEN_PI,
|
|
CalCtx.alignmentSpeedParams.thr_rot_small * (180.0 / EIGEN_PI), 20.0);
|
|
|
|
ImGui::EndTable();
|
|
}
|
|
|
|
ImGui::Separator();
|
|
ImGui::Text("Alignment speeds");
|
|
ScaledDragFloat("Decel", CalCtx.alignmentSpeedParams.align_speed_tiny, 1.0, 0, 2.0, 0);
|
|
ScaledDragFloat("Slow", CalCtx.alignmentSpeedParams.align_speed_small, 1.0, 0, 2.0, 0);
|
|
ScaledDragFloat("Fast", CalCtx.alignmentSpeedParams.align_speed_large, 1.0, 0, 2.0, 0);
|
|
}
|
|
|
|
void CCal_BasicInfo() {
|
|
if (ImGui::BeginTable("DeviceInfo", 2, 0)) {
|
|
ImGui::TableSetupColumn("Reference device");
|
|
ImGui::TableSetupColumn("Target device");
|
|
ImGui::TableHeadersRow();
|
|
|
|
ImGui::TableNextRow();
|
|
ImGui::TableSetColumnIndex(0);
|
|
ImGui::BeginGroup();
|
|
ImGui::Text("%s / %s / %s",
|
|
CalCtx.referenceStandby.trackingSystem.c_str(),
|
|
CalCtx.referenceStandby.model.c_str(),
|
|
CalCtx.referenceStandby.serial.c_str()
|
|
);
|
|
const char* status;
|
|
if (CalCtx.referenceID < 0) {
|
|
ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, 0xFF000080);
|
|
status = "NOT FOUND";
|
|
}
|
|
else if (!CalCtx.ReferencePoseIsValid()) {
|
|
ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, 0xFFFF0080);
|
|
status = "NOT TRACKING";
|
|
}
|
|
else {
|
|
status = "OK";
|
|
}
|
|
ImGui::Text("Status: %s", status);
|
|
ImGui::EndGroup();
|
|
|
|
ImGui::TableSetColumnIndex(1);
|
|
ImGui::BeginGroup();
|
|
ImGui::Text("%s / %s / %s",
|
|
CalCtx.targetStandby.trackingSystem.c_str(),
|
|
CalCtx.targetStandby.model.c_str(),
|
|
CalCtx.targetStandby.serial.c_str()
|
|
);
|
|
if (CalCtx.targetID < 0) {
|
|
ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, 0xFF000080);
|
|
status = "NOT FOUND";
|
|
}
|
|
else if (!CalCtx.TargetPoseIsValid()) {
|
|
ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, 0xFFFF0080);
|
|
status = "NOT TRACKING";
|
|
}
|
|
else {
|
|
status = "OK";
|
|
}
|
|
ImGui::Text("Status: %s", status);
|
|
ImGui::EndGroup();
|
|
|
|
ImGui::EndTable();
|
|
}
|
|
|
|
float width = ImGui::GetWindowContentRegionWidth(), scale = 1.0f;
|
|
|
|
if (ImGui::BeginTable("##CCal_Cancel", Metrics::enableLogs ? 3 : 2, 0, ImVec2(width * scale, ImGui::GetTextLineHeight() * 2))) {
|
|
ImGui::TableNextRow();
|
|
ImGui::TableSetColumnIndex(0);
|
|
if (ImGui::Button("Cancel Continuous Calibration", ImVec2(-FLT_MIN, 0.0f))) {
|
|
EndContinuousCalibration();
|
|
}
|
|
|
|
ImGui::TableSetColumnIndex(1);
|
|
if (ImGui::Button("Debug: Force break calibration", ImVec2(-FLT_MIN, 0.0f))) {
|
|
DebugApplyRandomOffset();
|
|
}
|
|
|
|
if (Metrics::enableLogs) {
|
|
ImGui::TableSetColumnIndex(2);
|
|
if (ImGui::Button("Debug: Mark logs", ImVec2(-FLT_MIN, 0.0f))) {
|
|
Metrics::WriteLogAnnotation("MARK LOGS");
|
|
}
|
|
}
|
|
|
|
ImGui::EndTable();
|
|
}
|
|
|
|
ImGui::Checkbox("Hide target device from application", &CalCtx.quashTargetInContinuous);
|
|
ImGui::SameLine();
|
|
ImGui::Checkbox("Enable static recalibration", &CalCtx.enableStaticRecalibration);
|
|
ImGui::SameLine();
|
|
ImGui::Checkbox("Enable debug logs", &Metrics::enableLogs);
|
|
|
|
// Status field...
|
|
|
|
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0, 0, 0, 1));
|
|
|
|
for (const auto& msg : CalCtx.messages) {
|
|
if (msg.type == CalibrationContext::Message::String) {
|
|
ImGui::TextWrapped("> %s", msg.str.c_str());
|
|
}
|
|
}
|
|
|
|
ImGui::PopStyleColor();
|
|
|
|
ShowCalibrationDebug(1, 3);
|
|
}
|
|
|
|
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(ImVec4(0.8f, 0.2f, 0.2f, 1), "Reference (%s) HMD not detected, profile disabled", CalCtx.referenceTrackingSystem.c_str());
|
|
ImGui::Text("");
|
|
}
|
|
|
|
float width = ImGui::GetWindowContentRegionWidth(), scale = 1.0f;
|
|
if (CalCtx.validProfile)
|
|
{
|
|
width -= style.FramePadding.x * 4.0f;
|
|
scale = 1.0f / 4.0f;
|
|
}
|
|
|
|
if (ImGui::Button("Start Calibration", ImVec2(width * scale, ImGui::GetTextLineHeight() * 2)))
|
|
{
|
|
ImGui::OpenPopup("Calibration Progress");
|
|
StartCalibration();
|
|
}
|
|
|
|
ImGui::SameLine();
|
|
if (ImGui::Button("Continuous Calibration", ImVec2(width * scale, ImGui::GetTextLineHeight() * 2))) {
|
|
StartContinuousCalibration();
|
|
}
|
|
|
|
if (CalCtx.validProfile)
|
|
{
|
|
ImGui::SameLine();
|
|
if (ImGui::Button("Edit Calibration", ImVec2(width * scale, ImGui::GetTextLineHeight() * 2)))
|
|
{
|
|
CalCtx.state = CalibrationState::Editing;
|
|
}
|
|
|
|
ImGui::SameLine();
|
|
if (ImGui::Button("Clear Calibration", ImVec2(width * scale, ImGui::GetTextLineHeight() * 2)))
|
|
{
|
|
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)
|
|
{
|
|
BuildProfileEditor();
|
|
|
|
if (ImGui::Button("Save Profile", ImVec2(ImGui::GetWindowContentRegionWidth(), ImGui::GetTextLineHeight() * 2)))
|
|
{
|
|
SaveProfile(CalCtx);
|
|
CalCtx.state = CalibrationState::None;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ImGui::Button("Calibration in progress...", ImVec2(ImGui::GetWindowContentRegionWidth(), ImGui::GetTextLineHeight() * 2));
|
|
}
|
|
|
|
ImGui::SetNextWindowPos(ImVec2(20.0f, 20.0f), ImGuiCond_Always);
|
|
ImGui::SetNextWindowSize(ImVec2(io.DisplaySize.x - 40.0f, io.DisplaySize.y - 40.0f), ImGuiCond_Always);
|
|
if (ImGui::BeginPopupModal("Calibration Progress", nullptr, bareWindowFlags))
|
|
{
|
|
ImGui::PushStyleColor(ImGuiCol_FrameBg, (ImVec4)ImVec4(0, 0, 0, 1));
|
|
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)
|
|
{
|
|
if (state.trackingSystems.empty())
|
|
{
|
|
ImGui::Text("No tracked devices are present");
|
|
return;
|
|
}
|
|
|
|
ImGuiStyle &style = ImGui::GetStyle();
|
|
float paneWidth = ImGui::GetWindowContentRegionWidth() / 2 - style.FramePadding.x;
|
|
|
|
TextWithWidth("ReferenceSystemLabel", "Reference Space", paneWidth);
|
|
ImGui::SameLine();
|
|
TextWithWidth("TargetSystemLabel", "Target Space", paneWidth);
|
|
|
|
int currentReferenceSystem = -1;
|
|
int currentTargetSystem = -1;
|
|
int firstReferenceSystemNotTargetSystem = -1;
|
|
|
|
std::vector<const char *> referenceSystems;
|
|
for (auto &str : state.trackingSystems)
|
|
{
|
|
if (str == CalCtx.referenceTrackingSystem)
|
|
{
|
|
currentReferenceSystem = (int) referenceSystems.size();
|
|
}
|
|
else if (firstReferenceSystemNotTargetSystem == -1 && str != CalCtx.targetTrackingSystem)
|
|
{
|
|
firstReferenceSystemNotTargetSystem = (int) referenceSystems.size();
|
|
}
|
|
referenceSystems.push_back(str.c_str());
|
|
}
|
|
|
|
if (currentReferenceSystem == -1 && CalCtx.referenceTrackingSystem == "")
|
|
{
|
|
if (CalCtx.state == CalibrationState::ContinuousStandby) {
|
|
auto iter = std::find(state.trackingSystems.begin(), state.trackingSystems.end(), CalCtx.referenceStandby.trackingSystem);
|
|
if (iter != state.trackingSystems.end()) {
|
|
currentReferenceSystem = iter - state.trackingSystems.begin();
|
|
}
|
|
}
|
|
else {
|
|
currentReferenceSystem = firstReferenceSystemNotTargetSystem;
|
|
}
|
|
}
|
|
|
|
ImGui::PushItemWidth(paneWidth);
|
|
ImGui::Combo("##ReferenceTrackingSystem", ¤tReferenceSystem, &referenceSystems[0], (int) referenceSystems.size());
|
|
|
|
if (currentReferenceSystem != -1 && currentReferenceSystem < (int) referenceSystems.size())
|
|
{
|
|
CalCtx.referenceTrackingSystem = std::string(referenceSystems[currentReferenceSystem]);
|
|
if (CalCtx.referenceTrackingSystem == CalCtx.targetTrackingSystem)
|
|
CalCtx.targetTrackingSystem = "";
|
|
}
|
|
|
|
if (CalCtx.targetTrackingSystem == "") {
|
|
if (CalCtx.state == CalibrationState::ContinuousStandby) {
|
|
auto iter = std::find(state.trackingSystems.begin(), state.trackingSystems.end(), CalCtx.targetStandby.trackingSystem);
|
|
if (iter != state.trackingSystems.end()) {
|
|
currentTargetSystem = iter - state.trackingSystems.begin();
|
|
}
|
|
}
|
|
else {
|
|
currentTargetSystem = 0;
|
|
}
|
|
}
|
|
|
|
std::vector<const char *> targetSystems;
|
|
for (auto &str : state.trackingSystems)
|
|
{
|
|
if (str != CalCtx.referenceTrackingSystem)
|
|
{
|
|
if (str != "" && str == CalCtx.targetTrackingSystem)
|
|
currentTargetSystem = (int) targetSystems.size();
|
|
targetSystems.push_back(str.c_str());
|
|
}
|
|
}
|
|
|
|
ImGui::SameLine();
|
|
ImGui::Combo("##TargetTrackingSystem", ¤tTargetSystem, &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)
|
|
{
|
|
if (!buffer.empty())
|
|
buffer += " | ";
|
|
buffer += suffix;
|
|
}
|
|
|
|
std::string LabelString(const VRDevice &device)
|
|
{
|
|
std::string label;
|
|
|
|
/*if (device.controllerRole == vr::TrackedControllerRole_LeftHand)
|
|
label = "Left Controller";
|
|
else if (device.controllerRole == vr::TrackedControllerRole_RightHand)
|
|
label = "Right Controller";
|
|
else if (device.deviceClass == vr::TrackedDeviceClass_Controller)
|
|
label = "Controller";
|
|
else if (device.deviceClass == vr::TrackedDeviceClass_HMD)
|
|
label = "HMD";
|
|
else if (device.deviceClass == vr::TrackedDeviceClass_GenericTracker)
|
|
label = "Tracker";*/
|
|
|
|
AppendSeparated(label, device.model);
|
|
AppendSeparated(label, device.serial);
|
|
return label;
|
|
}
|
|
|
|
std::string LabelString(const StandbyDevice& device) {
|
|
std::string label("< ");
|
|
|
|
label += device.model;
|
|
AppendSeparated(label, device.serial);
|
|
|
|
label += " >";
|
|
return label;
|
|
}
|
|
|
|
void BuildDeviceSelection(const VRState &state, int &initialSelected, const std::string &system, StandbyDevice &standbyDevice)
|
|
{
|
|
int selected = initialSelected;
|
|
ImGui::TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1), "Devices from: %s", system.c_str());
|
|
|
|
if (selected != -1)
|
|
{
|
|
bool matched = false;
|
|
for (auto &device : state.devices)
|
|
{
|
|
if (device.trackingSystem != system)
|
|
continue;
|
|
|
|
if (selected == device.id)
|
|
{
|
|
matched = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!matched)
|
|
{
|
|
// Device is no longer present.
|
|
selected = -1;
|
|
}
|
|
}
|
|
|
|
bool standby = CalCtx.state == CalibrationState::ContinuousStandby;
|
|
|
|
if (selected == -1 && !standby)
|
|
{
|
|
for (auto &device : state.devices)
|
|
{
|
|
if (device.trackingSystem != system)
|
|
continue;
|
|
|
|
if (device.controllerRole == vr::TrackedControllerRole_LeftHand)
|
|
{
|
|
selected = device.id;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (selected == -1) {
|
|
for (auto& device : state.devices)
|
|
{
|
|
if (device.trackingSystem != system)
|
|
continue;
|
|
|
|
selected = device.id;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (selected == -1 && standby) {
|
|
bool present = false;
|
|
for (auto& device : state.devices)
|
|
{
|
|
if (device.trackingSystem != system)
|
|
continue;
|
|
|
|
if (standbyDevice.model != device.model) continue;
|
|
if (standbyDevice.serial != device.serial) continue;
|
|
|
|
present = true;
|
|
break;
|
|
}
|
|
|
|
if (!present) {
|
|
auto label = LabelString(standbyDevice);
|
|
ImGui::Selectable(label.c_str(), true);
|
|
}
|
|
}
|
|
|
|
for (auto &device : state.devices)
|
|
{
|
|
if (device.trackingSystem != system)
|
|
continue;
|
|
|
|
auto label = LabelString(device);
|
|
if (ImGui::Selectable(label.c_str(), selected == device.id)) {
|
|
selected = device.id;
|
|
}
|
|
}
|
|
if (selected != initialSelected) {
|
|
const auto& device = std::find_if(state.devices.begin(), state.devices.end(), [&](const auto& d) { return d.id == selected; });
|
|
if (device == state.devices.end()) return;
|
|
|
|
initialSelected = selected;
|
|
standbyDevice.trackingSystem = system;
|
|
standbyDevice.model = device->model;
|
|
standbyDevice.serial = device->serial;
|
|
}
|
|
}
|
|
|
|
void BuildDeviceSelections(const VRState &state)
|
|
{
|
|
ImGuiStyle &style = ImGui::GetStyle();
|
|
ImVec2 paneSize(ImGui::GetWindowContentRegionWidth() / 2 - style.FramePadding.x, ImGui::GetTextLineHeightWithSpacing() * 5 + style.ItemSpacing.y * 4);
|
|
|
|
ImGui::BeginChild("left device pane", paneSize, true);
|
|
BuildDeviceSelection(state, CalCtx.referenceID, CalCtx.referenceTrackingSystem, CalCtx.referenceStandby);
|
|
ImGui::EndChild();
|
|
|
|
ImGui::SameLine();
|
|
|
|
ImGui::BeginChild("right device pane", paneSize, true);
|
|
BuildDeviceSelection(state, CalCtx.targetID, CalCtx.targetTrackingSystem, CalCtx.targetStandby);
|
|
ImGui::EndChild();
|
|
|
|
if (ImGui::Button("Identify selected devices (blinks LED or vibrates)", ImVec2(ImGui::GetWindowContentRegionWidth(), ImGui::GetTextLineHeightWithSpacing() + 4.0f)))
|
|
{
|
|
for (unsigned i = 0; i < 100; ++i)
|
|
{
|
|
vr::VRSystem()->TriggerHapticPulse(CalCtx.targetID, 0, 2000);
|
|
vr::VRSystem()->TriggerHapticPulse(CalCtx.referenceID, 0, 2000);
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
|
}
|
|
}
|
|
}
|
|
|
|
VRState LoadVRState() {
|
|
VRState state = VRState::Load();
|
|
auto& trackingSystems = state.trackingSystems;
|
|
|
|
// Inject entries for continuous calibration targets which have yet to load
|
|
|
|
if (CalCtx.state == CalibrationState::ContinuousStandby) {
|
|
auto existing = std::find(trackingSystems.begin(), trackingSystems.end(), CalCtx.referenceTrackingSystem);
|
|
if (existing == trackingSystems.end()) {
|
|
trackingSystems.push_back(CalCtx.referenceTrackingSystem);
|
|
}
|
|
|
|
existing = std::find(trackingSystems.begin(), trackingSystems.end(), CalCtx.targetTrackingSystem);
|
|
if (existing == trackingSystems.end()) {
|
|
trackingSystems.push_back(CalCtx.targetTrackingSystem);
|
|
}
|
|
}
|
|
|
|
return state;
|
|
}
|
|
|
|
void BuildProfileEditor()
|
|
{
|
|
ImGuiStyle &style = ImGui::GetStyle();
|
|
float width = ImGui::GetWindowContentRegionWidth() / 3.0f - style.FramePadding.x;
|
|
float widthF = width - style.FramePadding.x;
|
|
|
|
TextWithWidth("YawLabel", "Yaw", width);
|
|
ImGui::SameLine();
|
|
TextWithWidth("PitchLabel", "Pitch", width);
|
|
ImGui::SameLine();
|
|
TextWithWidth("RollLabel", "Roll", width);
|
|
|
|
ImGui::PushItemWidth(widthF);
|
|
ImGui::InputDouble("##Yaw", &CalCtx.calibratedRotation(1), 0.1, 1.0, "%.8f");
|
|
ImGui::SameLine();
|
|
ImGui::InputDouble("##Pitch", &CalCtx.calibratedRotation(2), 0.1, 1.0, "%.8f");
|
|
ImGui::SameLine();
|
|
ImGui::InputDouble("##Roll", &CalCtx.calibratedRotation(0), 0.1, 1.0, "%.8f");
|
|
|
|
TextWithWidth("XLabel", "X", width);
|
|
ImGui::SameLine();
|
|
TextWithWidth("YLabel", "Y", width);
|
|
ImGui::SameLine();
|
|
TextWithWidth("ZLabel", "Z", width);
|
|
|
|
ImGui::InputDouble("##X", &CalCtx.calibratedTranslation(0), 1.0, 10.0, "%.8f");
|
|
ImGui::SameLine();
|
|
ImGui::InputDouble("##Y", &CalCtx.calibratedTranslation(1), 1.0, 10.0, "%.8f");
|
|
ImGui::SameLine();
|
|
ImGui::InputDouble("##Z", &CalCtx.calibratedTranslation(2), 1.0, 10.0, "%.8f");
|
|
|
|
TextWithWidth("ScaleLabel", "Scale", width);
|
|
|
|
ImGui::InputDouble("##Scale", &CalCtx.calibratedScale, 0.0001, 0.01, "%.8f");
|
|
ImGui::PopItemWidth();
|
|
}
|
|
|
|
void TextWithWidth(const char *label, const char *text, float width)
|
|
{
|
|
ImGui::BeginChild(label, ImVec2(width, ImGui::GetTextLineHeightWithSpacing()));
|
|
ImGui::Text(text);
|
|
ImGui::EndChild();
|
|
}
|
|
|