The open source OpenXR runtime
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}