OVR: Controller support with keyboard emulation
authorAndreas Eversberg <jolly@eversberg.eu>
Sat, 21 Apr 2018 09:24:52 +0000 (11:24 +0200)
committerAndreas Eversberg <jolly@eversberg.eu>
Sun, 22 Apr 2018 10:04:09 +0000 (12:04 +0200)
src/libovr/Makefile.am
src/libovr/keyboard.c [new file with mode: 0644]
src/libovr/keyboard.h [new file with mode: 0644]
src/libovr/ovr.c
src/libovr/ovr.h
src/libsdl/opengl.c
src/mercenary/main.c

index e1c7c50..0a60605 100755 (executable)
@@ -5,5 +5,6 @@ noinst_LIBRARIES = libovr.a
 
 libovr_a_SOURCES = \
        helper.cpp \
-       ovr.c
+       ovr.c \
+       keyboard.c
 
diff --git a/src/libovr/keyboard.c b/src/libovr/keyboard.c
new file mode 100644 (file)
index 0000000..7784e55
--- /dev/null
@@ -0,0 +1,574 @@
+/* VR Keyboard
+ *
+ * (C) 2018 by Andreas Eversberg <jolly@eversberg.eu>
+ * All Rights Reserved
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+#include <math.h>
+#include "../../include/keycodes.h"
+#include "../libsdl/print.h"
+#include "keyboard.h"
+#define GL3_PROTOTYPES 1
+#include <GL/glew.h>
+
+#define MAX_KEYBOARDS  2       /* maximum number of keyboards to we have */
+#define MAX_KEYS       64      /* maximum number of keys per keyboard */
+#define MAX_KEY_LINES  16      /* maximum number of lines on each key */
+#define TILT_KEYBOARD          /* keyboard is tilted up from horzontal x-z-plane to vertical x-y-plane */
+
+/*
+ * The keys are defined by coordinates that are described by the following
+ * digits:
+ *
+ * a  b  c  d  e
+ *
+ * f  g  h  i  j
+ *
+ * k  l  m  n  o
+ *
+ * p  q  r  s  t
+ *
+ * u  v  w  x  y
+ *
+ * The lines are defined from digit to digit to digit.... To start with a
+ * new line (not connected to the previous one, add ' ' (space).
+ *
+ */
+
+static struct key_font {
+       char key;
+       const char *font;
+} key_font[] = {
+       { '0', "bdjtxvpfb qi" },
+       { '1', "gcw vx" },
+       { '2', "fbdjnlpuy" },
+       { '3', "fbdjntxvp ln" },
+       { '4', "ako hw" },
+       { '5', "eakntxu" },
+       { '6', "jdbfpvxtnk" },
+       { '7', "aemw" },
+       { '8', "lfbdjntxvpln" },
+       { '9', "jnlfbdjtxvp" },
+       { 'A', "ukoy kco" },
+       { 'B', "knjdauxtn" },
+       { 'C', "jdbfpvxt" },
+       { 'D', "adjtxua" },
+       { 'E', "eauy km" },
+       { 'F', "eau km" },
+       { 'G', "jdbfpvxt yom" },
+       { 'H', "au ey ko" },
+       { 'I', "bd vx cw" },
+       { 'J', "aetxvpk" },
+       { 'K', "au emy km" },
+       { 'L', "auy" },
+       { 'M', "uamey" },
+       { 'N', "uaye" },
+       { 'O', "bdjtxvpfb" },
+       { 'P', "uadjnk" },
+       { 'Q', "bdjtxvpfb ys" },
+       { 'R', "uadjnk my" },
+       { 'S', "jdbflntxvp" },
+       { 'T', "ae cw" },
+       { 'U', "apvxte" },
+       { 'V', "akwoe" },
+       { 'W', "aumye" },
+       { 'X', "ay ue" },
+       { 'Y', "ame mw" },
+       { 'Z', "aeuy" },
+       { 'u', "cw kco" },
+       { 'd', "cw kwo" },
+       { 'l', "ko ckw" },
+       { 'r', "ko cow" },
+       { '.', "rr" },
+       { ',', "rv" },
+       { '+', "cw ko" },
+       { '-', "ko" },
+       { '*', "ko ay ue" },
+       { 's', "" }, /* space */
+       { 'b', "beyvkb gs qi" },
+       { 'e', "dsp pl pv" },
+       { ' ', "" },
+       { 0, NULL }
+};
+
+/*
+ * f-keys are 25 percent larger, so 4 f-keys are as wide as 5 regular keys.
+ * This is why f-keys use 5 steps instead of four (regular keys).
+ */
+
+static const char *keyboard_layout[] = {
+       "1   2   3   4   5   6   7   8   9   0   G",
+       "         fh       e                      ",
+       "    f1    f2    f0     7   8   9   *     ",
+       "                       4   5   6   -     ",
+       "           u           1   2   3   +     ",
+       "       l   d   r       02      e2        ",
+       NULL, /* end of keyboard */
+       "1   2   3   4   5   6   7   8   9   0   b",
+       "  Q   W   E   R   T   Y   U   I   O   P  ",
+       "   A   S   D   F   G   H   J   K   L   e ",
+       "     Z   X   C   V   B   N   M   ,   .   ",
+       "          s6                             ",
+       NULL, /* end of keyboard */
+       NULL, /* end of all keyboards */
+};
+
+static enum keycode keyboard_codes_1[] = {
+       KEYCODE_1, KEYCODE_2, KEYCODE_3, KEYCODE_4, KEYCODE_5, KEYCODE_6, KEYCODE_7, KEYCODE_8, KEYCODE_9, KEYCODE_0, KEYCODE_g,
+       KEYCODE_HELP, KEYCODE_RETURN,
+       KEYCODE_F1, KEYCODE_F2, KEYCODE_F10, KEYCODE_KP_7, KEYCODE_KP_8, KEYCODE_KP_9, KEYCODE_KP_MULTIPLY,
+       KEYCODE_KP_4, KEYCODE_KP_5, KEYCODE_KP_6, KEYCODE_KP_MINUS,
+       KEYCODE_UP, KEYCODE_KP_1, KEYCODE_KP_2, KEYCODE_KP_3, KEYCODE_KP_PLUS,
+       KEYCODE_LEFT, KEYCODE_DOWN, KEYCODE_RIGHT, KEYCODE_KP_0, KEYCODE_KP_ENTER,
+};
+
+static enum keycode keyboard_codes_2[] = {
+       KEYCODE_1, KEYCODE_2, KEYCODE_3, KEYCODE_4, KEYCODE_5, KEYCODE_6, KEYCODE_7, KEYCODE_8, KEYCODE_9, KEYCODE_0, KEYCODE_BACKSPACE,
+       KEYCODE_q, KEYCODE_w, KEYCODE_e, KEYCODE_r, KEYCODE_t, KEYCODE_y, KEYCODE_u, KEYCODE_i, KEYCODE_o, KEYCODE_p,
+       KEYCODE_a, KEYCODE_s, KEYCODE_d, KEYCODE_f, KEYCODE_g, KEYCODE_h, KEYCODE_j, KEYCODE_k, KEYCODE_l, KEYCODE_RETURN,
+       KEYCODE_z, KEYCODE_x, KEYCODE_c, KEYCODE_v, KEYCODE_b, KEYCODE_n, KEYCODE_m, KEYCODE_COMMA, KEYCODE_PERIOD,
+       KEYCODE_SPACE,
+};
+
+static enum keycode *keyboard_codes[] = {
+       keyboard_codes_1, keyboard_codes_2,
+};
+
+/* definition of keys with coordinates for rendering */
+
+struct key_def {
+       char key;
+       int fkey;
+       double left_top_x, right_top_x;
+       double top_y;
+       double front_top_z, back_top_z;
+       double left_bottom_x, right_bottom_x;
+       double bottom_y;
+       double front_bottom_z, back_bottom_z;
+       double x1[MAX_KEY_LINES], y1[MAX_KEY_LINES], z1[MAX_KEY_LINES];
+       double x2[MAX_KEY_LINES], y2[MAX_KEY_LINES], z2[MAX_KEY_LINES];
+       int lines;
+};
+
+/* definition for each keyboard */
+static struct keyboard_def {
+       struct key_def key[MAX_KEYS];
+       int keys;
+} keyboard_def[MAX_KEYBOARDS];
+
+static int keyboards = 0;
+
+/*
+ * add key to keyboard_def[].key structure
+ *
+ *-4  -2     0     2 3 4
+ * +-------------------+ 4
+ * | +---------------+ | 3
+ * | | a  b  c  d  e | | 2
+ * | | f  g  h  i  j | | 1
+ * | | k  l  m  n  o | | 0
+ * | | p  q  r  s  t | | -1
+ * | | u  v  w  x  y | | -2
+ * | +---------------+ | -3
+ * +-------------------+ -4
+ *        (front)
+ */
+
+static int add_key(char key, int fkey, double width, struct key_def *key_def)
+{
+       int d, l, f, i;
+       double font_scale = 1.0, font_offset = 0.0;
+
+       memset(key_def, 0, sizeof(*key_def));
+
+       key_def->key = key;
+       key_def->fkey = fkey;
+
+       /* key case (see layout in the description above) */
+       key_def->left_top_x = -3.0;
+       key_def->right_top_x = 3.0 + (width - 1.0) * 9.0;
+       key_def->top_y = 0;
+       key_def->front_top_z = 3.0;
+       key_def->back_top_z = -3.0;
+       key_def->left_bottom_x = -4.0;
+       key_def->right_bottom_x = 4.0 + (width - 1.0) * 9.0;
+       key_def->bottom_y = -4.0;
+       key_def->front_bottom_z = 4.0;
+       key_def->back_bottom_z = -4.0;
+
+       /* lines to be drawn ontop of the key (see layout in the description above) */
+       l = 0;
+       /* up to 4 letters/digits are displayed on key */
+       for (d = 0; d < 4; d++) {
+               if (!key_def->fkey) {
+                       /* render regular key */
+                       if (d == 0)
+                               key = key_def->key;
+                       else
+                               break;
+               } else {
+                       /* render F-key */
+                       if (d == 0) {
+                               font_scale = 0.5;
+                               font_offset = -0.25;
+                               if (key_def->key == 'h')
+                                       key = 'H';
+                               else
+                                       key = 'F';
+                       }
+                       else if (d == 1) {
+                               font_offset += 2.5;
+                               if (key_def->key == 'h')
+                                       key = 'E';
+                               else if (key_def->key != '0')
+                                       key = key_def->key;
+                               else
+                                       key = '1';
+                       }
+                       else if (d == 2) {
+                               font_offset += 2.5;
+                               if (key_def->key == 'h')
+                                       key = 'L';
+                               else if (key_def->key != '0')
+                                       break;
+                               else
+                                       key = '0';
+                       }
+                       else if (d == 3) {
+                               font_offset += 2.5;
+                               if (key_def->key == 'h')
+                                       key = 'P';
+                               else
+                                       break;
+                       }
+               }
+               /* search for key font */
+               for (f = 0; key_font[f].key; f++) {
+                       if (key_font[f].key == key)
+                               break;
+               }
+               if (!key_font[f].key) {
+                       print_error("Failed to find keyboard character '%c' in key_font, please fix\n", key);
+                       return -EINVAL;
+               }
+               /* generate lines */
+               i = 0;
+               while (key_font[f].font[i] && key_font[f].font[i + 1]) {
+                       /* skip spaces, but this should not happen */
+                       if (key_font[f].font[i] == ' ') {
+                               i++;
+                               continue;
+                       }
+                       /* the second character is space, so we start over with a new line sequence */
+                       if (key_font[f].font[i + 1] == ' ') {
+                               i += 2;
+                               continue;
+                       }
+                       /* add line */
+                       if (l == MAX_KEY_LINES) {
+                               print_error("Failed to add key definition, maximum number of lines exeeded, please fix\n");
+                               return -EINVAL;
+                       }
+                       key_def->x1[l] = (double)(((key_font[f].font[i] - 'a') % 5) - 2) * font_scale + font_offset;
+                       key_def->y1[l] = 0;
+                       key_def->z1[l] = (double)((key_font[f].font[i] - 'a') / 5) - 2;
+                       key_def->x2[l] = (double)(((key_font[f].font[i + 1] - 'a') % 5) - 2) * font_scale + font_offset;
+                       key_def->y2[l] = 0;
+                       key_def->z2[l] = (double)((key_font[f].font[i + 1] - 'a') / 5) - 2;
+                       l++;
+                       i++;
+               }
+       }
+       key_def->lines = l;
+
+       return 0;
+}
+
+static void scale_key(double px, double py, double pz, double scale, struct key_def *key_def)
+{
+       int l;
+
+       /* scale and translate key case */
+       key_def->left_top_x = key_def->left_top_x * scale + px;
+       key_def->right_top_x = key_def->right_top_x * scale + px;
+       key_def->top_y = key_def->top_y * scale + py;
+       key_def->front_top_z = key_def->front_top_z * scale + pz;
+       key_def->back_top_z = key_def->back_top_z * scale + pz;
+       key_def->left_bottom_x = key_def->left_bottom_x * scale + px;
+       key_def->right_bottom_x = key_def->right_bottom_x * scale + px;
+       key_def->bottom_y = key_def->bottom_y * scale + py;
+       key_def->front_bottom_z = key_def->front_bottom_z * scale + pz;
+       key_def->back_bottom_z = key_def->back_bottom_z * scale + pz;
+
+       /* scale and translate line ontop of keys */
+       for (l = 0; l < key_def->lines; l++) {
+               key_def->x1[l] = key_def->x1[l] * scale + px;
+               key_def->y1[l] = key_def->y1[l] * scale + py;
+               key_def->z1[l] = key_def->z1[l] * scale + pz;
+               key_def->x2[l] = key_def->x2[l] * scale + px;
+               key_def->y2[l] = key_def->y2[l] * scale + py;
+               key_def->z2[l] = key_def->z2[l] * scale + pz;
+       }
+}
+
+#define KEY_SIZE_INCH  3.0
+
+int init_vr_keyboard(double keyboard_height, double keyboard_distance)
+{
+       double width;
+       int l, k, y, x;
+       int rc;
+       int key, fkey;
+
+       l = 0;
+       /* count all keyboards */
+       while (keyboard_layout[l]) {
+               k = 0;
+               /* for each row in keyboard */
+               for (y = 0; keyboard_layout[l]; y++, l++) {
+                       /* for each key in row */
+                       for (x = 0; keyboard_layout[l][x]; x++) {
+                               /* skip spaces, because this is no key */
+                               if (keyboard_layout[l][x] == ' ')
+                                       continue;
+                               width = 1;
+                               key = keyboard_layout[l][x];
+                               fkey = 0;
+                               /* if we have an F-key, this means size * 1.5 */
+                               if (keyboard_layout[l][x] == 'f') {
+                                       if (keyboard_layout[l][x + 1] == 'h')
+                                               width = 2.0; /* help key */
+                                       else
+                                               width = 1.5; /* f-key */
+                                       key = keyboard_layout[l][x + 1];
+                                       fkey = 1;
+                               } else
+                               /* if we have a digit after the key, this means different size */
+                               if (keyboard_layout[l][x + 1] > '0')
+                                       width = (double)(keyboard_layout[l][x + 1] - '0');
+                               rc = add_key(key, fkey, width, &keyboard_def[keyboards].key[k]);
+                               if (rc < 0)
+                                       return -EINVAL;
+#ifdef TILT_KEYBOARD
+                               scale_key(((double)x / 4.0 - 5.0) * KEY_SIZE_INCH, -keyboard_distance, (double)y * KEY_SIZE_INCH - keyboard_height, KEY_SIZE_INCH / 9, &keyboard_def[keyboards].key[k]);
+#else
+                               scale_key(((double)x / 4.0 - 5.0) * KEY_SIZE_INCH, keyboard_height, (double)y * KEY_SIZE_INCH - keyboard_distance, KEY_SIZE_INCH / 9, &keyboard_def[keyboards].key[k]);
+#endif
+                               k++;
+                               /* skip size information, if any */
+                               if (keyboard_layout[l][x + 1] > ' ')
+                                       x++;
+                       }
+               }
+               keyboard_def[keyboards].keys = k;
+               keyboards++;
+               l++;
+       }
+
+       return 0;
+}
+
+static int highlight_k;
+static double from_x, from_y, from_z;
+static double to_x, to_y, to_z;
+
+/* calculate pointer and to what key it points to */
+void handle_vr_keyboard(int keyboard, double pointer_x, double pointer_y, double pointer_z, double pointer_yaw, double pointer_pitch, enum keycode *key)
+{
+       int k;
+       struct key_def *key_def;
+#ifdef TILT_KEYBOARD
+       double keyboard_dist = keyboard_def[keyboard].key[0].y1[0]; /* just get the dist from first line of first key */
+#else
+       double keyboard_height = keyboard_def[keyboard].key[0].y1[0]; /* just get the height from first line of first key */
+#endif
+
+       /* generate poiting vector */
+       from_x = pointer_x;
+       from_y = pointer_y;
+       from_z = pointer_z;
+       to_x = from_x - sin(pointer_yaw) * cos(pointer_pitch) * 20;
+       to_y = from_y + sin(pointer_pitch) * 20;
+       to_z = from_z - cos(pointer_yaw) * cos(pointer_pitch) * 20;
+
+       /* if pointer points down, intersect with keyboard */
+       highlight_k = -1;
+#ifdef TILT_KEYBOARD
+       if (from_z - 1.0 > keyboard_dist && to_z < from_z - 1.0) {
+               to_x = (to_x - from_x) / (from_z - to_z) * (from_z - keyboard_dist) + from_x;
+               to_y = (to_y - from_y) / (from_z - to_z) * (from_z - keyboard_dist) + from_y;
+               to_z = keyboard_dist;
+
+               /* find key that we intersect with */
+               for (k = 0; k < keyboard_def[keyboard].keys; k++) {
+                       key_def = &keyboard_def[keyboard].key[k];
+                       if (to_x < key_def->left_top_x)
+                               continue;
+                       if (to_x > key_def->right_top_x)
+                               continue;
+                       if (-to_y < key_def->back_top_z)
+                               continue;
+                       if (-to_y > key_def->front_top_z)
+                               continue;
+                       highlight_k = k;
+                       *key = keyboard_codes[keyboard][k];
+                       break;
+               }
+       }
+#else
+       if (from_y - 1.0 > keyboard_height && to_y < from_y - 1.0) {
+               to_x = (to_x - from_x) / (from_y - to_y) * (from_y - keyboard_height) + from_x;
+               to_z = (to_z - from_z) / (from_y - to_y) * (from_y - keyboard_height) + from_z;
+               to_y = keyboard_height;
+
+               /* find key that we intersect with */
+               for (k = 0; k < keyboard_def[keyboard].keys; k++) {
+                       key_def = &keyboard_def[keyboard].key[k];
+                       if (to_x < key_def->left_top_x)
+                               continue;
+                       if (to_x > key_def->right_top_x)
+                               continue;
+                       if (to_z < key_def->back_top_z)
+                               continue;
+                       if (to_z > key_def->front_top_z)
+                               continue;
+                       highlight_k = k;
+                       *key = keyboard_codes[keyboard][k];
+                       break;
+               }
+       }
+#endif
+}
+
+#ifdef TILT_KEYBOARD
+#define FLIP_YZ(y, z) -z, y
+#else
+#define FLIP_YZ(y, z) y, z
+#endif
+
+/* render keyboard per eye */
+void render_vr_keyboard(int keyboard, double camera_x, double camera_y)
+{
+       int k, l, distant_k;
+       int rendered[MAX_KEYS];
+       double dists[MAX_KEYS], dist_x, dist_y, dist;
+       struct key_def *key_def;
+
+       glEnable(GL_CULL_FACE);
+       glFrontFace(GL_CW);
+       glCullFace(GL_FRONT);
+
+       memset(rendered, 0, sizeof(rendered));
+
+       /* calculate distances to keys */
+       for (k = 0; k < keyboard_def[keyboard].keys; k++) {
+               key_def = &keyboard_def[keyboard].key[k];
+#ifdef TILT_KEYBOARD
+               dist_x = (key_def->left_top_x + key_def->right_top_x) / 2.0 - camera_x;
+               dist_y = (key_def->front_top_z + key_def->back_top_z) / 2.0 + camera_y;
+#else
+               dist_x = (key_def->left_top_x + key_def->right_top_x) / 2.0 - camera_x;
+               dist_y = (key_def->front_top_z + key_def->back_top_z) / 2.0;
+#endif
+               dists[k] = sqrt(dist_x * dist_x + dist_y * dist_y);
+       }
+
+       while (1) {
+               key_def = NULL;
+               /* search for most distant key */
+               for (k = 0; k < keyboard_def[keyboard].keys; k++) {
+                       /* skip keys that have been already rendered */
+                       if (rendered[k])
+                               continue;
+                       if (!key_def || dists[k] > dist) {
+                               dist = dists[k];
+                               key_def = &keyboard_def[keyboard].key[k];
+                               distant_k = k;
+                       }
+               }
+               /* no unrendered key, we are done */
+               if (!key_def)
+                       break;
+               rendered[distant_k] = 1;
+
+               /* render case of key */
+               glBegin(GL_QUADS);
+               /* top plane */
+               if (distant_k == highlight_k)
+                       glColor4f(0.0, 0.7, 0.7, 1.0);
+               else
+                       glColor4f(0.5, 0.5, 0.5, 1.0);
+               glVertex3f(key_def->left_top_x, FLIP_YZ(key_def->top_y, key_def->back_top_z));
+               glVertex3f(key_def->right_top_x, FLIP_YZ(key_def->top_y, key_def->back_top_z));
+               glVertex3f(key_def->right_top_x, FLIP_YZ(key_def->top_y, key_def->front_top_z));
+               glVertex3f(key_def->left_top_x, FLIP_YZ(key_def->top_y, key_def->front_top_z));
+               /* front plane (in front of player) */
+               glColor4f(0.1, 0.1, 0.1, 1.0);
+               glVertex3f(key_def->left_top_x, FLIP_YZ(key_def->top_y, key_def->front_top_z));
+               glVertex3f(key_def->right_top_x, FLIP_YZ(key_def->top_y, key_def->front_top_z));
+               glVertex3f(key_def->right_bottom_x, FLIP_YZ(key_def->bottom_y, key_def->front_bottom_z));
+               glVertex3f(key_def->left_bottom_x, FLIP_YZ(key_def->bottom_y, key_def->front_bottom_z));
+               /* back plane (away from player) */
+               glColor4f(0.3, 0.3, 0.3, 1.0);
+               glVertex3f(key_def->right_top_x, FLIP_YZ(key_def->top_y, key_def->back_top_z));
+               glVertex3f(key_def->left_top_x, FLIP_YZ(key_def->top_y, key_def->back_top_z));
+               glVertex3f(key_def->left_bottom_x, FLIP_YZ(key_def->bottom_y, key_def->back_bottom_z));
+               glVertex3f(key_def->right_bottom_x, FLIP_YZ(key_def->bottom_y, key_def->back_bottom_z));
+               /* left plane */
+               glColor4f(0.2, 0.2, 0.2, 1.0);
+               glVertex3f(key_def->left_top_x, FLIP_YZ(key_def->top_y, key_def->back_top_z));
+               glVertex3f(key_def->left_top_x, FLIP_YZ(key_def->top_y, key_def->front_top_z));
+               glVertex3f(key_def->left_bottom_x, FLIP_YZ(key_def->bottom_y, key_def->front_bottom_z));
+               glVertex3f(key_def->left_bottom_x, FLIP_YZ(key_def->bottom_y, key_def->back_bottom_z));
+               /* right plane */
+               glVertex3f(key_def->right_top_x, FLIP_YZ(key_def->top_y, key_def->front_top_z));
+               glVertex3f(key_def->right_top_x, FLIP_YZ(key_def->top_y, key_def->back_top_z));
+               glVertex3f(key_def->right_bottom_x, FLIP_YZ(key_def->bottom_y, key_def->back_bottom_z));
+               glVertex3f(key_def->right_bottom_x, FLIP_YZ(key_def->bottom_y, key_def->front_bottom_z));
+               /* no bottom plane, because it is unlikely visible */
+               glEnd();
+
+               /* render lines of key */
+               glColor4f(1.0, 1.0, 1.0, 1.0);
+               for (l = 0; l < key_def->lines; l++) {
+                       if (key_def->x1[l] == key_def->x2[l] && key_def->z1[l] == key_def->z2[l]) {
+                               /* just a point */
+                               glBegin(GL_POINTS);
+                               glVertex3f(key_def->x1[l], FLIP_YZ(key_def->y1[l], key_def->z1[l]));
+                               glEnd();
+                       } else {
+                               glBegin(GL_LINES);
+                               glVertex3f(key_def->x1[l], FLIP_YZ(key_def->y1[l], key_def->z1[l]));
+                               glVertex3f(key_def->x2[l], FLIP_YZ(key_def->y2[l], key_def->z2[l]));
+                               glEnd();
+                       }
+               }
+       }
+
+       glDisable(GL_CULL_FACE);
+
+       /* render pointer */
+       glColor4f(1.0, 0.0, 0.0, 1.0);
+       glBegin(GL_LINES);
+       glVertex3f(from_x, from_y, from_z);
+       glVertex3f(to_x, to_y, to_z);
+       glEnd();
+}
+
diff --git a/src/libovr/keyboard.h b/src/libovr/keyboard.h
new file mode 100644 (file)
index 0000000..106f7c8
--- /dev/null
@@ -0,0 +1,5 @@
+
+int init_vr_keyboard(double keyboard_height, double keyboard_distance);
+void handle_vr_keyboard(int keyboard, double pointer_x, double pointer_y, double pointer_z, double pointer_yaw, double pointer_pitch, enum keycode *key);
+void render_vr_keyboard(int keyboard, double camera_x, double camera_y);
+
index 338897e..9083471 100755 (executable)
@@ -100,15 +100,20 @@ static int mirror_width;
 static int mirror_height;
 static ovrEyeRenderDesc eyeRenderDesc[2];
 static ovrPosef hmdToEyeViewPose[2];
+static ovrPosef handPoses[2];
+static ovrInputState inputState;
 static ovrLayerEyeFov layer;
 static int multisampling;
 static long long frameIndex = 0;
 static double observer_x = 0.0;
 static double observer_x_normalize = 0.0;
+static double hand_x_normalize = 0.0;
 static double observer_y = 0.0;
 static double observer_y_normalize = 0.0;
+static double hand_y_normalize = 0.0;
 static double observer_z = 0.0;
 static double observer_z_normalize = 0.0;
+static double hand_z_normalize = 0.0;
 
 int init_ovr(int _multisampling)
 {
@@ -234,14 +239,77 @@ error:
        return -EINVAL;
 }
 
-void begin_render_ovr(void)
+void get_poses_ovr(int *button_a, int *button_b, int *button_x, int *button_y, int *button_menu, int *button_trigger, int *button_left_thumb, int *button_right_thumb, double *hand_right_x, double *hand_right_y, double *hand_right_z, double *hand_right_yaw, double *hand_right_pitch, double *hand_right_roll, double *stick_left_x, double *stick_left_y, double *stick_right_x, double *stick_right_y)
 {      
        ovrResult result;
+       float yaw, pitch, roll;
+       double x, y, z;
 
        /* Get both eye poses simultaneously, with IPD offset already included. */
-       double displayMidpointSeconds = ovr_GetPredictedDisplayTime(session, 0);
+       double displayMidpointSeconds = ovr_GetPredictedDisplayTime(session, frameIndex);
        ovrTrackingState hmdState = ovr_GetTrackingState(session, displayMidpointSeconds, ovrTrue);
        ovr_CalcEyePoses(hmdState.HeadPose.ThePose, hmdToEyeViewPose, layer.RenderPose);
+       /* Grab hand poses useful for rendering hand or controller representation */
+       handPoses[ovrHand_Left]  = hmdState.HandPoses[ovrHand_Left].ThePose;
+       handPoses[ovrHand_Right] = hmdState.HandPoses[ovrHand_Right].ThePose;
+       x = handPoses[ovrHand_Right].Position.x;
+       y = handPoses[ovrHand_Right].Position.y;
+       z = handPoses[ovrHand_Right].Position.z;
+       x += hand_x_normalize;
+       y += hand_y_normalize;
+       z += hand_z_normalize;
+       *hand_right_x = x / 0.0254;
+       *hand_right_y = y / 0.0254;
+       *hand_right_z = z / 0.0254;
+       ovrOrientation2yawpitchroll(handPoses[ovrHand_Right].Orientation, &yaw, &pitch, &roll);
+       *hand_right_yaw = yaw;
+       *hand_right_pitch = pitch;
+       *hand_right_roll = roll;
+
+       result = ovr_GetInputState(session, ovrControllerType_Touch, &inputState);
+       if (OVR_SUCCESS(result)) {
+               if (inputState.Buttons & ovrButton_A)
+                       *button_a = 1;
+               else
+                       *button_a = 0;
+               if (inputState.Buttons & ovrButton_B)
+                       *button_b = 1;
+               else
+                       *button_b = 0;
+               if (inputState.Buttons & ovrButton_X)
+                       *button_x = 1;
+               else
+                       *button_x = 0;
+               if (inputState.Buttons & ovrButton_Y)
+                       *button_y = 1;
+               else
+                       *button_y = 0;
+               if (inputState.Buttons & ovrButton_Enter)
+                       *button_menu = 1;
+               else
+                       *button_menu = 0;
+               if (inputState.Buttons & ovrButton_LThumb)
+                       *button_left_thumb = 1;
+               else
+                       *button_left_thumb = 0;
+               if (inputState.Buttons & ovrButton_RThumb)
+                       *button_right_thumb = 1;
+               else
+                       *button_right_thumb = 0;
+               if (inputState.IndexTrigger[ovrHand_Right] >= 0.8)
+                       *button_trigger = 1;
+               else
+                       *button_trigger = 0;
+               *stick_left_x = inputState.Thumbstick[ovrHand_Left].x;
+               *stick_left_y = inputState.Thumbstick[ovrHand_Left].y;
+               *stick_right_x = inputState.Thumbstick[ovrHand_Right].x;
+               *stick_right_y = inputState.Thumbstick[ovrHand_Right].y;
+       }
+}
+
+void begin_render_ovr(void)
+{
+       ovrResult result;
 
        result = ovr_WaitToBeginFrame(session, frameIndex);
        if (!OVR_SUCCESS(result))
@@ -251,7 +319,7 @@ void begin_render_ovr(void)
                print_info("Failed to begin frame (error %d)\n", result);
 }
 
-void begin_render_ovr_eye(int eye)
+void begin_render_ovr_eye(int eye, double *camera_x, double *camera_y, double *camera_z)
 {
        int curIndex;
        GLuint chainTexId;
@@ -291,7 +359,10 @@ void begin_render_ovr_eye(int eye)
        glRotatef(-pitch / M_PI * 180.0,1,0,0);
        glRotatef(-yaw / M_PI * 180.0,0,1,0);
 
-       glTranslated(-x / 0.0254, -y / 0.0254, -z / 0.0254); /* convert to inch */
+       *camera_x = x / 0.0254;
+       *camera_y = y / 0.0254;
+       *camera_z = z / 0.0254;
+       glTranslated(-(*camera_x), -(*camera_y), -(*camera_z)); /* convert to inch */
 
        glMatrixMode(GL_MODELVIEW);
 
@@ -366,6 +437,9 @@ void normalize_observer_ovr(void)
        observer_x_normalize = -observer_x;
        observer_y_normalize = -observer_y;
        observer_z_normalize = -observer_z;
+       hand_x_normalize = -observer_x;
+       hand_y_normalize = -observer_y;
+       hand_z_normalize = -observer_z;
 }
 
 void exit_ovr(void)
index 1400127..9cd9aa5 100755 (executable)
@@ -1,8 +1,9 @@
 
 int init_ovr(int multisampling);
 void exit_ovr(void);
+void get_poses_ovr(int *button_a, int *button_b, int *button_x, int *button_y, int *button_menu, int *button_trigger, int *button_left_thumb, int *button_right_thumb, double *hand_right_x, double *hand_right_y, double *hand_right_z, double *hand_right_yaw, double *hand_right_pitch, double *hand_right_roll, double *stick_left_x, double *stick_left_y, double *stick_right_x, double *stick_right_y);
 void begin_render_ovr(void);
-void begin_render_ovr_eye(int eye);
+void begin_render_ovr_eye(int eye, double *camera_x, double *camera_y, double *camera_z);
 void end_render_ovr_eye(int eye);
 void end_render_ovr(void);
 void render_mirror_ovr(int view_width, int view_height);
index 069f877..83ec683 100644 (file)
@@ -29,7 +29,6 @@
 #include <GL/glew.h>
 
 #define MAX_OSD 2
-#define MONITOR_DISTANCE 35.0
 static uint8_t *image_rgb = NULL;
 static uint8_t *osd_rgba[MAX_OSD] = { NULL, NULL };
 static GLuint image_name;
index bbba112..2c1a526 100644 (file)
@@ -27,6 +27,7 @@
 #include "../libsdl/sdl.h"
 #ifdef HAVE_OVR
 #include "../libovr/ovr.h"
+#include "../libovr/keyboard.h"
 #endif
 #include "../libsdl/opengl.h"
 #include "../libsdl/print.h"
@@ -60,6 +61,10 @@ static int config_skip_intro = 0;
 static int config_multisampling = 8;
 static double config_fov = FOV_NOVAGEN;
 static double config_monitor_distance = 31.5; /* inch */
+#ifdef HAVE_OVR
+static double config_keyboard_distance = 28.5; /* inch */
+static double config_keyboard_height = 10.0; /* inch */
+#endif
 static double config_benson_size = 1.0;
 static int config_debug_transparent = 0;
 static int config_debug_opengl = 0;
@@ -270,12 +275,178 @@ illegal_parameter:
        return 0;
 }
 
+static void osd_info(const char *param, const char *value)
+{
+       char line[41] = "                                        ";
+
+       if (param[0]) {
+               strncpy(line + 21 - strlen(param), param, strlen(param));
+               line[22] = ':';
+       }
+       if (strlen(value) > 15) {
+               print_error("string too long\n");
+               return;
+       }
+       strncpy(line + 25, value, strlen(value));
+       text_render(info_osd, OSD_WIDTH, OSD_HEIGHT, line, 0x00, 4, 0, 0, 1);
+       osd_timer = ticks_sdl() + 2500;
+}
+
 static void resize_window(int width, int height)
 {
        window_width = width;
        window_height = height;
 }
 
+#ifdef HAVE_OVR
+static int __attribute__((unused)) button_a_last = 0, button_b_last = 0, button_x_last = 0, button_y_last = 0, button_menu_last = 0, button_trigger_last = 0, button_left_thumb_last = 0, button_right_thumb_last = 0;
+static double __attribute__((unused)) stick_left_x_last = 0.0, stick_left_y_last = 0.0, stick_right_x_last = 0.0, stick_right_y_last = 0.0;
+static int thrust_last = KEYCODE_SPACE;
+static int keyboard_on = 0;
+static enum keycode vr_key_pressed = 0, vr_key = 0;
+
+static void handle_vr_poses(void)
+{
+       int button_a = 0, button_b = 0, button_x = 0, button_y = 0, button_menu = 0, button_trigger = 0, button_left_thumb = 0, button_right_thumb = 0;
+       double hand_right_x = 0.0, hand_right_y = 0.0, hand_right_z = 0.0;
+       double hand_right_yaw = 0.0, hand_right_pitch = 0.0, hand_right_roll = 0.0;
+       double stick_left_x = 0.0, stick_left_y = 0.0, stick_right_x = 0.0, stick_right_y = 0.0;
+       int thrust = KEYCODE_SPACE;
+
+       get_poses_ovr(&button_a, &button_b, &button_x, &button_y, &button_menu, &button_trigger, &button_left_thumb, &button_right_thumb, &hand_right_x, &hand_right_y, &hand_right_z, &hand_right_yaw, &hand_right_pitch, &hand_right_roll, &stick_left_x, &stick_left_y, &stick_right_x, &stick_right_y);
+       if (button_menu && !button_menu_last) {
+               /* menu toggle */
+               help_view ^= 1;
+       }
+       if (help_view) {
+               if (button_trigger && !button_trigger_last) {
+                       /* rigger pressed */
+                       normalize_observer_ovr();
+                       osd_info("", "change height");
+               }
+       } else {
+               /* handle input */
+               if (button_a && !button_a_last) {
+                       /* keyboard A toggle */
+                       if (keyboard_on == 1)
+                               keyboard_on = 0;
+                       else
+                               keyboard_on = 1;
+               }
+               if (button_b && !button_b_last) {
+                       /* keyboard B toggle */
+                       if (keyboard_on == 2)
+                               keyboard_on = 0;
+                       else
+                               keyboard_on = 2;
+               }
+               if (button_x && !button_x_last) {
+                       /* 'board' pressed */
+                       set_amiga_key(KEYCODE_b, 1);
+                       set_amiga_key(KEYCODE_b, 0);
+               }
+               if (button_y && !button_y_last) {
+                       /* 'leave' pressed */
+                       set_amiga_key(KEYCODE_l, 1);
+                       set_amiga_key(KEYCODE_l, 0);
+               }
+               if (button_left_thumb && !button_left_thumb_last) {
+                       /* 'escape' pressed */
+                       set_amiga_key(KEYCODE_ESCAPE, 1);
+                       set_amiga_key(KEYCODE_ESCAPE, 0);
+               }
+               if (!button_left_thumb && button_left_thumb_last) {
+                       /* 'escape' released */
+                       set_amiga_key(KEYCODE_SPACE, 1);
+                       set_amiga_key(KEYCODE_SPACE, 0);
+               }
+               if (button_right_thumb && !button_right_thumb_last) {
+                       /* 'running' pressed */
+                       set_amiga_key(KEYCODE_r, 1);
+                       set_amiga_key(KEYCODE_r, 0);
+               }
+               if (button_trigger && !button_trigger_last) {
+                       /* trigger pressed */
+                       if (!keyboard_on) {
+                               set_joystick(-1, -1, -1, -1, 1);
+                       } else if (vr_key) {
+                               vr_key_pressed = vr_key;
+                               set_amiga_key(vr_key_pressed, 1);
+                       }
+               }
+               if (!button_trigger && button_trigger_last) {
+                       /* trigger released */
+                       set_joystick(-1, -1, -1, -1, 0);
+                       if (vr_key_pressed) {
+                               set_amiga_key(vr_key_pressed, 0);
+                               vr_key_pressed = 0;
+                       }
+               }
+               /* joystick */
+               if (stick_right_x > 0.5)
+                       set_joystick(0, 1, -1, -1, -1);
+               else
+               if (stick_right_x < -0.5)
+                       set_joystick(1, 0, -1, -1, -1);
+               else
+               if (stick_right_x_last > 0.5 || stick_right_x_last < -0.5)
+                       set_joystick(0, 0, -1, -1, -1);
+               if (stick_right_y > 0.5)
+                       set_joystick(-1, -1, 1, 0, -1);
+               else
+               if (stick_right_y < -0.5)
+                       set_joystick(-1, -1, 0, 1, -1);
+               else
+               if (stick_right_y_last > 0.5 || stick_right_y_last < -0.5)
+                       set_joystick(-1, -1, 0, 0, -1);
+               /* thrust */
+               if (stick_left_y > 0.3)
+                       thrust = (stick_left_y - 0.3) / 0.6 * 11.0;
+               else
+               if (stick_left_y < -0.3)
+                       thrust = (stick_left_y + 0.3) / 0.6 * 11.0;
+               else
+                       thrust = 0;
+               if (thrust >= 10)
+                       thrust = KEYCODE_0;
+               else
+               if (thrust > 0)
+                       thrust = KEYCODE_1 + thrust - 1;
+               else
+               if (thrust < 0)
+                       thrust = KEYCODE_F1 - thrust - 1;
+               else
+               if (thrust <= -10)
+                       thrust = KEYCODE_F10;
+               else
+                       thrust = KEYCODE_SPACE;
+               if (thrust_last != thrust) {
+                       set_amiga_key(thrust, 1);
+                       set_amiga_key(thrust, 0);
+               }
+       }
+       button_a_last = button_a;
+       button_b_last = button_b;
+       button_x_last = button_x;
+       button_y_last = button_y;
+       button_menu_last = button_menu;
+       button_trigger_last = button_trigger;
+       button_left_thumb_last = button_left_thumb;
+       button_right_thumb_last = button_right_thumb;
+       stick_left_x_last = stick_left_x;
+       stick_left_y_last = stick_left_y;
+       stick_right_x_last = stick_right_x;
+       stick_right_y_last = stick_right_y;
+       thrust_last = thrust;
+
+       /* we must handle keyboard after toggeling keyboard_on,
+        * so that keyboard_on will not change until keyboard is rendered */
+       vr_key = 0;
+       if (keyboard_on)
+               handle_vr_keyboard(keyboard_on - 1, hand_right_x, hand_right_y, hand_right_z, hand_right_yaw, hand_right_pitch, &vr_key);
+}
+#endif
+
 static void skip_intro(void)
 {
        int event;
@@ -325,7 +496,9 @@ static void main_loop(void)
        double render_delay = 0.0;
        uint32_t palette_address;
        uint16_t palette[16];
+#ifdef HAVE_OVR
        int eye;
+#endif
 
        last_time = ticks_sdl();
 
@@ -394,12 +567,15 @@ static void main_loop(void)
 
                /* STEP 2: transfer legacy image (or just benson) in memory to OpenGL texture */
 #ifdef HAVE_OVR
+               handle_vr_poses();
+
                begin_render_ovr();
+
                for (eye = 0; eye < 2; eye++) {
-                       /* viewport and frustum is set here */
-                       begin_render_ovr_eye(eye);
+                       double camera_x, camera_y, camera_z;
+                       /* begin of rendering eye, viewport and frustum is set here */
+                       begin_render_ovr_eye(eye, &camera_x, &camera_y, &camera_z);
 #else
-               eye = 0;
                {
 #endif
                        /* clear screen */
@@ -460,6 +636,11 @@ static void main_loop(void)
                                }
                        }
 #ifdef HAVE_OVR
+                       /* render keyboard */
+                       if (keyboard_on)
+                               render_vr_keyboard(keyboard_on - 1, camera_x, camera_y);
+
+                       /* end of rendering eye */
                        end_render_ovr_eye(eye);
                }
                /* at this point we are ready with our image, so we display */
@@ -664,23 +845,6 @@ fail:
        }
 }
 
-static void osd_info(const char *param, const char *value)
-{
-       char line[41] = "                                        ";
-
-       if (param[0]) {
-               strncpy(line + 21 - strlen(param), param, strlen(param));
-               line[22] = ':';
-       }
-       if (strlen(value) > 15) {
-               print_error("string too long\n");
-               return;
-       }
-       strncpy(line + 25, value, strlen(value));
-       text_render(info_osd, OSD_WIDTH, OSD_HEIGHT, line, 0x00, 4, 0, 0, 1);
-       osd_timer = ticks_sdl() + 2500;
-}
-
 static int shift = 0, ctrl = 0;
 
 static void keyboard_sdl(int down, enum keycode keycode)
@@ -955,6 +1119,9 @@ int main(int argc, char *argv[])
        rc = init_ovr(config_multisampling);
        if (rc < 0)
                goto done;
+       rc = init_vr_keyboard(config_keyboard_height, config_keyboard_distance);
+       if (rc < 0)
+               goto done;
 #endif
        rc = init_opengl_image((double_pixel_size) ? IMAGE_WIDTH * 2 : IMAGE_WIDTH, (double_pixel_size) ? IMAGE_HEIGHT * 2 : IMAGE_HEIGHT);
        if (rc < 0)