Compare commits

...

2 commits

Author SHA1 Message Date
b00dab62db properly align & scale monitors 2023-04-13 23:01:26 +02:00
7ff9475174 refactor 2023-04-13 21:45:21 +02:00
11 changed files with 377 additions and 171 deletions

1
.gitignore vendored
View file

@ -1 +1,2 @@
overlay
.vscode/

View file

@ -1,7 +1,17 @@
# CC := g++
CC := clang++
LFLAGS := -lX11 -lXrandr -lglfw -lGL
LIBS := openvr/libopenvr_api.so
SRC := src/*.cpp
OUT := ./overlay
CPPFLAGS := -Wall -std=c++17 $(LFLAGS) $(LIBS) $(SRC) -o $(OUT)
build:
g++ -Wall -lX11 -lXrandr -lglfw -lGL openvr/libopenvr_api.so src/*.cpp -o overlay
$(CC) -g $(CPPFLAGS)
release:
$(CC) $(CPPFLAGS)
run: build
./overlay

View file

@ -1,5 +1,4 @@
#include "app.h"
#include "panel.h"
#include <X11/extensions/Xrandr.h>
#include <cassert>
@ -10,6 +9,7 @@ App::App()
InitOVR();
InitX11();
InitGLFW();
InitRootOverlay();
printf("\n");
glGenTextures(1, &_gl_frame);
@ -21,18 +21,23 @@ App::App()
printf("found %d monitors:\n", monitor_count);
float pixels_per_meter = 1920;
float x_min = -(monitor_info[0].x + monitor_info[0].width / 2.0f);
// float x_min = _root_width / -2.0f;
float total_width_meters = _root_width / pixels_per_meter;
float total_height_meters = _root_height / pixels_per_meter;
for (int i = 0; i < monitor_count; i++)
{
XRRMonitorInfo mon = monitor_info[i];
printf("screen %d: pos(%d, %d) %dx%d\n", i, mon.x, mon.y, mon.width, mon.height);
float pos_x = (x_min + mon.x) / pixels_per_meter;
float pos_y = 1.2f;
vr::HmdMatrix34_t start_pose = {{{1, 0, 0, pos_x}, {0, 1, 0, pos_y}, {0, 0, 1, 0}}};
_panels.push_back(Panel(this, start_pose, i, mon.x, mon.y, mon.width, mon.height));
_panels.push_back(Panel(this, i, mon.x, mon.y, mon.width, mon.height));
float width = mon.width / pixels_per_meter;
float pos_x = mon.x / pixels_per_meter + width / 2.0f - total_width_meters / 2.0f;
float height = mon.height / pixels_per_meter;
float pos_y = 1.2f + mon.y / pixels_per_meter - height / 2.0f + total_height_meters / 2.0f;
VRMat start_pose = {{{1, 0, 0, pos_x}, {0, 1, 0, pos_y}, {0, 0, 1, 0}}};
_panels[i].GetOverlay()->SetTransformWorld(&start_pose);
_panels[i].GetOverlay()->SetWidth(width);
}
{ // initialize SteamVR input
@ -102,11 +107,40 @@ void App::InitGLFW()
printf("Created GLFW context\n");
}
void App::InitRootOverlay()
{
_root_overlay = Overlay(this, "root");
_root_overlay.SetAlpha(0.2f);
// clang-format off
VRMat root_start_pose = {{
{0.25f, 0.0f, 0.0f, 0},
{0.0f, 0.25f, 0.0f, 0.8f},
{0.0f, 0.0f, 1.0f, 0}
}};
// clang-format on
_root_overlay.SetTransformWorld(&root_start_pose);
_root_overlay._GrabBeginCallback = [this](TrackerID controller) {
for (auto &panel : _panels)
{
panel.GetOverlay()->ControllerGrab(controller);
}
};
_root_overlay._GrabEndCallback = [this](TrackerID controller) {
for (auto &panel : _panels)
{
panel.GetOverlay()->ControllerRelease();
}
};
printf("Created root overlay instance\n");
}
void App::Update()
{
UpdateInput();
if (!_hidden)
{
_root_overlay.Update();
UpdateFramebuffer();
for (auto &panel : _panels)
{
@ -129,6 +163,7 @@ void App::UpdateInput()
if (IsInputJustPressed(i, _input_handles.toggle))
{
_hidden = !_hidden;
_root_overlay.SetHidden(_hidden);
for (auto &panel : _panels)
{
panel.SetHidden(_hidden);

View file

@ -1,14 +1,14 @@
#pragma once
#define GL_GLEXT_PROTOTYPES
#include "overlay.h"
#include "panel.h"
#include "util.h"
#include <GLFW/glfw3.h>
#include <X11/Xutil.h>
#include <filesystem>
#include <vector>
class Panel;
struct CursorPos
{
int x, y;
@ -54,6 +54,7 @@ class App
InputHandles _input_handles;
vr::TrackedDevicePose_t _tracker_poses[vr::k_unMaxTrackedDeviceCount];
Overlay _root_overlay;
std::vector<Panel> _panels;
bool _hidden = false;
@ -61,6 +62,7 @@ class App
void InitX11();
void InitOVR();
void InitGLFW();
void InitRootOverlay();
void UpdateFramebuffer();
void UpdateInput();

View file

@ -1,94 +0,0 @@
#include "grab_component.h"
GrabComponent::GrabComponent(App *app)
{
_app = app;
_is_held = false;
_active_hand = 0;
}
void GrabComponent::Update(OverlayID id, float *meters)
{
_id = id;
if (!_is_held)
{
for (auto controller : _app->GetControllers())
{
if (_app->IsInputJustPressed(controller, _app->_input_handles.grab))
{
vr::HmdMatrix34_t overlay_pose;
vr::ETrackingUniverseOrigin tracking_universe;
_app->vr_overlay->GetOverlayTransformAbsolute(id, &tracking_universe, &overlay_pose);
auto controller_pos = GetPos(_app->GetTrackerPose(controller));
auto local_pos = glm::inverse(ConvertMat(overlay_pose)) * glm::vec4(controller_pos - GetPos(overlay_pose), 0);
float grab_area_thickness = 0.3f;
bool close_enough = glm::abs(local_pos.z) < grab_area_thickness;
close_enough &= glm::abs(local_pos.x) < *meters / 2.0f;
close_enough &= glm::abs(local_pos.y) < *meters / 2.0f;
if (close_enough)
{
ControllerGrab(controller);
}
}
}
}
else
{
if (!_app->GetControllerInputDigital(_active_hand, _app->_input_handles.grab).bState)
{
ControllerRelease();
}
}
}
bool GrabComponent::IsHeld()
{
return _is_held;
}
TrackerID GrabComponent::ActiveHand()
{
return _active_hand;
}
void GrabComponent::ControllerGrab(TrackerID controller)
{
_is_held = true;
_active_hand = controller;
_app->vr_overlay->SetOverlayColor(_id, 0.6f, 0.8f, 0.8f);
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 GrabComponent::ControllerRelease()
{
_is_held = false;
_active_hand = -1;
_app->vr_overlay->SetOverlayColor(_id, 1.0f, 1.0f, 1.0f);
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);
}

View file

@ -1,23 +0,0 @@
#pragma once
#include "app.h"
#include "util.h"
class GrabComponent
{
public:
GrabComponent(App *app);
bool IsHeld();
TrackerID ActiveHand();
void Update(OverlayID id, float *meters);
private:
void ControllerRelease();
void ControllerGrab(TrackerID controller);
App *_app;
OverlayID _id;
bool _is_held;
TrackerID _active_hand;
};

222
src/overlay.cpp Normal file
View file

@ -0,0 +1,222 @@
#include "overlay.h"
#include "app.h"
#include "util.h"
#include <glm/fwd.hpp>
Overlay::Overlay()
{
_initialized = false;
}
Overlay::Overlay(App *app, std::string name)
{
_initialized = true;
_name = name;
_app = app;
_is_held = false;
_active_hand = 0;
_width_m = 1;
_target.type = TargetType::World;
auto overlay_create_err = _app->vr_overlay->CreateOverlay(_name.c_str(), _name.c_str(), &_id);
assert(overlay_create_err == 0);
{
vr::ETrackingUniverseOrigin origin;
_app->vr_overlay->GetOverlayTransformAbsolute(_id, &origin, &_target.transform);
}
uint8_t col[4] = {20, 50, 50, 255};
_app->vr_overlay->SetOverlayRaw(_id, &col, 1, 1, 4);
printf("Created overlay instance %s\n", _name.c_str());
// (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);
SetHidden(false);
}
OverlayID Overlay::Id()
{
return _id;
}
bool Overlay::IsHeld()
{
return _is_held;
}
TrackerID Overlay::ActiveHand()
{
return _active_hand;
}
bool Overlay::IsHidden()
{
return _hidden;
}
float Overlay::Alpha()
{
return _alpha;
}
float Overlay::Width()
{
return _width_m;
}
void Overlay::SetWidth(float width_meters)
{
_width_m = width_meters;
_app->vr_overlay->SetOverlayWidthInMeters(_id, _width_m);
}
void Overlay::SetHidden(bool state)
{
_hidden = state;
if (_hidden)
_app->vr_overlay->HideOverlay(_id);
else
_app->vr_overlay->ShowOverlay(_id);
}
void Overlay::SetAlpha(float alpha)
{
_alpha = alpha;
_app->vr_overlay->SetOverlayAlpha(_id, alpha);
}
void Overlay::SetTexture(vr::Texture_t *texture)
{
auto set_texture_err = _app->vr_overlay->SetOverlayTexture(_id, texture);
assert(set_texture_err == 0);
}
void Overlay::SetTransformTracker(TrackerID tracker, VRMat *transform)
{
_app->vr_overlay->SetOverlayTransformTrackedDeviceRelative(_id, tracker, transform);
_target.type = TargetType::Tracker;
_target.id = tracker;
_target.transform = *transform;
}
void Overlay::SetTransformWorld(VRMat *transform)
{
_app->vr_overlay->SetOverlayTransformAbsolute(_id, vr::TrackingUniverseStanding, transform);
_target.type = TargetType::World;
_target.transform = *transform;
}
void Overlay::SetTargetWorld()
{
auto abs_pose = ConvertMat(GetTransformAbsolute());
_app->vr_overlay->SetOverlayTransformAbsolute(_id, vr::TrackingUniverseStanding, &abs_pose);
_target.type = TargetType::World;
}
glm::mat4x4 Overlay::GetTransformAbsolute()
{
if (_is_held)
{
VRMat pose;
auto err = _app->vr_overlay->GetOverlayTransformTrackedDeviceRelative(_id, &_active_hand, &pose);
assert(err == 0);
auto offset = ConvertMat(pose);
auto controller = _app->GetTrackerPose(_active_hand);
return controller * offset;
}
else
{
switch (_target.type)
{
case TargetType::World: {
VRMat pose;
vr::ETrackingUniverseOrigin tracking_universe;
_app->vr_overlay->GetOverlayTransformAbsolute(_id, &tracking_universe, &pose);
return ConvertMat(pose);
}
case TargetType::Tracker: {
VRMat pose;
_app->vr_overlay->GetOverlayTransformTrackedDeviceRelative(_id, &_target.id, &pose);
auto offset = ConvertMat(pose);
auto tracker_pose = _app->GetTrackerPose(_target.id);
return tracker_pose * offset;
}
}
}
}
void Overlay::Update()
{
if (!_initialized)
{
printf("Error: overlay %s is not initialized.\n", _name.c_str());
assert(_initialized);
}
if (!_is_held)
{
for (auto controller : _app->GetControllers())
{
if (_app->IsInputJustPressed(controller, _app->_input_handles.grab))
{
auto overlay_pose = GetTransformAbsolute();
auto controller_pos = GetPos(_app->GetTrackerPose(controller));
auto local_pos = glm::inverse(overlay_pose) * glm::vec4(controller_pos - GetPos(overlay_pose), 0);
float grab_area_thickness = 0.3f;
bool close_enough = glm::abs(local_pos.z) < grab_area_thickness;
close_enough &= glm::abs(local_pos.x) < _width_m / 2.0f;
close_enough &= glm::abs(local_pos.y) < _width_m / 2.0f;
if (close_enough)
{
ControllerGrab(controller);
}
}
}
}
else
{
if (!_app->GetControllerInputDigital(_active_hand, _app->_input_handles.grab).bState)
{
ControllerRelease();
}
}
}
void Overlay::ControllerGrab(TrackerID controller)
{
_app->vr_overlay->SetOverlayColor(_id, 0.6f, 0.8f, 0.8f);
auto abs_mat = GetTransformAbsolute();
auto controller_mat = _app->GetTrackerPose(controller);
VRMat relative_pose = ConvertMat(glm::inverse(controller_mat) * abs_mat);
_app->vr_overlay->SetOverlayTransformTrackedDeviceRelative(_id, controller, &relative_pose);
if (_GrabBeginCallback != nullptr)
{
_GrabBeginCallback(controller);
}
_is_held = true;
_active_hand = controller;
}
void Overlay::ControllerRelease()
{
_app->vr_overlay->SetOverlayColor(_id, 1.0f, 1.0f, 1.0f);
auto new_pose = ConvertMat(GetTransformAbsolute());
_app->vr_overlay->SetOverlayTransformAbsolute(_id, _app->_tracking_origin, &new_pose);
if (_GrabEndCallback != nullptr)
{
_GrabEndCallback(_active_hand);
}
_is_held = false;
_active_hand = -1;
}

69
src/overlay.h Normal file
View file

@ -0,0 +1,69 @@
#pragma once
#include "util.h"
#include <functional>
#include <string>
class App;
enum class TargetType
{
World,
Tracker,
};
struct Target
{
TargetType type;
TrackerID id;
VRMat transform;
};
class Overlay
{
public:
Overlay();
Overlay(App *app, std::string name);
void Update();
OverlayID Id();
bool IsHeld();
bool IsHidden();
TrackerID ActiveHand();
float Width();
float Alpha();
void SetWidth(float meters);
void SetHidden(bool state);
void SetAlpha(float alpha);
void SetTexture(vr::Texture_t *texture);
void SetTransformTracker(TrackerID tracker, VRMat *transform);
void SetTransformWorld(VRMat *transform);
// void SetTargetTracker(TrackerID tracker);
void SetTargetWorld();
std::function<void(TrackerID)> _GrabBeginCallback;
std::function<void(TrackerID)> _GrabEndCallback;
void ControllerRelease();
void ControllerGrab(TrackerID controller);
private:
glm::mat4x4 GetTransformAbsolute();
bool _initialized;
App *_app;
OverlayID _id;
std::string _name;
bool _is_held;
bool _hidden;
float _width_m;
float _alpha;
TrackerID _active_hand;
Target _target;
};

View file

@ -1,18 +1,16 @@
#include "panel.h"
#include "app.h"
#include "overlay.h"
Panel::Panel(App *app, vr::HmdMatrix34_t start_pose, int index, int x, int y, int width, int height)
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),
_grab_component(app)
_overlay(app, "screen_view_" + std::to_string(index))
{
_name = "screen_view_" + std::to_string(index);
_alpha = 1.0f;
_meters = 1.0f;
glGenTextures(1, &_gl_texture);
glBindTexture(GL_TEXTURE_2D, _gl_texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
@ -25,20 +23,7 @@ Panel::Panel(App *app, vr::HmdMatrix34_t start_pose, int index, int x, int y, in
_texture.eType = vr::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);
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, &start_pose);
}
// _overlay;
}
void Panel::Update()
@ -46,7 +31,12 @@ void Panel::Update()
Render();
UpdateCursor();
_grab_component.Update(_id, &_meters);
_overlay.Update();
}
Overlay *Panel::GetOverlay()
{
return &_overlay;
}
void Panel::Render()
@ -58,17 +48,12 @@ void Panel::Render()
0, 0, 0,
_width, _height, 1);
auto set_texture_err = _app->vr_overlay->SetOverlayTexture(_id, &_texture);
assert(set_texture_err == 0);
_overlay.SetTexture(&_texture);
}
void Panel::SetHidden(bool state)
{
_hidden = state;
if (state)
_app->vr_overlay->HideOverlay(_id);
else
_app->vr_overlay->ShowOverlay(_id);
_overlay.SetHidden(state);
}
void Panel::UpdateCursor()
@ -76,7 +61,7 @@ void Panel::UpdateCursor()
auto global_pos = _app->GetCursorPosition();
if (global_pos.x < _x || global_pos.x >= _x + _width || global_pos.y < _y || global_pos.y >= _y + _height)
{
_app->vr_overlay->ClearOverlayCursorPositionOverride(_id);
_app->vr_overlay->ClearOverlayCursorPositionOverride(_overlay.Id());
return;
}
int local_x = global_pos.x - _x;
@ -88,5 +73,5 @@ void Panel::UpdateCursor()
float x = local_x / (float)_width;
float y = 1.0f - (local_y / (float)_width + top_edge);
auto pos = vr::HmdVector2_t{x, y};
_app->vr_overlay->SetOverlayCursorPositionOverride(_id, &pos);
_app->vr_overlay->SetOverlayCursorPositionOverride(_overlay.Id(), &pos);
}

View file

@ -1,38 +1,36 @@
#pragma once
#include "overlay.h"
#define GL_GLEXT_PROTOTYPES
#include "grab_component.h"
#include "util.h"
#include <GLFW/glfw3.h>
const vr::HmdMatrix34_t DEFAULT_POSE = {{{1, 0, 0, 0}, {0, 1, 0, 1}, {0, 0, 1, 0}}};
const VRMat DEFAULT_POSE = {{{1, 0, 0, 0}, {0, 1, 0, 1}, {0, 0, 1, 0}}};
class App;
class Overlay;
class Panel
{
public:
Panel(App *app, vr::HmdMatrix34_t start_pose, int index, int xmin, int xmax, int ymin, int ymax);
Panel(App *app, int index, int xmin, int xmax, int ymin, int ymax);
void Update();
void SetHidden(bool state);
Overlay *GetOverlay();
private:
void Render();
void UpdateCursor();
App *_app;
OverlayID _id;
int _index;
std::string _name;
int _x, _y;
int _width, _height;
float _meters;
float _alpha;
bool _hidden;
GrabComponent _grab_component;
Overlay _overlay;
vr::Texture_t _texture;
GLuint _gl_texture;

View file

@ -5,8 +5,9 @@
typedef vr::TrackedDeviceIndex_t TrackerID;
typedef vr::VROverlayHandle_t OverlayID;
typedef vr::HmdMatrix34_t VRMat;
inline void PrintMat(vr::HmdMatrix34_t m)
inline void PrintMat(VRMat 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]);
@ -21,7 +22,7 @@ inline void PrintMat(glm::mat4x4 m)
printf("[%.2f, %.2f, %.2f, %.2f]\n", m[3][0], m[3][1], m[3][2], m[3][3]);
}
inline glm::mat4x4 ConvertMat(vr::HmdMatrix34_t mat)
inline glm::mat4x4 ConvertMat(VRMat mat)
{
auto m = mat.m;
return glm::mat4x4(
@ -31,10 +32,10 @@ inline glm::mat4x4 ConvertMat(vr::HmdMatrix34_t mat)
m[0][3], m[1][3], m[2][3], 1);
}
inline vr::HmdMatrix34_t ConvertMat(glm::mat4x4 m)
inline VRMat ConvertMat(glm::mat4x4 m)
{
// clang-format off
return vr::HmdMatrix34_t{{
return VRMat{{
{m[0][0], m[1][0], m[2][0], m[3][0]},
{m[0][1], m[1][1], m[2][1], m[3][1]},
{m[0][2], m[1][2], m[2][2], m[3][2]}
@ -47,7 +48,7 @@ inline glm::vec3 GetPos(glm::mat4x4 mat)
return glm::vec3(mat[3][0], mat[3][1], mat[3][2]);
}
inline glm::vec3 GetPos(vr::HmdMatrix34_t mat)
inline glm::vec3 GetPos(VRMat mat)
{
return glm::vec3(mat.m[0][3], mat.m[1][3], mat.m[2][3]);
}