mirror of
https://gitlab.freedesktop.org/monado/monado.git
synced 2025-01-29 18:08:29 +00:00
d/steamvr_lh: Add SteamVR Lighthouse driver wrapper
Implement support for SteamVR's Lighthouse driver (on Linux). Only tested/works with the OG Vive and Vive wands, but adding new device support should be simple.
This commit is contained in:
parent
71fa4fd3b4
commit
4a9f92e151
|
@ -215,6 +215,11 @@ set(ILLIXR_PATH
|
|||
# This one is named differently because that's what CTest uses
|
||||
option(BUILD_TESTING "Enable building of the test suite?" ON)
|
||||
|
||||
# Check if Steam's root folder exists
|
||||
if(EXISTS "$ENV{HOME}/.steam/root")
|
||||
set(XRT_HAVE_STEAM YES)
|
||||
endif()
|
||||
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
set(XRT_HAVE_INTERNAL_HID ON)
|
||||
endif()
|
||||
|
@ -336,6 +341,7 @@ option_with_deps(XRT_BUILD_DRIVER_QWERTY "Enable Qwerty driver" DEPENDS XRT_HAVE
|
|||
option_with_deps(XRT_BUILD_DRIVER_REALSENSE "Enable RealSense device driver" DEPENDS XRT_HAVE_REALSENSE)
|
||||
option_with_deps(XRT_BUILD_DRIVER_REMOTE "Enable remote debugging driver" DEPENDS "XRT_HAVE_LINUX OR ANDROID OR WIN32")
|
||||
option_with_deps(XRT_BUILD_DRIVER_RIFT_S "Enable Oculus Rift S device driver" DEPENDS XRT_HAVE_HIDAPI XRT_HAVE_V4L2)
|
||||
option_with_deps(XRT_BUILD_DRIVER_STEAMVR_LIGHTHOUSE "Enable SteamVR Lighthouse driver" DEPENDS XRT_HAVE_LINUX XRT_HAVE_STEAM XRT_MODULE_AUX_VIVE)
|
||||
option_with_deps(XRT_BUILD_DRIVER_SURVIVE "Enable libsurvive driver" DEPENDS SURVIVE_FOUND XRT_MODULE_AUX_VIVE)
|
||||
option_with_deps(XRT_BUILD_DRIVER_ULV2 "Enable Ultraleap v2 driver" DEPENDS LeapV2_FOUND)
|
||||
option_with_deps(XRT_BUILD_DRIVER_VF "Build video frame driver (for video file support, uses gstreamer)" DEPENDS XRT_HAVE_GST)
|
||||
|
@ -404,6 +410,7 @@ list(
|
|||
"EUROC"
|
||||
"SIMULAVR"
|
||||
"TWRAP"
|
||||
"STEAMVR_LIGHTHOUSE"
|
||||
)
|
||||
|
||||
# Package name needs to be known by the native code itself.
|
||||
|
@ -566,33 +573,34 @@ message(STATUS "# FEATURE_STEAMVR_PLUGIN: ${XRT_FEATURE_STEAMVR
|
|||
message(STATUS "# FEATURE_TRACING: ${XRT_FEATURE_TRACING}")
|
||||
message(STATUS "# FEATURE_WINDOW_PEEK: ${XRT_FEATURE_WINDOW_PEEK}")
|
||||
message(STATUS "#")
|
||||
message(STATUS "# DRIVER_ANDROID: ${XRT_BUILD_DRIVER_ANDROID}")
|
||||
message(STATUS "# DRIVER_ARDUINO: ${XRT_BUILD_DRIVER_ARDUINO}")
|
||||
message(STATUS "# DRIVER_DAYDREAM: ${XRT_BUILD_DRIVER_DAYDREAM}")
|
||||
message(STATUS "# DRIVER_DEPTHAI: ${XRT_BUILD_DRIVER_DEPTHAI}")
|
||||
message(STATUS "# DRIVER_EUROC: ${XRT_BUILD_DRIVER_EUROC}")
|
||||
message(STATUS "# DRIVER_HANDTRACKING: ${XRT_BUILD_DRIVER_HANDTRACKING}")
|
||||
message(STATUS "# DRIVER_HDK: ${XRT_BUILD_DRIVER_HDK}")
|
||||
message(STATUS "# DRIVER_HYDRA: ${XRT_BUILD_DRIVER_HYDRA}")
|
||||
message(STATUS "# DRIVER_ILLIXR: ${XRT_BUILD_DRIVER_ILLIXR}")
|
||||
message(STATUS "# DRIVER_NS: ${XRT_BUILD_DRIVER_NS}")
|
||||
message(STATUS "# DRIVER_OHMD: ${XRT_BUILD_DRIVER_OHMD}")
|
||||
message(STATUS "# DRIVER_OPENGLOVES: ${XRT_BUILD_DRIVER_OPENGLOVES}")
|
||||
message(STATUS "# DRIVER_PSMV: ${XRT_BUILD_DRIVER_PSMV}")
|
||||
message(STATUS "# DRIVER_PSSENSE: ${XRT_BUILD_DRIVER_PSSENSE}")
|
||||
message(STATUS "# DRIVER_PSVR: ${XRT_BUILD_DRIVER_PSVR}")
|
||||
message(STATUS "# DRIVER_QWERTY: ${XRT_BUILD_DRIVER_QWERTY}")
|
||||
message(STATUS "# DRIVER_REALSENSE: ${XRT_BUILD_DRIVER_REALSENSE}")
|
||||
message(STATUS "# DRIVER_REMOTE: ${XRT_BUILD_DRIVER_REMOTE}")
|
||||
message(STATUS "# DRIVER_RIFT_S: ${XRT_BUILD_DRIVER_RIFT_S}")
|
||||
message(STATUS "# DRIVER_SIMULATED: ${XRT_BUILD_DRIVER_SIMULATED}")
|
||||
message(STATUS "# DRIVER_SIMULAVR: ${XRT_BUILD_DRIVER_SIMULAVR}")
|
||||
message(STATUS "# DRIVER_SURVIVE: ${XRT_BUILD_DRIVER_SURVIVE}")
|
||||
message(STATUS "# DRIVER_TWRAP: ${XRT_BUILD_DRIVER_TWRAP}")
|
||||
message(STATUS "# DRIVER_ULV2: ${XRT_BUILD_DRIVER_ULV2}")
|
||||
message(STATUS "# DRIVER_VF: ${XRT_BUILD_DRIVER_VF}")
|
||||
message(STATUS "# DRIVER_VIVE: ${XRT_BUILD_DRIVER_VIVE}")
|
||||
message(STATUS "# DRIVER_WMR: ${XRT_BUILD_DRIVER_WMR}")
|
||||
message(STATUS "# DRIVER_ANDROID: ${XRT_BUILD_DRIVER_ANDROID}")
|
||||
message(STATUS "# DRIVER_ARDUINO: ${XRT_BUILD_DRIVER_ARDUINO}")
|
||||
message(STATUS "# DRIVER_DAYDREAM: ${XRT_BUILD_DRIVER_DAYDREAM}")
|
||||
message(STATUS "# DRIVER_DEPTHAI: ${XRT_BUILD_DRIVER_DEPTHAI}")
|
||||
message(STATUS "# DRIVER_EUROC: ${XRT_BUILD_DRIVER_EUROC}")
|
||||
message(STATUS "# DRIVER_HANDTRACKING: ${XRT_BUILD_DRIVER_HANDTRACKING}")
|
||||
message(STATUS "# DRIVER_HDK: ${XRT_BUILD_DRIVER_HDK}")
|
||||
message(STATUS "# DRIVER_HYDRA: ${XRT_BUILD_DRIVER_HYDRA}")
|
||||
message(STATUS "# DRIVER_ILLIXR: ${XRT_BUILD_DRIVER_ILLIXR}")
|
||||
message(STATUS "# DRIVER_NS: ${XRT_BUILD_DRIVER_NS}")
|
||||
message(STATUS "# DRIVER_OHMD: ${XRT_BUILD_DRIVER_OHMD}")
|
||||
message(STATUS "# DRIVER_OPENGLOVES: ${XRT_BUILD_DRIVER_OPENGLOVES}")
|
||||
message(STATUS "# DRIVER_PSMV: ${XRT_BUILD_DRIVER_PSMV}")
|
||||
message(STATUS "# DRIVER_PSSENSE: ${XRT_BUILD_DRIVER_PSSENSE}")
|
||||
message(STATUS "# DRIVER_PSVR: ${XRT_BUILD_DRIVER_PSVR}")
|
||||
message(STATUS "# DRIVER_QWERTY: ${XRT_BUILD_DRIVER_QWERTY}")
|
||||
message(STATUS "# DRIVER_REALSENSE: ${XRT_BUILD_DRIVER_REALSENSE}")
|
||||
message(STATUS "# DRIVER_REMOTE: ${XRT_BUILD_DRIVER_REMOTE}")
|
||||
message(STATUS "# DRIVER_RIFT_S: ${XRT_BUILD_DRIVER_RIFT_S}")
|
||||
message(STATUS "# DRIVER_SIMULATED: ${XRT_BUILD_DRIVER_SIMULATED}")
|
||||
message(STATUS "# DRIVER_SIMULAVR: ${XRT_BUILD_DRIVER_SIMULAVR}")
|
||||
message(STATUS "# DRIVER_SURVIVE: ${XRT_BUILD_DRIVER_SURVIVE}")
|
||||
message(STATUS "# DRIVER_TWRAP: ${XRT_BUILD_DRIVER_TWRAP}")
|
||||
message(STATUS "# DRIVER_ULV2: ${XRT_BUILD_DRIVER_ULV2}")
|
||||
message(STATUS "# DRIVER_VF: ${XRT_BUILD_DRIVER_VF}")
|
||||
message(STATUS "# DRIVER_VIVE: ${XRT_BUILD_DRIVER_VIVE}")
|
||||
message(STATUS "# DRIVER_WMR: ${XRT_BUILD_DRIVER_WMR}")
|
||||
message(STATUS "# DRIVER_STEAMVR_LIGHTHOUSE: ${XRT_BUILD_DRIVER_STEAMVR_LIGHTHOUSE}")
|
||||
message(STATUS "#####----- Config -----#####")
|
||||
|
||||
if(XRT_FEATURE_SERVICE AND NOT XRT_FEATURE_OPENXR)
|
||||
|
|
|
@ -432,6 +432,32 @@ if(XRT_BUILD_DRIVER_SIMULAVR)
|
|||
list(APPEND ENABLED_HEADSET_DRIVERS svr)
|
||||
endif()
|
||||
|
||||
if(XRT_BUILD_DRIVER_STEAMVR_LIGHTHOUSE)
|
||||
add_library(
|
||||
drv_steamvr_lh STATIC
|
||||
steamvr_lh/steamvr_lh.cpp
|
||||
steamvr_lh/device.cpp
|
||||
steamvr_lh/interfaces/driver_manager.cpp
|
||||
steamvr_lh/interfaces/iobuffer.cpp
|
||||
steamvr_lh/interfaces/resources.cpp
|
||||
steamvr_lh/interfaces/settings.cpp
|
||||
steamvr_lh/interfaces/blockqueue.cpp
|
||||
steamvr_lh/interfaces/paths.cpp
|
||||
)
|
||||
target_link_libraries(
|
||||
drv_steamvr_lh
|
||||
PRIVATE
|
||||
aux_util
|
||||
aux_math
|
||||
xrt-interfaces
|
||||
xrt-external-openvr
|
||||
xrt-external-cjson
|
||||
xrt-external-vdf
|
||||
${CMAKE_DL_LIBS}
|
||||
)
|
||||
list(APPEND ENABLED_HEADSET_DRIVERS steamvr_lh)
|
||||
endif()
|
||||
|
||||
if(XRT_BUILD_SAMPLES)
|
||||
# We build the sample driver to make sure it stays valid,
|
||||
# but it never gets linked into a final target.
|
||||
|
|
435
src/xrt/drivers/steamvr_lh/device.cpp
Normal file
435
src/xrt/drivers/steamvr_lh/device.cpp
Normal file
|
@ -0,0 +1,435 @@
|
|||
// Copyright 2023, Shawn Wallace
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
/*!
|
||||
* @file
|
||||
* @brief SteamVR driver device implementation.
|
||||
* @author Shawn Wallace <yungwallace@live.com>
|
||||
* @ingroup drv_steamvr_lh
|
||||
*/
|
||||
|
||||
#include <functional>
|
||||
#include <cstring>
|
||||
#include <thread>
|
||||
#include <algorithm>
|
||||
|
||||
#include "math/m_api.h"
|
||||
#include "device.hpp"
|
||||
#include "interfaces/context.hpp"
|
||||
#include "util/u_device.h"
|
||||
#include "util/u_logging.h"
|
||||
#include "util/u_json.hpp"
|
||||
|
||||
#define DEV_ERR(...) U_LOG_IFL_E(ctx->log_level, __VA_ARGS__)
|
||||
#define DEV_WARN(...) U_LOG_IFL_W(ctx->log_level, __VA_ARGS__)
|
||||
#define DEV_INFO(...) U_LOG_IFL_I(ctx->log_level, __VA_ARGS__)
|
||||
#define DEV_DEBUG(...) U_LOG_IFL_D(ctx->log_level, __VA_ARGS__)
|
||||
|
||||
// Each device will have its own input class.
|
||||
struct InputClass
|
||||
{
|
||||
xrt_device_name name;
|
||||
std::string description;
|
||||
const std::vector<xrt_input_name> poses;
|
||||
const std::unordered_map<std::string_view, xrt_input_name> non_poses;
|
||||
};
|
||||
|
||||
namespace {
|
||||
InputClass hmd_class{{}, "Generic HMD", {XRT_INPUT_GENERIC_HEAD_POSE}, {}};
|
||||
|
||||
// Adding support for a new controller is a simple as adding it here.
|
||||
// The key for the map needs to be the name of input profile as indicated by the lighthouse driver.
|
||||
const std::unordered_map<std::string_view, InputClass> controller_classes{
|
||||
{
|
||||
"vive_controller",
|
||||
InputClass{
|
||||
XRT_DEVICE_VIVE_WAND,
|
||||
"Vive Wand",
|
||||
{
|
||||
XRT_INPUT_VIVE_GRIP_POSE,
|
||||
XRT_INPUT_VIVE_AIM_POSE,
|
||||
},
|
||||
{
|
||||
{"/input/application_menu/click", XRT_INPUT_VIVE_MENU_CLICK},
|
||||
{"/input/trackpad/click", XRT_INPUT_VIVE_TRACKPAD_CLICK},
|
||||
{"/input/trackpad/touch", XRT_INPUT_VIVE_TRACKPAD_TOUCH},
|
||||
{"/input/system/click", XRT_INPUT_VIVE_SYSTEM_CLICK},
|
||||
{"/input/trigger/click", XRT_INPUT_VIVE_TRIGGER_CLICK},
|
||||
{"/input/trigger/value", XRT_INPUT_VIVE_TRIGGER_VALUE},
|
||||
{"/input/grip/click", XRT_INPUT_VIVE_SQUEEZE_CLICK},
|
||||
{"/input/trackpad", XRT_INPUT_VIVE_TRACKPAD},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// Template for calling a member function of Device from a free function
|
||||
template <typename DeviceType, auto Func, typename Ret, typename... Args>
|
||||
inline Ret
|
||||
device_bouncer(struct xrt_device *xdev, Args... args)
|
||||
{
|
||||
auto *dev = static_cast<DeviceType *>(xdev);
|
||||
return std::invoke(Func, dev, args...);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
HmdDevice::HmdDevice(const DeviceBuilder &builder) : Device(builder)
|
||||
{
|
||||
this->name = XRT_DEVICE_GENERIC_HMD;
|
||||
this->device_type = XRT_DEVICE_TYPE_HMD;
|
||||
this->container_handle = 0;
|
||||
|
||||
set_input_class(&hmd_class);
|
||||
|
||||
#define SETUP_MEMBER_FUNC(name) this->xrt_device::name = &device_bouncer<HmdDevice, &HmdDevice::name>
|
||||
SETUP_MEMBER_FUNC(get_view_poses);
|
||||
SETUP_MEMBER_FUNC(compute_distortion);
|
||||
#undef SETUP_MEMBER_FUNC
|
||||
}
|
||||
|
||||
ControllerDevice::ControllerDevice(vr::PropertyContainerHandle_t handle, const DeviceBuilder &builder) : Device(builder)
|
||||
{
|
||||
this->device_type = XRT_DEVICE_TYPE_ANY_HAND_CONTROLLER;
|
||||
this->container_handle = handle;
|
||||
this->xrt_device::set_output = &device_bouncer<ControllerDevice, &ControllerDevice::set_output>;
|
||||
}
|
||||
|
||||
Device::Device(const DeviceBuilder &builder) : xrt_device({}), ctx(builder.ctx), driver(builder.driver)
|
||||
{
|
||||
std::strncpy(this->serial, builder.serial, XRT_DEVICE_NAME_LEN);
|
||||
this->tracking_origin = ctx.get();
|
||||
this->orientation_tracking_supported = true;
|
||||
this->position_tracking_supported = true;
|
||||
this->hand_tracking_supported = false;
|
||||
this->force_feedback_supported = false;
|
||||
this->form_factor_check_supported = false;
|
||||
|
||||
#define SETUP_MEMBER_FUNC(name) this->xrt_device::name = &device_bouncer<Device, &Device::name>
|
||||
SETUP_MEMBER_FUNC(update_inputs);
|
||||
SETUP_MEMBER_FUNC(get_tracked_pose);
|
||||
#undef SETUP_MEMBER_FUNC
|
||||
|
||||
this->xrt_device::destroy = [](xrt_device *xdev) {
|
||||
auto *dev = static_cast<Device *>(xdev);
|
||||
dev->driver->Deactivate();
|
||||
delete dev;
|
||||
};
|
||||
|
||||
init_chaperone(builder.steam_install);
|
||||
}
|
||||
|
||||
void
|
||||
Device::set_input_class(const InputClass *input_class)
|
||||
{
|
||||
// this should only be called once
|
||||
assert(inputs_vec.empty());
|
||||
this->input_class = input_class;
|
||||
|
||||
// reserve to ensure our pointers don't get invalidated
|
||||
inputs_vec.reserve(input_class->poses.size() + input_class->non_poses.size());
|
||||
for (xrt_input_name input : input_class->poses) {
|
||||
inputs_vec.push_back({true, 0, input, {}});
|
||||
}
|
||||
for (const auto &[path, input] : input_class->non_poses) {
|
||||
assert(inputs_vec.capacity() >= inputs_vec.size() + 1);
|
||||
inputs_vec.push_back({true, 0, input, {}});
|
||||
inputs_map.insert({path, &inputs_vec.back()});
|
||||
}
|
||||
|
||||
this->inputs = inputs_vec.data();
|
||||
this->input_count = inputs_vec.size();
|
||||
}
|
||||
|
||||
xrt_input *
|
||||
Device::get_input_from_name(const std::string_view name)
|
||||
{
|
||||
auto input = inputs_map.find(name);
|
||||
if (input == inputs_map.end()) {
|
||||
DEV_WARN("requested unknown input name %s for device %s", std::string(name).c_str(), serial);
|
||||
return nullptr;
|
||||
}
|
||||
return input->second;
|
||||
}
|
||||
|
||||
void
|
||||
ControllerDevice::set_haptic_handle(vr::VRInputComponentHandle_t handle)
|
||||
{
|
||||
// this should only be set once
|
||||
assert(output == nullptr);
|
||||
DEV_DEBUG("setting haptic handle for %lu", handle);
|
||||
haptic_handle = handle;
|
||||
xrt_output_name name;
|
||||
switch (this->name) {
|
||||
case XRT_DEVICE_VIVE_WAND: {
|
||||
name = XRT_OUTPUT_NAME_VIVE_HAPTIC;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
DEV_WARN("Unknown device name (%u), haptics will not work", this->name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
output = std::make_unique<xrt_output>(xrt_output{name});
|
||||
this->output_count = 1;
|
||||
this->outputs = output.get();
|
||||
}
|
||||
|
||||
void
|
||||
Device::update_inputs()
|
||||
{
|
||||
ctx->maybe_run_frame(++current_frame);
|
||||
}
|
||||
|
||||
void
|
||||
Device::get_tracked_pose(xrt_input_name name, uint64_t at_timestamp_ns, xrt_space_relation *out_relation)
|
||||
{
|
||||
*out_relation = this->relation;
|
||||
}
|
||||
|
||||
void
|
||||
ControllerDevice::set_output(xrt_output_name name, const xrt_output_value *value)
|
||||
|
||||
{
|
||||
const auto &vib = value->vibration;
|
||||
if (vib.amplitude == 0.0)
|
||||
return;
|
||||
vr::VREvent_HapticVibration_t event;
|
||||
event.containerHandle = container_handle;
|
||||
event.componentHandle = haptic_handle;
|
||||
event.fDurationSeconds = (float)vib.duration_ns / 1e9f;
|
||||
// 0.0f in OpenXR means let the driver determine a frequency, but
|
||||
// in OpenVR means no haptic.
|
||||
event.fFrequency = std::max(vib.frequency, 1.0f);
|
||||
event.fAmplitude = vib.amplitude;
|
||||
|
||||
ctx->add_haptic_event(event);
|
||||
}
|
||||
|
||||
void
|
||||
HmdDevice::get_view_poses(const xrt_vec3 *default_eye_relation,
|
||||
uint64_t at_timestamp_ns,
|
||||
uint32_t view_count,
|
||||
xrt_space_relation *out_head_relation,
|
||||
xrt_fov *out_fovs,
|
||||
xrt_pose *out_poses)
|
||||
{
|
||||
u_device_get_view_poses(this, default_eye_relation, at_timestamp_ns, view_count, out_head_relation, out_fovs,
|
||||
out_poses);
|
||||
}
|
||||
|
||||
bool
|
||||
HmdDevice::compute_distortion(uint32_t view, float u, float v, xrt_uv_triplet *out_result)
|
||||
{
|
||||
vr::EVREye eye = (view == 0) ? vr::Eye_Left : vr::Eye_Right;
|
||||
vr::DistortionCoordinates_t coords = this->hmd_parts->display->ComputeDistortion(eye, u, v);
|
||||
out_result->r = {coords.rfRed[0], coords.rfRed[1]};
|
||||
out_result->g = {coords.rfGreen[0], coords.rfGreen[1]};
|
||||
out_result->b = {coords.rfBlue[0], coords.rfBlue[1]};
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
HmdDevice::set_hmd_parts(std::unique_ptr<Parts> parts)
|
||||
{
|
||||
{
|
||||
std::lock_guard lk(hmd_parts_mut);
|
||||
hmd_parts = std::move(parts);
|
||||
this->hmd = &hmd_parts->base;
|
||||
}
|
||||
hmd_parts_cv.notify_all();
|
||||
}
|
||||
|
||||
namespace {
|
||||
xrt_quat
|
||||
copy_quat(const vr::HmdQuaternion_t &quat)
|
||||
{
|
||||
return xrt_quat{(float)quat.x, (float)quat.y, (float)quat.z, (float)quat.w};
|
||||
}
|
||||
|
||||
xrt_vec3
|
||||
copy_vec3(const double (&vec)[3])
|
||||
{
|
||||
return xrt_vec3{(float)vec[0], (float)vec[1], (float)vec[2]};
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void
|
||||
Device::init_chaperone(const std::string &steam_install)
|
||||
{
|
||||
static bool initialized = false;
|
||||
if (initialized)
|
||||
return;
|
||||
|
||||
initialized = true;
|
||||
|
||||
// Lighthouse driver seems to create a lighthousedb.json and a chaperone_info.vrchap (which is json)
|
||||
// We will use the known_universes from the lighthousedb.json to match to a universe from chaperone_info.vrchap
|
||||
|
||||
using xrt::auxiliary::util::json::JSONNode;
|
||||
auto lighthousedb = JSONNode::loadFromFile(steam_install + "/config/lighthouse/lighthousedb.json");
|
||||
if (lighthousedb.isInvalid()) {
|
||||
DEV_ERR("Couldn't load lighthousedb file, playspace center will be off - was Room Setup run?");
|
||||
return;
|
||||
}
|
||||
auto chap_info = JSONNode::loadFromFile(steam_install + "/config/chaperone_info.vrchap");
|
||||
if (chap_info.isInvalid()) {
|
||||
DEV_ERR("Couldn't load chaperone info, playspace center will be off - was Room Setup run?");
|
||||
return;
|
||||
}
|
||||
|
||||
// XXX: This may be broken if there are multiple known universes - how do we determine which to use then?
|
||||
JSONNode universe = lighthousedb["known_universes"][0];
|
||||
std::string id = universe["id"].asString();
|
||||
JSONNode info;
|
||||
for (const JSONNode &u : chap_info["universes"].asArray()) {
|
||||
if (u["universeID"].asString() == id) {
|
||||
DEV_INFO("Found info for universe %s", id.c_str());
|
||||
info = u;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (info.isInvalid()) {
|
||||
DEV_ERR("Couldn't find chaperone info for universe %s, playspace center will be off", id.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<JSONNode> translation_arr = info["standing"]["translation"].asArray();
|
||||
double yaw = info["standing"]["yaw"].asDouble();
|
||||
chaperone_center = {static_cast<float>(translation_arr[0].asDouble()),
|
||||
static_cast<float>(translation_arr[1].asDouble()),
|
||||
static_cast<float>(translation_arr[2].asDouble())};
|
||||
const xrt_vec3 yaw_axis{0.0, -1.0, 0.0};
|
||||
math_quat_from_angle_vector(yaw, &yaw_axis, &chaperone_yaw);
|
||||
DEV_INFO("Initialized chaperone data.");
|
||||
}
|
||||
|
||||
void
|
||||
Device::update_pose(const vr::DriverPose_t &newPose)
|
||||
{
|
||||
xrt_space_relation relation;
|
||||
if (newPose.poseIsValid) {
|
||||
relation.relation_flags = XRT_SPACE_RELATION_BITMASK_ALL;
|
||||
|
||||
const xrt_vec3 to_local_pos = copy_vec3(newPose.vecDriverFromHeadTranslation);
|
||||
const xrt_quat to_local_rot = copy_quat(newPose.qDriverFromHeadRotation);
|
||||
const xrt_vec3 to_world_pos = copy_vec3(newPose.vecWorldFromDriverTranslation);
|
||||
const xrt_quat to_world_rot = copy_quat(newPose.qWorldFromDriverRotation);
|
||||
|
||||
xrt_pose &pose = relation.pose;
|
||||
pose.position = copy_vec3(newPose.vecPosition);
|
||||
pose.orientation = copy_quat(newPose.qRotation);
|
||||
relation.linear_velocity = copy_vec3(newPose.vecVelocity);
|
||||
relation.angular_velocity = copy_vec3(newPose.vecAngularVelocity);
|
||||
|
||||
// apply world transform
|
||||
auto world_transform = [&](xrt_vec3 &vec) {
|
||||
math_quat_rotate_vec3(&to_world_rot, &vec, &vec);
|
||||
math_vec3_accum(&to_world_pos, &vec);
|
||||
};
|
||||
world_transform(pose.position);
|
||||
world_transform(relation.linear_velocity);
|
||||
math_quat_rotate(&to_world_rot, &pose.orientation, &pose.orientation);
|
||||
math_quat_rotate_vec3(&pose.orientation, &relation.angular_velocity, &relation.angular_velocity);
|
||||
|
||||
// apply local transform
|
||||
xrt_vec3 local_rotated;
|
||||
math_quat_rotate_vec3(&pose.orientation, &to_local_pos, &local_rotated);
|
||||
math_vec3_accum(&local_rotated, &pose.position);
|
||||
math_vec3_accum(&local_rotated, &relation.linear_velocity);
|
||||
math_quat_rotate(&pose.orientation, &to_local_rot, &pose.orientation);
|
||||
|
||||
// apply chaperone transform
|
||||
auto chap_transform = [&](xrt_vec3 &vec) {
|
||||
math_vec3_accum(&chaperone_center, &vec);
|
||||
math_quat_rotate_vec3(&chaperone_yaw, &vec, &vec);
|
||||
};
|
||||
chap_transform(pose.position);
|
||||
chap_transform(relation.linear_velocity);
|
||||
math_quat_rotate(&chaperone_yaw, &pose.orientation, &pose.orientation);
|
||||
} else {
|
||||
relation.relation_flags = XRT_SPACE_RELATION_BITMASK_NONE;
|
||||
}
|
||||
this->relation = relation;
|
||||
}
|
||||
|
||||
void
|
||||
Device::handle_properties(const vr::PropertyWrite_t *batch, uint32_t count)
|
||||
{
|
||||
for (uint32_t i = 0; i < count; ++i) {
|
||||
handle_property_write(batch[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
HmdDevice::set_nominal_frame_interval(uint64_t interval_ns)
|
||||
{
|
||||
auto set = [this, interval_ns] { hmd_parts->base.screens[0].nominal_frame_interval_ns = interval_ns; };
|
||||
|
||||
if (hmd_parts) {
|
||||
set();
|
||||
} else {
|
||||
std::thread t([this, set] {
|
||||
std::unique_lock lk(hmd_parts_mut);
|
||||
hmd_parts_cv.wait(lk, [this] { return hmd_parts != nullptr; });
|
||||
set();
|
||||
});
|
||||
t.detach();
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
// From openvr driver documentation
|
||||
// (https://github.com/ValveSoftware/openvr/blob/master/docs/Driver_API_Documentation.md#Input-Profiles):
|
||||
// "Input profiles are expected to be a valid JSON file,
|
||||
// and should be located: <driver_name>/resources/input/<device_name>_profile.json"
|
||||
// So we will just parse the file name to get the device name.
|
||||
std::string_view
|
||||
parse_profile(std::string_view path)
|
||||
{
|
||||
size_t name_start_idx = path.find_last_of('/') + 1;
|
||||
size_t name_end_idx = path.find_last_of('_');
|
||||
return path.substr(name_start_idx, name_end_idx - name_start_idx);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void
|
||||
HmdDevice::handle_property_write(const vr::PropertyWrite_t &prop)
|
||||
{
|
||||
switch (prop.prop) {
|
||||
case vr::Prop_DisplayFrequency_Float: {
|
||||
assert(prop.unBufferSize == sizeof(float));
|
||||
float freq = *static_cast<float *>(prop.pvBuffer);
|
||||
set_nominal_frame_interval((1.f / freq) * 1e9f);
|
||||
break;
|
||||
}
|
||||
case vr::Prop_InputProfilePath_String: {
|
||||
std::string_view profile =
|
||||
parse_profile(std::string_view(static_cast<char *>(prop.pvBuffer), prop.unBufferSize));
|
||||
if (profile == "vive") {
|
||||
std::strcpy(this->str, "Vive HMD");
|
||||
}
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ControllerDevice::handle_property_write(const vr::PropertyWrite_t &prop)
|
||||
{
|
||||
switch (prop.prop) {
|
||||
case vr::Prop_InputProfilePath_String: {
|
||||
std::string_view profile =
|
||||
parse_profile(std::string_view(static_cast<char *>(prop.pvBuffer), prop.unBufferSize));
|
||||
auto input_class = controller_classes.find(profile);
|
||||
if (input_class == controller_classes.end()) {
|
||||
DEV_ERR("Could not find input class for controller profile %s", std::string(profile).c_str());
|
||||
} else {
|
||||
std::strcpy(this->str, input_class->second.description.c_str());
|
||||
this->name = input_class->second.name;
|
||||
set_input_class(&input_class->second);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
}
|
140
src/xrt/drivers/steamvr_lh/device.hpp
Normal file
140
src/xrt/drivers/steamvr_lh/device.hpp
Normal file
|
@ -0,0 +1,140 @@
|
|||
// Copyright 2023, Shawn Wallace
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
/*!
|
||||
* @file
|
||||
* @brief SteamVR driver device header - inherits xrt_device.
|
||||
* @author Shawn Wallace <yungwallace@live.com>
|
||||
* @ingroup drv_steamvr_lh
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
|
||||
#include "xrt/xrt_device.h"
|
||||
#include "openvr_driver.h"
|
||||
|
||||
class Context;
|
||||
struct InputClass;
|
||||
|
||||
struct DeviceBuilder
|
||||
{
|
||||
std::shared_ptr<Context> ctx;
|
||||
vr::ITrackedDeviceServerDriver *driver;
|
||||
const char *serial;
|
||||
const std::string &steam_install;
|
||||
|
||||
// no copies!
|
||||
DeviceBuilder(const DeviceBuilder &) = delete;
|
||||
DeviceBuilder
|
||||
operator=(const DeviceBuilder &) = delete;
|
||||
};
|
||||
|
||||
class Device : public xrt_device
|
||||
{
|
||||
|
||||
public:
|
||||
xrt_space_relation relation = XRT_SPACE_RELATION_ZERO;
|
||||
|
||||
virtual ~Device() = default;
|
||||
|
||||
xrt_input *
|
||||
get_input_from_name(std::string_view name);
|
||||
|
||||
void
|
||||
update_inputs();
|
||||
|
||||
void
|
||||
update_pose(const vr::DriverPose_t &newPose);
|
||||
|
||||
void
|
||||
get_tracked_pose(xrt_input_name name, uint64_t at_timestamp_ns, xrt_space_relation *out_relation);
|
||||
|
||||
void
|
||||
handle_properties(const vr::PropertyWrite_t *batch, uint32_t count);
|
||||
|
||||
protected:
|
||||
Device(const DeviceBuilder &builder);
|
||||
std::shared_ptr<Context> ctx;
|
||||
vr::PropertyContainerHandle_t container_handle{0};
|
||||
|
||||
virtual void
|
||||
handle_property_write(const vr::PropertyWrite_t &prop) = 0;
|
||||
|
||||
void
|
||||
set_input_class(const InputClass *input_class);
|
||||
|
||||
private:
|
||||
vr::ITrackedDeviceServerDriver *driver;
|
||||
const InputClass *input_class;
|
||||
std::vector<xrt_binding_profile> binding_profiles_vec;
|
||||
std::unordered_map<std::string_view, xrt_input *> inputs_map;
|
||||
std::vector<xrt_input> inputs_vec;
|
||||
uint64_t current_frame{0};
|
||||
|
||||
void
|
||||
init_chaperone(const std::string &steam_install);
|
||||
inline static xrt_vec3 chaperone_center{};
|
||||
inline static xrt_quat chaperone_yaw = XRT_QUAT_IDENTITY;
|
||||
};
|
||||
|
||||
class HmdDevice : public Device
|
||||
{
|
||||
public:
|
||||
struct Parts
|
||||
{
|
||||
xrt_hmd_parts base;
|
||||
vr::IVRDisplayComponent *display;
|
||||
};
|
||||
|
||||
HmdDevice(const DeviceBuilder &builder);
|
||||
|
||||
void
|
||||
get_view_poses(const xrt_vec3 *default_eye_relation,
|
||||
uint64_t at_timestamp_ns,
|
||||
uint32_t view_count,
|
||||
xrt_space_relation *out_head_relation,
|
||||
xrt_fov *out_fovs,
|
||||
xrt_pose *out_poses);
|
||||
|
||||
bool
|
||||
compute_distortion(uint32_t view, float u, float v, xrt_uv_triplet *out_result);
|
||||
|
||||
void
|
||||
set_hmd_parts(std::unique_ptr<Parts> parts);
|
||||
|
||||
private:
|
||||
std::unique_ptr<Parts> hmd_parts{nullptr};
|
||||
|
||||
void
|
||||
handle_property_write(const vr::PropertyWrite_t &prop) override;
|
||||
|
||||
void
|
||||
set_nominal_frame_interval(uint64_t interval_ns);
|
||||
|
||||
std::condition_variable hmd_parts_cv;
|
||||
std::mutex hmd_parts_mut;
|
||||
};
|
||||
|
||||
class ControllerDevice : public Device
|
||||
{
|
||||
public:
|
||||
ControllerDevice(vr::PropertyContainerHandle_t container_handle, const DeviceBuilder &builder);
|
||||
|
||||
void
|
||||
set_output(xrt_output_name name, const xrt_output_value *value);
|
||||
|
||||
void
|
||||
set_haptic_handle(vr::VRInputComponentHandle_t handle);
|
||||
|
||||
private:
|
||||
vr::VRInputComponentHandle_t haptic_handle{0};
|
||||
std::unique_ptr<xrt_output> output{nullptr};
|
||||
|
||||
void
|
||||
handle_property_write(const vr::PropertyWrite_t &prop) override;
|
||||
};
|
84
src/xrt/drivers/steamvr_lh/interfaces/blockqueue.cpp
Normal file
84
src/xrt/drivers/steamvr_lh/interfaces/blockqueue.cpp
Normal file
|
@ -0,0 +1,84 @@
|
|||
// Copyright 2023, Shawn Wallace
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
/*!
|
||||
* @file
|
||||
* @brief OpenVR IVRBlockQueue interface implementation.
|
||||
* @author Shawn Wallace <yungwallace@live.com>
|
||||
* @ingroup drv_steamvr_lh
|
||||
*/
|
||||
|
||||
#include "blockqueue.hpp"
|
||||
|
||||
|
||||
// NOLINTBEGIN(bugprone-easily-swappable-parameters)
|
||||
vr::EBlockQueueError
|
||||
BlockQueue::Create(vr::PropertyContainerHandle_t *pulQueueHandle,
|
||||
char *pchPath,
|
||||
uint32_t unBlockDataSize,
|
||||
uint32_t unBlockHeaderSize,
|
||||
uint32_t unBlockCount,
|
||||
uint32_t unFlags)
|
||||
{
|
||||
return vr::EBlockQueueError_BlockQueueError_None;
|
||||
}
|
||||
|
||||
vr::EBlockQueueError
|
||||
BlockQueue::Connect(vr::PropertyContainerHandle_t *pulQueueHandle, char *pchPath)
|
||||
{
|
||||
return vr::EBlockQueueError_BlockQueueError_None;
|
||||
}
|
||||
|
||||
vr::EBlockQueueError
|
||||
BlockQueue::Destroy(vr::PropertyContainerHandle_t ulQueueHandle)
|
||||
{
|
||||
return vr::EBlockQueueError_BlockQueueError_None;
|
||||
}
|
||||
|
||||
vr::EBlockQueueError
|
||||
BlockQueue::AcquireWriteOnlyBlock(vr::PropertyContainerHandle_t ulQueueHandle,
|
||||
vr::PropertyContainerHandle_t *pulBlockHandle,
|
||||
void **ppvBuffer)
|
||||
{
|
||||
return vr::EBlockQueueError_BlockQueueError_None;
|
||||
}
|
||||
|
||||
vr::EBlockQueueError
|
||||
BlockQueue::ReleaseWriteOnlyBlock(vr::PropertyContainerHandle_t ulQueueHandle,
|
||||
vr::PropertyContainerHandle_t ulBlockHandle)
|
||||
{
|
||||
return vr::EBlockQueueError_BlockQueueError_None;
|
||||
}
|
||||
|
||||
vr::EBlockQueueError
|
||||
BlockQueue::WaitAndAcquireReadOnlyBlock(vr::PropertyContainerHandle_t ulQueueHandle,
|
||||
vr::PropertyContainerHandle_t *pulBlockHandle,
|
||||
void **ppvBuffer,
|
||||
vr::EBlockQueueReadType eReadType,
|
||||
uint32_t unTimeoutMs)
|
||||
{
|
||||
return vr::EBlockQueueError_BlockQueueError_None;
|
||||
}
|
||||
|
||||
vr::EBlockQueueError
|
||||
BlockQueue::AcquireReadOnlyBlock(vr::PropertyContainerHandle_t ulQueueHandle,
|
||||
vr::PropertyContainerHandle_t *pulBlockHandle,
|
||||
void **ppvBuffer,
|
||||
vr::EBlockQueueReadType eReadType)
|
||||
{
|
||||
return vr::EBlockQueueError_BlockQueueError_None;
|
||||
}
|
||||
|
||||
vr::EBlockQueueError
|
||||
BlockQueue::ReleaseReadOnlyBlock(vr::PropertyContainerHandle_t ulQueueHandle,
|
||||
vr::PropertyContainerHandle_t ulBlockHandle)
|
||||
{
|
||||
return vr::EBlockQueueError_BlockQueueError_None;
|
||||
}
|
||||
|
||||
vr::EBlockQueueError
|
||||
BlockQueue::QueueHasReader(vr::PropertyContainerHandle_t ulQueueHandle, bool *pbHasReaders)
|
||||
|
||||
{
|
||||
return vr::EBlockQueueError_BlockQueueError_None;
|
||||
}
|
||||
// NOLINTEND(bugprone-easily-swappable-parameters)
|
77
src/xrt/drivers/steamvr_lh/interfaces/blockqueue.hpp
Normal file
77
src/xrt/drivers/steamvr_lh/interfaces/blockqueue.hpp
Normal file
|
@ -0,0 +1,77 @@
|
|||
// Copyright 2023, Shawn Wallace
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
/*!
|
||||
* @file
|
||||
* @brief OpenVR IVRBlockQueue interface header.
|
||||
* @author Shawn Wallace <yungwallace@live.com>
|
||||
* @ingroup drv_steamvr_lh
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "openvr_driver.h"
|
||||
|
||||
/** Definitions missing from C++ header, present in C */
|
||||
namespace vr {
|
||||
inline const char *IVRBlockQueue_Version = "IVRBlockQueue_005";
|
||||
|
||||
typedef enum EBlockQueueError
|
||||
{
|
||||
EBlockQueueError_BlockQueueError_None = 0,
|
||||
EBlockQueueError_BlockQueueError_QueueAlreadyExists = 1,
|
||||
EBlockQueueError_BlockQueueError_QueueNotFound = 2,
|
||||
EBlockQueueError_BlockQueueError_BlockNotAvailable = 3,
|
||||
EBlockQueueError_BlockQueueError_InvalidHandle = 4,
|
||||
EBlockQueueError_BlockQueueError_InvalidParam = 5,
|
||||
EBlockQueueError_BlockQueueError_ParamMismatch = 6,
|
||||
EBlockQueueError_BlockQueueError_InternalError = 7,
|
||||
EBlockQueueError_BlockQueueError_AlreadyInitialized = 8,
|
||||
EBlockQueueError_BlockQueueError_OperationIsServerOnly = 9,
|
||||
EBlockQueueError_BlockQueueError_TooManyConnections = 10,
|
||||
} EBlockQueueError;
|
||||
|
||||
typedef enum EBlockQueueReadType
|
||||
{
|
||||
EBlockQueueReadType_BlockQueueRead_Latest = 0,
|
||||
EBlockQueueReadType_BlockQueueRead_New = 1,
|
||||
EBlockQueueReadType_BlockQueueRead_Next = 2,
|
||||
} EBlockQueueReadType;
|
||||
} // namespace vr
|
||||
|
||||
/** This interface is missing in the C++ header but present in the C one, and the lighthouse driver requires it. */
|
||||
class BlockQueue
|
||||
{
|
||||
public:
|
||||
virtual vr::EBlockQueueError
|
||||
Create(vr::PropertyContainerHandle_t *pulQueueHandle,
|
||||
char *pchPath,
|
||||
uint32_t unBlockDataSize,
|
||||
uint32_t unBlockHeaderSize,
|
||||
uint32_t unBlockCount,
|
||||
uint32_t unFlags);
|
||||
virtual vr::EBlockQueueError
|
||||
Connect(vr::PropertyContainerHandle_t *pulQueueHandle, char *pchPath);
|
||||
virtual vr::EBlockQueueError
|
||||
Destroy(vr::PropertyContainerHandle_t ulQueueHandle);
|
||||
virtual vr::EBlockQueueError
|
||||
AcquireWriteOnlyBlock(vr::PropertyContainerHandle_t ulQueueHandle,
|
||||
vr::PropertyContainerHandle_t *pulBlockHandle,
|
||||
void **ppvBuffer);
|
||||
virtual vr::EBlockQueueError
|
||||
ReleaseWriteOnlyBlock(vr::PropertyContainerHandle_t ulQueueHandle, vr::PropertyContainerHandle_t ulBlockHandle);
|
||||
virtual vr::EBlockQueueError
|
||||
WaitAndAcquireReadOnlyBlock(vr::PropertyContainerHandle_t ulQueueHandle,
|
||||
vr::PropertyContainerHandle_t *pulBlockHandle,
|
||||
void **ppvBuffer,
|
||||
vr::EBlockQueueReadType eReadType,
|
||||
uint32_t unTimeoutMs);
|
||||
virtual vr::EBlockQueueError
|
||||
AcquireReadOnlyBlock(vr::PropertyContainerHandle_t ulQueueHandle,
|
||||
vr::PropertyContainerHandle_t *pulBlockHandle,
|
||||
void **ppvBuffer,
|
||||
vr::EBlockQueueReadType eReadType);
|
||||
virtual vr::EBlockQueueError
|
||||
ReleaseReadOnlyBlock(vr::PropertyContainerHandle_t ulQueueHandle, vr::PropertyContainerHandle_t ulBlockHandle);
|
||||
virtual vr::EBlockQueueError
|
||||
QueueHasReader(vr::PropertyContainerHandle_t ulQueueHandle, bool *pbHasReaders);
|
||||
};
|
233
src/xrt/drivers/steamvr_lh/interfaces/context.hpp
Normal file
233
src/xrt/drivers/steamvr_lh/interfaces/context.hpp
Normal file
|
@ -0,0 +1,233 @@
|
|||
// Copyright 2023, Shawn Wallace
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
/*!
|
||||
* @file
|
||||
* @brief SteamVR driver context - implements xrt_tracking_origin and IVRDriverContext.
|
||||
* @author Shawn Wallace <yungwallace@live.com>
|
||||
* @ingroup drv_steamvr_lh
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <unordered_map>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <chrono>
|
||||
#include <deque>
|
||||
#include <mutex>
|
||||
|
||||
#include "openvr_driver.h"
|
||||
|
||||
#include "settings.hpp"
|
||||
#include "resources.hpp"
|
||||
#include "iobuffer.hpp"
|
||||
#include "driver_manager.hpp"
|
||||
#include "server.hpp"
|
||||
#include "blockqueue.hpp"
|
||||
#include "paths.hpp"
|
||||
|
||||
#include "xrt/xrt_tracking.h"
|
||||
|
||||
struct xrt_input;
|
||||
class Device;
|
||||
class Context final : public xrt_tracking_origin,
|
||||
public vr::IVRDriverContext,
|
||||
public vr::IVRServerDriverHost,
|
||||
public vr::IVRDriverInput,
|
||||
public vr::IVRProperties,
|
||||
public vr::IVRDriverLog,
|
||||
public std::enable_shared_from_this<Context>
|
||||
|
||||
{
|
||||
Settings settings;
|
||||
Resources resources;
|
||||
IOBuffer iobuf;
|
||||
DriverManager man;
|
||||
Server server;
|
||||
BlockQueue blockqueue;
|
||||
Paths paths;
|
||||
|
||||
uint64_t current_frame{0};
|
||||
|
||||
std::unordered_map<vr::VRInputComponentHandle_t, xrt_input *> handle_to_input;
|
||||
struct Vec2Components
|
||||
{
|
||||
vr::VRInputComponentHandle_t x;
|
||||
vr::VRInputComponentHandle_t y;
|
||||
};
|
||||
std::unordered_map<vr::VRInputComponentHandle_t, Vec2Components *> vec2_inputs;
|
||||
std::unordered_map<xrt_input *, std::unique_ptr<Vec2Components>> vec2_input_to_components;
|
||||
|
||||
struct Event
|
||||
{
|
||||
std::chrono::steady_clock::time_point insert_time;
|
||||
vr::VREvent_t inner;
|
||||
};
|
||||
std::deque<Event> events;
|
||||
std::mutex event_queue_mut;
|
||||
|
||||
Device *
|
||||
prop_container_to_device(vr::PropertyContainerHandle_t handle);
|
||||
|
||||
vr::EVRInputError
|
||||
create_component_common(vr::PropertyContainerHandle_t container,
|
||||
const char *name,
|
||||
vr::VRInputComponentHandle_t *handle);
|
||||
|
||||
xrt_input *
|
||||
update_component_common(vr::VRInputComponentHandle_t handle,
|
||||
double offset,
|
||||
std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now());
|
||||
|
||||
bool
|
||||
setup_hmd(const char *serial, vr::ITrackedDeviceServerDriver *driver);
|
||||
|
||||
bool
|
||||
setup_controller(const char *serial, vr::ITrackedDeviceServerDriver *driver);
|
||||
vr::IServerTrackedDeviceProvider *provider;
|
||||
|
||||
protected:
|
||||
Context(const std::string &steam_install, const std::string &steamvr_install, u_logging_level level);
|
||||
|
||||
public:
|
||||
// These are owned by monado, context is destroyed when these are destroyed
|
||||
class HmdDevice *hmd{nullptr};
|
||||
class ControllerDevice *controller[2]{nullptr, nullptr};
|
||||
const u_logging_level log_level;
|
||||
|
||||
~Context();
|
||||
|
||||
[[nodiscard]] static std::shared_ptr<Context>
|
||||
create(const std::string &steam_install,
|
||||
const std::string &steamvr_install,
|
||||
vr::IServerTrackedDeviceProvider *p);
|
||||
|
||||
void
|
||||
maybe_run_frame(uint64_t new_frame);
|
||||
|
||||
void
|
||||
add_haptic_event(vr::VREvent_HapticVibration_t event);
|
||||
|
||||
void
|
||||
Log(const char *pchLogMessage) override;
|
||||
|
||||
/***** IVRDriverContext methods *****/
|
||||
|
||||
void *
|
||||
GetGenericInterface(const char *pchInterfaceVersion, vr::EVRInitError *peError) override;
|
||||
|
||||
vr::DriverHandle_t
|
||||
GetDriverHandle() override;
|
||||
|
||||
/***** IVRServerDriverHost methods *****/
|
||||
bool
|
||||
TrackedDeviceAdded(const char *pchDeviceSerialNumber,
|
||||
vr::ETrackedDeviceClass eDeviceClass,
|
||||
vr::ITrackedDeviceServerDriver *pDriver) override;
|
||||
|
||||
void
|
||||
TrackedDevicePoseUpdated(uint32_t unWhichDevice,
|
||||
const vr::DriverPose_t &newPose,
|
||||
uint32_t unPoseStructSize) override;
|
||||
|
||||
void
|
||||
VsyncEvent(double vsyncTimeOffsetSeconds) override;
|
||||
|
||||
void
|
||||
VendorSpecificEvent(uint32_t unWhichDevice,
|
||||
vr::EVREventType eventType,
|
||||
const vr::VREvent_Data_t &eventData,
|
||||
double eventTimeOffset) override;
|
||||
|
||||
bool
|
||||
IsExiting() override;
|
||||
|
||||
bool
|
||||
PollNextEvent(vr::VREvent_t *pEvent, uint32_t uncbVREvent) override;
|
||||
|
||||
void
|
||||
GetRawTrackedDevicePoses(float fPredictedSecondsFromNow,
|
||||
vr::TrackedDevicePose_t *pTrackedDevicePoseArray,
|
||||
uint32_t unTrackedDevicePoseArrayCount) override;
|
||||
|
||||
void
|
||||
RequestRestart(const char *pchLocalizedReason,
|
||||
const char *pchExecutableToStart,
|
||||
const char *pchArguments,
|
||||
const char *pchWorkingDirectory) override;
|
||||
|
||||
uint32_t
|
||||
GetFrameTimings(vr::Compositor_FrameTiming *pTiming, uint32_t nFrames) override;
|
||||
|
||||
void
|
||||
SetDisplayEyeToHead(uint32_t unWhichDevice,
|
||||
const vr::HmdMatrix34_t &eyeToHeadLeft,
|
||||
const vr::HmdMatrix34_t &eyeToHeadRight) override;
|
||||
|
||||
void
|
||||
SetDisplayProjectionRaw(uint32_t unWhichDevice,
|
||||
const vr::HmdRect2_t &eyeLeft,
|
||||
const vr::HmdRect2_t &eyeRight) override;
|
||||
|
||||
void
|
||||
SetRecommendedRenderTargetSize(uint32_t unWhichDevice, uint32_t nWidth, uint32_t nHeight) override;
|
||||
|
||||
/***** IVRDriverInput methods *****/
|
||||
|
||||
vr::EVRInputError
|
||||
CreateBooleanComponent(vr::PropertyContainerHandle_t ulContainer,
|
||||
const char *pchName,
|
||||
vr::VRInputComponentHandle_t *pHandle) override;
|
||||
|
||||
vr::EVRInputError
|
||||
UpdateBooleanComponent(vr::VRInputComponentHandle_t ulComponent, bool bNewValue, double fTimeOffset) override;
|
||||
|
||||
vr::EVRInputError
|
||||
CreateScalarComponent(vr::PropertyContainerHandle_t ulContainer,
|
||||
const char *pchName,
|
||||
vr::VRInputComponentHandle_t *pHandle,
|
||||
vr::EVRScalarType eType,
|
||||
vr::EVRScalarUnits eUnits) override;
|
||||
|
||||
vr::EVRInputError
|
||||
UpdateScalarComponent(vr::VRInputComponentHandle_t ulComponent, float fNewValue, double fTimeOffset) override;
|
||||
|
||||
vr::EVRInputError
|
||||
CreateHapticComponent(vr::PropertyContainerHandle_t ulContainer,
|
||||
const char *pchName,
|
||||
vr::VRInputComponentHandle_t *pHandle) override;
|
||||
|
||||
vr::EVRInputError
|
||||
CreateSkeletonComponent(vr::PropertyContainerHandle_t ulContainer,
|
||||
const char *pchName,
|
||||
const char *pchSkeletonPath,
|
||||
const char *pchBasePosePath,
|
||||
vr::EVRSkeletalTrackingLevel eSkeletalTrackingLevel,
|
||||
const vr::VRBoneTransform_t *pGripLimitTransforms,
|
||||
uint32_t unGripLimitTransformCount,
|
||||
vr::VRInputComponentHandle_t *pHandle) override;
|
||||
|
||||
vr::EVRInputError
|
||||
UpdateSkeletonComponent(vr::VRInputComponentHandle_t ulComponent,
|
||||
vr::EVRSkeletalMotionRange eMotionRange,
|
||||
const vr::VRBoneTransform_t *pTransforms,
|
||||
uint32_t unTransformCount) override;
|
||||
|
||||
/***** IVRProperties methods *****/
|
||||
|
||||
vr::ETrackedPropertyError
|
||||
ReadPropertyBatch(vr::PropertyContainerHandle_t ulContainerHandle,
|
||||
vr::PropertyRead_t *pBatch,
|
||||
uint32_t unBatchEntryCount) override;
|
||||
|
||||
vr::ETrackedPropertyError
|
||||
WritePropertyBatch(vr::PropertyContainerHandle_t ulContainerHandle,
|
||||
vr::PropertyWrite_t *pBatch,
|
||||
uint32_t unBatchEntryCount) override;
|
||||
|
||||
const char *
|
||||
GetPropErrorNameFromEnum(vr::ETrackedPropertyError error) override;
|
||||
|
||||
vr::PropertyContainerHandle_t
|
||||
TrackedDeviceToPropertyContainer(vr::TrackedDeviceIndex_t nDevice) override;
|
||||
};
|
34
src/xrt/drivers/steamvr_lh/interfaces/driver_manager.cpp
Normal file
34
src/xrt/drivers/steamvr_lh/interfaces/driver_manager.cpp
Normal file
|
@ -0,0 +1,34 @@
|
|||
// Copyright 2023, Shawn Wallace
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
/*!
|
||||
* @file
|
||||
* @brief OpenVR IVRDriverManager interface implementation.
|
||||
* @author Shawn Wallace <yungwallace@live.com>
|
||||
* @ingroup drv_steamvr_lh
|
||||
*/
|
||||
|
||||
#include "driver_manager.hpp"
|
||||
|
||||
uint32_t
|
||||
DriverManager::GetDriverCount() const
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
DriverManager::GetDriverName(vr::DriverId_t nDriver, VR_OUT_STRING() char *pchValue, uint32_t unBufferSize)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
vr::DriverHandle_t
|
||||
DriverManager::GetDriverHandle(const char *pchDriverName)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool
|
||||
DriverManager::IsEnabled(vr::DriverId_t nDriver) const
|
||||
{
|
||||
return nDriver == 1;
|
||||
}
|
28
src/xrt/drivers/steamvr_lh/interfaces/driver_manager.hpp
Normal file
28
src/xrt/drivers/steamvr_lh/interfaces/driver_manager.hpp
Normal file
|
@ -0,0 +1,28 @@
|
|||
// Copyright 2023, Shawn Wallace
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
/*!
|
||||
* @file
|
||||
* @brief OpenVR IVRDriverManager interface header.
|
||||
* @author Shawn Wallace <yungwallace@live.com>
|
||||
* @ingroup drv_steamvr_lh
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "openvr_driver.h"
|
||||
|
||||
class DriverManager : public vr::IVRDriverManager
|
||||
{
|
||||
public:
|
||||
uint32_t
|
||||
GetDriverCount() const override;
|
||||
|
||||
uint32_t
|
||||
GetDriverName(vr::DriverId_t nDriver, VR_OUT_STRING() char *pchValue, uint32_t unBufferSize) override;
|
||||
|
||||
vr::DriverHandle_t
|
||||
GetDriverHandle(const char *pchDriverName) override;
|
||||
|
||||
bool
|
||||
IsEnabled(vr::DriverId_t nDriver) const override;
|
||||
};
|
52
src/xrt/drivers/steamvr_lh/interfaces/iobuffer.cpp
Normal file
52
src/xrt/drivers/steamvr_lh/interfaces/iobuffer.cpp
Normal file
|
@ -0,0 +1,52 @@
|
|||
// Copyright 2023, Shawn Wallace
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
/*!
|
||||
* @file
|
||||
* @brief OpenVR IVRIOBuffer interface implementation.
|
||||
* @author Shawn Wallace <yungwallace@live.com>
|
||||
* @ingroup drv_steamvr_lh
|
||||
*/
|
||||
|
||||
#include "iobuffer.hpp"
|
||||
|
||||
// NOLINTBEGIN(bugprone-easily-swappable-parameters)
|
||||
vr::EIOBufferError
|
||||
IOBuffer::Open(const char *pchPath,
|
||||
vr::EIOBufferMode mode,
|
||||
uint32_t unElementSize,
|
||||
uint32_t unElements,
|
||||
vr::IOBufferHandle_t *pulBuffer)
|
||||
{
|
||||
return vr::IOBuffer_Success;
|
||||
}
|
||||
|
||||
vr::EIOBufferError
|
||||
IOBuffer::Close(vr::IOBufferHandle_t ulBuffer)
|
||||
{
|
||||
return vr::IOBuffer_Success;
|
||||
}
|
||||
|
||||
vr::EIOBufferError
|
||||
IOBuffer::Read(vr::IOBufferHandle_t ulBuffer, void *pDst, uint32_t unBytes, uint32_t *punRead)
|
||||
{
|
||||
return vr::IOBuffer_Success;
|
||||
}
|
||||
|
||||
vr::EIOBufferError
|
||||
IOBuffer::Write(vr::IOBufferHandle_t ulBuffer, void *pSrc, uint32_t unBytes)
|
||||
{
|
||||
return vr::IOBuffer_Success;
|
||||
}
|
||||
|
||||
vr::PropertyContainerHandle_t
|
||||
IOBuffer::PropertyContainer(vr::IOBufferHandle_t ulBuffer)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool
|
||||
IOBuffer::HasReaders(vr::IOBufferHandle_t ulBuffer)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// NOLINTEND(bugprone-easily-swappable-parameters)
|
44
src/xrt/drivers/steamvr_lh/interfaces/iobuffer.hpp
Normal file
44
src/xrt/drivers/steamvr_lh/interfaces/iobuffer.hpp
Normal file
|
@ -0,0 +1,44 @@
|
|||
// Copyright 2023, Shawn Wallace
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
/*!
|
||||
* @file
|
||||
* @brief OpenVR IVRIOBuffer interface header.
|
||||
* @author Shawn Wallace <yungwallace@live.com>
|
||||
* @ingroup drv_steamvr_lh
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "openvr_driver.h"
|
||||
|
||||
class IOBuffer : public vr::IVRIOBuffer
|
||||
{
|
||||
public:
|
||||
/** opens an existing or creates a new IOBuffer of unSize bytes */
|
||||
vr::EIOBufferError
|
||||
Open(const char *pchPath,
|
||||
vr::EIOBufferMode mode,
|
||||
uint32_t unElementSize,
|
||||
uint32_t unElements,
|
||||
vr::IOBufferHandle_t *pulBuffer) override;
|
||||
|
||||
/** closes a previously opened or created buffer */
|
||||
vr::EIOBufferError
|
||||
Close(vr::IOBufferHandle_t ulBuffer) override;
|
||||
|
||||
/** reads up to unBytes from buffer into *pDst, returning number of bytes read in *punRead */
|
||||
vr::EIOBufferError
|
||||
Read(vr::IOBufferHandle_t ulBuffer, void *pDst, uint32_t unBytes, uint32_t *punRead) override;
|
||||
|
||||
/** writes unBytes of data from *pSrc into a buffer. */
|
||||
vr::EIOBufferError
|
||||
Write(vr::IOBufferHandle_t ulBuffer, void *pSrc, uint32_t unBytes) override;
|
||||
|
||||
/** retrieves the property container of an buffer. */
|
||||
vr::PropertyContainerHandle_t
|
||||
PropertyContainer(vr::IOBufferHandle_t ulBuffer) override;
|
||||
|
||||
/** inexpensively checks for readers to allow writers to fast-fail potentially expensive copies and writes. */
|
||||
bool
|
||||
HasReaders(vr::IOBufferHandle_t ulBuffer) override;
|
||||
};
|
36
src/xrt/drivers/steamvr_lh/interfaces/paths.cpp
Normal file
36
src/xrt/drivers/steamvr_lh/interfaces/paths.cpp
Normal file
|
@ -0,0 +1,36 @@
|
|||
// Copyright 2023, Shawn Wallace
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
/*!
|
||||
* @file
|
||||
* @brief OpenVR IVRPaths interface implementation.
|
||||
* @author Shawn Wallace <yungwallace@live.com>
|
||||
* @ingroup drv_steamvr_lh
|
||||
*/
|
||||
|
||||
#include "paths.hpp"
|
||||
|
||||
vr::ETrackedPropertyError
|
||||
Paths::ReadPathBatch(vr::PropertyContainerHandle_t ulRootHandle, struct PathRead_t *pBatch, uint32_t unBatchEntryCount)
|
||||
{
|
||||
return vr::TrackedProp_Success;
|
||||
}
|
||||
|
||||
vr::ETrackedPropertyError
|
||||
Paths::WritePathBatch(vr::PropertyContainerHandle_t ulRootHandle,
|
||||
struct PathWrite_t *pBatch,
|
||||
uint32_t unBatchEntryCount)
|
||||
{
|
||||
return vr::TrackedProp_Success;
|
||||
}
|
||||
|
||||
vr::ETrackedPropertyError
|
||||
Paths::StringToHandle(vr::PathHandle_t *pHandle, char *pchPath)
|
||||
{
|
||||
return vr::TrackedProp_Success;
|
||||
}
|
||||
|
||||
vr::ETrackedPropertyError
|
||||
Paths::HandleToString(vr::PathHandle_t pHandle, char *pchBuffer, uint32_t unBufferSize, uint32_t *punBufferSizeUsed)
|
||||
{
|
||||
return vr::TrackedProp_Success;
|
||||
}
|
34
src/xrt/drivers/steamvr_lh/interfaces/paths.hpp
Normal file
34
src/xrt/drivers/steamvr_lh/interfaces/paths.hpp
Normal file
|
@ -0,0 +1,34 @@
|
|||
// Copyright 2023, Shawn Wallace
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
/*!
|
||||
* @file
|
||||
* @brief OpenVR IVRPaths interface header.
|
||||
* @author Shawn Wallace <yungwallace@live.com>
|
||||
* @ingroup drv_steamvr_lh
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "openvr_driver.h"
|
||||
|
||||
namespace vr {
|
||||
inline const char *IVRPaths_Version = "IVRPaths_001";
|
||||
typedef uint64_t PathHandle_t;
|
||||
}; // namespace vr
|
||||
|
||||
/** This interface is missing in the C++ header but present in the C one, and the lighthouse driver requires it. */
|
||||
class Paths
|
||||
{
|
||||
virtual vr::ETrackedPropertyError
|
||||
ReadPathBatch(vr::PropertyContainerHandle_t ulRootHandle,
|
||||
struct PathRead_t *pBatch,
|
||||
uint32_t unBatchEntryCount);
|
||||
virtual vr::ETrackedPropertyError
|
||||
WritePathBatch(vr::PropertyContainerHandle_t ulRootHandle,
|
||||
struct PathWrite_t *pBatch,
|
||||
uint32_t unBatchEntryCount);
|
||||
virtual vr::ETrackedPropertyError
|
||||
StringToHandle(vr::PathHandle_t *pHandle, char *pchPath);
|
||||
virtual vr::ETrackedPropertyError
|
||||
HandleToString(vr::PathHandle_t pHandle, char *pchBuffer, uint32_t unBufferSize, uint32_t *punBufferSizeUsed);
|
||||
};
|
62
src/xrt/drivers/steamvr_lh/interfaces/resources.cpp
Normal file
62
src/xrt/drivers/steamvr_lh/interfaces/resources.cpp
Normal file
|
@ -0,0 +1,62 @@
|
|||
// Copyright 2023, Shawn Wallace
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
/*!
|
||||
* @file
|
||||
* @brief OpenVR IVRResources interface implementation.
|
||||
* @author Shawn Wallace <yungwallace@live.com>
|
||||
* @ingroup drv_steamvr_lh
|
||||
*/
|
||||
|
||||
#include "resources.hpp"
|
||||
#include "util/u_logging.h"
|
||||
#include <cstring>
|
||||
|
||||
#define RES_ERR(...) U_LOG_IFL_E(log_level, __VA_ARGS__)
|
||||
// NOLINTBEGIN(bugprone-easily-swappable-parameters)
|
||||
uint32_t
|
||||
Resources::LoadSharedResource(const char *pchResourceName, char *pchBuffer, uint32_t unBufferLen)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
Resources::GetResourceFullPath(const char *pchResourceName,
|
||||
const char *pchResourceTypeDirectory,
|
||||
char *pchPathBuffer,
|
||||
uint32_t unBufferLen)
|
||||
{
|
||||
std::string resource(pchResourceName);
|
||||
|
||||
auto return_str = [&](const std::string &str) {
|
||||
const auto len = str.size() + 1;
|
||||
if (unBufferLen >= len) {
|
||||
std::strncpy(pchPathBuffer, str.c_str(), len);
|
||||
}
|
||||
return len;
|
||||
};
|
||||
// loading resource from driver folder (i.e. htc)
|
||||
if (resource[0] == '{') {
|
||||
const size_t idx = resource.find('}');
|
||||
if (idx == std::string::npos) {
|
||||
RES_ERR("malformed resource name: %s", resource.c_str());
|
||||
return 0;
|
||||
}
|
||||
const std::string driver = resource.substr(1, idx - 1);
|
||||
std::string path = steamvr_install + "/drivers/" + driver + "/resources/";
|
||||
if (pchResourceTypeDirectory)
|
||||
path += pchResourceTypeDirectory + std::string("/");
|
||||
|
||||
// for some reason sometimes it gives the paths like {driver}resource.file instead of
|
||||
// {driver}/resource.file
|
||||
path += resource.substr(idx + 1);
|
||||
return return_str(path);
|
||||
}
|
||||
|
||||
// loading from shared folder?
|
||||
std::string path = steamvr_install + "/resources/";
|
||||
path += pchResourceTypeDirectory;
|
||||
path += "/";
|
||||
path += pchResourceName;
|
||||
return return_str(path);
|
||||
}
|
||||
// NOLINTEND(bugprone-easily-swappable-parameters)
|
40
src/xrt/drivers/steamvr_lh/interfaces/resources.hpp
Normal file
40
src/xrt/drivers/steamvr_lh/interfaces/resources.hpp
Normal file
|
@ -0,0 +1,40 @@
|
|||
// Copyright 2023, Shawn Wallace
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
/*!
|
||||
* @file
|
||||
* @brief OpenVR IVRResources interface header.
|
||||
* @author Shawn Wallace <yungwallace@live.com>
|
||||
* @ingroup drv_steamvr_lh
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "openvr_driver.h"
|
||||
#include "util/u_logging.h"
|
||||
|
||||
class Resources : public vr::IVRResources
|
||||
{
|
||||
const u_logging_level log_level;
|
||||
const std::string steamvr_install;
|
||||
|
||||
public:
|
||||
Resources(u_logging_level l, const std::string &steamvr_install)
|
||||
: log_level(l), steamvr_install(steamvr_install){};
|
||||
// ------------------------------------
|
||||
// Shared Resource Methods
|
||||
// ------------------------------------
|
||||
|
||||
/** Loads the specified resource into the provided buffer if large enough.
|
||||
* Returns the size in bytes of the buffer required to hold the specified resource. */
|
||||
uint32_t
|
||||
LoadSharedResource(const char *pchResourceName, char *pchBuffer, uint32_t unBufferLen) override;
|
||||
|
||||
/** Provides the full path to the specified resource. Resource names can include named directories for
|
||||
* drivers and other things, and this resolves all of those and returns the actual physical path.
|
||||
* pchResourceTypeDirectory is the subdirectory of resources to look in. */
|
||||
uint32_t
|
||||
GetResourceFullPath(const char *pchResourceName,
|
||||
const char *pchResourceTypeDirectory,
|
||||
VR_OUT_STRING() char *pchPathBuffer,
|
||||
uint32_t unBufferLen) override;
|
||||
};
|
118
src/xrt/drivers/steamvr_lh/interfaces/server.hpp
Normal file
118
src/xrt/drivers/steamvr_lh/interfaces/server.hpp
Normal file
|
@ -0,0 +1,118 @@
|
|||
// Copyright 2023, Shawn Wallace
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
/*!
|
||||
* @file
|
||||
* @brief OpenVR IVRServer internal interface header.
|
||||
* @author Shawn Wallace <yungwallace@live.com>
|
||||
* @ingroup drv_steamvr_lh
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/** An internal interface utilized by the lighthouse driver. Who knows what it does... */
|
||||
|
||||
class Server
|
||||
{
|
||||
virtual void
|
||||
UnknownFunc001()
|
||||
{}
|
||||
virtual void
|
||||
UnknownFunc002()
|
||||
{}
|
||||
virtual void
|
||||
UnknownFunc003()
|
||||
{}
|
||||
virtual void
|
||||
UnknownFunc004()
|
||||
{}
|
||||
virtual void
|
||||
UnknownFunc005()
|
||||
{}
|
||||
virtual void
|
||||
UnknownFunc006()
|
||||
{}
|
||||
virtual void
|
||||
UnknownFunc007()
|
||||
{}
|
||||
virtual void
|
||||
UnknownFunc008()
|
||||
{}
|
||||
virtual void
|
||||
UnknownFunc009()
|
||||
{}
|
||||
virtual void
|
||||
UnknownFunc010()
|
||||
{}
|
||||
virtual void
|
||||
UnknownFunc011()
|
||||
{}
|
||||
virtual void
|
||||
UnknownFunc012()
|
||||
{}
|
||||
virtual void
|
||||
UnknownFunc013()
|
||||
{}
|
||||
virtual void
|
||||
UnknownFunc014()
|
||||
{}
|
||||
virtual void
|
||||
UnknownFunc015()
|
||||
{}
|
||||
virtual void
|
||||
UnknownFunc016()
|
||||
{}
|
||||
virtual void
|
||||
UnknownFunc017()
|
||||
{}
|
||||
virtual void
|
||||
UnknownFunc018()
|
||||
{}
|
||||
virtual void
|
||||
UnknownFunc019()
|
||||
{}
|
||||
virtual void
|
||||
UnknownFunc020()
|
||||
{}
|
||||
virtual void
|
||||
UnknownFunc021()
|
||||
{}
|
||||
virtual void
|
||||
UnknownFunc022()
|
||||
{}
|
||||
virtual void
|
||||
UnknownFunc023()
|
||||
{}
|
||||
virtual void
|
||||
UnknownFunc024()
|
||||
{}
|
||||
virtual void
|
||||
UnknownFunc025()
|
||||
{}
|
||||
virtual void
|
||||
UnknownFunc026()
|
||||
{}
|
||||
virtual void
|
||||
UnknownFunc027()
|
||||
{}
|
||||
virtual void
|
||||
UnknownFunc028()
|
||||
{}
|
||||
virtual void
|
||||
UnknownFunc029()
|
||||
{}
|
||||
virtual void
|
||||
UnknownFunc030()
|
||||
{}
|
||||
virtual void
|
||||
UnknownFunc031()
|
||||
{}
|
||||
virtual void
|
||||
UnknownFunc032()
|
||||
{}
|
||||
virtual void
|
||||
UnknownFunc033()
|
||||
{}
|
||||
virtual void
|
||||
UnknownFunc034()
|
||||
{}
|
||||
};
|
109
src/xrt/drivers/steamvr_lh/interfaces/settings.cpp
Normal file
109
src/xrt/drivers/steamvr_lh/interfaces/settings.cpp
Normal file
|
@ -0,0 +1,109 @@
|
|||
// Copyright 2023, Shawn Wallace
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
/*!
|
||||
* @file
|
||||
* @brief OpenVR IVRSettings interface implementation.
|
||||
* @author Shawn Wallace <yungwallace@live.com>
|
||||
* @ingroup drv_steamvr_lh
|
||||
*/
|
||||
|
||||
#include <optional>
|
||||
#include <cstring>
|
||||
|
||||
#include "settings.hpp"
|
||||
#include "util/u_json.hpp"
|
||||
|
||||
using xrt::auxiliary::util::json::JSONNode;
|
||||
|
||||
Settings::Settings(const std::string &steam_install, const std::string &steamvr_install)
|
||||
: steamvr_settings(JSONNode::loadFromFile(steam_install + "/config/steamvr.vrsettings")),
|
||||
driver_defaults(
|
||||
JSONNode::loadFromFile(steamvr_install + "/drivers/lighthouse/resources/settings/default.vrsettings"))
|
||||
{}
|
||||
|
||||
// NOLINTBEGIN(bugprone-easily-swappable-parameters)
|
||||
const char *
|
||||
Settings::GetSettingsErrorNameFromEnum(vr::EVRSettingsError eError)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
Settings::SetBool(const char *pchSection, const char *pchSettingsKey, bool bValue, vr::EVRSettingsError *peError)
|
||||
{}
|
||||
|
||||
void
|
||||
Settings::SetInt32(const char *pchSection, const char *pchSettingsKey, int32_t nValue, vr::EVRSettingsError *peError)
|
||||
{}
|
||||
|
||||
void
|
||||
Settings::SetFloat(const char *pchSection, const char *pchSettingsKey, float flValue, vr::EVRSettingsError *peError)
|
||||
{}
|
||||
|
||||
void
|
||||
Settings::SetString(const char *pchSection,
|
||||
const char *pchSettingsKey,
|
||||
const char *pchValue,
|
||||
vr::EVRSettingsError *peError)
|
||||
{}
|
||||
|
||||
bool
|
||||
Settings::GetBool(const char *pchSection, const char *pchSettingsKey, vr::EVRSettingsError *peError)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int32_t
|
||||
Settings::GetInt32(const char *pchSection, const char *pchSettingsKey, vr::EVRSettingsError *peError)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
float
|
||||
Settings::GetFloat(const char *pchSection, const char *pchSettingsKey, vr::EVRSettingsError *peError)
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
// Driver requires a few string settings to initialize properly
|
||||
void
|
||||
Settings::GetString(const char *pchSection,
|
||||
const char *pchSettingsKey,
|
||||
char *pchValue,
|
||||
uint32_t unValueLen,
|
||||
vr::EVRSettingsError *peError)
|
||||
{
|
||||
if (peError)
|
||||
*peError = vr::VRSettingsError_None;
|
||||
|
||||
auto get_string = [pchSection, pchSettingsKey](const JSONNode &root) -> std::optional<std::string> {
|
||||
JSONNode section = root[pchSection];
|
||||
if (!section.isValid())
|
||||
return std::nullopt;
|
||||
|
||||
JSONNode value = section[pchSettingsKey];
|
||||
if (!value.isValid() || !value.isString())
|
||||
return std::nullopt;
|
||||
|
||||
return std::optional(value.asString());
|
||||
};
|
||||
|
||||
std::optional value = get_string(driver_defaults);
|
||||
if (!value.has_value())
|
||||
value = get_string(steamvr_settings);
|
||||
|
||||
if (value.has_value()) {
|
||||
if (unValueLen > value->size())
|
||||
std::strncpy(pchValue, value->c_str(), value->size() + 1);
|
||||
} else if (peError)
|
||||
*peError = vr::VRSettingsError_ReadFailed;
|
||||
}
|
||||
|
||||
void
|
||||
Settings::RemoveSection(const char *pchSection, vr::EVRSettingsError *peError)
|
||||
{}
|
||||
|
||||
void
|
||||
Settings::RemoveKeyInSection(const char *pchSection, const char *pchSettingsKey, vr::EVRSettingsError *peError)
|
||||
{}
|
||||
// NOLINTEND(bugprone-easily-swappable-parameters)
|
70
src/xrt/drivers/steamvr_lh/interfaces/settings.hpp
Normal file
70
src/xrt/drivers/steamvr_lh/interfaces/settings.hpp
Normal file
|
@ -0,0 +1,70 @@
|
|||
// Copyright 2023, Shawn Wallace
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
/*!
|
||||
* @file
|
||||
* @brief OpenVR IVRSettings interface header.
|
||||
* @author Shawn Wallace <yungwallace@live.com>
|
||||
* @ingroup drv_steamvr_lh
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "openvr_driver.h"
|
||||
#include "util/u_json.hpp"
|
||||
|
||||
class Settings : public vr::IVRSettings
|
||||
{
|
||||
private:
|
||||
const xrt::auxiliary::util::json::JSONNode steamvr_settings;
|
||||
const xrt::auxiliary::util::json::JSONNode driver_defaults;
|
||||
|
||||
public:
|
||||
Settings(const std::string &steam_install, const std::string &steamvr_install);
|
||||
|
||||
const char *
|
||||
GetSettingsErrorNameFromEnum(vr::EVRSettingsError eError) override;
|
||||
|
||||
void
|
||||
SetBool(const char *pchSection,
|
||||
const char *pchSettingsKey,
|
||||
bool bValue,
|
||||
vr::EVRSettingsError *peError = nullptr) override;
|
||||
void
|
||||
SetInt32(const char *pchSection,
|
||||
const char *pchSettingsKey,
|
||||
int32_t nValue,
|
||||
vr::EVRSettingsError *peError = nullptr) override;
|
||||
void
|
||||
SetFloat(const char *pchSection,
|
||||
const char *pchSettingsKey,
|
||||
float flValue,
|
||||
vr::EVRSettingsError *peError = nullptr) override;
|
||||
void
|
||||
SetString(const char *pchSection,
|
||||
const char *pchSettingsKey,
|
||||
const char *pchValue,
|
||||
vr::EVRSettingsError *peError = nullptr) override;
|
||||
|
||||
// Users of the system need to provide a proper default in default.vrsettings in the resources/settings/
|
||||
// directory of either the runtime or the driver_xxx directory. Otherwise the default will be false, 0, 0.0 or
|
||||
// ""
|
||||
bool
|
||||
GetBool(const char *pchSection, const char *pchSettingsKey, vr::EVRSettingsError *peError = nullptr) override;
|
||||
int32_t
|
||||
GetInt32(const char *pchSection, const char *pchSettingsKey, vr::EVRSettingsError *peError = nullptr) override;
|
||||
float
|
||||
GetFloat(const char *pchSection, const char *pchSettingsKey, vr::EVRSettingsError *peError = nullptr) override;
|
||||
void
|
||||
GetString(const char *pchSection,
|
||||
const char *pchSettingsKey,
|
||||
VR_OUT_STRING() char *pchValue,
|
||||
uint32_t unValueLen,
|
||||
vr::EVRSettingsError *peError = nullptr) override;
|
||||
|
||||
void
|
||||
RemoveSection(const char *pchSection, vr::EVRSettingsError *peError = nullptr) override;
|
||||
void
|
||||
RemoveKeyInSection(const char *pchSection,
|
||||
const char *pchSettingsKey,
|
||||
vr::EVRSettingsError *peError = nullptr) override;
|
||||
};
|
644
src/xrt/drivers/steamvr_lh/steamvr_lh.cpp
Normal file
644
src/xrt/drivers/steamvr_lh/steamvr_lh.cpp
Normal file
|
@ -0,0 +1,644 @@
|
|||
// Copyright 2023, Shawn Wallace
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
/*!
|
||||
* @file
|
||||
* @brief SteamVR driver context implementation and entrypoint.
|
||||
* @author Shawn Wallace <yungwallace@live.com>
|
||||
* @ingroup drv_steamvr_lh
|
||||
*/
|
||||
|
||||
#include <cstring>
|
||||
#include <dlfcn.h>
|
||||
#include <memory>
|
||||
#include <cmath>
|
||||
#include <unordered_map>
|
||||
#include <string_view>
|
||||
#include <filesystem>
|
||||
#include <istream>
|
||||
|
||||
#include "openvr_driver.h"
|
||||
#include "vdf_parser.hpp"
|
||||
#include "steamvr_lh_interface.h"
|
||||
#include "interfaces/context.hpp"
|
||||
#include "device.hpp"
|
||||
#include "util/u_device.h"
|
||||
|
||||
namespace {
|
||||
DEBUG_GET_ONCE_LOG_OPTION(lh_log, "LIGHTHOUSE_LOG", U_LOGGING_INFO);
|
||||
|
||||
// ~/.steam/root is a symlink to where the Steam root is
|
||||
const std::string STEAM_INSTALL_DIR = std::string(getenv("HOME")) + "/.steam/root";
|
||||
constexpr auto STEAMVR_APPID = "250820";
|
||||
|
||||
// Parse libraryfolder.vdf to find where SteamVR is installed
|
||||
std::string
|
||||
find_steamvr_install()
|
||||
{
|
||||
using namespace tyti;
|
||||
std::ifstream file(STEAM_INSTALL_DIR + "/steamapps/libraryfolders.vdf");
|
||||
auto root = vdf::read(file);
|
||||
assert(root.name == "libraryfolders");
|
||||
for (auto &[_, child] : root.children) {
|
||||
U_LOG_D("Found library folder %s", child->attribs["path"].c_str());
|
||||
std::shared_ptr<vdf::object> apps = child->children["apps"];
|
||||
for (auto &[appid, _] : apps->attribs) {
|
||||
if (appid == STEAMVR_APPID) {
|
||||
return child->attribs["path"] + "/steamapps/common/SteamVR";
|
||||
}
|
||||
}
|
||||
}
|
||||
return std::string();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
#define CTX_ERR(...) U_LOG_IFL_E(log_level, __VA_ARGS__)
|
||||
#define CTX_WARN(...) U_LOG_IFL_E(log_level, __VA_ARGS__)
|
||||
#define CTX_INFO(...) U_LOG_IFL_I(log_level, __VA_ARGS__)
|
||||
#define CTX_TRACE(...) U_LOG_IFL_T(log_level, __VA_ARGS__)
|
||||
#define CTX_DEBUG(...) U_LOG_IFL_D(log_level, __VA_ARGS__)
|
||||
|
||||
/**
|
||||
* Since only the devices will live after our get_devices function is called, we make our Context
|
||||
* a shared ptr that is owned by the devices that exist, so that it is also cleaned up by the
|
||||
* devices that exist when they are all destroyed.
|
||||
*/
|
||||
std::shared_ptr<Context>
|
||||
Context::create(const std::string &steam_install,
|
||||
const std::string &steamvr_install,
|
||||
vr::IServerTrackedDeviceProvider *p)
|
||||
{
|
||||
// xrt_tracking_origin initialization
|
||||
Context *c = new Context(steam_install, steamvr_install, debug_get_log_option_lh_log());
|
||||
c->provider = p;
|
||||
std::strncpy(c->name, "SteamVR Lighthouse Tracking", XRT_TRACKING_NAME_LEN);
|
||||
c->type = XRT_TRACKING_TYPE_LIGHTHOUSE;
|
||||
c->offset = XRT_POSE_IDENTITY;
|
||||
return std::shared_ptr<Context>(c);
|
||||
}
|
||||
|
||||
Context::Context(const std::string &steam_install, const std::string &steamvr_install, u_logging_level level)
|
||||
: settings(steam_install, steamvr_install), resources(level, steamvr_install), log_level(level)
|
||||
{}
|
||||
|
||||
Context::~Context()
|
||||
{
|
||||
provider->Cleanup();
|
||||
}
|
||||
|
||||
/***** IVRDriverContext methods *****/
|
||||
|
||||
void *
|
||||
Context::GetGenericInterface(const char *pchInterfaceVersion, vr::EVRInitError *peError)
|
||||
{
|
||||
#define MATCH_INTERFACE(version, interface) \
|
||||
if (std::strcmp(pchInterfaceVersion, version) == 0) { \
|
||||
return interface; \
|
||||
}
|
||||
#define MATCH_INTERFACE_THIS(interface) MATCH_INTERFACE(interface##_Version, static_cast<interface *>(this))
|
||||
|
||||
// Known interfaces
|
||||
MATCH_INTERFACE_THIS(vr::IVRServerDriverHost);
|
||||
MATCH_INTERFACE_THIS(vr::IVRDriverInput);
|
||||
MATCH_INTERFACE_THIS(vr::IVRProperties);
|
||||
MATCH_INTERFACE_THIS(vr::IVRDriverLog);
|
||||
MATCH_INTERFACE(vr::IVRSettings_Version, &settings);
|
||||
MATCH_INTERFACE(vr::IVRResources_Version, &resources);
|
||||
MATCH_INTERFACE(vr::IVRIOBuffer_Version, &iobuf);
|
||||
MATCH_INTERFACE(vr::IVRDriverManager_Version, &man);
|
||||
MATCH_INTERFACE(vr::IVRBlockQueue_Version, &blockqueue);
|
||||
MATCH_INTERFACE(vr::IVRPaths_Version, &paths);
|
||||
|
||||
// Internal interfaces
|
||||
MATCH_INTERFACE("IVRServer_XXX", &server);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
vr::DriverHandle_t
|
||||
Context::GetDriverHandle()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/***** IVRServerDriverHost methods *****/
|
||||
|
||||
bool
|
||||
Context::setup_hmd(const char *serial, vr::ITrackedDeviceServerDriver *driver)
|
||||
{
|
||||
this->hmd = new HmdDevice(DeviceBuilder{this->shared_from_this(), driver, serial, STEAM_INSTALL_DIR});
|
||||
#define VERIFY(expr, msg) \
|
||||
if (!(expr)) { \
|
||||
CTX_ERR("Activating HMD failed: %s", msg); \
|
||||
delete this->hmd; \
|
||||
this->hmd = nullptr; \
|
||||
return false; \
|
||||
}
|
||||
vr::EVRInitError err = driver->Activate(0);
|
||||
VERIFY(err == vr::VRInitError_None, std::to_string(err).c_str());
|
||||
|
||||
auto *display = static_cast<vr::IVRDisplayComponent *>(driver->GetComponent(vr::IVRDisplayComponent_Version));
|
||||
VERIFY(display, "IVRDisplayComponent is null");
|
||||
#undef VERIFY
|
||||
|
||||
auto hmd_parts = std::make_unique<HmdDevice::Parts>();
|
||||
for (size_t idx = 0; idx < 2; ++idx) {
|
||||
vr::EVREye eye = (idx == 0) ? vr::Eye_Left : vr::Eye_Right;
|
||||
xrt_view &view = hmd_parts->base.views[idx];
|
||||
|
||||
display->GetEyeOutputViewport(eye, &view.viewport.x_pixels, &view.viewport.y_pixels,
|
||||
&view.viewport.w_pixels, &view.viewport.h_pixels);
|
||||
|
||||
view.display.w_pixels = view.viewport.w_pixels;
|
||||
view.display.h_pixels = view.viewport.h_pixels;
|
||||
view.rot = u_device_rotation_ident;
|
||||
}
|
||||
|
||||
hmd_parts->base.screens[0].w_pixels =
|
||||
hmd_parts->base.views[0].display.w_pixels + hmd_parts->base.views[1].display.w_pixels;
|
||||
hmd_parts->base.screens[0].h_pixels = hmd_parts->base.views[0].display.h_pixels;
|
||||
// nominal frame interval will be set when lighthouse gives us the display frequency
|
||||
// see HmdDevice::handle_property_write
|
||||
|
||||
hmd_parts->base.blend_modes[0] = XRT_BLEND_MODE_OPAQUE;
|
||||
hmd_parts->base.blend_mode_count = 1;
|
||||
|
||||
auto &distortion = hmd_parts->base.distortion;
|
||||
distortion.models = XRT_DISTORTION_MODEL_COMPUTE;
|
||||
distortion.preferred = XRT_DISTORTION_MODEL_COMPUTE;
|
||||
for (size_t idx = 0; idx < 2; ++idx) {
|
||||
xrt_fov &fov = distortion.fov[idx];
|
||||
float tan_left, tan_right, tan_top, tan_bottom;
|
||||
display->GetProjectionRaw((vr::EVREye)idx, &tan_left, &tan_right, &tan_top, &tan_bottom);
|
||||
fov.angle_left = atanf(tan_left);
|
||||
fov.angle_right = atanf(tan_right);
|
||||
fov.angle_up = atanf(tan_bottom);
|
||||
fov.angle_down = atanf(tan_top);
|
||||
}
|
||||
|
||||
hmd_parts->display = display;
|
||||
hmd->set_hmd_parts(std::move(hmd_parts));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
Context::setup_controller(const char *serial, vr::ITrackedDeviceServerDriver *driver)
|
||||
{
|
||||
if (controller[0] && controller[1]) {
|
||||
CTX_WARN("Attempted to activate more than two controllers - this is unsupported");
|
||||
return false;
|
||||
}
|
||||
size_t device_idx = (controller[0]) ? 2 : 1;
|
||||
auto &dev = controller[device_idx - 1];
|
||||
dev = new ControllerDevice(device_idx + 1,
|
||||
DeviceBuilder{this->shared_from_this(), driver, serial, STEAM_INSTALL_DIR});
|
||||
|
||||
vr::EVRInitError err = driver->Activate(device_idx);
|
||||
if (err != vr::VRInitError_None) {
|
||||
CTX_ERR("Activating controller failed: error %u", err);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
Context::maybe_run_frame(uint64_t new_frame)
|
||||
{
|
||||
if (new_frame > current_frame) {
|
||||
++current_frame;
|
||||
provider->RunFrame();
|
||||
}
|
||||
}
|
||||
// NOLINTBEGIN(bugprone-easily-swappable-parameters)
|
||||
bool
|
||||
Context::TrackedDeviceAdded(const char *pchDeviceSerialNumber,
|
||||
vr::ETrackedDeviceClass eDeviceClass,
|
||||
vr::ITrackedDeviceServerDriver *pDriver)
|
||||
{
|
||||
CTX_INFO("New device added: %s", pchDeviceSerialNumber);
|
||||
switch (eDeviceClass) {
|
||||
case vr::TrackedDeviceClass_HMD: {
|
||||
return setup_hmd(pchDeviceSerialNumber, pDriver);
|
||||
break;
|
||||
}
|
||||
case vr::TrackedDeviceClass_Controller: {
|
||||
return setup_controller(pchDeviceSerialNumber, pDriver);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
CTX_WARN("Attempted to add unsupported device class: %u", eDeviceClass);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Context::TrackedDevicePoseUpdated(uint32_t unWhichDevice, const vr::DriverPose_t &newPose, uint32_t unPoseStructSize)
|
||||
{
|
||||
assert(sizeof(newPose) == unPoseStructSize);
|
||||
if (unWhichDevice > 2)
|
||||
return;
|
||||
Device *dev = (unWhichDevice == 0) ? static_cast<Device *>(this->hmd)
|
||||
: static_cast<Device *>(this->controller[unWhichDevice - 1]);
|
||||
assert(dev);
|
||||
dev->update_pose(newPose);
|
||||
}
|
||||
|
||||
void
|
||||
Context::VsyncEvent(double vsyncTimeOffsetSeconds)
|
||||
{}
|
||||
|
||||
void
|
||||
Context::VendorSpecificEvent(uint32_t unWhichDevice,
|
||||
vr::EVREventType eventType,
|
||||
const vr::VREvent_Data_t &eventData,
|
||||
double eventTimeOffset)
|
||||
{}
|
||||
|
||||
bool
|
||||
Context::IsExiting()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
Context::add_haptic_event(vr::VREvent_HapticVibration_t event)
|
||||
{
|
||||
vr::VREvent_t e;
|
||||
e.eventType = vr::EVREventType::VREvent_Input_HapticVibration;
|
||||
e.trackedDeviceIndex = event.containerHandle - 1;
|
||||
vr::VREvent_Data_t d;
|
||||
d.hapticVibration = event;
|
||||
e.data = d;
|
||||
|
||||
std::lock_guard lk(event_queue_mut);
|
||||
events.push_back({std::chrono::steady_clock::now(), e});
|
||||
}
|
||||
|
||||
bool
|
||||
Context::PollNextEvent(vr::VREvent_t *pEvent, uint32_t uncbVREvent)
|
||||
{
|
||||
if (!events.empty()) {
|
||||
assert(sizeof(vr::VREvent_t) == uncbVREvent);
|
||||
Event e;
|
||||
{
|
||||
std::lock_guard lk(event_queue_mut);
|
||||
e = events.front();
|
||||
events.pop_front();
|
||||
}
|
||||
*pEvent = e.inner;
|
||||
using float_sec = std::chrono::duration<float>;
|
||||
float_sec event_age = std::chrono::steady_clock::now() - e.insert_time;
|
||||
pEvent->eventAgeSeconds = event_age.count();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
Context::GetRawTrackedDevicePoses(float fPredictedSecondsFromNow,
|
||||
vr::TrackedDevicePose_t *pTrackedDevicePoseArray,
|
||||
uint32_t unTrackedDevicePoseArrayCount)
|
||||
{}
|
||||
|
||||
void
|
||||
Context::RequestRestart(const char *pchLocalizedReason,
|
||||
const char *pchExecutableToStart,
|
||||
const char *pchArguments,
|
||||
const char *pchWorkingDirectory)
|
||||
{}
|
||||
|
||||
uint32_t
|
||||
Context::GetFrameTimings(vr::Compositor_FrameTiming *pTiming, uint32_t nFrames)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
Context::SetDisplayEyeToHead(uint32_t unWhichDevice,
|
||||
const vr::HmdMatrix34_t &eyeToHeadLeft,
|
||||
const vr::HmdMatrix34_t &eyeToHeadRight)
|
||||
{}
|
||||
|
||||
void
|
||||
Context::SetDisplayProjectionRaw(uint32_t unWhichDevice, const vr::HmdRect2_t &eyeLeft, const vr::HmdRect2_t &eyeRight)
|
||||
{}
|
||||
|
||||
void
|
||||
Context::SetRecommendedRenderTargetSize(uint32_t unWhichDevice, uint32_t nWidth, uint32_t nHeight)
|
||||
{}
|
||||
|
||||
/***** IVRDriverInput methods *****/
|
||||
|
||||
|
||||
vr::EVRInputError
|
||||
Context::create_component_common(vr::PropertyContainerHandle_t container,
|
||||
const char *name,
|
||||
vr::VRInputComponentHandle_t *pHandle)
|
||||
{
|
||||
*pHandle = vr::k_ulInvalidInputComponentHandle;
|
||||
Device *device = prop_container_to_device(container);
|
||||
if (!device) {
|
||||
return vr::VRInputError_InvalidHandle;
|
||||
}
|
||||
if (xrt_input *input = device->get_input_from_name(name); input) {
|
||||
CTX_DEBUG("creating component %s", name);
|
||||
vr::VRInputComponentHandle_t handle = handle_to_input.size() + 1;
|
||||
handle_to_input[handle] = input;
|
||||
*pHandle = handle;
|
||||
}
|
||||
return vr::VRInputError_None;
|
||||
}
|
||||
|
||||
xrt_input *
|
||||
Context::update_component_common(vr::VRInputComponentHandle_t handle,
|
||||
double offset,
|
||||
std::chrono::steady_clock::time_point now)
|
||||
{
|
||||
xrt_input *input{nullptr};
|
||||
if (handle != vr::k_ulInvalidInputComponentHandle) {
|
||||
input = handle_to_input[handle];
|
||||
std::chrono::duration<double, std::chrono::seconds::period> offset_dur(offset);
|
||||
std::chrono::duration offset = (now + offset_dur).time_since_epoch();
|
||||
int64_t timestamp = std::chrono::duration_cast<std::chrono::nanoseconds>(offset).count();
|
||||
input->active = true;
|
||||
input->timestamp = timestamp;
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
vr::EVRInputError
|
||||
Context::CreateBooleanComponent(vr::PropertyContainerHandle_t ulContainer,
|
||||
const char *pchName,
|
||||
vr::VRInputComponentHandle_t *pHandle)
|
||||
{
|
||||
return create_component_common(ulContainer, pchName, pHandle);
|
||||
}
|
||||
|
||||
vr::EVRInputError
|
||||
Context::UpdateBooleanComponent(vr::VRInputComponentHandle_t ulComponent, bool bNewValue, double fTimeOffset)
|
||||
{
|
||||
xrt_input *input = update_component_common(ulComponent, fTimeOffset);
|
||||
if (input) {
|
||||
input->value.boolean = bNewValue;
|
||||
}
|
||||
return vr::VRInputError_None;
|
||||
}
|
||||
|
||||
vr::EVRInputError
|
||||
Context::CreateScalarComponent(vr::PropertyContainerHandle_t ulContainer,
|
||||
const char *pchName,
|
||||
vr::VRInputComponentHandle_t *pHandle,
|
||||
vr::EVRScalarType eType,
|
||||
vr::EVRScalarUnits eUnits)
|
||||
{
|
||||
std::string_view name{pchName};
|
||||
// Lighthouse gives thumbsticks/trackpads as x/y components,
|
||||
// we need to combine them for Monado
|
||||
auto end = name.back();
|
||||
if (end == 'x' || end == 'y') {
|
||||
Device *device = prop_container_to_device(ulContainer);
|
||||
if (!device) {
|
||||
return vr::VRInputError_InvalidHandle;
|
||||
}
|
||||
bool x = end == 'x';
|
||||
name.remove_suffix(2);
|
||||
std::string n(name);
|
||||
xrt_input *input = device->get_input_from_name(n);
|
||||
if (!input) {
|
||||
return vr::VRInputError_None;
|
||||
}
|
||||
|
||||
// Create the component mapping if it hasn't been created yet
|
||||
Vec2Components *components =
|
||||
vec2_input_to_components.try_emplace(input, new Vec2Components).first->second.get();
|
||||
|
||||
vr::VRInputComponentHandle_t new_handle = handle_to_input.size() + 1;
|
||||
if (x)
|
||||
components->x = new_handle;
|
||||
else
|
||||
components->y = new_handle;
|
||||
|
||||
handle_to_input[new_handle] = input;
|
||||
*pHandle = new_handle;
|
||||
return vr::VRInputError_None;
|
||||
}
|
||||
return create_component_common(ulContainer, pchName, pHandle);
|
||||
}
|
||||
|
||||
vr::EVRInputError
|
||||
Context::UpdateScalarComponent(vr::VRInputComponentHandle_t ulComponent, float fNewValue, double fTimeOffset)
|
||||
{
|
||||
xrt_input *input = update_component_common(ulComponent, fTimeOffset);
|
||||
if (input) {
|
||||
if (XRT_GET_INPUT_TYPE(input->name) == XRT_INPUT_TYPE_VEC2_MINUS_ONE_TO_ONE) {
|
||||
std::unique_ptr<Vec2Components> &components = vec2_input_to_components.at(input);
|
||||
if (components->x == ulComponent) {
|
||||
input->value.vec2.x = fNewValue;
|
||||
} else if (components->y == ulComponent) {
|
||||
input->value.vec2.y = fNewValue;
|
||||
} else {
|
||||
CTX_WARN(
|
||||
"Attempted to update component with handle %lu"
|
||||
" but it was neither the x nor y "
|
||||
"component of its associated input",
|
||||
ulComponent);
|
||||
}
|
||||
|
||||
} else {
|
||||
input->value.vec1.x = fNewValue;
|
||||
}
|
||||
}
|
||||
return vr::VRInputError_None;
|
||||
}
|
||||
|
||||
vr::EVRInputError
|
||||
Context::CreateHapticComponent(vr::PropertyContainerHandle_t ulContainer,
|
||||
const char *pchName,
|
||||
vr::VRInputComponentHandle_t *pHandle)
|
||||
{
|
||||
*pHandle = vr::k_ulInvalidInputComponentHandle;
|
||||
Device *d = prop_container_to_device(ulContainer);
|
||||
if (!d) {
|
||||
return vr::VRInputError_InvalidHandle;
|
||||
}
|
||||
|
||||
// Assuming HMDs won't have haptics.
|
||||
// Maybe a wrong assumption.
|
||||
if (d == hmd) {
|
||||
CTX_WARN("Didn't expect HMD with haptics.");
|
||||
return vr::VRInputError_InvalidHandle;
|
||||
}
|
||||
|
||||
auto *device = static_cast<ControllerDevice *>(d);
|
||||
vr::VRInputComponentHandle_t handle = handle_to_input.size() + 1;
|
||||
handle_to_input[handle] = nullptr;
|
||||
device->set_haptic_handle(handle);
|
||||
*pHandle = handle;
|
||||
|
||||
return vr::VRInputError_None;
|
||||
}
|
||||
|
||||
vr::EVRInputError
|
||||
Context::CreateSkeletonComponent(vr::PropertyContainerHandle_t ulContainer,
|
||||
const char *pchName,
|
||||
const char *pchSkeletonPath,
|
||||
const char *pchBasePosePath,
|
||||
vr::EVRSkeletalTrackingLevel eSkeletalTrackingLevel,
|
||||
const vr::VRBoneTransform_t *pGripLimitTransforms,
|
||||
uint32_t unGripLimitTransformCount,
|
||||
vr::VRInputComponentHandle_t *pHandle)
|
||||
{
|
||||
return vr::VRInputError_None;
|
||||
}
|
||||
|
||||
vr::EVRInputError
|
||||
Context::UpdateSkeletonComponent(vr::VRInputComponentHandle_t ulComponent,
|
||||
vr::EVRSkeletalMotionRange eMotionRange,
|
||||
const vr::VRBoneTransform_t *pTransforms,
|
||||
uint32_t unTransformCount)
|
||||
{
|
||||
return vr::VRInputError_None;
|
||||
}
|
||||
|
||||
/***** IVRProperties methods *****/
|
||||
|
||||
vr::ETrackedPropertyError
|
||||
Context::ReadPropertyBatch(vr::PropertyContainerHandle_t ulContainerHandle,
|
||||
vr::PropertyRead_t *pBatch,
|
||||
uint32_t unBatchEntryCount)
|
||||
{
|
||||
return vr::TrackedProp_Success;
|
||||
}
|
||||
|
||||
vr::ETrackedPropertyError
|
||||
Context::WritePropertyBatch(vr::PropertyContainerHandle_t ulContainerHandle,
|
||||
vr::PropertyWrite_t *pBatch,
|
||||
uint32_t unBatchEntryCount)
|
||||
{
|
||||
Device *device = prop_container_to_device(ulContainerHandle);
|
||||
if (!device)
|
||||
return vr::TrackedProp_InvalidContainer;
|
||||
if (!pBatch)
|
||||
return vr::TrackedProp_InvalidOperation; // not verified vs steamvr
|
||||
device->handle_properties(pBatch, unBatchEntryCount);
|
||||
return vr::TrackedProp_Success;
|
||||
}
|
||||
|
||||
const char *
|
||||
Context::GetPropErrorNameFromEnum(vr::ETrackedPropertyError error)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Device *
|
||||
Context::prop_container_to_device(vr::PropertyContainerHandle_t handle)
|
||||
{
|
||||
switch (handle) {
|
||||
case 1: {
|
||||
return hmd;
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
case 3: {
|
||||
return controller[handle - 2];
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vr::PropertyContainerHandle_t
|
||||
Context::TrackedDeviceToPropertyContainer(vr::TrackedDeviceIndex_t nDevice)
|
||||
{
|
||||
size_t container = nDevice + 1;
|
||||
if (nDevice == 0 && this->hmd)
|
||||
return container;
|
||||
if ((nDevice == 1 || nDevice == 2) && this->controller[nDevice - 1]) {
|
||||
return container;
|
||||
}
|
||||
|
||||
return vr::k_ulInvalidPropertyContainer;
|
||||
}
|
||||
|
||||
void
|
||||
Context::Log(const char *pchLogMessage)
|
||||
{
|
||||
CTX_TRACE("[lighthouse]: %s", pchLogMessage);
|
||||
}
|
||||
// NOLINTEND(bugprone-easily-swappable-parameters)
|
||||
|
||||
|
||||
extern "C" int
|
||||
steamvr_lh_get_devices(struct xrt_device **out_xdevs)
|
||||
{
|
||||
u_logging_level level = debug_get_log_option_lh_log();
|
||||
// The driver likes to create a bunch of transient folder - lets make sure they're created where they normally
|
||||
// are.
|
||||
std::filesystem::current_path(STEAM_INSTALL_DIR + "/config/lighthouse");
|
||||
std::string steamvr = find_steamvr_install();
|
||||
if (steamvr.empty()) {
|
||||
U_LOG_IFL_E(level, "Could not find where SteamVR is installed!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
U_LOG_IFL_I(level, "Found SteamVR install: %s", steamvr.c_str());
|
||||
|
||||
// TODO: support windows?
|
||||
auto driver_so = steamvr + "/drivers/lighthouse/bin/linux64/driver_lighthouse.so";
|
||||
|
||||
void *lighthouse_lib = dlopen(driver_so.c_str(), RTLD_LAZY);
|
||||
if (!lighthouse_lib) {
|
||||
U_LOG_IFL_E(level, "Couldn't open lighthouse lib: %s", dlerror());
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *sym = dlsym(lighthouse_lib, "HmdDriverFactory");
|
||||
if (!sym) {
|
||||
U_LOG_IFL_E(level, "Couldn't find HmdDriverFactory in lighthouse lib: %s", dlerror());
|
||||
return 0;
|
||||
}
|
||||
using HmdDriverFactory_t = void *(*)(const char *, int *);
|
||||
auto factory = reinterpret_cast<HmdDriverFactory_t>(sym);
|
||||
|
||||
vr::EVRInitError err = vr::VRInitError_None;
|
||||
auto *driver = static_cast<vr::IServerTrackedDeviceProvider *>(
|
||||
factory(vr::IServerTrackedDeviceProvider_Version, (int *)&err));
|
||||
if (err != vr::VRInitError_None) {
|
||||
U_LOG_IFL_E(level, "Couldn't get tracked device driver: error %u", err);
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::shared_ptr ctx = Context::create(STEAM_INSTALL_DIR, steamvr, driver);
|
||||
|
||||
err = driver->Init(ctx.get());
|
||||
if (err != vr::VRInitError_None) {
|
||||
U_LOG_IFL_E(level, "Lighthouse driver initialization failed: error %u", err);
|
||||
return 0;
|
||||
}
|
||||
|
||||
U_LOG_IFL_I(level, "Lighthouse initialization complete, giving time to setup connected devices...");
|
||||
// RunFrame needs to be called to detect controllers
|
||||
using namespace std::chrono_literals;
|
||||
auto start_time = std::chrono::steady_clock::now();
|
||||
while (true) {
|
||||
driver->RunFrame();
|
||||
auto cur_time = std::chrono::steady_clock::now();
|
||||
if (cur_time - start_time >= 1s) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
U_LOG_IFL_I(level, "Device search time complete.");
|
||||
|
||||
int devices = 0;
|
||||
Device *devs[] = {ctx->hmd, ctx->controller[0], ctx->controller[1]};
|
||||
for (Device *dev : devs) {
|
||||
if (dev) {
|
||||
out_xdevs[devices++] = dev;
|
||||
}
|
||||
}
|
||||
return devices;
|
||||
}
|
44
src/xrt/drivers/steamvr_lh/steamvr_lh_interface.h
Normal file
44
src/xrt/drivers/steamvr_lh/steamvr_lh_interface.h
Normal file
|
@ -0,0 +1,44 @@
|
|||
// Copyright 2023, Shawn Wallace
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
/*!
|
||||
* @file
|
||||
* @brief SteamVR driver device interface.
|
||||
* @author Shawn Wallace <yungwallace@live.com>
|
||||
* @ingroup drv_steamvr_lh
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
struct xrt_device;
|
||||
|
||||
/*!
|
||||
* @defgroup drv_steamvr_lh Wrapper for the SteamVR Lighthouse driver.
|
||||
* @ingroup drv
|
||||
*
|
||||
* @brief Wrapper driver around the SteamVR Lighthouse driver.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* @dir drivers/steamvr_lh
|
||||
*
|
||||
* @brief @ref drv_steamvr_lh files.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* Create devices.
|
||||
*
|
||||
* @ingroup drv_steamvr_lh
|
||||
*/
|
||||
int
|
||||
steamvr_lh_get_devices(struct xrt_device **out_xdevs);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
Loading…
Reference in a new issue