diff --git a/CMakeLists.txt b/CMakeLists.txt index 0e6933188..b130c6e7e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -322,6 +322,7 @@ option_with_deps(XRT_HAVE_OPENVR "Enable OpenVR support" DEPENDS OPENVR_FOUND) # Drivers to build (sorted) option_with_deps(XRT_BUILD_DRIVER_ANDROID "Enable Android sensors driver" DEPENDS ANDROID) option_with_deps(XRT_BUILD_DRIVER_ARDUINO "Enable Arduino input device with BLE" DEPENDS XRT_HAVE_DBUS) +option_with_deps(XRT_BUILD_DRIVER_BLUBUR_S1 "Enable 3Glasses Blubur S1 device driver" DEPENDS XRT_HAVE_INTERNAL_HID) option_with_deps(XRT_BUILD_DRIVER_DAYDREAM "Enable the Google Daydream View controller driver (BLE)" DEPENDS XRT_HAVE_DBUS) option_with_deps(XRT_BUILD_DRIVER_DEPTHAI "DepthAI" DEPENDS depthai_FOUND) option_with_deps(XRT_BUILD_DRIVER_EUROC "Enable EuRoC dataset driver for SLAM evaluation" DEPENDS XRT_HAVE_OPENCV "NOT MSVC") @@ -424,6 +425,7 @@ list( AVAILABLE_DRIVERS "ANDROID" "ARDUINO" + "BLUBUR_S1" "DAYDREAM" "SIMULATED" "HANDTRACKING" @@ -658,6 +660,7 @@ message(STATUS "# FEATURE_WINDOW_PEEK: ${XRT_FEATUR message(STATUS "#") message(STATUS "# DRIVER_ANDROID: ${XRT_BUILD_DRIVER_ANDROID}") message(STATUS "# DRIVER_ARDUINO: ${XRT_BUILD_DRIVER_ARDUINO}") +message(STATUS "# DRIVER_BLUBUR_S1: ${XRT_BUILD_DRIVER_BLUBUR_S1}") message(STATUS "# DRIVER_DAYDREAM: ${XRT_BUILD_DRIVER_DAYDREAM}") message(STATUS "# DRIVER_DEPTHAI: ${XRT_BUILD_DRIVER_DEPTHAI}") message(STATUS "# DRIVER_EUROC: ${XRT_BUILD_DRIVER_EUROC}") diff --git a/src/xrt/drivers/CMakeLists.txt b/src/xrt/drivers/CMakeLists.txt index c29a28aaa..98a8fc432 100644 --- a/src/xrt/drivers/CMakeLists.txt +++ b/src/xrt/drivers/CMakeLists.txt @@ -21,6 +21,15 @@ add_library(drv_cemu STATIC ht_ctrl_emu/ht_ctrl_emu.cpp ht_ctrl_emu/ht_ctrl_emu_ target_link_libraries(drv_cemu PRIVATE xrt-interfaces aux_generated_bindings_helpers aux_util) list(APPEND ENABLED_HEADSET_DRIVERS drv_cemu) +if(XRT_BUILD_DRIVER_BLUBUR_S1) + add_library( + drv_blubur_s1 STATIC blubur_s1/blubur_s1_hmd.c blubur_s1/blubur_s1_interface.h + blubur_s1/blubur_s1_prober.c + ) + target_link_libraries(drv_blubur_s1 PRIVATE xrt-interfaces aux_util aux_os) + list(APPEND ENABLED_DRIVERS blubur_s1) +endif() + if(XRT_BUILD_DRIVER_DAYDREAM) add_library( drv_daydream STATIC daydream/daydream_device.c daydream/daydream_device.h diff --git a/src/xrt/drivers/blubur_s1/blubur_s1_hmd.c b/src/xrt/drivers/blubur_s1/blubur_s1_hmd.c new file mode 100644 index 000000000..f3fd04397 --- /dev/null +++ b/src/xrt/drivers/blubur_s1/blubur_s1_hmd.c @@ -0,0 +1,189 @@ +// Copyright 2025, Beyley Cardellio +// SPDX-License-Identifier: BSL-1.0 +/*! + * @file + * @brief Implementation of Blubur S1 HMD driver. + * @author Beyley Cardellio + * @ingroup drv_blubur_s1 + */ + +#include "math/m_api.h" +#include "math/m_mathinclude.h" + +#include "util/u_device.h" +#include "util/u_distortion_mesh.h" +#include "util/u_misc.h" +#include "util/u_time.h" + +#include "blubur_s1_interface.h" +#include "blubur_s1_internal.h" + + +static struct blubur_s1_hmd * +blubur_s1_hmd(struct xrt_device *xdev) +{ + return (struct blubur_s1_hmd *)xdev; +} + +static void +blubur_s1_hmd_destroy(struct xrt_device *xdev) +{ + struct blubur_s1_hmd *hmd = blubur_s1_hmd(xdev); + free(hmd); +} + +static xrt_result_t +blubur_s1_hmd_compute_distortion( + struct xrt_device *xdev, uint32_t view, float u, float v, struct xrt_uv_triplet *out_result) +{ + float x = u * 2.0f - 1.0f; + float y = v * 2.0f - 1.0f; + + float r2 = x * x + y * y; + float r = sqrtf(r2); + float r3 = r2 * r; + float r4 = r2 * r2; + float r5 = r4 * r; + + float radial = (0.5978f * r5) - (0.7257f * r4) + (0.504f * r3) - (0.0833f * r2) + (0.709f * r) - 0.00006f; + + struct xrt_vec2 result = { + .x = (x * radial) / 2.0f + 0.5f, + .y = (y * radial) / 2.0f + 0.5f, + }; + out_result->r = result; + out_result->g = result; + out_result->b = result; + + return XRT_SUCCESS; +} + +static xrt_result_t +blubur_s1_hmd_update_inputs(struct xrt_device *xdev) +{ + return XRT_SUCCESS; +} + +static xrt_result_t +blubur_s1_hmd_get_tracked_pose(struct xrt_device *xdev, + enum xrt_input_name name, + int64_t at_timestamp_ns, + struct xrt_space_relation *out_relation) +{ + if (name != XRT_INPUT_GENERIC_HEAD_POSE) { + return XRT_ERROR_INPUT_UNSUPPORTED; + } + + // TODO: track pose + *out_relation = (struct xrt_space_relation){ + .relation_flags = 0, + }; + + return XRT_SUCCESS; +} + +static xrt_result_t +blubur_s1_hmd_get_presence(struct xrt_device *xdev, bool *presence) +{ + // TODO: read the presence sensor from the device + *presence = true; + + return XRT_SUCCESS; +} + +static xrt_result_t +blubur_s1_get_view_poses(struct xrt_device *xdev, + const struct xrt_vec3 *default_eye_relation, + int64_t at_timestamp_ns, + uint32_t view_count, + struct xrt_space_relation *out_head_relation, + struct xrt_fov *out_fovs, + struct xrt_pose *out_poses) +{ + return u_device_get_view_poses(xdev, default_eye_relation, at_timestamp_ns, view_count, out_head_relation, + out_fovs, out_poses); +} + +struct blubur_s1_hmd * +blubur_s1_hmd_create(struct os_hid_device *dev, const char *serial) +{ + struct blubur_s1_hmd *hmd = + U_DEVICE_ALLOCATE(struct blubur_s1_hmd, U_DEVICE_ALLOC_HMD | U_DEVICE_ALLOC_TRACKING_NONE, 1, 0); + if (hmd == NULL) { + return NULL; + } + + hmd->base.destroy = blubur_s1_hmd_destroy; + hmd->base.name = XRT_DEVICE_GENERIC_HMD; + hmd->base.device_type = XRT_DEVICE_TYPE_HMD; + + const int view_size = 1440; + + hmd->base.hmd->screens[0].w_pixels = view_size * 2; + hmd->base.hmd->screens[0].h_pixels = view_size; + hmd->base.hmd->screens[0].nominal_frame_interval_ns = 1000000000LLU / 120; // 120hz + + hmd->base.hmd->view_count = 2; + hmd->base.hmd->views[0] = (struct xrt_view){ + .viewport = + { + .x_pixels = 0, + .y_pixels = 0, + .w_pixels = view_size, + .h_pixels = view_size, + }, + .display = + { + .w_pixels = view_size, + .h_pixels = view_size, + }, + .rot = u_device_rotation_ident, + }; + hmd->base.hmd->views[1] = (struct xrt_view){ + .viewport = + { + .x_pixels = view_size, + .y_pixels = 0, + .w_pixels = view_size, + .h_pixels = view_size, + }, + .display = + { + .w_pixels = view_size, + .h_pixels = view_size, + }, + .rot = u_device_rotation_ident, + }; + + hmd->base.hmd->blend_modes[0] = XRT_BLEND_MODE_OPAQUE; + hmd->base.hmd->blend_mode_count = 1; + + // TODO: what's the real FOV? + hmd->base.hmd->distortion.fov[0] = (struct xrt_fov){ + .angle_left = 45.0f * (M_PI / 180.0f), + .angle_right = 45.0f * (M_PI / 180.0f), + .angle_up = 45.0f * (M_PI / 180.0f), + .angle_down = 45.0f * (M_PI / 180.0f), + }; + hmd->base.hmd->distortion.fov[1] = hmd->base.hmd->distortion.fov[0]; + + hmd->base.hmd->distortion.models = XRT_DISTORTION_MODEL_COMPUTE; + hmd->base.hmd->distortion.preferred = XRT_DISTORTION_MODEL_COMPUTE; + hmd->base.compute_distortion = blubur_s1_hmd_compute_distortion; + u_distortion_mesh_fill_in_compute(&hmd->base); + + snprintf(hmd->base.str, XRT_DEVICE_NAME_LEN, "Blubur S1"); + snprintf(hmd->base.serial, XRT_DEVICE_NAME_LEN, "%s", serial); + + hmd->base.supported.position_tracking = true; + hmd->base.supported.presence = true; + + hmd->base.inputs[0].name = XRT_INPUT_GENERIC_HEAD_POSE; + + hmd->base.update_inputs = blubur_s1_hmd_update_inputs; + hmd->base.get_tracked_pose = blubur_s1_hmd_get_tracked_pose; + hmd->base.get_presence = blubur_s1_hmd_get_presence; + hmd->base.get_view_poses = blubur_s1_get_view_poses; + + return hmd; +} diff --git a/src/xrt/drivers/blubur_s1/blubur_s1_interface.h b/src/xrt/drivers/blubur_s1/blubur_s1_interface.h new file mode 100644 index 000000000..d552c08a5 --- /dev/null +++ b/src/xrt/drivers/blubur_s1/blubur_s1_interface.h @@ -0,0 +1,56 @@ +// Copyright 2025, Beyley Cardellio +// SPDX-License-Identifier: BSL-1.0 +/*! + * @file + * @brief Interface to Blubur S1 driver code. + * @author Beyley Cardellio + * @ingroup drv_blubur_s1 + */ + +#pragma once + +#include "xrt/xrt_device.h" +#include "xrt/xrt_prober.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +#define BLUBUR_S1_VID 0x2b1c +#define BLUBUR_S1_PID 0x0001 + +struct blubur_s1_hmd; + +/*! + * Probing function for Blubur S1 devices. + * + * @ingroup drv_blubur_s1 + * @see xrt_prober_found_func_t + */ +int +blubur_s1_found(struct xrt_prober *xp, + struct xrt_prober_device **devices, + size_t device_count, + size_t index, + cJSON *attached_data, + struct xrt_device **out_xdev); + +/*! + * Creates a Blubur S1 HMD device from an opened HID handle. + * + * @ingroup drv_blubur_s1 + */ +struct blubur_s1_hmd * +blubur_s1_hmd_create(struct os_hid_device *dev, const char *serial); + + +/*! + * @dir drivers/blubur_s1 + * + * @brief @ref drv_blubur_s1 files. + */ + +#ifdef __cplusplus +} +#endif diff --git a/src/xrt/drivers/blubur_s1/blubur_s1_internal.h b/src/xrt/drivers/blubur_s1/blubur_s1_internal.h new file mode 100644 index 000000000..044ef2213 --- /dev/null +++ b/src/xrt/drivers/blubur_s1/blubur_s1_internal.h @@ -0,0 +1,18 @@ +// Copyright 2025, Beyley Cardellio +// SPDX-License-Identifier: BSL-1.0 +/*! + * @file + * @brief Internal Blubur S1 driver definitions. + * @author Beyley Cardellio + * @ingroup drv_blubur_s1 + */ + +#pragma once + +#include "blubur_s1_interface.h" + + +struct blubur_s1_hmd +{ + struct xrt_device base; +}; diff --git a/src/xrt/drivers/blubur_s1/blubur_s1_prober.c b/src/xrt/drivers/blubur_s1/blubur_s1_prober.c new file mode 100644 index 000000000..ddb7cea04 --- /dev/null +++ b/src/xrt/drivers/blubur_s1/blubur_s1_prober.c @@ -0,0 +1,42 @@ +// Copyright 2025, Beyley Cardellio +// SPDX-License-Identifier: BSL-1.0 +/*! + * @file + * @brief Implementation of Blubur S1 prober code. + * @author Beyley Cardellio + * @ingroup drv_blubur_s1 + */ + +#include "blubur_s1_interface.h" +#include "blubur_s1_internal.h" + + +int +blubur_s1_found(struct xrt_prober *xp, + struct xrt_prober_device **devices, + size_t device_count, + size_t index, + cJSON *attached_data, + struct xrt_device **out_xdev) +{ + struct xrt_prober_device *dev = devices[index]; + + struct os_hid_device *hid = NULL; + int result = xrt_prober_open_hid_interface(xp, dev, 0, &hid); + if (result != 0) { + return -1; + } + + // TODO: figure out how to get the actual serial number of the device, since the official driver has multiple + // methods, and the string descriptor doesn't work on the unit used for development. + char serial[32] = {0}; + snprintf(serial, sizeof(serial), "%04x:%04x", dev->vendor_id, dev->product_id); + + struct blubur_s1_hmd *hmd = blubur_s1_hmd_create(hid, serial); + if (hmd == NULL) { + return -1; + } + *out_xdev = &hmd->base; + + return 1; +} diff --git a/src/xrt/targets/common/CMakeLists.txt b/src/xrt/targets/common/CMakeLists.txt index e7655456a..08cbdd213 100644 --- a/src/xrt/targets/common/CMakeLists.txt +++ b/src/xrt/targets/common/CMakeLists.txt @@ -72,6 +72,10 @@ if(XRT_BUILD_DRIVER_ARDUINO) target_link_libraries(target_lists PRIVATE drv_arduino) endif() +if(XRT_BUILD_DRIVER_BLUBUR_S1) + target_link_libraries(target_lists PRIVATE drv_blubur_s1) +endif() + target_link_libraries(target_lists PRIVATE drv_cemu) if(XRT_BUILD_DRIVER_DAYDREAM) diff --git a/src/xrt/targets/common/target_lists.c b/src/xrt/targets/common/target_lists.c index 29fab0b8d..74482e0b6 100644 --- a/src/xrt/targets/common/target_lists.c +++ b/src/xrt/targets/common/target_lists.c @@ -16,6 +16,10 @@ #include "arduino/arduino_interface.h" #endif +#ifdef XRT_BUILD_DRIVER_BLUBUR_S1 +#include "blubur_s1/blubur_s1_interface.h" +#endif + #ifdef XRT_BUILD_DRIVER_SIMULATED #include "simulated/simulated_interface.h" #endif @@ -192,6 +196,10 @@ struct xrt_prober_entry target_entry_list[] = { {OCULUS_VR_VID, OCULUS_DK2_PID, rift_found, "Rift (DK2)", "rift"}, #endif // XRT_BUILD_DRIVER_RIFT +#ifdef XRT_BUILD_DRIVER_BLUBUR_S1 + {BLUBUR_S1_VID, BLUBUR_S1_PID, blubur_s1_found, "Blubur S1", "blubur_s1"}, +#endif // XRT_BUILD_DRIVER_BLUBUR_S1 + #ifdef XRT_BUILD_DRIVER_ROKID {ROKID_VID, ROKID_PID, rokid_found, "Rokid Air or Max", "rokid"}, #endif // XRT_BUILD_DRIVER_ROKID