mirror of
https://github.com/hyblocker/OpenVR-SpaceCalibrator.git
synced 2026-10-07 06:00:23 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7f60358eac | ||
|
|
85c6b3cea4 |
No files matched your search
@@ -332,6 +332,19 @@ void ScanAndApplyProfile(CalibrationContext &ctx)
|
||||
};
|
||||
Driver.SendBlocking(req);
|
||||
}
|
||||
|
||||
if (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()
|
||||
@@ -461,3 +474,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);
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <Eigen/Core>
|
||||
#include <openvr.h>
|
||||
#include <vector>
|
||||
|
||||
enum class CalibrationState
|
||||
{
|
||||
@@ -29,6 +30,15 @@ struct CalibrationContext
|
||||
|
||||
vr::TrackedDevicePose_t devicePoses[vr::k_unMaxTrackedDeviceCount];
|
||||
|
||||
struct Chaperone
|
||||
{
|
||||
bool valid = false;
|
||||
bool autoApply = true;
|
||||
std::vector<vr::HmdQuad_t> geometry;
|
||||
vr::HmdMatrix34_t standingCenter;
|
||||
vr::HmdVector2_t playSpaceSize;
|
||||
} chaperone;
|
||||
|
||||
std::string messages;
|
||||
|
||||
void Message(const std::string &msg)
|
||||
@@ -42,4 +52,6 @@ extern CalibrationContext CalCtx;
|
||||
|
||||
void InitCalibrator();
|
||||
void CalibrationTick(double time);
|
||||
void StartCalibration();
|
||||
void StartCalibration();
|
||||
void LoadChaperoneBounds();
|
||||
void ApplyChaperoneBounds();
|
||||
@@ -18,6 +18,29 @@ static std::string ConfigFileName()
|
||||
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<picojson::array>())
|
||||
throw std::runtime_error("expected array, got " + obj.to_str());
|
||||
|
||||
auto &arr = obj.get<picojson::array>();
|
||||
if (arr.size() != numFloats)
|
||||
throw std::runtime_error("wrong buffer size");
|
||||
|
||||
for (int i = 0; i < numFloats; i++)
|
||||
buf[i] = (float) arr[i].get<double>();
|
||||
}
|
||||
|
||||
void LoadProfile(CalibrationContext &ctx)
|
||||
{
|
||||
ctx.validProfile = false;
|
||||
@@ -71,6 +94,33 @@ static void ParseProfileV2(CalibrationContext &ctx, std::istream &stream)
|
||||
ctx.calibratedTranslation(1) = obj["y"].get<double>();
|
||||
ctx.calibratedTranslation(2) = obj["z"].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) / 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;
|
||||
}
|
||||
|
||||
@@ -90,6 +140,25 @@ void SaveProfile(CalibrationContext &ctx)
|
||||
profile["y"].set<double>(ctx.calibratedTranslation(1));
|
||||
profile["z"].set<double>(ctx.calibratedTranslation(2));
|
||||
|
||||
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) / 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);
|
||||
|
||||
|
||||
@@ -231,6 +231,8 @@ void RunLoop()
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::GetIO().DisplaySize = ImVec2((float) fboTextureWidth, (float) fboTextureHeight);
|
||||
|
||||
ImGui_ImplGlfw_SetReadMouseFromGlfw(!dashboardVisible);
|
||||
ImGui_ImplOpenGL3_NewFrame();
|
||||
ImGui_ImplGlfw_NewFrame();
|
||||
@@ -241,16 +243,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 +270,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())
|
||||
{
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#include <algorithm>
|
||||
#include <imgui/imgui.h>
|
||||
|
||||
#define VERSION_STRING "0.7"
|
||||
#define VERSION_STRING "0.8"
|
||||
|
||||
struct VRDevice
|
||||
{
|
||||
@@ -91,6 +91,35 @@ void BuildMainWindow(bool runningInOverlay)
|
||||
CalCtx.validProfile = false;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (CalCtx.state == CalibrationState::Editing)
|
||||
{
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in new issue
Block a user