diff --git a/CMakeLists.txt b/CMakeLists.txt index a637d567d..43c1238e7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -80,6 +80,7 @@ cmake_dependent_option(BUILD_WITH_SDL2 "Enable SDL2 based test application" ON " option(BUILD_WITH_HDK "Enable HDK driver" ON) option(BUILD_WITH_PSMV "Enable Playstation Move driver" ON) option(BUILD_WITH_HYDRA "Enable Hydra driver" ON) +option(BUILD_WITH_NS "Enable North Star driver" ON) ### # Flags @@ -139,6 +140,10 @@ if(BUILD_WITH_OPENHMD) set(BUILD_DRIVER_OHMD TRUE) endif() +if(BUILD_WITH_NS) + set(BUILD_DRIVER_NS TRUE) +endif() + if(BUILD_WITH_PSVR) if (NOT ${HIDAPI_FOUND}) message(FATAL_ERROR "PSVR driver requires hidapi") diff --git a/meson.build b/meson.build index 6f3e47ef6..6f2bc3a33 100644 --- a/meson.build +++ b/meson.build @@ -131,6 +131,9 @@ drivers = get_option('drivers') if 'ohmd' in drivers openhmd_required = true endif +if 'ns' in drivers + ns_required = true +endif if 'psvr' in drivers hidapi_required = true endif @@ -146,6 +149,12 @@ openhmd = dependency('openhmd', required: openhmd_required) hidapi = dependency('hidapi-libusb', required: hidapi_required) v4l2 = dependency('libv4l2', required: v4l2_required) +if 'auto' in drivers or 'ns' in drivers + if 'ns' not in drivers + drivers += ['ns'] + endif +endif + if openhmd.found() and ('auto' in drivers or 'ohmd' in drivers) if 'ohmd' not in drivers drivers += ['ohmd'] diff --git a/meson_options.txt b/meson_options.txt index abfaaa8a4..5b264b247 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -3,7 +3,7 @@ option('drivers', type: 'array', - choices: ['auto', 'dummy', 'hdk', 'hydra', 'ohmd', 'psmv', 'psvr', 'v4l2', 'vive'], + choices: ['auto', 'dummy', 'hdk', 'hydra', 'ns', 'ohmd', 'psmv', 'psvr', 'v4l2', 'vive'], value: ['auto'], description: 'Set of drivers to build') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a01ae616b..25a663ed3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -18,6 +18,10 @@ if(BUILD_DRIVER_HYDRA) set(XRT_BUILD_DRIVER_HYDRA TRUE) endif() +if(BUILD_DRIVER_NS) + set(XRT_BUILD_DRIVER_NS TRUE) +endif() + if(BUILD_DRIVER_OHMD) set(XRT_BUILD_DRIVER_OHMD TRUE) endif() diff --git a/src/meson.build b/src/meson.build index 9edb2351d..e09440c9a 100644 --- a/src/meson.build +++ b/src/meson.build @@ -6,6 +6,7 @@ conf_data.set('XRT_BUILD_TRACKING', build_tracking) conf_data.set('XRT_BUILD_DRIVER_DUMMY', 'dummy' in drivers) conf_data.set('XRT_BUILD_DRIVER_HDK', 'hdk' in drivers) conf_data.set('XRT_BUILD_DRIVER_HYDRA', 'hydra' in drivers) +conf_data.set('XRT_BUILD_DRIVER_NS', 'ns' in drivers) conf_data.set('XRT_BUILD_DRIVER_OHMD', 'ohmd' in drivers) conf_data.set('XRT_BUILD_DRIVER_PSMV', 'psmv' in drivers) conf_data.set('XRT_BUILD_DRIVER_PSVR', 'psvr' in drivers) diff --git a/src/targets_enabled_drivers.h.cmake_in b/src/targets_enabled_drivers.h.cmake_in index db6bafecf..05899445e 100644 --- a/src/targets_enabled_drivers.h.cmake_in +++ b/src/targets_enabled_drivers.h.cmake_in @@ -15,6 +15,8 @@ #cmakedefine XRT_BUILD_DRIVER_HYDRA +#cmakedefine XRT_BUILD_DRIVER_NS + #cmakedefine XRT_BUILD_DRIVER_OHMD #cmakedefine XRT_BUILD_DRIVER_PSMV diff --git a/src/targets_enabled_drivers.h.meson_in b/src/targets_enabled_drivers.h.meson_in index 6209d7a07..61cadd983 100644 --- a/src/targets_enabled_drivers.h.meson_in +++ b/src/targets_enabled_drivers.h.meson_in @@ -15,6 +15,8 @@ #mesondefine XRT_BUILD_DRIVER_HYDRA +#mesondefine XRT_BUILD_DRIVER_NS + #mesondefine XRT_BUILD_DRIVER_OHMD #mesondefine XRT_BUILD_DRIVER_PSMV diff --git a/src/xrt/drivers/CMakeLists.txt b/src/xrt/drivers/CMakeLists.txt index 0e716da6b..e972cc626 100644 --- a/src/xrt/drivers/CMakeLists.txt +++ b/src/xrt/drivers/CMakeLists.txt @@ -48,6 +48,18 @@ if(BUILD_DRIVER_HYDRA) list(APPEND ENABLED_DRIVERS hydra) endif() +if(BUILD_DRIVER_NS) + set(NS_SOURCE_FILES + north_star/ns_hmd.c + north_star/ns_interface.h + north_star/ns_prober.c + ) + + # Use OBJECT to not create a archive, since it just gets in the way. + add_library(drv_ns OBJECT ${NS_SOURCE_FILES}) + list(APPEND ENABLED_HEADSET_DRIVERS ns) +endif() + if(BUILD_DRIVER_OHMD) set(OHMD_SOURCE_FILES ohmd/oh_device.c diff --git a/src/xrt/drivers/meson.build b/src/xrt/drivers/meson.build index 16499dc17..3b4b4af24 100644 --- a/src/xrt/drivers/meson.build +++ b/src/xrt/drivers/meson.build @@ -39,6 +39,18 @@ lib_drv_hydra = static_library( build_by_default: 'hydra' in drivers, ) +lib_drv_ns = static_library( + 'drv_ns', + files( + 'north_star/ns_hmd.c', + 'north_star/ns_interface.h', + 'north_star/ns_prober.c', + ), + include_directories: xrt_include, + dependencies: [aux], + build_by_default: 'ns' in drivers, +) + lib_drv_ohmd = static_library( 'drv_ohmd', files( diff --git a/src/xrt/drivers/north_star/ns_hmd.c b/src/xrt/drivers/north_star/ns_hmd.c new file mode 100644 index 000000000..1c7f09584 --- /dev/null +++ b/src/xrt/drivers/north_star/ns_hmd.c @@ -0,0 +1,198 @@ +// Copyright 2020, Collabora, Ltd. +// Copyright 2020, Nova King. +// SPDX-License-Identifier: BSL-1.0 +/*! + * @file + * @brief North Star HMD code. + * @author Nova King + * @author Jakob Bornecrantz + * @ingroup drv_ns + */ + + +#include +#include +#include +#include +#include +#include + +#include "math/m_api.h" +#include "xrt/xrt_device.h" +#include "util/u_var.h" +#include "util/u_misc.h" +#include "util/u_debug.h" +#include "util/u_device.h" +#include "util/u_time.h" +#include "util/u_distortion_mesh.h" + +/* + * + * Structs and defines. + * + */ + +struct ns_hmd +{ + struct xrt_device base; + + struct xrt_pose pose; + + bool print_spew; + bool print_debug; +}; + + +/* + * + * Functions + * + */ + +static inline struct ns_hmd * +ns_hmd(struct xrt_device *xdev) +{ + return (struct ns_hmd *)xdev; +} + +#define NS_SPEW(c, ...) \ + do { \ + if (c->print_spew) { \ + fprintf(stderr, "%s - ", __func__); \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "\n"); \ + } \ + } while (false) + +#define NS_DEBUG(c, ...) \ + do { \ + if (c->print_debug) { \ + fprintf(stderr, "%s - ", __func__); \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "\n"); \ + } \ + } while (false) + +#define NS_ERROR(c, ...) \ + do { \ + fprintf(stderr, "%s - ", __func__); \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "\n"); \ + } while (false) + +static void +ns_hmd_destroy(struct xrt_device *xdev) +{ + struct ns_hmd *ns = ns_hmd(xdev); + + // Remove the variable tracking. + u_var_remove_root(ns); + + u_device_free(&ns->base); +} + +static void +ns_hmd_update_inputs(struct xrt_device *xdev, struct time_state *timekeeping) +{ + // Empty +} + +static void +ns_hmd_get_tracked_pose(struct xrt_device *xdev, + enum xrt_input_name name, + struct time_state *timekeeping, + int64_t *out_timestamp, + struct xrt_space_relation *out_relation) +{ + struct ns_hmd *ns = ns_hmd(xdev); + + if (name != XRT_INPUT_GENERIC_HEAD_POSE) { + NS_ERROR(ns, "unknown input name"); + return; + } + + int64_t now = time_state_get_now(timekeeping); + + *out_timestamp = now; + out_relation->pose = ns->pose; + out_relation->relation_flags = (enum xrt_space_relation_flags)( + XRT_SPACE_RELATION_ORIENTATION_VALID_BIT | + XRT_SPACE_RELATION_POSITION_VALID_BIT); +} + +static void +ns_hmd_get_view_pose(struct xrt_device *xdev, + struct xrt_vec3 *eye_relation, + uint32_t view_index, + struct xrt_pose *out_pose) +{ + struct xrt_pose pose = {{0.0f, 0.0f, 0.0f, 1.0f}, {0.0f, 0.0f, 0.0f}}; + bool adjust = view_index == 0; + + pose.position.x = eye_relation->x / 2.0f; + pose.position.y = eye_relation->y / 2.0f; + pose.position.z = eye_relation->z / 2.0f; + + // Adjust for left/right while also making sure there aren't any -0.f. + if (pose.position.x > 0.0f && adjust) { + pose.position.x = -pose.position.x; + } + if (pose.position.y > 0.0f && adjust) { + pose.position.y = -pose.position.y; + } + if (pose.position.z > 0.0f && adjust) { + pose.position.z = -pose.position.z; + } + + *out_pose = pose; +} + +struct xrt_device * +ns_hmd_create(bool print_spew, bool print_debug) +{ + enum u_device_alloc_flags flags = (enum u_device_alloc_flags)( + U_DEVICE_ALLOC_HMD | U_DEVICE_ALLOC_TRACKING_NONE); + struct ns_hmd *ns = U_DEVICE_ALLOCATE(struct ns_hmd, flags, 1, 0); + ns->base.update_inputs = ns_hmd_update_inputs; + ns->base.get_tracked_pose = ns_hmd_get_tracked_pose; + ns->base.get_view_pose = ns_hmd_get_view_pose; + ns->base.destroy = ns_hmd_destroy; + ns->base.name = XRT_DEVICE_GENERIC_HMD; + ns->pose.orientation.w = 1.0f; // All other values set to zero. + ns->print_spew = print_spew; + ns->print_debug = print_debug; + + // Print name. + snprintf(ns->base.str, XRT_DEVICE_NAME_LEN, "North Star"); + + // Setup input. + ns->base.inputs[0].name = XRT_INPUT_GENERIC_HEAD_POSE; + + // Setup info. + struct u_device_simple_info info; + info.display.w_pixels = 1920; + info.display.h_pixels = 1080; + info.display.w_meters = 0.13f; + info.display.h_meters = 0.07f; + info.lens_horizontal_separation_meters = 0.13f / 2.0f; + info.lens_vertical_position_meters = 0.07f / 2.0f; + info.views[0].fov = 85.0f * (M_PI / 180.0f); + info.views[1].fov = 85.0f * (M_PI / 180.0f); + + if (!u_device_setup_split_side_by_side(&ns->base, &info)) { + NS_ERROR(ns, "Failed to setup basic device info"); + ns_hmd_destroy(&ns->base); + return NULL; + } + + // Setup variable tracker. + u_var_add_root(ns, "North Star", true); + u_var_add_pose(ns, &ns->pose, "pose"); + + if (ns->base.hmd->distortion.preferred == XRT_DISTORTION_MODEL_NONE) { + // Setup the distortion mesh. + u_distortion_mesh_none(ns->base.hmd); + } + + return &ns->base; +} diff --git a/src/xrt/drivers/north_star/ns_interface.h b/src/xrt/drivers/north_star/ns_interface.h new file mode 100644 index 000000000..84904fdcd --- /dev/null +++ b/src/xrt/drivers/north_star/ns_interface.h @@ -0,0 +1,49 @@ +// Copyright 2019, Collabora, Ltd. +// SPDX-License-Identifier: BSL-1.0 +/*! + * @file + * @brief Interface to North Star driver code. + * @author Nova King + * @ingroup drv_ns + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +/*! + * @defgroup drv_ns North Star Driver + * @ingroup drv + * + * @brief Driver for the North Star HMD. + */ + + +/*! + * Create a probe for NS devices. + * + * @ingroup drv_ns + */ +struct xrt_auto_prober * +ns_create_auto_prober(void); + +/*! + * Create a North Star hmd. + * + * @ingroup drv_ns + */ +struct xrt_device * +ns_hmd_create(bool print_spew, bool print_debug); + +/*! + * @dir drivers/ns + * + * @brief @ref drv_ns files. + */ + + +#ifdef __cplusplus +} +#endif diff --git a/src/xrt/drivers/north_star/ns_prober.c b/src/xrt/drivers/north_star/ns_prober.c new file mode 100644 index 000000000..2b9072caf --- /dev/null +++ b/src/xrt/drivers/north_star/ns_prober.c @@ -0,0 +1,70 @@ +// Copyright 2019, Collabora, Ltd. +// SPDX-License-Identifier: BSL-1.0 +/*! + * @file + * @brief North Star prober code. + * @author Nova King + * @author Jakob Bornecrantz + * @ingroup drv_ns + */ + +#include +#include + +#include "xrt/xrt_prober.h" + +#include "util/u_misc.h" +#include "util/u_debug.h" + +#include "ns_interface.h" + + +DEBUG_GET_ONCE_BOOL_OPTION(ns_spew, "NS_PRINT_SPEW", false) +DEBUG_GET_ONCE_BOOL_OPTION(ns_debug, "NS_PRINT_DEBUG", false) + +struct ns_prober +{ + struct xrt_auto_prober base; + bool print_spew; + bool print_debug; +}; + +static inline struct ns_prober * +ns_prober(struct xrt_auto_prober *p) +{ + return (struct ns_prober *)p; +} + +static void +ns_prober_destroy(struct xrt_auto_prober *p) +{ + struct ns_prober *nsp = ns_prober(p); + + free(nsp); +} + +static struct xrt_device * +ns_prober_autoprobe(struct xrt_auto_prober *xap, + bool no_hmds, + struct xrt_prober *xp) +{ + struct ns_prober *nsp = ns_prober(xap); + + if (no_hmds) { + return NULL; + } + + return ns_hmd_create(nsp->print_spew, nsp->print_debug); +} + +struct xrt_auto_prober * +ns_create_auto_prober() +{ + struct ns_prober *nsp = U_TYPED_CALLOC(struct ns_prober); + nsp->base.destroy = ns_prober_destroy; + nsp->base.lelo_dallas_autoprobe = ns_prober_autoprobe; + nsp->print_spew = debug_get_bool_option_ns_spew(); + nsp->print_debug = debug_get_bool_option_ns_debug(); + + return &nsp->base; +} diff --git a/src/xrt/targets/CMakeLists.txt b/src/xrt/targets/CMakeLists.txt index 86330fc97..e9eab2f0f 100644 --- a/src/xrt/targets/CMakeLists.txt +++ b/src/xrt/targets/CMakeLists.txt @@ -33,6 +33,10 @@ if(BUILD_DRIVER_HYDRA) list(APPEND DRIVER_OBJECTS $) endif() +if(BUILD_DRIVER_NS) + list(APPEND DRIVER_OBJECTS $) +endif() + if(BUILD_DRIVER_OHMD) list(APPEND DRIVER_OBJECTS $) list(APPEND DRIVER_LIBRARIES OpenHMD::OpenHMD) diff --git a/src/xrt/targets/common/target_lists.c b/src/xrt/targets/common/target_lists.c index a4e2d59d0..43c1b272c 100644 --- a/src/xrt/targets/common/target_lists.c +++ b/src/xrt/targets/common/target_lists.c @@ -22,6 +22,10 @@ #include "ohmd/oh_interface.h" #endif +#ifdef XRT_BUILD_DRIVER_NS +#include "north_star/ns_interface.h" +#endif + #ifdef XRT_BUILD_DRIVER_PSMV #include "psmv/psmv_interface.h" #endif @@ -89,10 +93,15 @@ xrt_auto_prober_creator target_auto_list[] = { #endif #ifdef XRT_BUILD_DRIVER_OHMD - // OpenHMD second last as we want to override it with native drivers. + // OpenHMD almost as the end as we want to override it with native drivers. oh_create_auto_prober, #endif +#ifdef XRT_BUILD_DRIVER_NS + // North star driver here for now. + ns_create_auto_prober, +#endif + #ifdef XRT_BUILD_DRIVER_DUMMY // Dummy headset driver last. dummy_create_auto_prober, diff --git a/src/xrt/targets/meson.build b/src/xrt/targets/meson.build index c37b6b2d5..d4b8a3500 100644 --- a/src/xrt/targets/meson.build +++ b/src/xrt/targets/meson.build @@ -24,6 +24,10 @@ if 'hydra' in drivers driver_libs += [lib_drv_hydra] endif +if 'ns' in drivers + driver_libs += [lib_drv_ns] +endif + if 'ohmd' in drivers driver_libs += [lib_drv_ohmd] endif