mirror of
https://github.com/CrispyPin/sinpin-vr.git
synced 2025-04-19 08:38:32 +02:00
Compare commits
9 commits
Author | SHA1 | Date | |
---|---|---|---|
66227ae744 | |||
2c0386b141 | |||
22496fcc35 | |||
ebb52a404d | |||
6f44ea6b5a | |||
47ebf9d64c | |||
8b7a3e0067 | |||
0b7f841fbf | |||
035d9e7296 |
14 changed files with 190 additions and 58 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,3 +1,4 @@
|
|||
sinpin_vr
|
||||
.vscode/
|
||||
*.zip
|
||||
*.tar.xz
|
||||
|
|
25
Makefile
25
Makefile
|
@ -1,18 +1,23 @@
|
|||
VERSION=v0.2.0
|
||||
# CC := g++
|
||||
CC := clang++
|
||||
VERSION=v0.2.3
|
||||
|
||||
CXX := g++
|
||||
# CXX := clang++
|
||||
CPPFLAGS := -g -Wall -std=c++17
|
||||
LFLAGS := -lX11 -lXrandr -lXtst -lglfw -lGL
|
||||
LIBS := openvr/libopenvr_api.so
|
||||
SRC := src/*.cpp
|
||||
OUT := ./sinpin_vr
|
||||
CPPFLAGS := -Wall -std=c++17 $(LFLAGS) $(LIBS) $(SRC) -o $(OUT)
|
||||
OVR := -Llib -lopenvr_api
|
||||
TARGET := ./sinpin_vr
|
||||
|
||||
build:
|
||||
$(CC) -g $(CPPFLAGS)
|
||||
$(CXX) src/*.cpp $(CPPFLAGS) $(LFLAGS) -Wl,-rpath,'$$ORIGIN/lib' $(OVR) -o $(TARGET)
|
||||
|
||||
release: build
|
||||
zip -r sinpin_vr-$(VERSION).zip sinpin_vr bindings openvr/libopenvr_api.so
|
||||
mkdir -p sinpin-vr/lib
|
||||
cp lib/libopenvr_api.so sinpin-vr/lib
|
||||
cp -r bindings sinpin-vr
|
||||
cp sinpin_vr sinpin-vr
|
||||
tar -caf sinpin-vr-$(VERSION).tar.xz sinpin-vr
|
||||
rm -rf sinpin-vr
|
||||
|
||||
run: build
|
||||
$(OUT)
|
||||
$(TARGET)
|
||||
|
||||
|
|
|
@ -13,6 +13,8 @@ Note: only index controllers have default bindings right now, feel free to make
|
|||
- 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)
|
||||
- resize screens (move with two controllers)
|
||||
|
|
|
@ -45,6 +45,26 @@
|
|||
"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",
|
||||
"type": "vector2"
|
||||
},
|
||||
{
|
||||
"name": "/actions/cursor/in/toggle_transparent",
|
||||
"requirement": "suggested",
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"name": "/actions/cursor/out/scroll_haptic",
|
||||
"requirement": "suggested",
|
||||
"type": "vibration"
|
||||
}
|
||||
],
|
||||
"action_sets": [
|
||||
|
@ -70,8 +90,12 @@
|
|||
"/actions/edit/in/grab": "grab panel",
|
||||
"/actions/edit/in/distance": "push/pull overlay",
|
||||
"/actions/cursor/in/activate_cursor": "activate cursor",
|
||||
"/actions/cursor/in/mouse_left": "mouse left",
|
||||
"/actions/cursor/in/mouse_right": "mouse right"
|
||||
"/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",
|
||||
"/actions/cursor/in/toggle_transparent": "toggle transparency",
|
||||
"/actions/cursor/out/scroll_haptic": "scrolling haptic feedback"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -141,6 +141,16 @@
|
|||
"app_key" : "system.generated.sinpin_vr",
|
||||
"bindings" : {
|
||||
"/actions/cursor" : {
|
||||
"haptics" : [
|
||||
{
|
||||
"output" : "/actions/cursor/out/scroll_haptic",
|
||||
"path" : "/user/hand/left/output/haptic"
|
||||
},
|
||||
{
|
||||
"output" : "/actions/cursor/out/scroll_haptic",
|
||||
"path" : "/user/hand/right/output/haptic"
|
||||
}
|
||||
],
|
||||
"sources" : [
|
||||
{
|
||||
"inputs" : {
|
||||
|
@ -195,6 +205,24 @@
|
|||
},
|
||||
"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"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
38
src/app.cpp
38
src/app.cpp
|
@ -9,6 +9,9 @@
|
|||
|
||||
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
|
||||
const float TRANSPARENCY = 0.6f;
|
||||
|
||||
App::App()
|
||||
{
|
||||
_tracking_origin = vr::TrackingUniverseStanding;
|
||||
|
@ -69,6 +72,14 @@ App::App()
|
|||
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->GetActionHandle("/actions/cursor/in/toggle_transparent", &_input_handles.cursor.toggle_transparent);
|
||||
assert(action_err == 0);
|
||||
action_err = vr_input->GetActionHandle("/actions/cursor/out/scroll_haptic", &_input_handles.cursor.scroll_haptic);
|
||||
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);
|
||||
|
@ -136,11 +147,12 @@ void App::InitRootOverlay()
|
|||
_root_overlay.SetWidth(0.25f);
|
||||
_root_overlay.SetTransformWorld(&root_start_pose);
|
||||
_root_overlay.SetTextureToColor(110, 30, 190);
|
||||
_root_overlay.SetHidden(true);
|
||||
}
|
||||
|
||||
void App::Update()
|
||||
void App::Update(float dtime)
|
||||
{
|
||||
UpdateInput();
|
||||
UpdateInput(dtime);
|
||||
if (!_hidden)
|
||||
{
|
||||
_root_overlay.Update();
|
||||
|
@ -153,7 +165,7 @@ void App::Update()
|
|||
_frames_since_framebuffer += 1;
|
||||
}
|
||||
|
||||
void App::UpdateInput()
|
||||
void App::UpdateInput(float dtime)
|
||||
{
|
||||
vr::VRActiveActionSet_t active_sets[2];
|
||||
active_sets[0].ulActionSet = _input_handles.main_set;
|
||||
|
@ -184,6 +196,20 @@ void App::UpdateInput()
|
|||
}
|
||||
UpdateUIVisibility();
|
||||
}
|
||||
if (IsInputJustPressed(_input_handles.cursor.toggle_transparent))
|
||||
{
|
||||
_transparent = !_transparent;
|
||||
if (_transparent)
|
||||
{
|
||||
for (auto &panel : _panels)
|
||||
panel.GetOverlay()->SetAlpha(TRANSPARENCY);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto &panel : _panels)
|
||||
panel.GetOverlay()->SetAlpha(1);
|
||||
}
|
||||
}
|
||||
if (!_hidden)
|
||||
{
|
||||
if (IsInputJustPressed(_input_handles.main.reset))
|
||||
|
@ -206,8 +232,8 @@ void App::UpdateInput()
|
|||
}
|
||||
}
|
||||
}
|
||||
_controllers[0]->Update();
|
||||
_controllers[1]->Update();
|
||||
_controllers[0]->Update(dtime);
|
||||
_controllers[1]->Update(dtime);
|
||||
}
|
||||
|
||||
void App::UpdateUIVisibility()
|
||||
|
@ -218,7 +244,7 @@ void App::UpdateUIVisibility()
|
|||
|
||||
void App::UpdateFramebuffer()
|
||||
{
|
||||
if (_frames_since_framebuffer < 2)
|
||||
if (_frames_since_framebuffer < FRAME_INTERVAL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -30,6 +30,10 @@ struct InputHandles
|
|||
vr::VRActionHandle_t activate;
|
||||
vr::VRActionHandle_t mouse_left;
|
||||
vr::VRActionHandle_t mouse_right;
|
||||
vr::VRActionHandle_t mouse_middle;
|
||||
vr::VRActionHandle_t scroll;
|
||||
vr::VRActionHandle_t scroll_haptic;
|
||||
vr::VRActionHandle_t toggle_transparent;
|
||||
} cursor;
|
||||
vr::VRActionSetHandle_t cursor_set;
|
||||
struct
|
||||
|
@ -45,7 +49,7 @@ class App
|
|||
public:
|
||||
App();
|
||||
~App();
|
||||
void Update();
|
||||
void Update(float dtime);
|
||||
|
||||
std::vector<TrackerID> GetControllers();
|
||||
glm::mat4 GetTrackerPose(TrackerID tracker);
|
||||
|
@ -84,6 +88,7 @@ class App
|
|||
Overlay _root_overlay;
|
||||
std::vector<Panel> _panels;
|
||||
bool _hidden = false;
|
||||
bool _transparent = false;
|
||||
bool _edit_mode = false;
|
||||
std::optional<Controller *> _active_cursor;
|
||||
|
||||
|
@ -94,6 +99,6 @@ class App
|
|||
void InitRootOverlay();
|
||||
|
||||
void UpdateFramebuffer();
|
||||
void UpdateInput();
|
||||
void UpdateInput(float dtime);
|
||||
void UpdateUIVisibility();
|
||||
};
|
|
@ -4,9 +4,13 @@
|
|||
#include "util.h"
|
||||
#include <string>
|
||||
|
||||
const float laser_width = 0.004f;
|
||||
const Color edit_col{1, 0.6f, 1};
|
||||
const Color cursor_col{0.3f, 1, 1};
|
||||
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;
|
||||
const float SCROLL_HAPTIC_STRENGTH = 0.15f;
|
||||
const float SCROLL_HAPTIC_TIME = 0.1f;
|
||||
const float MOUSE_DRAG_THRESHOLD = 48;
|
||||
|
||||
Controller::Controller(App *app, ControllerSide side)
|
||||
{
|
||||
|
@ -15,7 +19,6 @@ Controller::Controller(App *app, ControllerSide side)
|
|||
_input_handle = 0;
|
||||
_is_connected = false;
|
||||
_side = side;
|
||||
_cursor_active = false;
|
||||
|
||||
std::string laser_name = "controller_laser_";
|
||||
if (side == ControllerSide::Left)
|
||||
|
@ -70,7 +73,7 @@ glm::vec3 Controller::GetLastRot()
|
|||
return _last_rotation;
|
||||
}
|
||||
|
||||
void Controller::Update()
|
||||
void Controller::Update(float dtime)
|
||||
{
|
||||
UpdateStatus();
|
||||
if (!_is_connected)
|
||||
|
@ -80,7 +83,7 @@ void Controller::Update()
|
|||
|
||||
if (_app->_edit_mode)
|
||||
{
|
||||
_laser.SetColor(edit_col);
|
||||
_laser.SetColor(EDIT_COLOR);
|
||||
if (_last_ray.overlay != nullptr)
|
||||
{
|
||||
auto ray = _last_ray;
|
||||
|
@ -100,7 +103,7 @@ void Controller::Update()
|
|||
|
||||
if (_grabbed_overlay != nullptr)
|
||||
{
|
||||
float move = _app->GetInputAnalog(_app->_input_handles.edit.distance, _input_handle).y * 0.1; // TODO use frame time
|
||||
float move = _app->GetInputAnalog(_app->_input_handles.edit.distance, _input_handle).y * dtime * 8;
|
||||
if (move != 0.0f)
|
||||
{
|
||||
auto transform = _grabbed_overlay->GetTarget()->transform;
|
||||
|
@ -120,11 +123,10 @@ void Controller::Update()
|
|||
}
|
||||
_cursor_active = !_cursor_active;
|
||||
_app->_active_cursor = this;
|
||||
_laser.SetColor(cursor_col);
|
||||
_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);
|
||||
|
@ -137,22 +139,54 @@ void Controller::Update()
|
|||
pos.y += 0.5f * _last_ray.overlay->Ratio();
|
||||
|
||||
pos *= _last_ray.hit_panel->Width();
|
||||
_last_ray.hit_panel->SetCursor(pos.x, pos.y);
|
||||
if (glm::length(pos - _last_set_mouse_pos) > MOUSE_DRAG_THRESHOLD)
|
||||
{
|
||||
_mouse_drag_lock = false;
|
||||
}
|
||||
if (!_mouse_drag_lock)
|
||||
{
|
||||
_last_ray.hit_panel->SetCursor(pos.x, pos.y);
|
||||
_last_set_mouse_pos = pos;
|
||||
}
|
||||
}
|
||||
auto mouse_left = _app->GetInputDigital(_app->_input_handles.cursor.mouse_left, _input_handle);
|
||||
if (mouse_left.bChanged)
|
||||
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)
|
||||
{
|
||||
_app->SendMouseInput(1, mouse_left.bState);
|
||||
}
|
||||
auto mouse_right = _app->GetInputDigital(_app->_input_handles.cursor.mouse_right, _input_handle);
|
||||
if (mouse_right.bChanged)
|
||||
{
|
||||
_app->SendMouseInput(3, mouse_right.bState);
|
||||
_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);
|
||||
_app->vr_input->TriggerHapticVibrationAction(_app->_input_handles.cursor.scroll_haptic, 0, SCROLL_HAPTIC_TIME, 1 / SCROLL_HAPTIC_TIME, SCROLL_HAPTIC_STRENGTH, _input_handle);
|
||||
}
|
||||
else if (scroll_state.y < 0)
|
||||
{
|
||||
_app->SendMouseInput(5, true);
|
||||
_app->SendMouseInput(5, false);
|
||||
_app->vr_input->TriggerHapticVibrationAction(_app->_input_handles.cursor.scroll_haptic, 0, SCROLL_HAPTIC_TIME, 1 / SCROLL_HAPTIC_TIME, SCROLL_HAPTIC_STRENGTH, _input_handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
_mouse_drag_lock = state.bState;
|
||||
}
|
||||
}
|
||||
|
||||
void Controller::UpdateLaser()
|
||||
{
|
||||
auto controller_pose = _app->GetTrackerPose(_device_index);
|
||||
|
@ -170,7 +204,7 @@ void Controller::UpdateLaser()
|
|||
hmd_local_pos.z = 0;
|
||||
auto hmd_dir = glm::normalize(hmd_local_pos);
|
||||
|
||||
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}}};
|
||||
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.SetHidden(!_is_connected || _app->_hidden || (!_app->_edit_mode && !_cursor_active));
|
||||
}
|
||||
|
|
|
@ -26,14 +26,16 @@ class Controller
|
|||
|
||||
void ReleaseOverlay();
|
||||
|
||||
void Update();
|
||||
void Update(float dtime);
|
||||
|
||||
bool _cursor_active;
|
||||
bool _cursor_active = false;
|
||||
|
||||
private:
|
||||
void UpdateStatus();
|
||||
void UpdateLaser();
|
||||
|
||||
void UpdateMouseButton(vr::VRActionHandle_t binding, unsigned int button);
|
||||
|
||||
App *_app;
|
||||
Overlay _laser;
|
||||
ControllerSide _side;
|
||||
|
@ -47,4 +49,8 @@ class Controller
|
|||
Ray _last_ray;
|
||||
glm::vec3 _last_rotation;
|
||||
glm::vec3 _last_pos;
|
||||
|
||||
float _last_sent_scroll = 0;
|
||||
bool _mouse_drag_lock = false;
|
||||
glm::vec2 _last_set_mouse_pos;
|
||||
};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#include "app.h"
|
||||
#include <signal.h>
|
||||
|
||||
#define UPDATE_RATE 60
|
||||
#define UPDATE_RATE 120
|
||||
|
||||
bool should_exit = false;
|
||||
|
||||
|
@ -18,8 +18,8 @@ int main()
|
|||
|
||||
while (!should_exit)
|
||||
{
|
||||
app.Update();
|
||||
usleep(1000000 / UPDATE_RATE);
|
||||
app.Update(1.0 / UPDATE_RATE);
|
||||
}
|
||||
printf("\nShutting down\n");
|
||||
return 0;
|
||||
|
|
|
@ -159,28 +159,29 @@ void Overlay::SetTargetWorld()
|
|||
SetTransformWorld(&abs_pose);
|
||||
}
|
||||
|
||||
Ray Overlay::IntersectRay(glm::vec3 origin, glm::vec3 direction, float max_len)
|
||||
Ray Overlay::IntersectRay(glm::vec3 ray_start_g, glm::vec3 direction, float max_len)
|
||||
{
|
||||
float dist = max_len;
|
||||
auto end = origin + direction * max_len;
|
||||
auto ray_end_g = ray_start_g + direction * max_len;
|
||||
|
||||
auto panel_transform = GetTransformAbsolute();
|
||||
auto panel_pos = GetPos(panel_transform);
|
||||
auto a = glm::inverse(panel_transform) * glm::vec4(origin - panel_pos, 0);
|
||||
auto b = glm::inverse(panel_transform) * glm::vec4(end - panel_pos, 0);
|
||||
float r = a.z / (a.z - b.z);
|
||||
auto p = a + (b - a) * r;
|
||||
// printf("panel pos: (%.2f,%.2f,%.2f)\n", panel_pos.x, panel_pos.y, panel_pos.z);
|
||||
// printf("a: (%.2f,%.2f,%.2f)\n", a.x, a.y, a.z);
|
||||
// printf("b: (%.2f,%.2f,%.2f)\n", b.x, b.y, b.z);
|
||||
// printf("r: %.2f\n", r);
|
||||
// printf("p: (%.2f,%.2f,%.2f)\n", p.x, p.y, p.z);
|
||||
auto ray_start = glm::inverse(panel_transform) * glm::vec4(ray_start_g - panel_pos, 0);
|
||||
auto ray_end = glm::inverse(panel_transform) * glm::vec4(ray_end_g - panel_pos, 0);
|
||||
float length_frac = ray_start.z / (ray_start.z - ray_end.z);
|
||||
auto hit_pos = ray_start + (ray_end - ray_start) * length_frac;
|
||||
|
||||
if (b.z < a.z && b.z < 0 && glm::abs(p.x) < (_width_m * 0.5f) && glm::abs(p.y) < (_width_m * 0.5f * _ratio))
|
||||
// clang-format off
|
||||
if (ray_end.z < ray_start.z
|
||||
&& ray_end.z < 0
|
||||
&& glm::abs(hit_pos.x) < (_width_m * 0.5f)
|
||||
&& glm::abs(hit_pos.y) < (_width_m * 0.5f * _ratio)
|
||||
&& length_frac > 0)
|
||||
{
|
||||
dist = glm::min(r * max_len, max_len);
|
||||
// clang-format on
|
||||
dist = glm::min(length_frac * max_len, max_len);
|
||||
}
|
||||
return Ray{.overlay = this, .distance = dist, .local_pos = p, .hit_panel = nullptr};
|
||||
return Ray{.overlay = this, .distance = dist, .local_pos = hit_pos, .hit_panel = nullptr};
|
||||
}
|
||||
|
||||
glm::mat4x4 Overlay::GetTransformAbsolute()
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "../openvr/openvr.h"
|
||||
#include "../lib/openvr.h"
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
typedef vr::TrackedDeviceIndex_t TrackerID;
|
||||
|
|
Loading…
Add table
Reference in a new issue