diff --git a/Makefile b/Makefile index 7630d09..09207d5 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ build: - CPATH=. g++ -lX11 -lglfw -lGL openvr/libopenvr_api.so src/main.cpp -o overlay + CPATH=. g++ -Wall -lX11 -lglfw -lGL openvr/libopenvr_api.so src/*.cpp -o overlay run: build ./overlay diff --git a/src/app.cpp b/src/app.cpp new file mode 100644 index 0000000..b437ddb --- /dev/null +++ b/src/app.cpp @@ -0,0 +1,119 @@ +#include "app.h" +#include "panel.h" +#include "util.h" +#include + +App::App() +{ + _tracking_origin = vr::ETrackingUniverseOrigin::TrackingUniverseStanding; + + InitOVR(); + InitX11(); + InitGLFW(); + + _panels.push_back(Panel(this, 0, 0, 0, _root_width, _root_height)); +} + +App::~App() +{ + vr::VR_Shutdown(); + glfwDestroyWindow(_gl_window); + glfwTerminate(); +} + +void App::InitX11() +{ + _xdisplay = XOpenDisplay(nullptr); + assert(_xdisplay != nullptr); + printf("Created X11 display\n"); + _root_window = XRootWindow(_xdisplay, 0); + XWindowAttributes attributes; + XGetWindowAttributes(_xdisplay, _root_window, &attributes); + _root_width = attributes.width; + _root_height = attributes.height; +} + +void App::InitOVR() +{ + vr::EVRInitError init_err; + vr_sys = vr::VR_Init(&init_err, vr::EVRApplicationType::VRApplication_Background); + if (init_err == vr::EVRInitError::VRInitError_Init_NoServerForBackgroundApp) + { + printf("SteamVR is not running\n"); + exit(1); + } + else if (init_err != 0) + { + printf("Could not initialize OpenVR session. Error code: %d\n", init_err); + exit(1); + } + printf("Initialized OpenVR\n"); + vr_overlay = vr::VROverlay(); +} + +void App::InitGLFW() +{ + assert(glfwInit() == true); + glfwWindowHint(GLFW_VISIBLE, false); + // TODO this is creating a 1x1 window, should it be bigger? + _gl_window = glfwCreateWindow(1, 1, "Overlay", nullptr, nullptr); + assert(_gl_window != nullptr); + glfwMakeContextCurrent(_gl_window); + printf("Created GLFW context\n"); +} + +void App::Update() +{ + for (auto &panel : _panels) + { + panel.Update(); + } +} + +glm::mat4 App::GetTrackerPose(TrackerID tracker) +{ + vr::VRControllerState_t state; + vr::TrackedDevicePose_t tracked_pose; + vr_sys->GetControllerStateWithPose( + _tracking_origin, + tracker, + &state, + sizeof(vr::VRControllerState_t), + &tracked_pose); + return ConvertMat(tracked_pose.mDeviceToAbsoluteTracking); +} + +bool App::IsGrabActive(vr::TrackedDeviceIndex_t controller) +{ + vr::VRControllerState_t state; + auto get_state_err = vr_sys->GetControllerState(controller, &state, sizeof(vr::VRControllerState_t)); + if (get_state_err == false) + { + printf("Error getting controller state: %d\n", get_state_err); + return false; + } + // printf("got state\n"); + + auto trigger_mask = vr::ButtonMaskFromId(vr::EVRButtonId::k_EButton_SteamVR_Trigger); + auto b_mask = vr::ButtonMaskFromId(vr::EVRButtonId::k_EButton_IndexController_B); + auto mask = trigger_mask | b_mask; + return (state.ulButtonPressed & mask) == mask; +} + +CursorPos App::GetCursorPosition() +{ + Window curr_root; + Window curr_win; + CursorPos pos; + CursorPos pos_local; + unsigned int buttons; + XQueryPointer( + _xdisplay, + _root_window, + &curr_root, + &curr_win, + &pos.x, &pos.y, + &pos_local.x, &pos_local.y, + &buttons); + return pos; +} diff --git a/src/app.h b/src/app.h new file mode 100644 index 0000000..c3dc926 --- /dev/null +++ b/src/app.h @@ -0,0 +1,42 @@ +#include "util.h" +#include +#include +#include + +class Panel; + +struct CursorPos +{ + int x, y; +}; + +class App +{ + public: + App(); + ~App(); + void Update(); + + glm::mat4 GetTrackerPose(TrackerID tracker); + bool IsGrabActive(vr::TrackedDeviceIndex_t controller); + CursorPos GetCursorPosition(); + + Display *_xdisplay; + Window _root_window; + GLFWwindow *_gl_window; + + int _root_width; + int _root_height; + + vr::ETrackingUniverseOrigin _tracking_origin; + + vr::IVRSystem *vr_sys; + vr::IVROverlay *vr_overlay; + + std::vector _panels; + + private: + void InitX11(); + void InitOVR(); + void InitGLFW(); +}; \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index fe963e5..9644184 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,212 +1,10 @@ -#include +#include "app.h" #include -#include -#include -#include - -#include "../openvr/openvr.h" -#include "util.h" - -auto TRACKING_UNIVERSE = vr::ETrackingUniverseOrigin::TrackingUniverseStanding; -const vr::HmdMatrix34_t DEFAULT_POSE = {{{1, 0, 0, 0}, {0, 1, 0, 1}, {0, 0, 1, 0}}}; - #define FRAMERATE 30 -uint16_t width; -uint16_t height; bool should_exit = false; -Display *xdisplay; -Window root_window; - -vr::IVRSystem *ovr_sys; -vr::IVROverlay *ovr_overlay; -vr::VROverlayHandle_t main_overlay; -vr::Texture_t vr_texture; - -GLuint screen_texture; -GLFWwindow *gl_window; - -void init_x11() -{ - xdisplay = XOpenDisplay(nullptr); - assert(xdisplay != nullptr); - printf("Created X11 display\n"); - root_window = XRootWindow(xdisplay, 0); - XWindowAttributes attributes; - XGetWindowAttributes(xdisplay, root_window, &attributes); - width = attributes.width; - height = attributes.height; -} - -void init_glfw() -{ - assert(glfwInit() == true); - glfwWindowHint(GLFW_VISIBLE, false); - gl_window = glfwCreateWindow(width, height, "Overlay", nullptr, nullptr); - assert(gl_window != nullptr); - glfwMakeContextCurrent(gl_window); - printf("Created GLFW context\n"); - - glGenTextures(1, &screen_texture); - glBindTexture(GL_TEXTURE_2D, screen_texture); - - vr_texture.eColorSpace = vr::EColorSpace::ColorSpace_Auto; - vr_texture.eType = vr::ETextureType::TextureType_OpenGL; - vr_texture.handle = (void *)(uintptr_t)screen_texture; -} - -void init_vr() -{ - vr::EVRInitError init_err; - ovr_sys = vr::VR_Init(&init_err, vr::EVRApplicationType::VRApplication_Background); - if (init_err == vr::EVRInitError::VRInitError_Init_NoServerForBackgroundApp) - { - printf("SteamVR is not running\n"); - exit(1); - } - else if (init_err != 0) - { - printf("Could not initialize OpenVR session. Error code: %d\n", init_err); - exit(1); - } - vr::VR_ShutdownInternal(); - ovr_sys = vr::VR_Init(&init_err, vr::EVRApplicationType::VRApplication_Overlay); - assert(init_err == 0); - printf("Initialized OpenVR\n"); - ovr_overlay = vr::VROverlay(); -} - -void init_overlay() -{ - auto overlay_err = ovr_overlay->CreateOverlay("deskpot", "Desktop view", &main_overlay); - assert(overlay_err == 0); - ovr_overlay->ShowOverlay(main_overlay); - ovr_overlay->SetOverlayWidthInMeters(main_overlay, 2.5f); - uint8_t col[4] = {20, 50, 50, 255}; - ovr_overlay->SetOverlayRaw(main_overlay, &col, 1, 1, 4); - printf("Created overlay instance\n"); - - vr::VRTextureBounds_t bounds{0, 1, 1, 0}; // (flipping uv on y axis because opengl and xorg are opposite) - ovr_overlay->SetOverlayTextureBounds(main_overlay, &bounds); - - ovr_overlay->SetOverlayTransformAbsolute(main_overlay, TRACKING_UNIVERSE, &DEFAULT_POSE); -} - -void render_desktop() -{ - auto frame = XGetImage(xdisplay, root_window, 0, 0, width, height, AllPlanes, ZPixmap); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, frame->data); - XDestroyImage(frame); - - auto set_err = ovr_overlay->SetOverlayTexture(main_overlay, &vr_texture); - // if (set_err) - // printf("error setting texture: %d\n", set_err); - assert(set_err == 0); -} - -void update_cursor() -{ - int pix_x, pix_y; - { - Window _t1; - int _t2; - unsigned int _t3; - XQueryPointer(xdisplay, root_window, &_t1, &_t1, &pix_x, &pix_y, &_t2, &_t2, &_t3); - } - // TODO: make this work when aspect ratio is >1 (root window is taller than it is wide) - float ratio = (float)height / (float)width; - float top_edge = 0.5f - ratio / 2.0f; - float x = pix_x / (float)width; - float y = 1.0f - (pix_y / (float)width + top_edge); - auto pos = vr::HmdVector2_t{x, y}; - ovr_overlay->SetOverlayCursorPositionOverride(main_overlay, &pos); -} - -bool is_grab_active(vr::TrackedDeviceIndex_t controller) -{ - vr::VRControllerState_t state; - ovr_sys->GetControllerState(controller, &state, sizeof(state)); - - auto trigger_mask = vr::ButtonMaskFromId(vr::EVRButtonId::k_EButton_SteamVR_Trigger); - auto b_mask = vr::ButtonMaskFromId(vr::EVRButtonId::k_EButton_IndexController_B); - auto mask = trigger_mask | b_mask; - return (state.ulButtonPressed & mask) == mask; -} - -vr::HmdMatrix34_t get_controller_pose(vr::TrackedDeviceIndex_t controller) -{ - vr::VRControllerState_t state; - vr::TrackedDevicePose_t tracked_pose; - ovr_sys->GetControllerStateWithPose(TRACKING_UNIVERSE, controller, &state, sizeof(state), &tracked_pose); - return tracked_pose.mDeviceToAbsoluteTracking; -} - -void update_pos() -{ - static bool is_held = false; - static vr::TrackedDeviceIndex_t active_controller; - - if (!is_held) - { - vr::TrackedDeviceIndex_t controllers[8]; - auto controller_count = ovr_sys->GetSortedTrackedDeviceIndicesOfClass(vr::ETrackedDeviceClass::TrackedDeviceClass_Controller, controllers, 8); - - for (int i = 0; i < controller_count; i++) - { - auto controller = controllers[i]; - - auto controller_pose = get_controller_pose(controller); - - vr::HmdMatrix34_t overlay_pose; - ovr_overlay->GetOverlayTransformAbsolute(main_overlay, &TRACKING_UNIVERSE, &overlay_pose); - - auto controller_pos = glm::vec3(controller_pose.m[0][3], controller_pose.m[1][3], controller_pose.m[2][3]); - auto overlay_pos = glm::vec3(overlay_pose.m[0][3], overlay_pose.m[1][3], overlay_pose.m[2][3]); - - bool close_enough = glm::length(overlay_pos - controller_pos) < 1.0f; - // close_enough = true; - - if (close_enough && is_grab_active(controller)) - { - // printf("Grabbed screen\n"); - is_held = true; - active_controller = controller; - - vr::HmdMatrix34_t abs_pose; - - ovr_overlay->GetOverlayTransformAbsolute(main_overlay, &TRACKING_UNIVERSE, &abs_pose); - auto abs_mat = convert_mat(abs_pose); - - auto controller_mat = convert_mat(get_controller_pose(controller)); - - vr::HmdMatrix34_t relative_pose = convert_mat(glm::inverse(controller_mat) * (abs_mat)); - - ovr_overlay->SetOverlayTransformTrackedDeviceRelative(main_overlay, controller, &relative_pose); - } - } - } - else - { - if (!is_grab_active(active_controller)) - { - // printf("Released screen\n"); - is_held = false; - - vr::HmdMatrix34_t relative_pose; - ovr_overlay->GetOverlayTransformTrackedDeviceRelative(main_overlay, &active_controller, &relative_pose); - auto relative_mat = convert_mat(relative_pose); - - auto controller_mat = convert_mat(get_controller_pose(active_controller)); - - vr::HmdMatrix34_t new_pose = convert_mat(controller_mat * relative_mat); - - ovr_overlay->SetOverlayTransformAbsolute(main_overlay, TRACKING_UNIVERSE, &new_pose); - } - } -} - void interrupted(int _sig) { should_exit = true; @@ -216,25 +14,13 @@ int main() { signal(SIGINT, interrupted); - init_vr(); - init_x11(); - init_glfw(); - init_overlay(); + auto app = App(); while (!should_exit) { - render_desktop(); - update_cursor(); - - update_pos(); - - glfwSwapBuffers(gl_window); + app.Update(); usleep(1000000 / FRAMERATE); } - printf("\nShutting down\n"); - vr::VR_Shutdown(); - glfwDestroyWindow(gl_window); - glfwTerminate(); return 0; } diff --git a/src/panel.cpp b/src/panel.cpp new file mode 100644 index 0000000..e95a683 --- /dev/null +++ b/src/panel.cpp @@ -0,0 +1,141 @@ +#include "panel.h" +#include "app.h" +#include "util.h" + +#include +#include +#include + +Panel::Panel(App *app, int index, int x, int y, int width, int height) + : _app(app), + _index(index), + _x(x), + _y(y), + _width(width), + _height(height) +{ + _name = "screen_view_" + std::to_string(index); + _alpha = 1.0f; + _active_hand = -1; + glGenTextures(1, &_gl_texture); + glBindTexture(GL_TEXTURE_2D, _gl_texture); + + _texture.eColorSpace = vr::EColorSpace::ColorSpace_Auto; + _texture.eType = vr::ETextureType::TextureType_OpenGL; + _texture.handle = (void *)(uintptr_t)_gl_texture; + + // create overlay + { + auto overlay_create_err = _app->vr_overlay->CreateOverlay(_name.c_str(), _name.c_str(), &_id); + assert(overlay_create_err == 0); + _app->vr_overlay->ShowOverlay(_id); + _app->vr_overlay->SetOverlayWidthInMeters(_id, 2.5f); + uint8_t col[4] = {20, 50, 50, 255}; + _app->vr_overlay->SetOverlayRaw(_id, &col, 1, 1, 4); + printf("Created overlay instance %d\n", _index); + + // (flipping uv on y axis because opengl and xorg are opposite) + vr::VRTextureBounds_t bounds{0, 1, 1, 0}; + _app->vr_overlay->SetOverlayTextureBounds(_id, &bounds); + + _app->vr_overlay->SetOverlayTransformAbsolute(_id, _app->_tracking_origin, &DEFAULT_POSE); + } +} + +void Panel::Update() +{ + Render(); + UpdateCursor(); + + if (!_is_held) + { + vr::TrackedDeviceIndex_t controllers[8]; + auto controller_count = _app->vr_sys->GetSortedTrackedDeviceIndicesOfClass(vr::ETrackedDeviceClass::TrackedDeviceClass_Controller, controllers, 8); + + for (unsigned int i = 0; i < controller_count; i++) + { + auto controller = controllers[i]; + + vr::HmdMatrix34_t overlay_pose; + vr::ETrackingUniverseOrigin tracking_universe; + _app->vr_overlay->GetOverlayTransformAbsolute(_id, &tracking_universe, &overlay_pose); + + auto controller_pose = _app->GetTrackerPose(controller); + auto controller_pos = glm::vec3(controller_pose[3]); + auto overlay_pos = glm::vec3(ConvertMat(overlay_pose)[3]); + + bool close_enough = glm::length(overlay_pos - controller_pos) < 1.0f; + // close_enough = true; + + if (close_enough && _app->IsGrabActive(controller)) + { + ControllerGrab(controller); + } + } + } + else + { + if (!_app->IsGrabActive(_active_hand)) + { + ControllerRelease(); + } + } +} + +void Panel::Render() +{ + auto frame = XGetImage(_app->_xdisplay, _app->_root_window, _x, _y, _width, _height, AllPlanes, ZPixmap); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, _width, _height, 0, GL_BGRA, GL_UNSIGNED_BYTE, frame->data); + XDestroyImage(frame); + + auto set_texture_err = _app->vr_overlay->SetOverlayTexture(_id, &_texture); + assert(set_texture_err == 0); +} + +void Panel::UpdateCursor() +{ + auto global = _app->GetCursorPosition(); + // TODO: make this work when aspect ratio is >1 (root window is taller than it is wide) + // TODO take into account that the panel is smaller than the root window + float ratio = (float)_height / (float)_width; + float top_edge = 0.5f - ratio / 2.0f; + float x = global.x / (float)_width; + float y = 1.0f - (global.y / (float)_width + top_edge); + auto pos = vr::HmdVector2_t{x, y}; + _app->vr_overlay->SetOverlayCursorPositionOverride(_id, &pos); +} + +void Panel::ControllerGrab(TrackerID controller) +{ + printf("Grabbed panel %d\n", _index); + _is_held = true; + _active_hand = controller; + + vr::HmdMatrix34_t abs_pose; + vr::ETrackingUniverseOrigin tracking_universe; + + _app->vr_overlay->GetOverlayTransformAbsolute(_id, &tracking_universe, &abs_pose); + auto abs_mat = ConvertMat(abs_pose); + + auto controller_mat = _app->GetTrackerPose(controller); + + vr::HmdMatrix34_t relative_pose = ConvertMat(glm::inverse(controller_mat) * (abs_mat)); + + _app->vr_overlay->SetOverlayTransformTrackedDeviceRelative(_id, controller, &relative_pose); +} + +void Panel::ControllerRelease() +{ + printf("Released panel %d\n", _index); + _is_held = false; + + vr::HmdMatrix34_t relative_pose; + _app->vr_overlay->GetOverlayTransformTrackedDeviceRelative(_id, &_active_hand, &relative_pose); + auto relative_mat = ConvertMat(relative_pose); + + auto controller_mat = _app->GetTrackerPose(_active_hand); + + vr::HmdMatrix34_t new_pose = ConvertMat(controller_mat * relative_mat); + + _app->vr_overlay->SetOverlayTransformAbsolute(_id, _app->_tracking_origin, &new_pose); +} \ No newline at end of file diff --git a/src/panel.h b/src/panel.h new file mode 100644 index 0000000..e2becd8 --- /dev/null +++ b/src/panel.h @@ -0,0 +1,35 @@ +#include "util.h" +#include +#include + +const vr::HmdMatrix34_t DEFAULT_POSE = {{{1, 0, 0, 0}, {0, 1, 0, 1}, {0, 0, 1, 0}}}; +class App; + +class Panel +{ + public: + Panel(App *app, int index, int xmin, int xmax, int ymin, int ymax); + + void Update(); + + private: + void Render(); + void UpdateCursor(); + void ControllerGrab(TrackerID); + void ControllerRelease(); + + App *_app; + OverlayID _id; + int _index; + std::string _name; + + TrackerID _active_hand; + bool _is_held; + + unsigned int _x, _y; + unsigned int _width, _height; + float _alpha; + + vr::Texture_t _texture; + GLuint _gl_texture; +}; \ No newline at end of file diff --git a/src/util.h b/src/util.h index a8f6ebd..1ddd58b 100644 --- a/src/util.h +++ b/src/util.h @@ -1,14 +1,19 @@ +#pragma once + #include "../openvr/openvr.h" #include -inline void print_matrix(vr::HmdMatrix34_t m) +typedef vr::TrackedDeviceIndex_t TrackerID; +typedef vr::VROverlayHandle_t OverlayID; + +inline void PrintMat(vr::HmdMatrix34_t m) { printf("[%.2f, %.2f, %.2f, %.2f]\n", m.m[0][0], m.m[0][1], m.m[0][2], m.m[0][3]); printf("[%.2f, %.2f, %.2f, %.2f]\n", m.m[1][0], m.m[1][1], m.m[1][2], m.m[1][3]); printf("[%.2f, %.2f, %.2f, %.2f]\n", m.m[2][0], m.m[2][1], m.m[2][2], m.m[2][3]); } -inline void print_matrix(glm::mat4x4 m) +inline void PrintMat(glm::mat4x4 m) { printf("[%.2f, %.2f, %.2f, %.2f]\n", m[0][0], m[0][1], m[0][2], m[0][3]); printf("[%.2f, %.2f, %.2f, %.2f]\n", m[1][0], m[1][1], m[1][2], m[1][3]); @@ -16,7 +21,7 @@ inline void print_matrix(glm::mat4x4 m) printf("[%.2f, %.2f, %.2f, %.2f]\n", m[3][0], m[3][1], m[3][2], m[3][3]); } -inline glm::mat4x4 convert_mat(vr::HmdMatrix34_t mat) +inline glm::mat4x4 ConvertMat(vr::HmdMatrix34_t mat) { auto m = mat.m; return glm::mat4x4( @@ -26,7 +31,7 @@ inline glm::mat4x4 convert_mat(vr::HmdMatrix34_t mat) m[0][3], m[1][3], m[2][3], 1); } -inline vr::HmdMatrix34_t convert_mat(glm::mat4x4 m) +inline vr::HmdMatrix34_t ConvertMat(glm::mat4x4 m) { // clang-format off return vr::HmdMatrix34_t{{ @@ -35,4 +40,9 @@ inline vr::HmdMatrix34_t convert_mat(glm::mat4x4 m) {m[0][2], m[1][2], m[2][2], m[3][2]} }}; // clang-format on -} \ No newline at end of file +} + +inline glm::vec3 GetPos(glm::mat4x4 mat) +{ + return glm::vec3(mat[3][0], mat[3][1], mat[3][2]); +}