From 0697fa5b2ae3cdcc82f35cf8235b819197f21b18 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Sat, 21 Apr 2018 11:24:52 +0200 Subject: [PATCH] OVR: Controller support with keyboard emulation --- src/libovr/Makefile.am | 3 +- src/libovr/keyboard.c | 574 +++++++++++++++++++++++++++++++++++++++++++++++++ src/libovr/keyboard.h | 5 + src/libovr/ovr.c | 82 ++++++- src/libovr/ovr.h | 3 +- src/libsdl/opengl.c | 1 - src/mercenary/main.c | 207 ++++++++++++++++-- 7 files changed, 848 insertions(+), 27 deletions(-) create mode 100644 src/libovr/keyboard.c create mode 100644 src/libovr/keyboard.h diff --git a/src/libovr/Makefile.am b/src/libovr/Makefile.am index e1c7c50..0a60605 100755 --- a/src/libovr/Makefile.am +++ b/src/libovr/Makefile.am @@ -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 index 0000000..7784e55 --- /dev/null +++ b/src/libovr/keyboard.c @@ -0,0 +1,574 @@ +/* VR Keyboard + * + * (C) 2018 by Andreas Eversberg + * 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 . + */ + +#include +#include +#include +#include +#include +#include "../../include/keycodes.h" +#include "../libsdl/print.h" +#include "keyboard.h" +#define GL3_PROTOTYPES 1 +#include + +#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 index 0000000..106f7c8 --- /dev/null +++ b/src/libovr/keyboard.h @@ -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); + diff --git a/src/libovr/ovr.c b/src/libovr/ovr.c index 338897e..9083471 100755 --- a/src/libovr/ovr.c +++ b/src/libovr/ovr.c @@ -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) diff --git a/src/libovr/ovr.h b/src/libovr/ovr.h index 1400127..9cd9aa5 100755 --- a/src/libovr/ovr.h +++ b/src/libovr/ovr.h @@ -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); diff --git a/src/libsdl/opengl.c b/src/libsdl/opengl.c index 069f877..83ec683 100644 --- a/src/libsdl/opengl.c +++ b/src/libsdl/opengl.c @@ -29,7 +29,6 @@ #include #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; diff --git a/src/mercenary/main.c b/src/mercenary/main.c index bbba112..2c1a526 100644 --- a/src/mercenary/main.c +++ b/src/mercenary/main.c @@ -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) -- 2.13.6