Compare commits

..

10 commits

Author SHA1 Message Date
0b7f841fbf update README.md 2023-04-29 19:39:51 +02:00
035d9e7296 add scrolling 2023-04-29 19:38:36 +02:00
858da78820 bump version 2023-04-29 18:31:54 +02:00
Crispy
44cf1f2aef
update demo video 2023-04-29 18:31:17 +02:00
6d69a24668 update README.md 2023-04-29 18:29:35 +02:00
d33eb40c69 color code lasers 2023-04-29 18:14:15 +02:00
d94e3040bb use separate steamvr action sets for different modes 2023-04-29 17:40:03 +02:00
29f4595750 mouse left & right click 2023-04-29 17:06:02 +02:00
d204387e19 move cursor with lasers 2023-04-29 16:44:25 +02:00
0042ee3b58 don't reset positions while hidden 2023-04-25 18:41:02 +02:00
14 changed files with 476 additions and 165 deletions

View file

@ -1,7 +1,7 @@
VERSION=v0.1.0 VERSION=v0.2.1
# CC := g++ # CC := g++
CC := clang++ CC := clang++
LFLAGS := -lX11 -lXrandr -lglfw -lGL LFLAGS := -lX11 -lXrandr -lXtst -lglfw -lGL
LIBS := openvr/libopenvr_api.so LIBS := openvr/libopenvr_api.so
SRC := src/*.cpp SRC := src/*.cpp
OUT := ./sinpin_vr OUT := ./sinpin_vr

View file

@ -1,14 +1,21 @@
# sinpin-vr # sinpin-vr
A SteamVR overlay for Linux+X11 that displays all your screens in VR. A SteamVR overlay for Linux+X11 that displays all your screens in VR.
https://user-images.githubusercontent.com/54243225/233798783-27d1a6ae-b71d-448f-bb67-76015e539452.mp4 https://user-images.githubusercontent.com/54243225/235313348-2ce9b2d8-8458-49f8-ba94-16e577c4f502.mp4
Note: only index controllers have default bindings right now, feel free to make a PR.
## features ## features
- one overlay per screen - one overlay per screen
- shows cursor position - shows cursor position
- global visibility toggle (default: press left touch) - global visibility toggle (default: long press left B)
- reset positions (default: long press left A) - reset positions (default: long press left A)
- edit mode (default: press right touch) - activate cursor input (default: press touchpad)
- left mouse default: trigger
- right mouse default: A
- middle mouse default: not bound
- scrolling default: joystick up/down
- edit mode (default: long press press right B)
- move screens around (default: trigger) - move screens around (default: trigger)
- resize screens (move with two controllers) - resize screens (move with two controllers)
- push/pull screens (default: joystick up/down) - push/pull screens (default: joystick up/down)

View file

@ -16,18 +16,43 @@
"requirement": "mandatory", "requirement": "mandatory",
"type": "boolean" "type": "boolean"
}, },
{
"name": "/actions/main/in/grab",
"requirement": "mandatory",
"type": "boolean"
},
{ {
"name": "/actions/main/in/reset", "name": "/actions/main/in/reset",
"requirement": "suggested", "requirement": "suggested",
"type": "boolean" "type": "boolean"
}, },
{ {
"name": "/actions/main/in/distance", "name": "/actions/edit/in/grab",
"requirement": "mandatory",
"type": "boolean"
},
{
"name": "/actions/edit/in/distance",
"requirement": "suggested",
"type": "vector2"
},
{
"name": "/actions/cursor/in/activate_cursor",
"requirement": "suggested",
"type": "boolean"
},
{
"name": "/actions/cursor/in/mouse_left",
"requirement": "suggested",
"type": "boolean"
},
{
"name": "/actions/cursor/in/mouse_right",
"requirement": "suggested",
"type": "boolean"
},
{
"name": "/actions/cursor/in/mouse_middle",
"requirement": "optional",
"type": "boolean"
},
{
"name": "/actions/cursor/in/scroll",
"requirement": "suggested", "requirement": "suggested",
"type": "vector2" "type": "vector2"
} }
@ -36,17 +61,29 @@
{ {
"name": "/actions/main", "name": "/actions/main",
"usage": "leftright" "usage": "leftright"
},
{
"name": "/actions/edit",
"usage": "single"
},
{
"name": "/actions/cursor",
"usage": "single"
} }
], ],
"localization": [ "localization": [
{ {
"language_tag": "en_us", "language_tag": "en_us",
"/actions/main": "Overlay actions", "/actions/main/in/toggle_visibility": "toggle visibility",
"/actions/main/in/toggle_visibility": "Toggle visibility", "/actions/main/in/edit_mode": "toggle edit mode",
"/actions/main/in/edit_mode": "Toggle edit mode", "/actions/main/in/reset": "reset positions",
"/actions/main/in/grab": "grab panel", "/actions/edit/in/grab": "grab panel",
"/actions/main/in/distance": "Move away", "/actions/edit/in/distance": "push/pull overlay",
"/actions/main/in/reset": "Reset positions" "/actions/cursor/in/activate_cursor": "activate cursor",
"/actions/cursor/in/mouse_left": "left mouse button",
"/actions/cursor/in/mouse_right": "right mouse button",
"/actions/cursor/in/mouse_middle": "middle mouse button",
"/actions/cursor/in/scroll": "scroll"
} }
] ]
} }

View file

@ -140,6 +140,122 @@
}, },
"app_key" : "system.generated.sinpin_vr", "app_key" : "system.generated.sinpin_vr",
"bindings" : { "bindings" : {
"/actions/cursor" : {
"sources" : [
{
"inputs" : {
"click" : {
"output" : "/actions/cursor/in/mouse_right"
}
},
"mode" : "button",
"path" : "/user/hand/left/input/a"
},
{
"inputs" : {
"click" : {
"output" : "/actions/cursor/in/mouse_right"
}
},
"mode" : "button",
"path" : "/user/hand/right/input/a"
},
{
"inputs" : {
"click" : {
"output" : "/actions/cursor/in/mouse_left"
}
},
"mode" : "button",
"path" : "/user/hand/left/input/trigger"
},
{
"inputs" : {
"click" : {
"output" : "/actions/cursor/in/mouse_left"
}
},
"mode" : "button",
"path" : "/user/hand/right/input/trigger"
},
{
"inputs" : {
"click" : {
"output" : "/actions/cursor/in/activate_cursor"
}
},
"mode" : "button",
"path" : "/user/hand/left/input/trackpad"
},
{
"inputs" : {
"click" : {
"output" : "/actions/cursor/in/activate_cursor"
}
},
"mode" : "button",
"path" : "/user/hand/right/input/trackpad"
},
{
"inputs" : {
"position" : {
"output" : "/actions/cursor/in/scroll"
}
},
"mode" : "joystick",
"path" : "/user/hand/left/input/thumbstick"
},
{
"inputs" : {
"position" : {
"output" : "/actions/cursor/in/scroll"
}
},
"mode" : "joystick",
"path" : "/user/hand/right/input/thumbstick"
}
]
},
"/actions/edit" : {
"sources" : [
{
"inputs" : {
"position" : {
"output" : "/actions/edit/in/distance"
}
},
"mode" : "joystick",
"path" : "/user/hand/left/input/thumbstick"
},
{
"inputs" : {
"position" : {
"output" : "/actions/edit/in/distance"
}
},
"mode" : "joystick",
"path" : "/user/hand/right/input/thumbstick"
},
{
"inputs" : {
"click" : {
"output" : "/actions/edit/in/grab"
}
},
"mode" : "button",
"path" : "/user/hand/left/input/trigger"
},
{
"inputs" : {
"click" : {
"output" : "/actions/edit/in/grab"
}
},
"mode" : "button",
"path" : "/user/hand/right/input/trigger"
}
]
},
"/actions/legacy" : { "/actions/legacy" : {
"haptics" : [ "haptics" : [
{ {
@ -588,60 +704,6 @@
}, },
"/actions/main" : { "/actions/main" : {
"sources" : [ "sources" : [
{
"inputs" : {
"position" : {
"output" : "/actions/main/in/distance"
}
},
"mode" : "joystick",
"path" : "/user/hand/right/input/thumbstick"
},
{
"inputs" : {
"click" : {
"output" : "/actions/main/in/grab"
}
},
"mode" : "button",
"path" : "/user/hand/right/input/trigger"
},
{
"inputs" : {
"click" : {
"output" : "/actions/main/in/grab"
}
},
"mode" : "button",
"path" : "/user/hand/left/input/trigger"
},
{
"inputs" : {
"position" : {
"output" : "/actions/main/in/distance"
}
},
"mode" : "joystick",
"path" : "/user/hand/left/input/thumbstick"
},
{
"inputs" : {
"click" : {
"output" : "/actions/main/in/toggle_visibility"
}
},
"mode" : "button",
"path" : "/user/hand/left/input/trackpad"
},
{
"inputs" : {
"click" : {
"output" : "/actions/main/in/edit_mode"
}
},
"mode" : "button",
"path" : "/user/hand/right/input/trackpad"
},
{ {
"inputs" : { "inputs" : {
"long" : { "long" : {
@ -650,6 +712,27 @@
}, },
"mode" : "button", "mode" : "button",
"path" : "/user/hand/left/input/a" "path" : "/user/hand/left/input/a"
},
{
"inputs" : {
"click" : {
"output" : "/actions/main/in/mouse_left"
},
"long" : {
"output" : "/actions/main/in/toggle_visibility"
}
},
"mode" : "button",
"path" : "/user/hand/left/input/b"
},
{
"inputs" : {
"long" : {
"output" : "/actions/main/in/edit_mode"
}
},
"mode" : "button",
"path" : "/user/hand/right/input/b"
} }
] ]
} }
@ -658,7 +741,7 @@
"controller_type" : "knuckles", "controller_type" : "knuckles",
"description" : "yes", "description" : "yes",
"interaction_profile" : "", "interaction_profile" : "",
"name" : "Saved sinpin_vr bindings", "name" : "Default sinpin_vr bindings",
"options" : { "options" : {
"mirror_actions" : true, "mirror_actions" : true,
"simulated_controller_type" : "none" "simulated_controller_type" : "none"

View file

@ -1,12 +1,16 @@
#include "app.h" #include "app.h"
#include "controller.h" #include "controller.h"
#include "util.h" #include "util.h"
#include <X11/Xlib.h>
#include <X11/extensions/XTest.h>
#include <X11/extensions/Xrandr.h> #include <X11/extensions/Xrandr.h>
#include <cassert> #include <cassert>
#include <glm/matrix.hpp> #include <glm/matrix.hpp>
const VRMat root_start_pose = {{{1, 0, 0, 0}, {0, 1, 0, 0.8f}, {0, 0, 1, 0}}}; // 0.8m above origin const VRMat root_start_pose = {{{1, 0, 0, 0}, {0, 1, 0, 0.8f}, {0, 0, 1, 0}}}; // 0.8m above origin
const int FRAME_INTERVAL = 4; // number of update loops until the frame buffer is updated
App::App() App::App()
{ {
_tracking_origin = vr::TrackingUniverseStanding; _tracking_origin = vr::TrackingUniverseStanding;
@ -51,17 +55,31 @@ App::App()
printf("actions path: %s\n", _actions_path.c_str()); printf("actions path: %s\n", _actions_path.c_str());
vr_input->SetActionManifestPath(_actions_path.c_str()); vr_input->SetActionManifestPath(_actions_path.c_str());
auto action_err = vr_input->GetActionHandle("/actions/main/in/grab", &_input_handles.grab); auto action_err = vr_input->GetActionHandle("/actions/edit/in/grab", &_input_handles.edit.grab);
assert(action_err == 0); assert(action_err == 0);
action_err = vr_input->GetActionHandle("/actions/main/in/toggle_visibility", &_input_handles.toggle_hidden); action_err = vr_input->GetActionHandle("/actions/main/in/toggle_visibility", &_input_handles.main.toggle_hidden);
assert(action_err == 0); assert(action_err == 0);
action_err = vr_input->GetActionHandle("/actions/main/in/edit_mode", &_input_handles.edit_mode); action_err = vr_input->GetActionHandle("/actions/cursor/in/activate_cursor", &_input_handles.cursor.activate);
assert(action_err == 0); assert(action_err == 0);
action_err = vr_input->GetActionHandle("/actions/main/in/reset", &_input_handles.reset); action_err = vr_input->GetActionHandle("/actions/main/in/edit_mode", &_input_handles.main.edit_mode);
assert(action_err == 0); assert(action_err == 0);
action_err = vr_input->GetActionHandle("/actions/main/in/distance", &_input_handles.distance); action_err = vr_input->GetActionHandle("/actions/main/in/reset", &_input_handles.main.reset);
assert(action_err == 0); assert(action_err == 0);
action_err = vr_input->GetActionSetHandle("/actions/main", &_input_handles.set); action_err = vr_input->GetActionHandle("/actions/edit/in/distance", &_input_handles.edit.distance);
assert(action_err == 0);
action_err = vr_input->GetActionHandle("/actions/cursor/in/mouse_left", &_input_handles.cursor.mouse_left);
assert(action_err == 0);
action_err = vr_input->GetActionHandle("/actions/cursor/in/mouse_right", &_input_handles.cursor.mouse_right);
assert(action_err == 0);
action_err = vr_input->GetActionHandle("/actions/cursor/in/mouse_middle", &_input_handles.cursor.mouse_middle);
assert(action_err == 0);
action_err = vr_input->GetActionHandle("/actions/cursor/in/scroll", &_input_handles.cursor.scroll);
assert(action_err == 0);
action_err = vr_input->GetActionSetHandle("/actions/main", &_input_handles.main_set);
assert(action_err == 0);
action_err = vr_input->GetActionSetHandle("/actions/edit", &_input_handles.edit_set);
assert(action_err == 0);
action_err = vr_input->GetActionSetHandle("/actions/cursor", &_input_handles.cursor_set);
assert(action_err == 0); assert(action_err == 0);
} }
} }
@ -126,9 +144,9 @@ void App::InitRootOverlay()
_root_overlay.SetTextureToColor(110, 30, 190); _root_overlay.SetTextureToColor(110, 30, 190);
} }
void App::Update() void App::Update(float dtime)
{ {
UpdateInput(); UpdateInput(dtime);
if (!_hidden) if (!_hidden)
{ {
_root_overlay.Update(); _root_overlay.Update();
@ -141,32 +159,40 @@ void App::Update()
_frames_since_framebuffer += 1; _frames_since_framebuffer += 1;
} }
void App::UpdateInput() void App::UpdateInput(float dtime)
{ {
vr::VRActiveActionSet_t main; vr::VRActiveActionSet_t active_sets[2];
main.ulActionSet = _input_handles.set; active_sets[0].ulActionSet = _input_handles.main_set;
main.ulRestrictedToDevice = 0; active_sets[0].ulRestrictedToDevice = 0;
main.nPriority = 10; active_sets[0].nPriority = 10;
vr::EVRInputError err = vr_input->UpdateActionState(&main, sizeof(vr::VRActiveActionSet_t), 1); int set_count = 1;
if (err) if (!_hidden)
{ {
printf("Error: (update action state): %d\n", err); set_count = 2;
active_sets[1].ulRestrictedToDevice = 0;
active_sets[1].nPriority = 10;
active_sets[1].ulActionSet = _input_handles.cursor_set;
if (_edit_mode)
active_sets[1].ulActionSet = _input_handles.edit_set;
} }
vr::EVRInputError err = vr_input->UpdateActionState(active_sets, sizeof(vr::VRActiveActionSet_t), set_count);
if (err)
printf("Error updating action state: %d\n", err);
vr_sys->GetDeviceToAbsoluteTrackingPose(_tracking_origin, 0, _tracker_poses, MAX_TRACKERS); vr_sys->GetDeviceToAbsoluteTrackingPose(_tracking_origin, 0, _tracker_poses, MAX_TRACKERS);
if (IsInputJustPressed(_input_handles.toggle_hidden)) if (IsInputJustPressed(_input_handles.main.toggle_hidden))
{ {
_hidden = !_hidden; _hidden = !_hidden;
for (auto &panel : _panels) for (auto &panel : _panels)
{ {
panel.SetHidden(_hidden); panel.SetHidden(_hidden);
} }
_root_overlay.SetHidden(_hidden || !_edit_mode); UpdateUIVisibility();
_controllers[0]->SetHidden(_hidden || !_edit_mode);
_controllers[1]->SetHidden(_hidden || !_edit_mode);
} }
if (IsInputJustPressed(_input_handles.reset)) if (!_hidden)
{
if (IsInputJustPressed(_input_handles.main.reset))
{ {
_root_overlay.SetTransformWorld(&root_start_pose); _root_overlay.SetTransformWorld(&root_start_pose);
_root_overlay.SetWidth(0.25f); _root_overlay.SetWidth(0.25f);
@ -175,23 +201,30 @@ void App::UpdateInput()
panel.ResetTransform(); panel.ResetTransform();
} }
} }
if (!_hidden && IsInputJustPressed(_input_handles.edit_mode)) if (IsInputJustPressed(_input_handles.main.edit_mode))
{ {
_edit_mode = !_edit_mode; _edit_mode = !_edit_mode;
_root_overlay.SetHidden(_hidden || !_edit_mode); UpdateUIVisibility();
_controllers[0]->SetHidden(_hidden || !_edit_mode); if (_edit_mode && _active_cursor.has_value())
_controllers[1]->SetHidden(_hidden || !_edit_mode);
}
if (_edit_mode)
{ {
_controllers[0]->Update(); _active_cursor.value()->_cursor_active = false;
_controllers[1]->Update(); _active_cursor = {};
} }
}
}
_controllers[0]->Update(dtime);
_controllers[1]->Update(dtime);
}
void App::UpdateUIVisibility()
{
bool state = _hidden || !_edit_mode;
_root_overlay.SetHidden(state);
} }
void App::UpdateFramebuffer() void App::UpdateFramebuffer()
{ {
if (_frames_since_framebuffer < 2) if (_frames_since_framebuffer < FRAME_INTERVAL)
{ {
return; return;
} }
@ -255,7 +288,7 @@ Ray App::IntersectRay(glm::vec3 origin, glm::vec3 direction, float max_len)
for (auto &panel : _panels) for (auto &panel : _panels)
{ {
auto r_panel = panel.GetOverlay()->IntersectRay(origin, direction, max_len); auto r_panel = panel.IntersectRay(origin, direction, max_len);
if (r_panel.distance < ray.distance) if (r_panel.distance < ray.distance)
{ {
ray = r_panel; ray = r_panel;
@ -281,3 +314,14 @@ CursorPos App::GetCursorPosition()
&buttons); &buttons);
return pos; return pos;
} }
void App::SetCursor(int x, int y)
{
// I don't know what the return value of XWarpPointer means, it seems to be 1 on success.
XWarpPointer(_xdisplay, _root_window, _root_window, 0, 0, _root_width, _root_height, x, y);
}
void App::SendMouseInput(unsigned int button, bool state)
{
XTestFakeButtonEvent(_xdisplay, button, state, 0);
}

View file

@ -18,12 +18,28 @@ struct CursorPos
struct InputHandles struct InputHandles
{ {
vr::VRActionSetHandle_t set; struct
{
vr::VRActionHandle_t toggle_hidden; vr::VRActionHandle_t toggle_hidden;
vr::VRActionHandle_t distance;
vr::VRActionHandle_t grab;
vr::VRActionHandle_t edit_mode; vr::VRActionHandle_t edit_mode;
vr::VRActionHandle_t reset; vr::VRActionHandle_t reset;
} main;
vr::VRActionSetHandle_t main_set;
struct
{
vr::VRActionHandle_t activate;
vr::VRActionHandle_t mouse_left;
vr::VRActionHandle_t mouse_right;
vr::VRActionHandle_t mouse_middle;
vr::VRActionHandle_t scroll;
} cursor;
vr::VRActionSetHandle_t cursor_set;
struct
{
vr::VRActionHandle_t distance;
vr::VRActionHandle_t grab;
} edit;
vr::VRActionSetHandle_t edit_set;
}; };
class App class App
@ -31,7 +47,7 @@ class App
public: public:
App(); App();
~App(); ~App();
void Update(); void Update(float dtime);
std::vector<TrackerID> GetControllers(); std::vector<TrackerID> GetControllers();
glm::mat4 GetTrackerPose(TrackerID tracker); glm::mat4 GetTrackerPose(TrackerID tracker);
@ -41,6 +57,8 @@ class App
CursorPos GetCursorPosition(); CursorPos GetCursorPosition();
Ray IntersectRay(glm::vec3 origin, glm::vec3 direction, float max_len); Ray IntersectRay(glm::vec3 origin, glm::vec3 direction, float max_len);
void SetCursor(int x, int y);
void SendMouseInput(unsigned int button, bool state);
Display *_xdisplay; Display *_xdisplay;
Window _root_window; Window _root_window;
@ -69,6 +87,7 @@ class App
std::vector<Panel> _panels; std::vector<Panel> _panels;
bool _hidden = false; bool _hidden = false;
bool _edit_mode = false; bool _edit_mode = false;
std::optional<Controller *> _active_cursor;
private: private:
void InitX11(); void InitX11();
@ -77,5 +96,6 @@ class App
void InitRootOverlay(); void InitRootOverlay();
void UpdateFramebuffer(); void UpdateFramebuffer();
void UpdateInput(); void UpdateInput(float dtime);
void UpdateUIVisibility();
}; };

View file

@ -4,7 +4,10 @@
#include "util.h" #include "util.h"
#include <string> #include <string>
const float width = 0.004f; const float LASER_WIDTH = 0.004f;
const Color EDIT_COLOR{1, 0.6f, 1};
const Color CURSOR_COLOR{0.3f, 1, 1};
const float SCROLL_SPEED = 48.0f;
Controller::Controller(App *app, ControllerSide side) Controller::Controller(App *app, ControllerSide side)
{ {
@ -13,7 +16,8 @@ Controller::Controller(App *app, ControllerSide side)
_input_handle = 0; _input_handle = 0;
_is_connected = false; _is_connected = false;
_side = side; _side = side;
_hidden = false; _cursor_active = false;
_last_sent_scroll = 0;
std::string laser_name = "controller_laser_"; std::string laser_name = "controller_laser_";
if (side == ControllerSide::Left) if (side == ControllerSide::Left)
@ -23,7 +27,7 @@ Controller::Controller(App *app, ControllerSide side)
_laser = Overlay(app, laser_name); _laser = Overlay(app, laser_name);
UpdateStatus(); UpdateStatus();
_laser.SetTextureToColor(255, 200, 255); _laser.SetTextureToColor(255, 255, 255);
_laser.SetAlpha(0.2f); _laser.SetAlpha(0.2f);
_laser.SetHidden(true); _laser.SetHidden(true);
} }
@ -48,12 +52,6 @@ bool Controller::IsConnected()
return _is_connected; return _is_connected;
} }
void Controller::SetHidden(bool state)
{
_hidden = state;
_laser.SetHidden(_hidden);
}
void Controller::ReleaseOverlay() void Controller::ReleaseOverlay()
{ {
_grabbed_overlay = nullptr; _grabbed_overlay = nullptr;
@ -74,21 +72,108 @@ glm::vec3 Controller::GetLastRot()
return _last_rotation; return _last_rotation;
} }
void Controller::Update() void Controller::Update(float dtime)
{ {
UpdateStatus(); UpdateStatus();
if (!_is_connected || _hidden) if (!_is_connected)
return; return;
UpdateLaser(); UpdateLaser();
float move = _app->GetInputAnalog(_app->_input_handles.distance, _input_handle).y * 0.1; // TODO use frame time if (_app->_edit_mode)
if (_grabbed_overlay && move != 0.0f) {
_laser.SetColor(EDIT_COLOR);
if (_last_ray.overlay != nullptr)
{
auto ray = _last_ray;
if (_app->IsInputJustPressed(_app->_input_handles.edit.grab, _input_handle))
{
if (ray.overlay->IsHeld())
{
ray.overlay->ControllerResize(this);
}
else
{
_grabbed_overlay = ray.overlay;
ray.overlay->ControllerGrab(this);
}
}
}
if (_grabbed_overlay != nullptr)
{
float move = _app->GetInputAnalog(_app->_input_handles.edit.distance, _input_handle).y * dtime * 8;
if (move != 0.0f)
{ {
auto transform = _grabbed_overlay->GetTarget()->transform; auto transform = _grabbed_overlay->GetTarget()->transform;
transform.m[2][3] = glm::clamp(transform.m[2][3] - move, -5.0f, -0.1f); // moving along z axis transform.m[2][3] = glm::clamp(transform.m[2][3] - move, -5.0f, -0.1f); // moving along z axis
_grabbed_overlay->SetTransformTracker(_device_index, &transform); _grabbed_overlay->SetTransformTracker(_device_index, &transform);
} }
}
}
else // cursor mode
{
if (_app->IsInputJustPressed(_app->_input_handles.cursor.activate, _input_handle))
{
if (!_cursor_active && _app->_active_cursor.has_value())
{
_app->_active_cursor.value()->_cursor_active = false;
_app->_active_cursor = this;
}
_cursor_active = !_cursor_active;
_app->_active_cursor = this;
_laser.SetColor(CURSOR_COLOR);
}
if (_cursor_active)
{
// printf("update cursor on hand %d\n", _side);
if (_last_ray.overlay != nullptr && _last_ray.hit_panel != nullptr)
{
auto pos = glm::vec2(_last_ray.local_pos.x, _last_ray.local_pos.y);
// normalize positions to +-0.5
pos /= _last_ray.overlay->Width();
pos.y *= -1;
// shift to 0-1
pos.x += 0.5f;
pos.y += 0.5f * _last_ray.overlay->Ratio();
pos *= _last_ray.hit_panel->Width();
_last_ray.hit_panel->SetCursor(pos.x, pos.y);
}
UpdateMouseButton(_app->_input_handles.cursor.mouse_left, 1);
UpdateMouseButton(_app->_input_handles.cursor.mouse_middle, 2);
UpdateMouseButton(_app->_input_handles.cursor.mouse_right, 3);
auto scroll_state = _app->GetInputAnalog(_app->_input_handles.cursor.scroll, _input_handle);
if (scroll_state.y != 0)
{
_last_sent_scroll += dtime * glm::abs(scroll_state.y) * SCROLL_SPEED;
if (_last_sent_scroll > 1)
{
_last_sent_scroll = 0;
if (scroll_state.y > 0)
{
_app->SendMouseInput(4, true);
_app->SendMouseInput(4, false);
}
else if (scroll_state.y < 0)
{
_app->SendMouseInput(5, true);
_app->SendMouseInput(5, false);
}
}
}
}
}
}
void Controller::UpdateMouseButton(vr::VRActionHandle_t binding, unsigned int button)
{
auto state = _app->GetInputDigital(binding, _input_handle);
if (state.bChanged)
{
_app->SendMouseInput(button, state.bState);
}
} }
void Controller::UpdateLaser() void Controller::UpdateLaser()
@ -108,24 +193,9 @@ void Controller::UpdateLaser()
hmd_local_pos.z = 0; hmd_local_pos.z = 0;
auto hmd_dir = glm::normalize(hmd_local_pos); auto hmd_dir = glm::normalize(hmd_local_pos);
VRMat transform = {{{width * hmd_dir.y, 0, width * hmd_dir.x, 0}, {width * -hmd_dir.x, 0, width * hmd_dir.y, 0}, {0, len, 0, len * -0.5f}}}; VRMat transform = {{{LASER_WIDTH * hmd_dir.y, 0, LASER_WIDTH * hmd_dir.x, 0}, {LASER_WIDTH * -hmd_dir.x, 0, LASER_WIDTH * hmd_dir.y, 0}, {0, len, 0, len * -0.5f}}};
_laser.SetTransformTracker(_device_index, &transform); _laser.SetTransformTracker(_device_index, &transform);
_laser.SetHidden(!_is_connected || _app->_hidden || (!_app->_edit_mode && !_cursor_active));
if (ray.overlay != nullptr)
{
if (_app->IsInputJustPressed(_app->_input_handles.grab, _input_handle))
{
if (ray.overlay->IsHeld())
{
ray.overlay->ControllerResize(this);
}
else
{
_grabbed_overlay = ray.overlay;
ray.overlay->ControllerGrab(this);
}
}
}
} }
void Controller::UpdateStatus() void Controller::UpdateStatus()
@ -145,8 +215,4 @@ void Controller::UpdateStatus()
_device_index = _app->vr_sys->GetTrackedDeviceIndexForControllerRole(vr::TrackedControllerRole_RightHand); _device_index = _app->vr_sys->GetTrackedDeviceIndexForControllerRole(vr::TrackedControllerRole_RightHand);
} }
_is_connected &= _device_index < MAX_TRACKERS; _is_connected &= _device_index < MAX_TRACKERS;
if (!_is_connected)
{
_laser.SetHidden(true);
}
} }

View file

@ -24,25 +24,31 @@ class Controller
glm::vec3 GetLastPos(); glm::vec3 GetLastPos();
glm::vec3 GetLastRot(); glm::vec3 GetLastRot();
void SetHidden(bool state);
void ReleaseOverlay(); void ReleaseOverlay();
void Update(); void Update(float dtime);
bool _cursor_active;
private: private:
void UpdateStatus(); void UpdateStatus();
void UpdateLaser(); void UpdateLaser();
void UpdateMouseButton(vr::VRActionHandle_t binding, unsigned int button);
App *_app; App *_app;
Overlay _laser; Overlay _laser;
ControllerSide _side; ControllerSide _side;
TrackerID _device_index; TrackerID _device_index;
vr::VRInputValueHandle_t _input_handle; vr::VRInputValueHandle_t _input_handle;
bool _is_connected; bool _is_connected;
bool _hidden;
Overlay *_grabbed_overlay; Overlay *_grabbed_overlay;
Ray _last_ray; Ray _last_ray;
glm::vec3 _last_rotation; glm::vec3 _last_rotation;
glm::vec3 _last_pos; glm::vec3 _last_pos;
float _last_sent_scroll;
}; };

View file

@ -1,7 +1,7 @@
#include "app.h" #include "app.h"
#include <signal.h> #include <signal.h>
#define UPDATE_RATE 60 #define UPDATE_RATE 120
bool should_exit = false; bool should_exit = false;
@ -18,8 +18,8 @@ int main()
while (!should_exit) while (!should_exit)
{ {
app.Update();
usleep(1000000 / UPDATE_RATE); usleep(1000000 / UPDATE_RATE);
app.Update(1.0 / UPDATE_RATE);
} }
printf("\nShutting down\n"); printf("\nShutting down\n");
return 0; return 0;

View file

@ -108,6 +108,17 @@ void Overlay::SetTextureToColor(uint8_t r, uint8_t g, uint8_t b)
assert(set_texture_err == 0); assert(set_texture_err == 0);
} }
void Overlay::SetColor(float r, float g, float b)
{
auto set_color_err = _app->vr_overlay->SetOverlayColor(_id, r, g, b);
assert(set_color_err == 0);
}
void Overlay::SetColor(Color c)
{
SetColor(c.r, c.g, c.b);
}
void Overlay::SetTransformTracker(TrackerID tracker, const VRMat *transform) void Overlay::SetTransformTracker(TrackerID tracker, const VRMat *transform)
{ {
auto original_pose = _target.transform; auto original_pose = _target.transform;
@ -169,7 +180,7 @@ Ray Overlay::IntersectRay(glm::vec3 origin, glm::vec3 direction, float max_len)
{ {
dist = glm::min(r * max_len, max_len); dist = glm::min(r * max_len, max_len);
} }
return Ray{.overlay = this, .distance = dist}; return Ray{.overlay = this, .distance = dist, .local_pos = p, .hit_panel = nullptr};
} }
glm::mat4x4 Overlay::GetTransformAbsolute() glm::mat4x4 Overlay::GetTransformAbsolute()
@ -208,10 +219,10 @@ void Overlay::Update()
if (_holding_controller != nullptr) if (_holding_controller != nullptr)
{ {
bool hold_controller_holding = _app->GetInputDigital(_app->_input_handles.grab, _holding_controller->InputHandle()).bState; bool hold_controller_holding = _app->GetInputDigital(_app->_input_handles.edit.grab, _holding_controller->InputHandle()).bState;
if (_resize_controller != nullptr) if (_resize_controller != nullptr)
{ {
bool resize_controller_holding = _app->GetInputDigital(_app->_input_handles.grab, _resize_controller->InputHandle()).bState; bool resize_controller_holding = _app->GetInputDigital(_app->_input_handles.edit.grab, _resize_controller->InputHandle()).bState;
if (!resize_controller_holding) if (!resize_controller_holding)
{ {
_resize_controller = nullptr; _resize_controller = nullptr;

View file

@ -43,6 +43,8 @@ class Overlay
void SetRatio(float ratio); void SetRatio(float ratio);
void SetTexture(vr::Texture_t *texture); void SetTexture(vr::Texture_t *texture);
void SetTextureToColor(uint8_t r, uint8_t g, uint8_t b); void SetTextureToColor(uint8_t r, uint8_t g, uint8_t b);
void SetColor(float r, float g, float b);
void SetColor(Color c);
glm::mat4x4 GetTransformAbsolute(); glm::mat4x4 GetTransformAbsolute();
Target *GetTarget(); Target *GetTarget();

View file

@ -68,6 +68,18 @@ void Panel::SetHidden(bool state)
_overlay.SetHidden(state); _overlay.SetHidden(state);
} }
Ray Panel::IntersectRay(glm::vec3 origin, glm::vec3 direction, float max_len)
{
auto ray = _overlay.IntersectRay(origin, direction, max_len);
ray.hit_panel = this;
return ray;
}
void Panel::SetCursor(int x, int y)
{
_app->SetCursor(x + _x, y + _y);
}
void Panel::UpdateCursor() void Panel::UpdateCursor()
{ {
auto global_pos = _app->GetCursorPosition(); auto global_pos = _app->GetCursorPosition();

View file

@ -19,6 +19,19 @@ class Panel
void SetHidden(bool state); void SetHidden(bool state);
void ResetTransform(); void ResetTransform();
int Width()
{
return _width;
}
int Height()
{
return _height;
}
void SetCursor(int x, int y);
Ray IntersectRay(glm::vec3 origin, glm::vec3 direction, float max_len);
Overlay *GetOverlay(); Overlay *GetOverlay();
private: private:

View file

@ -11,10 +11,20 @@ const VRMat VRMatIdentity{{{1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}}};
const int MAX_TRACKERS = vr::k_unMaxTrackedDeviceCount; const int MAX_TRACKERS = vr::k_unMaxTrackedDeviceCount;
class Overlay; class Overlay;
class Panel;
struct Ray struct Ray
{ {
Overlay *overlay; Overlay *overlay;
float distance; float distance;
glm::vec3 local_pos;
Panel *hit_panel;
};
struct Color
{
float r;
float g;
float b;
}; };
inline void PrintVec(glm::vec3 v) inline void PrintVec(glm::vec3 v)