vrheights

annotate src/game.cc @ 7:0eca023ed909

- fixed the hmd-tracking / mouselook interaction - added game variable system
author John Tsiombikas <nuclear@member.fsf.org>
date Wed, 01 Oct 2014 01:06:55 +0300
parents 608e03b688f8
children 3f221bdc9bab
rev   line source
nuclear@2 1 #include <stdio.h>
nuclear@2 2 #include <assert.h>
nuclear@6 3 #include <vector>
nuclear@2 4 #include <algorithm>
nuclear@4 5 #include <vmath/vmath.h>
nuclear@2 6 #include "opengl.h"
nuclear@2 7 #include "game.h"
nuclear@2 8 #include "goatvr.h"
nuclear@3 9 #include "teapot.h"
nuclear@5 10 #include "console.h"
nuclear@5 11 #include "drawtext.h"
nuclear@7 12 #include "game_var.h"
nuclear@2 13
nuclear@2 14 static void draw_scene();
nuclear@4 15 static void material(float r, float g, float b, float roughness);
nuclear@2 16 static void toggle_hmd_fullscr();
nuclear@2 17 static void create_rtarg(int x, int y);
nuclear@2 18 static int next_pow2(int x);
nuclear@6 19 static void con_handle(const char *cmd);
nuclear@2 20
nuclear@2 21 static int win_width, win_height;
nuclear@2 22 static unsigned int fb_tex;
nuclear@2 23 static unsigned int fbo, fb_depth;
nuclear@2 24 static int fb_xsz, fb_ysz;
nuclear@2 25 static int fb_tex_xsz, fb_tex_ysz;
nuclear@2 26
nuclear@5 27 static unsigned int chess_tex;
nuclear@5 28
nuclear@4 29 static float cam_theta, cam_phi;
nuclear@4 30 static Vector3 cam_pos;
nuclear@4 31 static bool keystate[256];
nuclear@4 32
nuclear@5 33 static Console con;
nuclear@5 34 static dtx_font *con_font;
nuclear@5 35
nuclear@7 36 #define OPT_LOOK_WALK "look-walk"
nuclear@7 37
nuclear@2 38 bool game_init()
nuclear@2 39 {
nuclear@2 40 init_opengl();
nuclear@2 41
nuclear@2 42 if(vr_init() == -1) {
nuclear@2 43 return false;
nuclear@2 44 }
nuclear@2 45
nuclear@2 46 glEnable(GL_DEPTH_TEST);
nuclear@2 47 glEnable(GL_CULL_FACE);
nuclear@3 48 glEnable(GL_LIGHTING);
nuclear@2 49
nuclear@5 50 unsigned char chess_pixels[] = {
nuclear@5 51 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff,
nuclear@5 52 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff
nuclear@5 53 };
nuclear@5 54 glGenTextures(1, &chess_tex);
nuclear@5 55 glBindTexture(GL_TEXTURE_2D, chess_tex);
nuclear@5 56 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
nuclear@5 57 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
nuclear@5 58 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 4, 4, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, chess_pixels);
nuclear@5 59
nuclear@5 60 if(!(con_font = dtx_open_font_glyphmap("data/mono14.glyphmap"))) {
nuclear@5 61 fprintf(stderr, "failed to open console font\n");
nuclear@5 62 return false;
nuclear@5 63 }
nuclear@5 64 con.set_font(con_font, 14);
nuclear@7 65 con.set_size(7, 52);
nuclear@5 66 con.set_position(0, 0, Console::CENTER);
nuclear@6 67 con.set_command_func(con_handle);
nuclear@7 68 con.set_echo(false);
nuclear@7 69
nuclear@7 70 /* initialize all option variables */
nuclear@7 71 set_gvar_bool(OPT_LOOK_WALK, true);
nuclear@5 72
nuclear@2 73 return true;
nuclear@2 74 }
nuclear@2 75
nuclear@2 76 void game_cleanup()
nuclear@2 77 {
nuclear@2 78 vr_shutdown();
nuclear@2 79 }
nuclear@2 80
nuclear@2 81 void game_update(long tm)
nuclear@2 82 {
nuclear@4 83 static long prev_upd;
nuclear@4 84 float dt = (tm - prev_upd) / 1000.0;
nuclear@4 85 prev_upd = tm;
nuclear@4 86
nuclear@5 87 if(con.is_visible()) {
nuclear@5 88 con.update();
nuclear@5 89 return;
nuclear@5 90 }
nuclear@5 91
nuclear@5 92 float offs = dt * 5.0;
nuclear@4 93 Vector3 dir;
nuclear@4 94
nuclear@4 95 if(keystate['d'] || keystate['D']) {
nuclear@4 96 dir += Vector3(offs, 0, 0);
nuclear@4 97 }
nuclear@4 98 if(keystate['a'] || keystate['A']) {
nuclear@4 99 dir += Vector3(-offs, 0, 0);
nuclear@4 100 }
nuclear@4 101 if(keystate['w'] || keystate['W']) {
nuclear@4 102 dir += Vector3(0, 0, -offs);
nuclear@4 103 }
nuclear@4 104 if(keystate['s'] || keystate['S']) {
nuclear@4 105 dir += Vector3(0, 0, offs);
nuclear@4 106 }
nuclear@4 107
nuclear@7 108 if(get_gvar_bool(OPT_LOOK_WALK)) {
nuclear@7 109 float rot[4];
nuclear@7 110 vr_view_rotation(0, rot);
nuclear@7 111 Quaternion q(rot[3], rot[0], rot[1], rot[2]);
nuclear@7 112
nuclear@7 113 dir.transform(q);
nuclear@7 114 }
nuclear@7 115
nuclear@5 116 float cos_theta = cos(DEG_TO_RAD(cam_theta));
nuclear@5 117 float sin_theta = sin(DEG_TO_RAD(cam_theta));
nuclear@5 118 cam_pos.x += dir.x * cos_theta - dir.z * sin_theta;
nuclear@5 119 cam_pos.z += dir.x * sin_theta + dir.z * cos_theta;
nuclear@5 120
nuclear@2 121 }
nuclear@2 122
nuclear@2 123 void game_display()
nuclear@2 124 {
nuclear@2 125 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
nuclear@2 126 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
nuclear@2 127
nuclear@2 128 for(int i=0; i<2; i++) {
nuclear@2 129 glViewport(i == 0 ? 0 : fb_xsz / 2, 0, fb_xsz / 2, fb_ysz);
nuclear@2 130 vr_begin(i);
nuclear@2 131
nuclear@2 132 float proj[16];
nuclear@2 133 if(vr_proj_matrix(i, 0.5, 500.0, proj)) {
nuclear@2 134 glMatrixMode(GL_PROJECTION);
nuclear@2 135 glLoadMatrixf(proj);
nuclear@2 136 }
nuclear@2 137
nuclear@2 138 glMatrixMode(GL_MODELVIEW);
nuclear@2 139
nuclear@2 140 float view[16];
nuclear@2 141 vr_view_matrix(i, view);
nuclear@2 142 glLoadMatrixf(view);
nuclear@4 143 glRotatef(cam_phi, 1, 0, 0);
nuclear@4 144 glRotatef(cam_theta, 0, 1, 0);
nuclear@2 145 /* move the camera to the eye level of the user */
nuclear@2 146 glTranslatef(0, -vr_getf_def(VR_EYE_HEIGHT, 1.65), 0);
nuclear@4 147 glTranslatef(-cam_pos.x, -cam_pos.y, -cam_pos.z);
nuclear@2 148
nuclear@2 149 draw_scene();
nuclear@5 150 con.draw();
nuclear@2 151
nuclear@2 152 vr_end();
nuclear@2 153 }
nuclear@2 154
nuclear@2 155 glBindFramebuffer(GL_FRAMEBUFFER, 0);
nuclear@2 156 glViewport(0, 0, win_width, win_height);
nuclear@2 157
nuclear@2 158 vr_swap_buffers();
nuclear@2 159 assert(glGetError() == GL_NO_ERROR);
nuclear@2 160 }
nuclear@2 161
nuclear@2 162 void game_reshape(int x, int y)
nuclear@2 163 {
nuclear@2 164 win_width = x;
nuclear@2 165 win_height = y;
nuclear@2 166
nuclear@5 167 create_rtarg(vr_geti_def(VR_RENDER_XRES, x * 2), vr_geti_def(VR_RENDER_YRES, y));
nuclear@2 168 vr_output_texture(fb_tex, 0, 0, (float)fb_xsz / (float)fb_tex_xsz, (float)fb_ysz / (float)fb_tex_ysz);
nuclear@2 169
nuclear@2 170 /* these might be overriden in VR mode (see game_display) */
nuclear@2 171 glViewport(0, 0, x, y);
nuclear@2 172 glMatrixMode(GL_PROJECTION);
nuclear@2 173 glLoadIdentity();
nuclear@2 174 gluPerspective(50.0, (float)x / (float)y, 0.5, 500.0);
nuclear@2 175 }
nuclear@2 176
nuclear@2 177 void game_keyboard(int key, bool pressed)
nuclear@2 178 {
nuclear@2 179 if(pressed) {
nuclear@5 180 if(con.is_visible()) {
nuclear@5 181 if(key == 27 || key == '`') {
nuclear@5 182 con.hide();
nuclear@5 183 } else {
nuclear@5 184 con.input_key(key);
nuclear@5 185 }
nuclear@5 186 } else {
nuclear@5 187 switch(key) {
nuclear@5 188 case 27:
nuclear@5 189 exit_game();
nuclear@5 190 break;
nuclear@2 191
nuclear@5 192 case 'f':
nuclear@5 193 toggle_hmd_fullscr();
nuclear@5 194 break;
nuclear@2 195
nuclear@5 196 case 'r':
nuclear@5 197 vr_recenter();
nuclear@5 198 break;
nuclear@2 199
nuclear@5 200 case '`':
nuclear@5 201 con.show();
nuclear@5 202 break;
nuclear@5 203
nuclear@5 204 default:
nuclear@5 205 break;
nuclear@5 206 }
nuclear@2 207 }
nuclear@2 208 }
nuclear@4 209
nuclear@4 210 if(key < 256) {
nuclear@4 211 keystate[key] = pressed;
nuclear@4 212 }
nuclear@2 213 }
nuclear@2 214
nuclear@4 215
nuclear@4 216 static int prev_x, prev_y;
nuclear@4 217 static bool bnstate[32];
nuclear@4 218
nuclear@2 219 void game_mouse_button(int bn, bool state, int x, int y)
nuclear@2 220 {
nuclear@4 221 bnstate[bn] = state;
nuclear@4 222 prev_x = x;
nuclear@4 223 prev_y = y;
nuclear@2 224 }
nuclear@2 225
nuclear@2 226 void game_mouse_motion(int x, int y)
nuclear@2 227 {
nuclear@4 228 int dx = x - prev_x;
nuclear@4 229 int dy = y - prev_y;
nuclear@4 230 prev_x = x;
nuclear@4 231 prev_y = y;
nuclear@2 232
nuclear@4 233 if(!dx && !dy) {
nuclear@4 234 return;
nuclear@4 235 }
nuclear@4 236 if(bnstate[0]) {
nuclear@4 237 cam_theta += dx * 0.5;
nuclear@4 238 cam_phi += dy * 0.5;
nuclear@3 239
nuclear@4 240 if(cam_phi < -90) cam_phi = -90;
nuclear@4 241 if(cam_phi > 90) cam_phi = 90;
nuclear@4 242 }
nuclear@3 243 }
nuclear@3 244
nuclear@2 245 static void draw_scene()
nuclear@2 246 {
nuclear@3 247 float lpos[][4] = {
nuclear@3 248 {-0.7, 0.7, 1, 0},
nuclear@3 249 {1, 0, 1, 0}
nuclear@3 250 };
nuclear@3 251 float lcol[][4] = {
nuclear@3 252 {0.9, 0.7, 0.6, 1},
nuclear@3 253 {0.3, 0.4, 0.75, 1}
nuclear@3 254 };
nuclear@3 255 for(int i=0; i<2; i++) {
nuclear@3 256 glEnable(GL_LIGHT0 + i);
nuclear@3 257 glLightfv(GL_LIGHT0 + i, GL_POSITION, lpos[i]);
nuclear@3 258 glLightfv(GL_LIGHT0 + i, GL_DIFFUSE, lcol[i]);
nuclear@3 259 glLightfv(GL_LIGHT0 + i, GL_SPECULAR, lcol[i]);
nuclear@3 260 }
nuclear@3 261
nuclear@3 262 glMatrixMode(GL_MODELVIEW);
nuclear@3 263
nuclear@5 264 glBindTexture(GL_TEXTURE_2D, chess_tex);
nuclear@5 265 glEnable(GL_TEXTURE_2D);
nuclear@3 266 material(1, 1, 1, 1);
nuclear@3 267 glBegin(GL_QUADS);
nuclear@3 268 glNormal3f(0, 1, 0);
nuclear@5 269 glTexCoord2f(0, 0); glVertex3f(-10, 0, 10);
nuclear@5 270 glTexCoord2f(1, 0); glVertex3f(10, 0, 10);
nuclear@5 271 glTexCoord2f(1, 1); glVertex3f(10, 0, -10);
nuclear@5 272 glTexCoord2f(0, 1); glVertex3f(-10, 0, -10);
nuclear@3 273 glEnd();
nuclear@5 274 glDisable(GL_TEXTURE_2D);
nuclear@3 275
nuclear@3 276 material(1, 1, 1, 0.4);
nuclear@3 277 glPushMatrix();
nuclear@3 278 glTranslatef(0, 1.3, -10);
nuclear@3 279 glFrontFace(GL_CW);
nuclear@3 280 bezier_teapot(2.0);
nuclear@3 281 glFrontFace(GL_CCW);
nuclear@3 282 glPopMatrix();
nuclear@2 283 }
nuclear@2 284
nuclear@4 285 static void material(float r, float g, float b, float roughness)
nuclear@4 286 {
nuclear@4 287 float gloss = 1.0 - roughness;
nuclear@4 288 float diffuse[] = {r * roughness, g * roughness, b * roughness, 1.0};
nuclear@4 289 float specular[] = {gloss, gloss, gloss, 1.0};
nuclear@4 290 float shin = gloss * 128.0;
nuclear@4 291
nuclear@4 292 glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, diffuse);
nuclear@4 293 glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specular);
nuclear@4 294 glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, shin);
nuclear@4 295 }
nuclear@4 296
nuclear@4 297
nuclear@2 298 static void toggle_hmd_fullscr()
nuclear@2 299 {
nuclear@2 300 static bool fullscr;
nuclear@2 301 static int prev_x, prev_y;
nuclear@2 302 //static int prev_xsz, prev_ysz;
nuclear@2 303
nuclear@2 304 fullscr = !fullscr;
nuclear@2 305 if(fullscr) {
nuclear@2 306 /* entering fullscreen on the HMD */
nuclear@2 307 int xoffs = vr_geti_def(VR_WIN_XOFFS, -1);
nuclear@2 308 int yoffs = vr_geti_def(VR_WIN_YOFFS, -1);
nuclear@2 309 if(xoffs != -1) {
nuclear@2 310 get_window_pos(&prev_x, &prev_y);
nuclear@2 311 move_window(xoffs, yoffs);
nuclear@2 312 }
nuclear@2 313
nuclear@2 314 int xsz = vr_geti_def(VR_DISPLAY_WIDTH, -1);
nuclear@2 315 int ysz = vr_geti_def(VR_DISPLAY_HEIGHT, -1);
nuclear@2 316 if(xsz != -1) {
nuclear@2 317 //prev_xsz = win_width;
nuclear@2 318 //prev_ysz = win_height;
nuclear@2 319 resize_window(xsz, ysz);
nuclear@2 320 }
nuclear@2 321 enter_fullscreen();
nuclear@2 322
nuclear@2 323 } else {
nuclear@2 324 /* leaving fullscreen */
nuclear@2 325 leave_fullscreen();
nuclear@2 326 /*move_window(prev_x, prev_y);
nuclear@2 327 resize_window(prev_xsz, prev_ysz);*/
nuclear@2 328 }
nuclear@2 329 }
nuclear@2 330
nuclear@2 331 static void create_rtarg(int x, int y)
nuclear@2 332 {
nuclear@2 333 if(x == fb_xsz && y == fb_ysz) {
nuclear@2 334 return; // nothing changed
nuclear@2 335 }
nuclear@2 336
nuclear@2 337 fb_xsz = x;
nuclear@2 338 fb_ysz = y;
nuclear@2 339 fb_tex_xsz = next_pow2(fb_xsz);
nuclear@2 340 fb_tex_ysz = next_pow2(fb_ysz);
nuclear@2 341
nuclear@5 342 printf("creating %dx%d render target (tex size: %dx%d)\n", fb_xsz, fb_ysz, fb_tex_xsz, fb_tex_ysz);
nuclear@5 343
nuclear@2 344 if(!fbo) {
nuclear@2 345 glGenFramebuffers(1, &fbo);
nuclear@2 346
nuclear@2 347 glGenTextures(1, &fb_tex);
nuclear@2 348 glBindTexture(GL_TEXTURE_2D, fb_tex);
nuclear@2 349 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
nuclear@2 350 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
nuclear@2 351
nuclear@2 352 glGenRenderbuffers(1, &fb_depth);
nuclear@2 353 }
nuclear@2 354
nuclear@2 355 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
nuclear@2 356
nuclear@2 357 glBindTexture(GL_TEXTURE_2D, fb_tex);
nuclear@2 358 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, fb_tex_xsz, fb_tex_ysz, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
nuclear@2 359 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb_tex, 0);
nuclear@2 360
nuclear@2 361 glBindRenderbuffer(GL_RENDERBUFFER, fb_depth);
nuclear@2 362 glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, fb_tex_xsz, fb_tex_ysz);
nuclear@2 363 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, fb_depth);
nuclear@2 364
nuclear@2 365 assert(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
nuclear@2 366 glBindFramebuffer(GL_FRAMEBUFFER, 0);
nuclear@2 367 }
nuclear@2 368
nuclear@2 369 static int next_pow2(int x)
nuclear@2 370 {
nuclear@2 371 x -= 1;
nuclear@2 372 x |= x >> 1;
nuclear@2 373 x |= x >> 2;
nuclear@2 374 x |= x >> 4;
nuclear@2 375 x |= x >> 8;
nuclear@2 376 x |= x >> 16;
nuclear@2 377 return x + 1;
nuclear@2 378 }
nuclear@6 379
nuclear@6 380 static const char *strip_spaces(const char *str)
nuclear@6 381 {
nuclear@6 382 while(*str && isspace(*str)) str++;
nuclear@6 383 return str;
nuclear@6 384 }
nuclear@6 385
nuclear@7 386 struct Variable {
nuclear@7 387 const char *var;
nuclear@7 388 enum { BOOL, NUM } type;
nuclear@7 389 bool bool_val;
nuclear@7 390 float num_val;
nuclear@7 391 };
nuclear@7 392
nuclear@6 393 static void con_handle(const char *cmd)
nuclear@6 394 {
nuclear@6 395 std::vector<char*> argv;
nuclear@6 396 cmd = strip_spaces(cmd);
nuclear@6 397
nuclear@6 398 if(!*cmd || *cmd == '#') {
nuclear@6 399 return;
nuclear@6 400 }
nuclear@6 401
nuclear@6 402 char *line = (char*)alloca(strlen(cmd) + 1);
nuclear@6 403 strcpy(line, cmd);
nuclear@6 404
nuclear@6 405 char *tok = 0;
nuclear@6 406 while((tok = strtok(tok ? 0 : line, " \t\v\n\r"))) {
nuclear@6 407 argv.push_back(tok);
nuclear@6 408 }
nuclear@6 409
nuclear@6 410 if(strcmp(argv[0], "set") == 0) {
nuclear@6 411 if(argv.size() < 3) {
nuclear@7 412 con.printf("set must be followed by 2 arguments\n");
nuclear@6 413 return;
nuclear@6 414 }
nuclear@7 415 if(have_gvar(argv[1])) {
nuclear@7 416 GameVariable &gv = get_gvar(argv[1]);
nuclear@7 417 con.printf("%s->%s (prev: %s)\n", argv[1], argv[2], gv.to_str().c_str());
nuclear@6 418 } else {
nuclear@7 419 con.printf("%s->%s\n", argv[1], argv[2]);
nuclear@6 420 }
nuclear@7 421 set_gvar_parse(argv[1], argv[2]);
nuclear@7 422
nuclear@7 423 } else if(strcmp(argv[0], "get") == 0) {
nuclear@7 424 if(argv.size() < 2) {
nuclear@7 425 con.printf("get must be followed by the variable name\n");
nuclear@7 426 return;
nuclear@7 427 }
nuclear@7 428 if(have_gvar(argv[1])) {
nuclear@7 429 GameVariable &gv = get_gvar(argv[1]);
nuclear@7 430 con.printf("%s: %s\n", argv[1], gv.to_str().c_str());
nuclear@7 431 }
nuclear@7 432
nuclear@7 433 } else if(strcmp(argv[0], "vars") == 0) {
nuclear@7 434 std::list<std::string> vars = get_gvar_list();
nuclear@7 435 std::list<std::string>::const_iterator it = vars.begin();
nuclear@7 436 while(it != vars.end()) {
nuclear@7 437 con.printf(" %s\n", it++->c_str());
nuclear@7 438 }
nuclear@7 439
nuclear@6 440 } else if(strcmp(argv[0], "exit") == 0 || strcmp(argv[0], "quit") == 0) {
nuclear@6 441 exit_game();
nuclear@7 442
nuclear@7 443 } else if(strcmp(argv[0], "help") == 0) {
nuclear@7 444 con.printf("available commands:\n");
nuclear@7 445 con.printf(" set <var> <val> set value to a variable\n");
nuclear@7 446 con.printf(" get <var> print the value of a variable\n");
nuclear@7 447 con.printf(" vars print a list of all variables\n");
nuclear@7 448 con.printf(" help print this help screen\n");
nuclear@7 449 con.printf(" exit or quit quit the program\n");
nuclear@7 450
nuclear@6 451 } else {
nuclear@6 452 con.printf("invalid command: %s\n", argv[0]);
nuclear@6 453 }
nuclear@6 454 }