The open source OpenXR runtime
at main 1341 lines 49 kB view raw
1// Copyright 2019-2023, Collabora, Ltd. 2// SPDX-License-Identifier: BSL-1.0 3/*! 4 * @file 5 * @brief Adaptor to a OpenHMD device. 6 * @author Jakob Bornecrantz <jakob@collabora.com> 7 * @ingroup drv_ohmd 8 */ 9 10#include "xrt/xrt_config_os.h" 11#include "xrt/xrt_device.h" 12#include "xrt/xrt_prober.h" 13 14#include "os/os_time.h" 15 16#include "math/m_mathinclude.h" 17#include "math/m_api.h" 18#include "math/m_vec2.h" 19 20#include "util/u_var.h" 21#include "util/u_misc.h" 22#include "util/u_debug.h" 23#include "util/u_device.h" 24#include "util/u_time.h" 25#include "util/u_distortion_mesh.h" 26#include "util/u_logging.h" 27 28#include "oh_device.h" 29 30#include "openhmd.h" 31 32#include <stdio.h> 33#include <stdlib.h> 34#include <string.h> 35#include <assert.h> 36 37 38// Should we permit finite differencing to compute angular velocities when not 39// directly retrieved? 40DEBUG_GET_ONCE_BOOL_OPTION(ohmd_finite_diff, "OHMD_ALLOW_FINITE_DIFF", true) 41DEBUG_GET_ONCE_LOG_OPTION(ohmd_log, "OHMD_LOG", U_LOGGING_WARN) 42DEBUG_GET_ONCE_BOOL_OPTION(ohmd_external, "OHMD_EXTERNAL_DRIVER", false) 43 44// Define this if you have the appropriately hacked-up OpenHMD version. 45#undef OHMD_HAVE_ANG_VEL 46 47enum input_indices 48{ 49 // khronos simple inputs for generic controllers 50 SIMPLE_SELECT_CLICK = 0, 51 SIMPLE_MENU_CLICK, 52 53 // use same input field for touch controller, simple controller, and tracker 54 GRIP_POSE, 55 AIM_POSE, 56 57 OCULUS_TOUCH_X_CLICK, 58 OCULUS_TOUCH_X_TOUCH, 59 OCULUS_TOUCH_Y_CLICK, 60 OCULUS_TOUCH_Y_TOUCH, 61 OCULUS_TOUCH_MENU_CLICK, 62 OCULUS_TOUCH_A_CLICK, 63 OCULUS_TOUCH_A_TOUCH, 64 OCULUS_TOUCH_B_CLICK, 65 OCULUS_TOUCH_B_TOUCH, 66 OCULUS_TOUCH_SYSTEM_CLICK, 67 OCULUS_TOUCH_SQUEEZE_VALUE, 68 OCULUS_TOUCH_TRIGGER_TOUCH, 69 OCULUS_TOUCH_TRIGGER_VALUE, 70 OCULUS_TOUCH_THUMBSTICK_CLICK, 71 OCULUS_TOUCH_THUMBSTICK_TOUCH, 72 OCULUS_TOUCH_THUMBSTICK, 73 OCULUS_TOUCH_THUMBREST_TOUCH, 74 75 INPUT_INDICES_LAST 76}; 77#define SET_TOUCH_INPUT(NAME) (ohd->base.inputs[OCULUS_TOUCH_##NAME].name = XRT_INPUT_TOUCH_##NAME) 78 79// all OpenHMD controls are floats, even "digital" controls. 80// this means a trigger can be fed by a discrete button or by a pullable [0,1] analog trigger. 81// in case of not so good calibrated triggers we may not reach 1.0 82#define PRESS_FLOAT_THRESHOLD 0.95 83#define FLOAT_TO_DIGITAL_THRESHOLD 0.5 84 85// one mapping for each of enum ohmd_control_hint 86#define CONTROL_MAPPING_SIZE 16 87 88// Default haptic frequency for when the driver needs to decide frequency. 89#if defined(OHMD_HAVE_HAPTICS_API_v0) 90#define DEFAULT_HAPTIC_FREQ 160.0 91#endif 92 93 94// generic controllers are mapped to the khronos simple profile 95// touch controllers input mappings are special cased 96enum openhmd_device_type 97{ 98 OPENHMD_GENERIC_HMD, 99 OPENHMD_GENERIC_CONTROLLER, 100 OPENHMD_OCULUS_RIFT_HMD, 101 OPENHMD_OCULUS_RIFT_CONTROLLER, 102 OPENHMD_GENERIC_TRACKER, 103}; 104 105struct openhmd_values 106{ 107 float hmd_warp_param[4]; 108 float aberr[3]; 109 struct xrt_vec2 lens_center; 110 struct xrt_vec2 viewport_scale; 111 float warp_scale; 112}; 113 114enum OHMD_DEVICE_INDEX 115{ 116 OHMD_HMD_INDEX = 0, 117 OHMD_LEFT_INDEX, 118 OHMD_RIGHT_INDEX, 119 OHMD_FIRST_TRACKER_INDEX, 120}; 121 122struct oh_device; 123struct oh_system 124{ 125 struct xrt_tracking_origin base; 126 127 struct oh_device *devices[XRT_MAX_DEVICES_PER_PROBE]; 128}; 129 130/*! 131 * @implements xrt_device 132 */ 133struct oh_device 134{ 135 struct xrt_device base; 136 ohmd_context *ctx; 137 ohmd_device *dev; 138 139 bool skip_ang_vel; 140 141 int64_t last_update; 142 struct xrt_space_relation last_relation; 143 144 enum u_logging_level log_level; 145 bool enable_finite_difference; 146 147 struct 148 { 149 struct u_vive_values vive[2]; 150 struct openhmd_values openhmd[2]; 151 } distortion; 152 153 struct oh_system *sys; 154 155 156 enum openhmd_device_type ohmd_device_type; 157 158 // what function controls serve. 159 int controls_fn[64]; 160 161 // whether controls are digital or analog. 162 // unused because the OpenXR interaction profile forces the type. 163 int controls_types[64]; 164 165 //! maps OpenHMD control hint enum to the corresponding index into base.inputs 166 enum input_indices controls_mapping[CONTROL_MAPPING_SIZE]; 167 168 // For touch controller we map an analog trigger to a float interaction profile input. 169 // For simple controller we map a potentially analog trigger to a bool input. 170 bool make_trigger_digital; 171 172 float last_control_state[256]; 173}; 174 175static inline struct oh_device * 176oh_device(struct xrt_device *xdev) 177{ 178 return (struct oh_device *)xdev; 179} 180 181static void 182oh_device_destroy(struct xrt_device *xdev) 183{ 184 struct oh_device *ohd = oh_device(xdev); 185 186 if (ohd->dev != NULL) { 187 ohmd_close_device(ohd->dev); 188 ohd->dev = NULL; 189 } 190 191 bool all_null = true; 192 for (int i = 0; i < XRT_MAX_DEVICES_PER_PROBE; i++) { 193 if (ohd->sys->devices[i] == ohd) { 194 ohd->sys->devices[i] = NULL; 195 } 196 197 if (ohd->sys->devices[i] != NULL) { 198 all_null = false; 199 } 200 } 201 202 if (all_null) { 203 // Remove the variable tracking. 204 u_var_remove_root(ohd->sys); 205 206 free(ohd->sys); 207 } 208 209 u_device_free(&ohd->base); 210} 211 212#define CASE_VEC1(OHMD_CONTROL) \ 213 case OHMD_CONTROL: \ 214 if (ohd->controls_mapping[OHMD_CONTROL] == INPUT_INDICES_LAST) { \ 215 break; \ 216 } \ 217 if (control_state[i] != ohd->last_control_state[i]) { \ 218 ohd->base.inputs[ohd->controls_mapping[OHMD_CONTROL]].value.vec1.x = control_state[i]; \ 219 ohd->base.inputs[ohd->controls_mapping[OHMD_CONTROL]].timestamp = ts; \ 220 } \ 221 break; 222 223#define CASE_VEC1_OR_DIGITAL(OHMD_CONTROL, MAKE_DIGITAL) \ 224 case OHMD_CONTROL: \ 225 if (ohd->controls_mapping[OHMD_CONTROL] == INPUT_INDICES_LAST) { \ 226 break; \ 227 } \ 228 if (MAKE_DIGITAL) { \ 229 if ((control_state[i] > FLOAT_TO_DIGITAL_THRESHOLD) != \ 230 (ohd->last_control_state[i] > FLOAT_TO_DIGITAL_THRESHOLD)) { \ 231 ohd->base.inputs[ohd->controls_mapping[OHMD_CONTROL]].value.vec1.x = \ 232 control_state[i] > FLOAT_TO_DIGITAL_THRESHOLD; \ 233 ohd->base.inputs[ohd->controls_mapping[OHMD_CONTROL]].timestamp = ts; \ 234 } \ 235 } else { \ 236 if (control_state[i] != ohd->last_control_state[i]) { \ 237 ohd->base.inputs[ohd->controls_mapping[OHMD_CONTROL]].value.vec1.x = control_state[i]; \ 238 ohd->base.inputs[ohd->controls_mapping[OHMD_CONTROL]].timestamp = ts; \ 239 } \ 240 } \ 241 break; 242 243#define CASE_DIGITAL(OHMD_CONTROL, THRESHOLD) \ 244 case OHMD_CONTROL: \ 245 if (ohd->controls_mapping[OHMD_CONTROL] == INPUT_INDICES_LAST) { \ 246 break; \ 247 } \ 248 if (control_state[i] != ohd->last_control_state[i]) { \ 249 ohd->base.inputs[ohd->controls_mapping[OHMD_CONTROL]].value.boolean = \ 250 control_state[i] > THRESHOLD; \ 251 ohd->base.inputs[ohd->controls_mapping[OHMD_CONTROL]].timestamp = ts; \ 252 } \ 253 break; 254 255#define CASE_VEC2_X(OHMD_CONTROL) \ 256 case OHMD_CONTROL: \ 257 if (ohd->controls_mapping[OHMD_CONTROL] == INPUT_INDICES_LAST) { \ 258 break; \ 259 } \ 260 if (control_state[i] != ohd->last_control_state[i]) { \ 261 ohd->base.inputs[ohd->controls_mapping[OHMD_CONTROL]].value.vec2.x = control_state[i]; \ 262 ohd->base.inputs[ohd->controls_mapping[OHMD_CONTROL]].timestamp = ts; \ 263 } \ 264 break; 265 266#define CASE_VEC2_Y(OHMD_CONTROL) \ 267 case OHMD_CONTROL: \ 268 if (ohd->controls_mapping[OHMD_CONTROL] == INPUT_INDICES_LAST) { \ 269 break; \ 270 } \ 271 if (control_state[i] != ohd->last_control_state[i]) { \ 272 ohd->base.inputs[ohd->controls_mapping[OHMD_CONTROL]].value.vec2.y = control_state[i]; \ 273 ohd->base.inputs[ohd->controls_mapping[OHMD_CONTROL]].timestamp = ts; \ 274 } \ 275 break; 276 277static void 278update_ohmd_controller(struct oh_device *ohd, int control_count, float *control_state) 279{ 280 timepoint_ns ts = os_monotonic_get_ns(); 281 for (int i = 0; i < control_count; i++) { 282 switch (ohd->controls_fn[i]) { 283 // CASE macro does nothing, if ohd->controls_mapping[OHMD_CONTROL] has not been assigned 284 CASE_VEC1_OR_DIGITAL(OHMD_TRIGGER, ohd->make_trigger_digital); 285 CASE_DIGITAL(OHMD_TRIGGER_CLICK, PRESS_FLOAT_THRESHOLD); 286 CASE_VEC1(OHMD_SQUEEZE); 287 CASE_DIGITAL(OHMD_MENU, PRESS_FLOAT_THRESHOLD); 288 CASE_DIGITAL(OHMD_HOME, PRESS_FLOAT_THRESHOLD); 289 CASE_VEC2_X(OHMD_ANALOG_X); 290 CASE_VEC2_Y(OHMD_ANALOG_Y); 291 CASE_DIGITAL(OHMD_ANALOG_PRESS, PRESS_FLOAT_THRESHOLD); 292 CASE_DIGITAL(OHMD_BUTTON_A, PRESS_FLOAT_THRESHOLD); 293 CASE_DIGITAL(OHMD_BUTTON_B, PRESS_FLOAT_THRESHOLD); 294 CASE_DIGITAL(OHMD_BUTTON_X, PRESS_FLOAT_THRESHOLD); 295 CASE_DIGITAL(OHMD_BUTTON_Y, PRESS_FLOAT_THRESHOLD); 296 CASE_DIGITAL(OHMD_VOLUME_PLUS, PRESS_FLOAT_THRESHOLD); 297 CASE_DIGITAL(OHMD_VOLUME_MINUS, PRESS_FLOAT_THRESHOLD); 298 CASE_DIGITAL(OHMD_MIC_MUTE, PRESS_FLOAT_THRESHOLD); 299 } 300 } 301} 302 303static xrt_result_t 304oh_device_update_inputs(struct xrt_device *xdev) 305{ 306 struct oh_device *ohd = oh_device(xdev); 307 308 int control_count; 309 float control_state[256] = {0}; 310 311 ohmd_device_geti(ohd->dev, OHMD_CONTROL_COUNT, &control_count); 312 if (control_count > 64) 313 control_count = 64; 314 315 ohmd_device_getf(ohd->dev, OHMD_CONTROLS_STATE, control_state); 316 317 if (ohd->ohmd_device_type == OPENHMD_OCULUS_RIFT_CONTROLLER || 318 ohd->ohmd_device_type == OPENHMD_GENERIC_CONTROLLER) { 319 update_ohmd_controller(ohd, control_count, control_state); 320 } 321 322 for (int i = 0; i < 256; i++) { 323 ohd->last_control_state[i] = control_state[i]; 324 } 325 326 return XRT_SUCCESS; 327} 328 329static xrt_result_t 330oh_device_set_output(struct xrt_device *xdev, enum xrt_output_name name, const struct xrt_output_value *value) 331{ 332 struct oh_device *ohd = oh_device(xdev); 333 334#if defined(OHMD_HAVE_HAPTICS_API_v0) 335 // Use the unofficial Haptics API from thaytan's fork of OpenHMD: 336 // https://github.com/thaytan/OpenHMD/blob/rift-room-config/include/openhmd.h#L481 337 338 float frequency = value->vibration.frequency; 339 340 // A frequency of 0.0f from OpenXR means to let the driver decide. 341 if (frequency == XRT_FREQUENCY_UNSPECIFIED) { 342 frequency = DEFAULT_HAPTIC_FREQ; 343 } 344 345 int result = ohmd_device_set_haptics_on(ohd->dev, (float)value->vibration.duration_ns / 1e9f, frequency, 346 value->vibration.amplitude); 347 if (result == -1) { 348 return XRT_ERROR_OUTPUT_REQUEST_FAILURE; 349 } 350#else 351 // There is no official OpenHMD Haptic API. 352 (void)ohd; 353#endif 354 return XRT_SUCCESS; 355} 356 357static bool 358check_head_pose(struct oh_device *ohd, enum xrt_input_name name) 359{ 360 return (ohd->ohmd_device_type == OPENHMD_OCULUS_RIFT_HMD || ohd->ohmd_device_type == OPENHMD_GENERIC_HMD) && 361 name == XRT_INPUT_GENERIC_HEAD_POSE; 362} 363 364static bool 365check_controller_pose(struct oh_device *ohd, enum xrt_input_name name) 366{ 367 bool touch_pose = (ohd->ohmd_device_type == OPENHMD_OCULUS_RIFT_CONTROLLER && 368 (name == XRT_INPUT_TOUCH_AIM_POSE || name == XRT_INPUT_TOUCH_GRIP_POSE)); 369 bool generic_pose = ohd->ohmd_device_type == OPENHMD_GENERIC_CONTROLLER && 370 (name == XRT_INPUT_SIMPLE_AIM_POSE || name == XRT_INPUT_SIMPLE_GRIP_POSE); 371 return touch_pose || generic_pose; 372} 373 374static bool 375check_tracker_pose(struct oh_device *ohd, enum xrt_input_name name) 376{ 377 return (ohd->ohmd_device_type == OPENHMD_GENERIC_TRACKER) && name == XRT_INPUT_GENERIC_TRACKER_POSE; 378} 379 380static xrt_result_t 381oh_device_get_tracked_pose(struct xrt_device *xdev, 382 enum xrt_input_name name, 383 int64_t at_timestamp_ns, 384 struct xrt_space_relation *out_relation) 385{ 386 struct oh_device *ohd = oh_device(xdev); 387 struct xrt_quat quat = XRT_QUAT_IDENTITY; 388 struct xrt_vec3 pos = XRT_VEC3_ZERO; 389 390 if (!check_head_pose(ohd, name) && !check_controller_pose(ohd, name) && !check_tracker_pose(ohd, name)) { 391 U_LOG_XDEV_UNSUPPORTED_INPUT(&ohd->base, ohd->log_level, name); 392 return XRT_ERROR_INPUT_UNSUPPORTED; 393 } 394 395 ohmd_ctx_update(ohd->ctx); 396 uint64_t now = os_monotonic_get_ns(); 397 398 //! @todo adjust for latency here 399 ohmd_device_getf(ohd->dev, OHMD_ROTATION_QUAT, &quat.x); 400 ohmd_device_getf(ohd->dev, OHMD_POSITION_VECTOR, &pos.x); 401 out_relation->pose.orientation = quat; 402 out_relation->pose.position = pos; 403 //! @todo assuming that orientation is actually currently tracked 404 out_relation->relation_flags = (enum xrt_space_relation_flags)(XRT_SPACE_RELATION_ORIENTATION_VALID_BIT | 405 XRT_SPACE_RELATION_ORIENTATION_TRACKED_BIT | 406 XRT_SPACE_RELATION_POSITION_VALID_BIT); 407 408 // we assume the position is tracked if and only if it is not zero 409 if (pos.x != 0.0 || pos.y != 0.0 || pos.z != 0.0) { 410 out_relation->relation_flags = (enum xrt_space_relation_flags)(out_relation->relation_flags | 411 XRT_SPACE_RELATION_POSITION_TRACKED_BIT); 412 } 413 414 bool have_ang_vel = false; 415 struct xrt_vec3 ang_vel; 416#ifdef OHMD_HAVE_ANG_VEL 417 if (!ohd->skip_ang_vel) { 418 if (0 == ohmd_device_getf(ohd->dev, OHMD_ANGULAR_VELOCITY, &ang_vel.x)) { 419 have_ang_vel = true; 420 } else { 421 // we now know this device doesn't return angular 422 // velocity. 423 ohd->skip_ang_vel = true; 424 } 425 } 426#endif 427 struct xrt_quat old_quat = ohd->last_relation.pose.orientation; 428 if (0 == memcmp(&quat, &old_quat, sizeof(quat))) { 429 // Looks like the exact same as last time, let's pretend we got 430 // no new report. 431 /*! @todo this is a hack - should really get a timestamp on the 432 * USB data and use that instead. 433 */ 434 *out_relation = ohd->last_relation; 435 OHMD_TRACE(ohd, "GET_TRACKED_POSE (%s) - no new data", ohd->base.str); 436 return XRT_SUCCESS; 437 } 438 439 /*! 440 * @todo possibly hoist this out of the driver level, to provide as a 441 * common service? 442 */ 443 if (ohd->enable_finite_difference && !have_ang_vel) { 444 // No angular velocity 445 float dt = time_ns_to_s(now - ohd->last_update); 446 if (ohd->last_update == 0) { 447 // This is the first report, so just print a warning 448 // instead of estimating ang vel. 449 OHMD_DEBUG(ohd, 450 "Will use finite differencing to estimate " 451 "angular velocity."); 452 } else if (dt < 1.0f && dt > 0.0005) { 453 // but we can compute it: 454 // last report was not long ago but not 455 // instantaneously (at least half a millisecond), 456 // so approximately safe to do this. 457 math_quat_finite_difference(&old_quat, &quat, dt, &ang_vel); 458 have_ang_vel = true; 459 } 460 } 461 462 if (have_ang_vel) { 463 out_relation->angular_velocity = ang_vel; 464 out_relation->relation_flags = (enum xrt_space_relation_flags)( 465 out_relation->relation_flags | XRT_SPACE_RELATION_ANGULAR_VELOCITY_VALID_BIT); 466 467 OHMD_TRACE(ohd, "GET_TRACKED_POSE (%s) (%f, %f, %f, %f) (%f, %f, %f)", ohd->base.str, quat.x, quat.y, 468 quat.z, quat.w, ang_vel.x, ang_vel.y, ang_vel.z); 469 } else { 470 OHMD_TRACE(ohd, "GET_TRACKED_POSE (%s) (%f, %f, %f, %f)", ohd->base.str, quat.x, quat.y, quat.z, 471 quat.w); 472 } 473 474 // Update state within driver 475 ohd->last_update = (int64_t)now; 476 ohd->last_relation = *out_relation; 477 478 return XRT_SUCCESS; 479} 480 481 482struct display_info 483{ 484 float w_meters; 485 float h_meters; 486 int w_pixels; 487 int h_pixels; 488 float nominal_frame_interval_ns; 489}; 490 491struct device_info 492{ 493 /* the display (or virtual display consisting of multiple physical 494 * displays) in its "physical" configuration as the user looks at it. 495 * e.g. a 1440x2560 portrait display that is rotated and built 496 * into a HMD in landscape mode, will be treated as 2560x1440. */ 497 struct display_info display; 498 499 float lens_horizontal_separation; 500 float lens_vertical_position; 501 502 float pano_distortion_k[4]; 503 float pano_aberration_k[3]; 504 float pano_warp_scale; 505 506 struct 507 { 508 float fov; 509 510 /* the display or part of the display covering this view in its 511 * "physical" configuration as the user looks at it. 512 * e.g. a 1440x2560 portrait display that is rotated and built 513 * into a HMD in landscape mode, will be treated as 1280x1440 514 * per view */ 515 struct display_info display; 516 517 float lens_center_x_meters; 518 float lens_center_y_meters; 519 } views[2]; 520 521 struct 522 { 523 bool rotate_lenses_right; 524 bool rotate_lenses_left; 525 bool rotate_lenses_inwards; 526 bool video_see_through; 527 bool video_distortion_none; 528 bool video_distortion_vive; 529 bool left_center_pano_scale; 530 bool rotate_screen_right_after; 531 bool delay_after_initialization; 532 } quirks; 533}; 534 535static struct device_info 536get_info(ohmd_device *dev, const char *prod) 537{ 538 struct device_info info = {0}; 539 540 // clang-format off 541 ohmd_device_getf(dev, OHMD_SCREEN_HORIZONTAL_SIZE, &info.display.w_meters); 542 ohmd_device_getf(dev, OHMD_SCREEN_VERTICAL_SIZE, &info.display.h_meters); 543 ohmd_device_getf(dev, OHMD_LENS_HORIZONTAL_SEPARATION, &info.lens_horizontal_separation); 544 ohmd_device_getf(dev, OHMD_LENS_VERTICAL_POSITION, &info.lens_vertical_position); 545 ohmd_device_getf(dev, OHMD_LEFT_EYE_FOV, &info.views[0].fov); 546 ohmd_device_getf(dev, OHMD_RIGHT_EYE_FOV, &info.views[1].fov); 547 ohmd_device_geti(dev, OHMD_SCREEN_HORIZONTAL_RESOLUTION, &info.display.w_pixels); 548 ohmd_device_geti(dev, OHMD_SCREEN_VERTICAL_RESOLUTION, &info.display.h_pixels); 549 ohmd_device_getf(dev, OHMD_UNIVERSAL_DISTORTION_K, &info.pano_distortion_k[0]); 550 ohmd_device_getf(dev, OHMD_UNIVERSAL_ABERRATION_K, &info.pano_aberration_k[0]); 551 552 // Default to 90FPS 553 info.display.nominal_frame_interval_ns = 554 time_s_to_ns(1.0f / 90.0f); 555 556 // Find any needed quirks. 557 if (strcmp(prod, "3Glasses-D3V2") == 0) { 558 info.quirks.rotate_lenses_right = true; 559 info.quirks.rotate_screen_right_after = true; 560 info.quirks.left_center_pano_scale = true; 561 562 // 70.43 FPS 563 info.display.nominal_frame_interval_ns = 564 time_s_to_ns(1.0f / 70.43f); 565 } 566 567 if (strcmp(prod, "HTC Vive") == 0) { 568 info.quirks.video_distortion_vive = true; 569 info.quirks.video_see_through = true; 570 } 571 572 if (strcmp(prod, "LGR100") == 0) { 573 info.quirks.rotate_lenses_inwards = true; 574 } 575 576 if (strcmp(prod, "External Device") == 0) { 577 info.quirks.video_distortion_none = true; 578 info.display.w_pixels = 1920; 579 info.display.h_pixels = 1080; 580 info.lens_horizontal_separation = 0.0630999878f; 581 info.lens_vertical_position = 0.0394899882f; 582 info.views[0].fov = 103.57f * M_PI / 180.0f; 583 info.views[1].fov = 103.57f * M_PI / 180.0f; 584 } 585 586 if (strcmp(prod, "PSVR") == 0) { 587 info.quirks.video_distortion_none = true; 588 } 589 590 if (strcmp(prod, "Rift (DK2)") == 0) { 591 info.quirks.rotate_lenses_left = true; 592 } 593 594 if (strcmp(prod, "Rift (CV1)") == 0) { 595 info.quirks.delay_after_initialization = true; 596 } 597 598 if (strcmp(prod, "Rift S") == 0) { 599 info.quirks.delay_after_initialization = true; 600 info.quirks.rotate_lenses_right = true; 601 } 602 603 /* Only the WVR2 display is rotated. OpenHMD can't easily tell us 604 * the WVR SKU, so just recognize it by resolution */ 605 if (strcmp(prod, "VR-Tek WVR") == 0 && 606 info.display.w_pixels == 2560 && 607 info.display.h_pixels == 1440) { 608 info.quirks.rotate_lenses_left = true; 609 } 610 611 612 /* 613 * Assumptions made here: 614 * 615 * - There is a single, continuous, flat display serving both eyes, with 616 * no dead space/gap between eyes. 617 * - This single panel is (effectively) perpendicular to the forward 618 * (-Z) direction, with edges aligned with the X and Y axes. 619 * - Lens position is symmetrical about the center ("bridge of nose"). 620 * - Pixels are square and uniform across the entirety of the panel. 621 * 622 * If any of these are not true, then either the rendering will 623 * be inaccurate, or the properties will have to be "fudged" to 624 * make the math work. 625 */ 626 627 info.views[0].display.w_meters = info.display.w_meters / 2.0; 628 info.views[0].display.h_meters = info.display.h_meters; 629 info.views[1].display.w_meters = info.display.w_meters / 2.0; 630 info.views[1].display.h_meters = info.display.h_meters; 631 632 info.views[0].display.w_pixels = info.display.w_pixels / 2; 633 info.views[0].display.h_pixels = info.display.h_pixels; 634 info.views[1].display.w_pixels = info.display.w_pixels / 2; 635 info.views[1].display.h_pixels = info.display.h_pixels; 636 637 /* 638 * Assuming the lenses are centered vertically on the 639 * display. It's not universal, but 0.5 COP on Y is more 640 * common than on X, and it looked like many of the 641 * driver lens_vpos values were copy/pasted or marked 642 * with FIXME. Safer to fix it to 0.5 than risk an 643 * extreme geometry mismatch. 644 */ 645 646 const double cop_y = 0.5; 647 const double h_1 = cop_y * info.display.h_meters; 648 649 //! @todo This are probably all wrong! 650 info.views[0].lens_center_x_meters = info.views[0].display.w_meters - info.lens_horizontal_separation / 2.0; 651 info.views[0].lens_center_y_meters = h_1; 652 653 info.views[1].lens_center_x_meters = info.lens_horizontal_separation / 2.0; 654 info.views[1].lens_center_y_meters = h_1; 655 656 // From OpenHMD: Assume calibration was for lens view to which ever edge 657 // of screen is further away from lens center. 658 info.pano_warp_scale = 659 (info.views[0].lens_center_x_meters > info.views[1].lens_center_x_meters) ? 660 info.views[0].lens_center_x_meters : 661 info.views[1].lens_center_x_meters; 662 // clang-format on 663 664 if (info.quirks.rotate_screen_right_after) { 665 // OpenHMD describes the logical orintation not the physical. 666 // clang-format off 667 ohmd_device_getf(dev, OHMD_SCREEN_HORIZONTAL_SIZE, &info.display.h_meters); 668 ohmd_device_getf(dev, OHMD_SCREEN_VERTICAL_SIZE, &info.display.w_meters); 669 ohmd_device_geti(dev, OHMD_SCREEN_HORIZONTAL_RESOLUTION, &info.display.h_pixels); 670 ohmd_device_geti(dev, OHMD_SCREEN_VERTICAL_RESOLUTION, &info.display.w_pixels); 671 // clang-format on 672 } 673 674 return info; 675} 676 677#define mul m_vec2_mul 678#define mul_scalar m_vec2_mul_scalar 679#define add m_vec2_add 680#define sub m_vec2_sub 681#define div m_vec2_div 682#define div_scalar m_vec2_div_scalar 683#define len m_vec2_len 684 685// slightly different to u_compute_distortion_panotools in u_distortion_mesh 686static void 687u_compute_distortion_openhmd(struct openhmd_values *values, float u, float v, struct xrt_uv_triplet *result) 688{ 689 struct openhmd_values val = *values; 690 691 struct xrt_vec2 r = {u, v}; 692 r = mul(r, val.viewport_scale); 693 r = sub(r, val.lens_center); 694 r = div_scalar(r, val.warp_scale); 695 696 float r_mag = len(r); 697 r_mag = val.hmd_warp_param[3] + // r^1 698 val.hmd_warp_param[2] * r_mag + // r^2 699 val.hmd_warp_param[1] * r_mag * r_mag + // r^3 700 val.hmd_warp_param[0] * r_mag * r_mag * r_mag; // r^4 701 702 struct xrt_vec2 r_dist = mul_scalar(r, r_mag); 703 r_dist = mul_scalar(r_dist, val.warp_scale); 704 705 struct xrt_vec2 r_uv = mul_scalar(r_dist, val.aberr[0]); 706 r_uv = add(r_uv, val.lens_center); 707 r_uv = div(r_uv, val.viewport_scale); 708 709 struct xrt_vec2 g_uv = mul_scalar(r_dist, val.aberr[1]); 710 g_uv = add(g_uv, val.lens_center); 711 g_uv = div(g_uv, val.viewport_scale); 712 713 struct xrt_vec2 b_uv = mul_scalar(r_dist, val.aberr[2]); 714 b_uv = add(b_uv, val.lens_center); 715 b_uv = div(b_uv, val.viewport_scale); 716 717 result->r = r_uv; 718 result->g = g_uv; 719 result->b = b_uv; 720} 721 722static xrt_result_t 723compute_distortion_openhmd(struct xrt_device *xdev, uint32_t view, float u, float v, struct xrt_uv_triplet *result) 724{ 725 struct oh_device *ohd = oh_device(xdev); 726 u_compute_distortion_openhmd(&ohd->distortion.openhmd[view], u, v, result); 727 return XRT_SUCCESS; 728} 729 730static xrt_result_t 731compute_distortion_vive(struct xrt_device *xdev, uint32_t view, float u, float v, struct xrt_uv_triplet *result) 732{ 733 struct oh_device *ohd = oh_device(xdev); 734 u_compute_distortion_vive(&ohd->distortion.vive[view], u, v, result); 735 return XRT_SUCCESS; 736} 737 738static inline void 739swap(int *a, int *b) 740{ 741 int temp = *a; 742 *a = *b; 743 *b = temp; 744} 745 746static struct oh_device * 747create_hmd(ohmd_context *ctx, int device_idx, int device_flags) 748{ 749 const char *prod = ohmd_list_gets(ctx, device_idx, OHMD_PRODUCT); 750 ohmd_device *dev = ohmd_list_open_device(ctx, device_idx); 751 if (dev == NULL) { 752 return NULL; 753 } 754 755 756 const struct device_info info = get_info(dev, prod); 757 758 enum u_device_alloc_flags flags = U_DEVICE_ALLOC_HMD; 759 struct oh_device *ohd = U_DEVICE_ALLOCATE(struct oh_device, flags, 1, 0); 760 u_device_populate_function_pointers(&ohd->base, oh_device_get_tracked_pose, oh_device_destroy); 761 ohd->base.update_inputs = oh_device_update_inputs; 762 ohd->base.get_view_poses = u_device_get_view_poses; 763 ohd->base.inputs[0].name = XRT_INPUT_GENERIC_HEAD_POSE; 764 ohd->base.name = XRT_DEVICE_GENERIC_HMD; 765 ohd->ctx = ctx; 766 ohd->dev = dev; 767 ohd->log_level = debug_get_log_option_ohmd_log(); 768 ohd->enable_finite_difference = debug_get_bool_option_ohmd_finite_diff(); 769 if (strcmp(prod, "Rift (CV1)") == 0 || strcmp(prod, "Rift S") == 0) { 770 ohd->ohmd_device_type = OPENHMD_OCULUS_RIFT_HMD; 771 } else { 772 ohd->ohmd_device_type = OPENHMD_GENERIC_HMD; 773 } 774 775 snprintf(ohd->base.str, XRT_DEVICE_NAME_LEN, "%s (OpenHMD)", prod); 776 snprintf(ohd->base.serial, XRT_DEVICE_NAME_LEN, "%s (OpenHMD)", prod); 777 778 { 779 /* right eye */ 780 if (!math_compute_fovs(info.views[1].display.w_meters, info.views[1].lens_center_x_meters, 781 info.views[1].fov, info.views[1].display.h_meters, 782 info.views[1].lens_center_y_meters, 0, &ohd->base.hmd->distortion.fov[1])) { 783 OHMD_ERROR(ohd, "Failed to compute the partial fields of view."); 784 free(ohd); 785 return NULL; 786 } 787 } 788 { 789 /* left eye - just mirroring right eye now */ 790 ohd->base.hmd->distortion.fov[0].angle_up = ohd->base.hmd->distortion.fov[1].angle_up; 791 ohd->base.hmd->distortion.fov[0].angle_down = ohd->base.hmd->distortion.fov[1].angle_down; 792 793 ohd->base.hmd->distortion.fov[0].angle_left = -ohd->base.hmd->distortion.fov[1].angle_right; 794 ohd->base.hmd->distortion.fov[0].angle_right = -ohd->base.hmd->distortion.fov[1].angle_left; 795 } 796 797 // clang-format off 798 // Main display. 799 ohd->base.hmd->screens[0].w_pixels = info.display.w_pixels; 800 ohd->base.hmd->screens[0].h_pixels = info.display.h_pixels; 801 ohd->base.hmd->screens[0].nominal_frame_interval_ns = info.display.nominal_frame_interval_ns; 802 ohd->base.hmd->screens[0].scanout_direction = XRT_SCANOUT_DIRECTION_NONE; 803 ohd->base.hmd->screens[0].scanout_time_ns = 0; 804 805 // Left 806 ohd->base.hmd->views[0].display.w_pixels = info.views[0].display.w_pixels; 807 ohd->base.hmd->views[0].display.h_pixels = info.views[0].display.h_pixels; 808 ohd->base.hmd->views[0].viewport.x_pixels = 0; 809 ohd->base.hmd->views[0].viewport.y_pixels = 0; 810 ohd->base.hmd->views[0].viewport.w_pixels = info.views[0].display.w_pixels; 811 ohd->base.hmd->views[0].viewport.h_pixels = info.views[0].display.h_pixels; 812 ohd->base.hmd->views[0].rot = u_device_rotation_ident; 813 814 // Right 815 ohd->base.hmd->views[1].display.w_pixels = info.views[1].display.w_pixels; 816 ohd->base.hmd->views[1].display.h_pixels = info.views[1].display.h_pixels; 817 ohd->base.hmd->views[1].viewport.x_pixels = info.views[0].display.w_pixels; 818 ohd->base.hmd->views[1].viewport.y_pixels = 0; 819 ohd->base.hmd->views[1].viewport.w_pixels = info.views[1].display.w_pixels; 820 ohd->base.hmd->views[1].viewport.h_pixels = info.views[1].display.h_pixels; 821 ohd->base.hmd->views[1].rot = u_device_rotation_ident; 822 823 OHMD_DEBUG(ohd, 824 "Display/viewport/offset before rotation %dx%d/%dx%d/%dx%d, " 825 "%dx%d/%dx%d/%dx%d", 826 ohd->base.hmd->views[0].display.w_pixels, 827 ohd->base.hmd->views[0].display.h_pixels, 828 ohd->base.hmd->views[0].viewport.w_pixels, 829 ohd->base.hmd->views[0].viewport.h_pixels, 830 ohd->base.hmd->views[0].viewport.x_pixels, 831 ohd->base.hmd->views[0].viewport.y_pixels, 832 ohd->base.hmd->views[1].display.w_pixels, 833 ohd->base.hmd->views[1].display.h_pixels, 834 ohd->base.hmd->views[1].viewport.w_pixels, 835 ohd->base.hmd->views[1].viewport.h_pixels, 836 ohd->base.hmd->views[0].viewport.x_pixels, 837 ohd->base.hmd->views[0].viewport.y_pixels); 838 839 for (int view = 0; view < 2; view++) { 840 ohd->distortion.openhmd[view].hmd_warp_param[0] = info.pano_distortion_k[0]; 841 ohd->distortion.openhmd[view].hmd_warp_param[1] = info.pano_distortion_k[1]; 842 ohd->distortion.openhmd[view].hmd_warp_param[2] = info.pano_distortion_k[2]; 843 ohd->distortion.openhmd[view].hmd_warp_param[3] = info.pano_distortion_k[3]; 844 ohd->distortion.openhmd[view].aberr[0] = info.pano_aberration_k[0]; 845 ohd->distortion.openhmd[view].aberr[1] = info.pano_aberration_k[1]; 846 ohd->distortion.openhmd[view].aberr[2] = info.pano_aberration_k[2]; 847 ohd->distortion.openhmd[view].warp_scale = info.pano_warp_scale; 848 849 ohd->distortion.openhmd[view].lens_center.x = info.views[view].lens_center_x_meters; 850 ohd->distortion.openhmd[view].lens_center.y = info.views[view].lens_center_y_meters; 851 852 ohd->distortion.openhmd[view].viewport_scale.x = info.views[view].display.w_meters; 853 ohd->distortion.openhmd[view].viewport_scale.y = info.views[view].display.h_meters; 854 } 855 // clang-format on 856 857 ohd->base.hmd->distortion.models |= XRT_DISTORTION_MODEL_COMPUTE; 858 ohd->base.hmd->distortion.preferred = XRT_DISTORTION_MODEL_COMPUTE; 859 ohd->base.compute_distortion = compute_distortion_openhmd; 860 861 // Which blend modes does the device support. 862 863 size_t bm_idx = 0; 864 if (info.quirks.video_see_through) { 865 ohd->base.hmd->blend_modes[bm_idx++] = XRT_BLEND_MODE_ALPHA_BLEND; 866 } 867 ohd->base.hmd->blend_modes[bm_idx++] = XRT_BLEND_MODE_OPAQUE; 868 ohd->base.hmd->blend_mode_count = bm_idx; 869 870 if (info.quirks.video_distortion_vive) { 871 // clang-format off 872 // These need to be acquired from the vive config 873 for (int view = 0; view < 2; view++) { 874 ohd->distortion.vive[view].aspect_x_over_y = 0.8999999761581421f; 875 ohd->distortion.vive[view].grow_for_undistort = 0.6000000238418579f; 876 } 877 ohd->distortion.vive[0].undistort_r2_cutoff = 1.11622154712677f; 878 ohd->distortion.vive[1].undistort_r2_cutoff = 1.101870775222778f; 879 ohd->distortion.vive[0].center[0].x = 0.08946027017045266f; 880 ohd->distortion.vive[0].center[0].y = -0.009002181016260827f; 881 ohd->distortion.vive[0].center[1].x = 0.08946027017045266f; 882 ohd->distortion.vive[0].center[1].y = -0.009002181016260827f; 883 ohd->distortion.vive[0].center[2].x = 0.08946027017045266f; 884 ohd->distortion.vive[0].center[2].y = -0.009002181016260827f; 885 ohd->distortion.vive[1].center[0].x = -0.08933516629552526f; 886 ohd->distortion.vive[1].center[0].y = -0.006014565287238661f; 887 ohd->distortion.vive[1].center[1].x = -0.08933516629552526f; 888 ohd->distortion.vive[1].center[1].y = -0.006014565287238661f; 889 ohd->distortion.vive[1].center[2].x = -0.08933516629552526f; 890 ohd->distortion.vive[1].center[2].y = -0.006014565287238661f; 891 892 //! @todo These values are most likely wrong, needs to be transposed and correct channel. 893 // left 894 // green 895 ohd->distortion.vive[0].coefficients[0][0] = -0.188236068524731f; 896 ohd->distortion.vive[0].coefficients[0][1] = -0.221086205321053f; 897 ohd->distortion.vive[0].coefficients[0][2] = -0.2537849057915209f; 898 ohd->distortion.vive[0].coefficients[0][3] = 0.0f; 899 900 // blue 901 ohd->distortion.vive[0].coefficients[1][0] = -0.07316590815739493f; 902 ohd->distortion.vive[0].coefficients[1][1] = -0.02332400789561968f; 903 ohd->distortion.vive[0].coefficients[1][2] = 0.02469959434698275f; 904 ohd->distortion.vive[0].coefficients[1][3] = 0.0f; 905 906 // red 907 ohd->distortion.vive[0].coefficients[2][0] = -0.02223805567703767f; 908 ohd->distortion.vive[0].coefficients[2][1] = -0.04931309279533211f; 909 ohd->distortion.vive[0].coefficients[2][2] = -0.07862881939243466f; 910 ohd->distortion.vive[0].coefficients[2][3] = 0.0f; 911 912 // right 913 // green 914 ohd->distortion.vive[1].coefficients[0][0] = -0.1906209981894497f; 915 ohd->distortion.vive[1].coefficients[0][1] = -0.2248896677207884f; 916 ohd->distortion.vive[1].coefficients[0][2] = -0.2721364516782803f; 917 ohd->distortion.vive[1].coefficients[0][3] = 0.0f; 918 919 // blue 920 ohd->distortion.vive[1].coefficients[1][0] = -0.07346071902951497f; 921 ohd->distortion.vive[1].coefficients[1][1] = -0.02189527566250131f; 922 ohd->distortion.vive[1].coefficients[1][2] = 0.0581378652359256f; 923 ohd->distortion.vive[1].coefficients[1][3] = 0.0f; 924 925 // red 926 ohd->distortion.vive[1].coefficients[2][0] = -0.01755850332081247f; 927 ohd->distortion.vive[1].coefficients[2][1] = -0.04517245633373419f; 928 ohd->distortion.vive[1].coefficients[2][2] = -0.0928909347763f; 929 ohd->distortion.vive[1].coefficients[2][3] = 0.0f; 930 // clang-format on 931 932 ohd->base.compute_distortion = compute_distortion_vive; 933 } 934 935 if (info.quirks.video_distortion_none) { 936 u_distortion_mesh_set_none(&ohd->base); 937 } 938 939 if (info.quirks.left_center_pano_scale) { 940 for (int view = 0; view < 2; view++) { 941 ohd->distortion.openhmd[view].warp_scale = info.views[0].lens_center_x_meters; 942 } 943 } 944 945 if (info.quirks.rotate_lenses_right) { 946 OHMD_DEBUG(ohd, "Displays rotated right"); 947 948 // openhmd display dimensions are *after* all rotations 949 swap(&ohd->base.hmd->screens->w_pixels, &ohd->base.hmd->screens->h_pixels); 950 951 // display dimensions are *after* all rotations 952 int w0 = info.views[0].display.w_pixels; 953 int w1 = info.views[1].display.w_pixels; 954 int h0 = info.views[0].display.h_pixels; 955 int h1 = info.views[1].display.h_pixels; 956 957 // viewports is *before* rotations, as the OS sees the display 958 ohd->base.hmd->views[0].viewport.x_pixels = 0; 959 ohd->base.hmd->views[0].viewport.y_pixels = 0; 960 ohd->base.hmd->views[0].viewport.w_pixels = h0; 961 ohd->base.hmd->views[0].viewport.h_pixels = w0; 962 ohd->base.hmd->views[0].rot = u_device_rotation_right; 963 964 ohd->base.hmd->views[1].viewport.x_pixels = 0; 965 ohd->base.hmd->views[1].viewport.y_pixels = w0; 966 ohd->base.hmd->views[1].viewport.w_pixels = h1; 967 ohd->base.hmd->views[1].viewport.h_pixels = w1; 968 ohd->base.hmd->views[1].rot = u_device_rotation_right; 969 } 970 971 if (info.quirks.rotate_lenses_left) { 972 OHMD_DEBUG(ohd, "Displays rotated left"); 973 974 // openhmd display dimensions are *after* all rotations 975 swap(&ohd->base.hmd->screens->w_pixels, &ohd->base.hmd->screens->h_pixels); 976 977 // display dimensions are *after* all rotations 978 int w0 = info.views[0].display.w_pixels; 979 int w1 = info.views[1].display.w_pixels; 980 int h0 = info.views[0].display.h_pixels; 981 int h1 = info.views[1].display.h_pixels; 982 983 // viewports is *before* rotations, as the OS sees the display 984 ohd->base.hmd->views[0].viewport.x_pixels = 0; 985 ohd->base.hmd->views[0].viewport.y_pixels = w0; 986 ohd->base.hmd->views[0].viewport.w_pixels = h1; 987 ohd->base.hmd->views[0].viewport.h_pixels = w1; 988 ohd->base.hmd->views[0].rot = u_device_rotation_left; 989 990 ohd->base.hmd->views[1].viewport.x_pixels = 0; 991 ohd->base.hmd->views[1].viewport.y_pixels = 0; 992 ohd->base.hmd->views[1].viewport.w_pixels = h0; 993 ohd->base.hmd->views[1].viewport.h_pixels = w0; 994 ohd->base.hmd->views[1].rot = u_device_rotation_left; 995 } 996 997 if (info.quirks.rotate_lenses_inwards) { 998 OHMD_DEBUG(ohd, "Displays rotated inwards"); 999 1000 int w2 = info.display.w_pixels / 2; 1001 int h = info.display.h_pixels; 1002 1003 ohd->base.hmd->views[0].display.w_pixels = h; 1004 ohd->base.hmd->views[0].display.h_pixels = w2; 1005 ohd->base.hmd->views[0].viewport.x_pixels = 0; 1006 ohd->base.hmd->views[0].viewport.y_pixels = 0; 1007 ohd->base.hmd->views[0].viewport.w_pixels = w2; 1008 ohd->base.hmd->views[0].viewport.h_pixels = h; 1009 ohd->base.hmd->views[0].rot = u_device_rotation_right; 1010 1011 ohd->base.hmd->views[1].display.w_pixels = h; 1012 ohd->base.hmd->views[1].display.h_pixels = w2; 1013 ohd->base.hmd->views[1].viewport.x_pixels = w2; 1014 ohd->base.hmd->views[1].viewport.y_pixels = 0; 1015 ohd->base.hmd->views[1].viewport.w_pixels = w2; 1016 ohd->base.hmd->views[1].viewport.h_pixels = h; 1017 ohd->base.hmd->views[1].rot = u_device_rotation_left; 1018 } 1019 1020 OHMD_DEBUG(ohd, 1021 "Display/viewport/offset after rotation %dx%d/%dx%d/%dx%d, " 1022 "%dx%d/%dx%d/%dx%d", 1023 ohd->base.hmd->views[0].display.w_pixels, ohd->base.hmd->views[0].display.h_pixels, 1024 ohd->base.hmd->views[0].viewport.w_pixels, ohd->base.hmd->views[0].viewport.h_pixels, 1025 ohd->base.hmd->views[0].viewport.x_pixels, ohd->base.hmd->views[0].viewport.y_pixels, 1026 ohd->base.hmd->views[1].display.w_pixels, ohd->base.hmd->views[1].display.h_pixels, 1027 ohd->base.hmd->views[1].viewport.w_pixels, ohd->base.hmd->views[1].viewport.h_pixels, 1028 ohd->base.hmd->views[0].viewport.x_pixels, ohd->base.hmd->views[0].viewport.y_pixels); 1029 1030 1031 if (info.quirks.delay_after_initialization) { 1032 os_nanosleep(time_s_to_ns(1.0)); 1033 } 1034 1035 if (ohd->log_level <= U_LOGGING_DEBUG) { 1036 u_device_dump_config(&ohd->base, __func__, prod); 1037 } 1038 1039 ohd->base.supported.orientation_tracking = (device_flags & OHMD_DEVICE_FLAGS_ROTATIONAL_TRACKING) != 0; 1040 ohd->base.supported.position_tracking = (device_flags & OHMD_DEVICE_FLAGS_POSITIONAL_TRACKING) != 0; 1041 ohd->base.device_type = XRT_DEVICE_TYPE_HMD; 1042 1043 1044 if (ohd->log_level <= U_LOGGING_DEBUG) { 1045 u_device_dump_config(&ohd->base, __func__, prod); 1046 } 1047 1048 return ohd; 1049} 1050 1051static struct oh_device * 1052create_controller(ohmd_context *ctx, int device_idx, int device_flags, enum xrt_device_type device_type) 1053{ 1054 const char *prod = ohmd_list_gets(ctx, device_idx, OHMD_PRODUCT); 1055 ohmd_device *dev = ohmd_list_open_device(ctx, device_idx); 1056 if (dev == NULL) { 1057 return 0; 1058 } 1059 1060 bool oculus_touch = false; 1061 1062 // khronos simple controller has 4 inputs 1063 int input_count = 4; 1064 int output_count = 0; 1065 1066 if (strcmp(prod, "Rift (CV1): Right Controller") == 0 || strcmp(prod, "Rift (CV1): Left Controller") == 0 || 1067 strcmp(prod, "Rift S: Right Controller") == 0 || strcmp(prod, "Rift S: Left Controller") == 0) { 1068 oculus_touch = true; 1069 1070 input_count = INPUT_INDICES_LAST; 1071 output_count = 1; 1072 } 1073 1074 enum u_device_alloc_flags flags = 0; 1075 struct oh_device *ohd = U_DEVICE_ALLOCATE(struct oh_device, flags, input_count, output_count); 1076 u_device_populate_function_pointers(&ohd->base, oh_device_get_tracked_pose, oh_device_destroy); 1077 ohd->base.update_inputs = oh_device_update_inputs; 1078 ohd->base.set_output = oh_device_set_output; 1079 if (oculus_touch) { 1080 ohd->ohmd_device_type = OPENHMD_OCULUS_RIFT_CONTROLLER; 1081 ohd->base.name = XRT_DEVICE_TOUCH_CONTROLLER; 1082 } else { 1083 if (device_type == XRT_DEVICE_TYPE_GENERIC_TRACKER) { 1084 ohd->ohmd_device_type = OPENHMD_GENERIC_TRACKER; 1085 } else { 1086 ohd->ohmd_device_type = OPENHMD_GENERIC_CONTROLLER; 1087 } 1088 ohd->base.name = XRT_DEVICE_SIMPLE_CONTROLLER; //! @todo Generic tracker input profile? 1089 } 1090 ohd->ctx = ctx; 1091 ohd->dev = dev; 1092 ohd->log_level = debug_get_log_option_ohmd_log(); 1093 ohd->enable_finite_difference = debug_get_bool_option_ohmd_finite_diff(); 1094 1095 for (int i = 0; i < CONTROL_MAPPING_SIZE; i++) { 1096 ohd->controls_mapping[i] = INPUT_INDICES_LAST; 1097 } 1098 1099 if (device_type == XRT_DEVICE_TYPE_LEFT_HAND_CONTROLLER || 1100 device_type == XRT_DEVICE_TYPE_RIGHT_HAND_CONTROLLER || 1101 device_type == XRT_DEVICE_TYPE_ANY_HAND_CONTROLLER) { 1102 if (oculus_touch) { 1103 SET_TOUCH_INPUT(X_CLICK); 1104 SET_TOUCH_INPUT(X_TOUCH); 1105 SET_TOUCH_INPUT(Y_CLICK); 1106 SET_TOUCH_INPUT(Y_TOUCH); 1107 SET_TOUCH_INPUT(MENU_CLICK); 1108 SET_TOUCH_INPUT(A_CLICK); 1109 SET_TOUCH_INPUT(A_TOUCH); 1110 SET_TOUCH_INPUT(B_CLICK); 1111 SET_TOUCH_INPUT(B_TOUCH); 1112 SET_TOUCH_INPUT(SYSTEM_CLICK); 1113 SET_TOUCH_INPUT(SQUEEZE_VALUE); 1114 SET_TOUCH_INPUT(TRIGGER_TOUCH); 1115 SET_TOUCH_INPUT(TRIGGER_VALUE); 1116 SET_TOUCH_INPUT(THUMBSTICK_CLICK); 1117 SET_TOUCH_INPUT(THUMBSTICK_TOUCH); 1118 SET_TOUCH_INPUT(THUMBSTICK); 1119 SET_TOUCH_INPUT(THUMBREST_TOUCH); 1120 ohd->base.inputs[GRIP_POSE].name = XRT_INPUT_TOUCH_GRIP_POSE; 1121 ohd->base.inputs[AIM_POSE].name = XRT_INPUT_TOUCH_AIM_POSE; 1122 1123 1124 ohd->make_trigger_digital = false; 1125 1126 ohd->base.outputs[0].name = XRT_OUTPUT_NAME_TOUCH_HAPTIC; 1127 1128 ohd->controls_mapping[OHMD_TRIGGER] = OCULUS_TOUCH_TRIGGER_VALUE; 1129 ohd->controls_mapping[OHMD_SQUEEZE] = OCULUS_TOUCH_SQUEEZE_VALUE; 1130 ohd->controls_mapping[OHMD_MENU] = OCULUS_TOUCH_MENU_CLICK; 1131 ohd->controls_mapping[OHMD_HOME] = OCULUS_TOUCH_SYSTEM_CLICK; 1132 ohd->controls_mapping[OHMD_ANALOG_X] = OCULUS_TOUCH_THUMBSTICK; 1133 ohd->controls_mapping[OHMD_ANALOG_Y] = OCULUS_TOUCH_THUMBSTICK; 1134 ohd->controls_mapping[OHMD_ANALOG_PRESS] = OCULUS_TOUCH_THUMBSTICK_CLICK; 1135 ohd->controls_mapping[OHMD_BUTTON_A] = OCULUS_TOUCH_A_CLICK; 1136 ohd->controls_mapping[OHMD_BUTTON_B] = OCULUS_TOUCH_B_CLICK; 1137 ohd->controls_mapping[OHMD_BUTTON_X] = OCULUS_TOUCH_X_CLICK; 1138 ohd->controls_mapping[OHMD_BUTTON_Y] = OCULUS_TOUCH_Y_CLICK; 1139 } else { 1140 ohd->base.inputs[SIMPLE_SELECT_CLICK].name = XRT_INPUT_SIMPLE_SELECT_CLICK; 1141 ohd->base.inputs[SIMPLE_MENU_CLICK].name = XRT_INPUT_SIMPLE_MENU_CLICK; 1142 ohd->base.inputs[GRIP_POSE].name = XRT_INPUT_SIMPLE_GRIP_POSE; 1143 ohd->base.inputs[AIM_POSE].name = XRT_INPUT_SIMPLE_AIM_POSE; 1144 1145 // XRT_INPUT_SIMPLE_SELECT_CLICK is digital input. 1146 // in case the hardware is an analog trigger, change the input after a half pulled trigger. 1147 ohd->make_trigger_digital = true; 1148 1149 if (output_count > 0) { 1150 ohd->base.outputs[0].name = XRT_OUTPUT_NAME_SIMPLE_VIBRATION; 1151 } 1152 1153 ohd->controls_mapping[OHMD_TRIGGER] = SIMPLE_SELECT_CLICK; 1154 ohd->controls_mapping[OHMD_MENU] = SIMPLE_MENU_CLICK; 1155 } 1156 } else if (device_type == XRT_DEVICE_TYPE_GENERIC_TRACKER) { 1157 ohd->base.inputs[GRIP_POSE].name = XRT_INPUT_GENERIC_TRACKER_POSE; 1158 ohd->base.inputs[AIM_POSE].name = XRT_INPUT_GENERIC_TRACKER_POSE; 1159 } 1160 1161 snprintf(ohd->base.str, XRT_DEVICE_NAME_LEN, "%s (OpenHMD)", prod); 1162 snprintf(ohd->base.serial, XRT_DEVICE_NAME_LEN, "%s (OpenHMD)", prod); 1163 1164 ohd->base.supported.orientation_tracking = (device_flags & OHMD_DEVICE_FLAGS_ROTATIONAL_TRACKING) != 0; 1165 ohd->base.supported.position_tracking = (device_flags & OHMD_DEVICE_FLAGS_POSITIONAL_TRACKING) != 0; 1166 ohd->base.device_type = device_type; 1167 1168 ohmd_device_geti(ohd->dev, OHMD_CONTROLS_HINTS, ohd->controls_fn); 1169 ohmd_device_geti(ohd->dev, OHMD_CONTROLS_TYPES, ohd->controls_types); 1170 1171 OHMD_DEBUG(ohd, "Created %s controller", 1172 device_type == XRT_DEVICE_TYPE_LEFT_HAND_CONTROLLER ? "left" : "right"); 1173 1174 return ohd; 1175} 1176 1177int 1178oh_device_create(ohmd_context *ctx, bool no_hmds, struct xrt_device **out_xdevs) 1179{ 1180 int hmd_idx = -1; 1181 int hmd_flags = 0; 1182 int left_idx = -1; 1183 int left_flags = 0; 1184 int right_idx = -1; 1185 int right_flags = 0; 1186 1187 int trackers[XRT_MAX_DEVICES_PER_PROBE]; 1188 ohmd_device_flags tracker_flags[XRT_MAX_DEVICES_PER_PROBE]; 1189 uint32_t tracker_count = 0; 1190 1191 1192 /* Probe for devices */ 1193 int device_count = ohmd_ctx_probe(ctx); 1194 1195 // clamp device_count to XRT_MAX_DEVICES_PER_PROBE 1196 if (device_count > XRT_MAX_DEVICES_PER_PROBE) { 1197 U_LOG_W("Too many devices from OpenHMD, ignoring %d devices!", 1198 device_count - XRT_MAX_DEVICES_PER_PROBE); 1199 device_count = XRT_MAX_DEVICES_PER_PROBE; 1200 } 1201 1202 /* Find first HMD, first left controller, first right controller, and all trackers */ 1203 for (int i = 0; i < device_count; i++) { 1204 int device_class = 0, device_flags = 0; 1205 1206 ohmd_list_geti(ctx, i, OHMD_DEVICE_CLASS, &device_class); 1207 ohmd_list_geti(ctx, i, OHMD_DEVICE_FLAGS, &device_flags); 1208 1209 if (device_flags & OHMD_DEVICE_FLAGS_NULL_DEVICE) { 1210 U_LOG_D("Rejecting device idx %i, is a NULL device.", i); 1211 continue; 1212 } 1213 1214 const char *prod = ohmd_list_gets(ctx, i, OHMD_PRODUCT); 1215 if (strcmp(prod, "External Device") == 0 && !debug_get_bool_option_ohmd_external()) { 1216 U_LOG_D("Rejecting device idx %i, is a External device.", i); 1217 continue; 1218 } else if (strcmp(prod, "HoloLens Sensors") == 0) { 1219 U_LOG_W("Ignoring OpenHMD WMR device idx %i. Use the native Monado WMR driver.", i); 1220 continue; 1221 } else if (strncmp(prod, "Rift S", strlen("Rift S")) == 0) { 1222 U_LOG_W("Ignoring OpenHMD Rift S device idx %i. Use the native Monado Rift S driver.", i); 1223 continue; 1224 } /* 1225 else if (strncmp(prod, "Rift (DK2)", strlen("Rift (DK2)")) == 0) { 1226 U_LOG_W("Ignoring OpenHMD Rift DK2 device idx %i. Use the native Monado Rift DK2 driver.", i); 1227 continue; 1228 } */ // This block of code is disabled until 6dof support is upstreamed. 1229 1230 if (device_class == OHMD_DEVICE_CLASS_CONTROLLER) { 1231 if ((device_flags & OHMD_DEVICE_FLAGS_LEFT_CONTROLLER) != 0) { 1232 if (left_idx != -1) { 1233 continue; 1234 } 1235 U_LOG_D("Selecting left controller idx %i", i); 1236 left_idx = i; 1237 left_flags = device_flags; 1238 } 1239 if ((device_flags & OHMD_DEVICE_FLAGS_RIGHT_CONTROLLER) != 0) { 1240 if (right_idx != -1) { 1241 continue; 1242 } 1243 U_LOG_D("Selecting right controller idx %i", i); 1244 right_idx = i; 1245 right_flags = device_flags; 1246 } 1247 1248 continue; 1249 } else if (device_class == OHMD_DEVICE_CLASS_HMD) { 1250 if (hmd_idx != -1) { 1251 continue; 1252 } 1253 U_LOG_D("Selecting hmd idx %i", i); 1254 hmd_idx = i; 1255 hmd_flags = device_flags; 1256 } else if (device_class == OHMD_DEVICE_CLASS_GENERIC_TRACKER) { 1257 uint32_t tracker_index = tracker_count++; 1258 trackers[tracker_index] = i; 1259 tracker_flags[tracker_index] = device_flags; 1260 } 1261 } 1262 1263 1264 // All OpenHMD devices share the same tracking origin. 1265 // Default everything to 3dof (NONE), but 6dof when the HMD supports position tracking. 1266 //! @todo: support mix of 3dof and 6dof OpenHMD devices 1267 struct oh_system *sys = U_TYPED_CALLOC(struct oh_system); 1268 sys->base.type = XRT_TRACKING_TYPE_NONE; 1269 sys->base.initial_offset.orientation.w = 1.0f; 1270 1271 u_var_add_root(sys, "OpenHMD Wrapper", false); 1272 1273 int created = 0; 1274 if (!no_hmds && hmd_idx != -1) { 1275 struct oh_device *hmd = create_hmd(ctx, hmd_idx, hmd_flags); 1276 if (hmd) { 1277 hmd->sys = sys; 1278 hmd->base.tracking_origin = &sys->base; 1279 1280 sys->devices[OHMD_HMD_INDEX] = hmd; 1281 1282 if (hmd->base.supported.position_tracking) { 1283 sys->base.type = XRT_TRACKING_TYPE_OTHER; 1284 } 1285 1286 out_xdevs[created++] = &hmd->base; 1287 } 1288 } 1289 1290 if (left_idx != -1) { 1291 struct oh_device *left = 1292 create_controller(ctx, left_idx, left_flags, XRT_DEVICE_TYPE_LEFT_HAND_CONTROLLER); 1293 if (left) { 1294 left->sys = sys; 1295 left->base.tracking_origin = &sys->base; 1296 1297 sys->devices[OHMD_LEFT_INDEX] = left; 1298 1299 out_xdevs[created++] = &left->base; 1300 } 1301 } 1302 1303 if (right_idx != -1) { 1304 struct oh_device *right = 1305 create_controller(ctx, right_idx, right_flags, XRT_DEVICE_TYPE_RIGHT_HAND_CONTROLLER); 1306 if (right) { 1307 right->sys = sys; 1308 right->base.tracking_origin = &sys->base; 1309 1310 sys->devices[OHMD_RIGHT_INDEX] = right; 1311 1312 out_xdevs[created++] = &right->base; 1313 } 1314 } 1315 1316 for (uint32_t i = 0; i < tracker_count; i++) { 1317 struct oh_device *tracker = 1318 create_controller(ctx, trackers[i], tracker_flags[i], XRT_DEVICE_TYPE_GENERIC_TRACKER); 1319 if (tracker) { 1320 tracker->sys = sys; 1321 tracker->base.tracking_origin = &sys->base; 1322 1323 sys->devices[OHMD_FIRST_TRACKER_INDEX + i] = tracker; 1324 1325 out_xdevs[created++] = &tracker->base; 1326 } 1327 } 1328 1329 for (int i = 0; i < XRT_MAX_DEVICES_PER_PROBE; i++) { 1330 if (sys->devices[i] != NULL) { 1331 u_var_add_ro_text(sys, sys->devices[i]->base.str, "OpenHMD Device"); 1332 } 1333 } 1334 1335 if (created == 0) { 1336 u_var_remove_root(sys); 1337 free(sys); 1338 } 1339 1340 return created; 1341}