~sircmpwn/xrgears

ref: 1ac9460a3fe49c9a47bc6d8262e124fd3d369758 xrgears/vitamin-k/input/vikHMD.hpp -rw-r--r-- 5.3 KiB
1ac9460aDrew DeVault Add wayland-direct window mode 2 years ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
/*
 * vitamin-k
 *
 * Copyright 2017-2018 Collabora Ltd.
 *
 * Authors: Lubosz Sarnecki <lubosz.sarnecki@collabora.com>
 * SPDX-License-Identifier: MIT
 */

#pragma once

#define GLM_FORCE_RADIANS
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
#define GLM_ENABLE_EXPERIMENTAL

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <gli/gli.hpp>
#include <glm/gtc/type_ptr.hpp>

#include <openhmd.h>

#include "../scene/vikCamera.hpp"
#include "../render/vikBuffer.hpp"
#include "../system/vikLog.hpp"
#include "../system/vikSettings.hpp"

namespace vik {
class HMD {
 private:
  ohmd_context* context = nullptr;

 public:
  ohmd_device* device = nullptr;

  explicit HMD(Settings *s) {
    context = ohmd_ctx_create();
    int num_devices = ohmd_ctx_probe(context);

    if (num_devices < 0)
      vik_log_f("Failed to probe HMD: %s", ohmd_ctx_get_error(context));

    ohmd_device_settings* settings = ohmd_device_settings_create(context);

    // If OHMD_IDS_AUTOMATIC_UPDATE is set to 0, ohmd_ctx_update()
    // must be called at least 10 times per second.
    // It is enabled by default.
    int update = 1;
    ohmd_device_settings_seti(settings, OHMD_IDS_AUTOMATIC_UPDATE, &update);

    // select first hmd by default
    if (s->hmd == -1)
      s->hmd = 0;

    device = ohmd_list_open_device_s(context, s->hmd, settings);

    if (!device)
      vik_log_f("Failed to open device: %s", ohmd_ctx_get_error(context));

    vik_log_i("Using HMD %d: %s: %s (%s)", s->hmd,
              ohmd_list_gets(context, s->hmd, OHMD_VENDOR),
              ohmd_list_gets(context, s->hmd, OHMD_PRODUCT),
              ohmd_list_gets(context, s->hmd, OHMD_PATH));

    ohmd_device_settings_destroy(settings);
  }

  ~HMD() {
    ohmd_ctx_destroy(context);
  }

  static void enumerate_hmds() {
    ohmd_context *c = ohmd_ctx_create();
    int num_devices = ohmd_ctx_probe(c);
    if (num_devices < 0)
      vik_log_f("Failed to probe HMD: %s", ohmd_ctx_get_error(c));

    vik_log_i("Found %d HMDs.", num_devices);
    for (int i = 0; i < num_devices; i++) {
      vik_log_i_short("%d: %s: %s (%s)", i,
                ohmd_list_gets(c, i, OHMD_VENDOR),
                ohmd_list_gets(c, i, OHMD_PRODUCT),
                ohmd_list_gets(c, i, OHMD_PATH));

      ohmd_device *d = ohmd_list_open_device(c, i);
      print_info(d);
    }
    ohmd_ctx_destroy(c);
  }

  static void print_info(ohmd_device* d) {
    int hmd_w, hmd_h;
    ohmd_device_geti(d, OHMD_SCREEN_HORIZONTAL_RESOLUTION, &hmd_w);
    ohmd_device_geti(d, OHMD_SCREEN_VERTICAL_RESOLUTION, &hmd_h);

    float ipd;
    ohmd_device_getf(d, OHMD_EYE_IPD, &ipd);

    float viewport_scale[2];
    ohmd_device_getf(d, OHMD_SCREEN_HORIZONTAL_SIZE, &(viewport_scale[0]));
    // viewport is half the screen
    viewport_scale[0] /= 2.0f;

    ohmd_device_getf(d, OHMD_SCREEN_VERTICAL_SIZE, &(viewport_scale[1]));

    float distortion_coeffs[4];
    ohmd_device_getf(d, OHMD_UNIVERSAL_DISTORTION_K, &(distortion_coeffs[0]));

    float aberr_scale[3];
    ohmd_device_getf(d, OHMD_UNIVERSAL_ABERRATION_K, &(aberr_scale[0]));

    float sep;
    ohmd_device_getf(d, OHMD_LENS_HORIZONTAL_SEPARATION, &sep);

    float left_lens_center[2];
    ohmd_device_getf(d, OHMD_LENS_VERTICAL_POSITION, &(left_lens_center[1]));

    float right_lens_center[2];
    ohmd_device_getf(d, OHMD_LENS_VERTICAL_POSITION, &(right_lens_center[1]));

    // calculate lens centers
    // assuming the eye separation is the distance between the lense centers
    left_lens_center[0] = viewport_scale[0] - sep/2.0f;
    right_lens_center[0] = sep/2.0f;

    // asume calibration was for lens view to which ever
    // edge of screen is further away from lens center
    float warp_scale = (left_lens_center[0] > right_lens_center[0]) ?
          left_lens_center[0] : right_lens_center[0];

    vik_log_i_short("\tResolution           %dx%d",
        hmd_w,
        hmd_h);

    vik_log_i_short("\tWarp parameters      %.4f %.4f %.4f %.4f",
        distortion_coeffs[0],
        distortion_coeffs[1],
        distortion_coeffs[2],
        distortion_coeffs[3]);
    vik_log_i_short("\tWarp scale           %.4f", warp_scale);
    vik_log_i_short("\tChromatic aberration %.4f %.4f %.4f %.4f",
        aberr_scale[0],
        aberr_scale[1],
        aberr_scale[2]);
    vik_log_i_short("\tLens center left     %.4f %.4f",
        left_lens_center[0],
        left_lens_center[1]);
    vik_log_i_short("\tLens center right    %.4f %.4f",
        right_lens_center[0],
        right_lens_center[1]);
    vik_log_i_short("\tLens separation      %.4f", sep);
    vik_log_i_short("\tIPD                  %.4f", ipd);
    vik_log_i_short("\tViewport scale       %.4f %.4f",
        viewport_scale[0],
        viewport_scale[1]);
  }

  void get_transformation(glm::mat4 *projection_left,
                          glm::mat4 *projection_right,
                          glm::mat4 *view_left, glm::mat4 *view_right) {
    ohmd_ctx_update(context);

    float mat[16];
    ohmd_device_getf(device, OHMD_LEFT_EYE_GL_PROJECTION_MATRIX, mat);
    *projection_left = glm::make_mat4(mat);

    ohmd_device_getf(device, OHMD_RIGHT_EYE_GL_PROJECTION_MATRIX, mat);
    *projection_right = glm::make_mat4(mat);

    ohmd_device_getf(device, OHMD_LEFT_EYE_GL_MODELVIEW_MATRIX, mat);
    *view_left = glm::make_mat4(mat);

    ohmd_device_getf(device, OHMD_RIGHT_EYE_GL_MODELVIEW_MATRIX, mat);
    *view_right = glm::make_mat4(mat);
  }
};
}  // namespace vik