nuclear@2: #include nuclear@2: #include nuclear@6: #include nuclear@2: #include nuclear@4: #include nuclear@2: #include "opengl.h" nuclear@2: #include "game.h" nuclear@2: #include "goatvr.h" nuclear@3: #include "teapot.h" nuclear@5: #include "console.h" nuclear@5: #include "drawtext.h" nuclear@7: #include "game_var.h" nuclear@8: #include "scenefile.h" nuclear@2: nuclear@2: static void draw_scene(); nuclear@4: static void material(float r, float g, float b, float roughness); nuclear@2: static void toggle_hmd_fullscr(); nuclear@2: static void create_rtarg(int x, int y); nuclear@2: static int next_pow2(int x); nuclear@6: static void con_handle(const char *cmd); nuclear@2: nuclear@2: static int win_width, win_height; nuclear@2: static unsigned int fb_tex; nuclear@2: static unsigned int fbo, fb_depth; nuclear@2: static int fb_xsz, fb_ysz; nuclear@2: static int fb_tex_xsz, fb_tex_ysz; nuclear@2: nuclear@5: static unsigned int chess_tex; nuclear@5: nuclear@8: static float cam_theta = -90, cam_phi; nuclear@9: static Vector3 cam_pos = Vector3(15.4, 0, 0); nuclear@4: static bool keystate[256]; nuclear@4: nuclear@9: static float cam_vvel; nuclear@9: static const float grav = -9.8; nuclear@9: nuclear@5: static Console con; nuclear@5: static dtx_font *con_font; nuclear@5: nuclear@8: static SceneFile scn; nuclear@8: nuclear@8: #define GVAR_LOOK_WALK "look-walk" nuclear@8: #define GVAR_MAX_STEP "max-step" nuclear@7: nuclear@2: bool game_init() nuclear@2: { nuclear@2: init_opengl(); nuclear@2: nuclear@8: if(!(con_font = dtx_open_font_glyphmap("data/mono14.glyphmap"))) { nuclear@8: fprintf(stderr, "failed to open console font\n"); nuclear@8: return false; nuclear@8: } nuclear@8: con.set_font(con_font, 14); nuclear@8: con.set_size(7, 52); nuclear@8: con.set_position(0, 0, Console::CENTER); nuclear@8: con.set_command_func(con_handle); nuclear@8: con.set_echo(false); nuclear@8: nuclear@8: nuclear@2: if(vr_init() == -1) { nuclear@2: return false; nuclear@2: } nuclear@2: nuclear@2: glEnable(GL_DEPTH_TEST); nuclear@2: glEnable(GL_CULL_FACE); nuclear@3: glEnable(GL_LIGHTING); nuclear@2: nuclear@5: unsigned char chess_pixels[] = { nuclear@5: 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, nuclear@5: 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff nuclear@5: }; nuclear@5: glGenTextures(1, &chess_tex); nuclear@5: glBindTexture(GL_TEXTURE_2D, chess_tex); nuclear@5: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); nuclear@5: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); nuclear@5: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 4, 4, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, chess_pixels); nuclear@5: nuclear@8: if(!scn.load("data/sibenik.goatsce")) { nuclear@8: fprintf(stderr, "failed to load scene file\n"); nuclear@5: return false; nuclear@5: } nuclear@7: nuclear@7: /* initialize all option variables */ nuclear@8: set_gvar_bool(GVAR_LOOK_WALK, true); nuclear@8: set_gvar_num(GVAR_MAX_STEP, 0.5); nuclear@5: nuclear@2: return true; nuclear@2: } nuclear@2: nuclear@2: void game_cleanup() nuclear@2: { nuclear@2: vr_shutdown(); nuclear@2: } nuclear@2: nuclear@2: void game_update(long tm) nuclear@2: { nuclear@4: static long prev_upd; nuclear@4: float dt = (tm - prev_upd) / 1000.0; nuclear@4: prev_upd = tm; nuclear@4: nuclear@5: if(con.is_visible()) { nuclear@5: con.update(); nuclear@5: return; nuclear@5: } nuclear@5: nuclear@5: float offs = dt * 5.0; nuclear@4: Vector3 dir; nuclear@4: nuclear@4: if(keystate['d'] || keystate['D']) { nuclear@4: dir += Vector3(offs, 0, 0); nuclear@4: } nuclear@4: if(keystate['a'] || keystate['A']) { nuclear@4: dir += Vector3(-offs, 0, 0); nuclear@4: } nuclear@4: if(keystate['w'] || keystate['W']) { nuclear@4: dir += Vector3(0, 0, -offs); nuclear@4: } nuclear@4: if(keystate['s'] || keystate['S']) { nuclear@4: dir += Vector3(0, 0, offs); nuclear@4: } nuclear@4: nuclear@8: if(get_gvar_bool(GVAR_LOOK_WALK)) { nuclear@7: float rot[4]; nuclear@7: vr_view_rotation(0, rot); nuclear@7: Quaternion q(rot[3], rot[0], rot[1], rot[2]); nuclear@7: nuclear@7: dir.transform(q); nuclear@7: } nuclear@7: nuclear@5: float cos_theta = cos(DEG_TO_RAD(cam_theta)); nuclear@5: float sin_theta = sin(DEG_TO_RAD(cam_theta)); nuclear@5: cam_pos.x += dir.x * cos_theta - dir.z * sin_theta; nuclear@5: cam_pos.z += dir.x * sin_theta + dir.z * cos_theta; nuclear@5: nuclear@8: float max_step = get_gvar_num(GVAR_MAX_STEP); nuclear@9: Vector3 floor_pos = scn.find_walk_pos(cam_pos + Vector3(0, max_step, 0)); nuclear@9: nuclear@9: if(floor_pos.y < cam_pos.y) { nuclear@9: cam_vvel += grav * dt; nuclear@9: cam_pos.y += cam_vvel * dt; nuclear@10: if(cam_pos.y <= floor_pos.y) { nuclear@9: cam_pos.y = floor_pos.y; nuclear@10: cam_vvel = 0; nuclear@9: } nuclear@10: } else { nuclear@10: cam_pos.y = floor_pos.y; nuclear@10: cam_vvel = 0; nuclear@9: } nuclear@2: } nuclear@2: nuclear@2: void game_display() nuclear@2: { nuclear@2: glBindFramebuffer(GL_FRAMEBUFFER, fbo); nuclear@2: glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); nuclear@2: nuclear@2: for(int i=0; i<2; i++) { nuclear@2: glViewport(i == 0 ? 0 : fb_xsz / 2, 0, fb_xsz / 2, fb_ysz); nuclear@2: vr_begin(i); nuclear@2: nuclear@2: float proj[16]; nuclear@2: if(vr_proj_matrix(i, 0.5, 500.0, proj)) { nuclear@2: glMatrixMode(GL_PROJECTION); nuclear@2: glLoadMatrixf(proj); nuclear@2: } nuclear@2: nuclear@2: glMatrixMode(GL_MODELVIEW); nuclear@2: nuclear@2: float view[16]; nuclear@2: vr_view_matrix(i, view); nuclear@2: glLoadMatrixf(view); nuclear@4: glRotatef(cam_phi, 1, 0, 0); nuclear@4: glRotatef(cam_theta, 0, 1, 0); nuclear@2: /* move the camera to the eye level of the user */ nuclear@2: glTranslatef(0, -vr_getf_def(VR_EYE_HEIGHT, 1.65), 0); nuclear@4: glTranslatef(-cam_pos.x, -cam_pos.y, -cam_pos.z); nuclear@2: nuclear@2: draw_scene(); nuclear@5: con.draw(); nuclear@2: nuclear@2: vr_end(); nuclear@2: } nuclear@2: nuclear@2: glBindFramebuffer(GL_FRAMEBUFFER, 0); nuclear@2: glViewport(0, 0, win_width, win_height); nuclear@2: nuclear@2: vr_swap_buffers(); nuclear@2: assert(glGetError() == GL_NO_ERROR); nuclear@2: } nuclear@2: nuclear@2: void game_reshape(int x, int y) nuclear@2: { nuclear@2: win_width = x; nuclear@2: win_height = y; nuclear@2: nuclear@5: create_rtarg(vr_geti_def(VR_RENDER_XRES, x * 2), vr_geti_def(VR_RENDER_YRES, y)); nuclear@2: vr_output_texture(fb_tex, 0, 0, (float)fb_xsz / (float)fb_tex_xsz, (float)fb_ysz / (float)fb_tex_ysz); nuclear@2: nuclear@2: /* these might be overriden in VR mode (see game_display) */ nuclear@2: glViewport(0, 0, x, y); nuclear@2: glMatrixMode(GL_PROJECTION); nuclear@2: glLoadIdentity(); nuclear@2: gluPerspective(50.0, (float)x / (float)y, 0.5, 500.0); nuclear@2: } nuclear@2: nuclear@2: void game_keyboard(int key, bool pressed) nuclear@2: { nuclear@2: if(pressed) { nuclear@5: if(con.is_visible()) { nuclear@5: if(key == 27 || key == '`') { nuclear@5: con.hide(); nuclear@5: } else { nuclear@5: con.input_key(key); nuclear@5: } nuclear@5: } else { nuclear@5: switch(key) { nuclear@5: case 27: nuclear@5: exit_game(); nuclear@5: break; nuclear@2: nuclear@5: case 'f': nuclear@5: toggle_hmd_fullscr(); nuclear@5: break; nuclear@2: nuclear@5: case 'r': nuclear@5: vr_recenter(); nuclear@5: break; nuclear@2: nuclear@5: case '`': nuclear@5: con.show(); nuclear@5: break; nuclear@5: nuclear@5: default: nuclear@5: break; nuclear@5: } nuclear@2: } nuclear@2: } nuclear@4: nuclear@4: if(key < 256) { nuclear@4: keystate[key] = pressed; nuclear@4: } nuclear@2: } nuclear@2: nuclear@4: nuclear@4: static int prev_x, prev_y; nuclear@4: static bool bnstate[32]; nuclear@4: nuclear@2: void game_mouse_button(int bn, bool state, int x, int y) nuclear@2: { nuclear@4: bnstate[bn] = state; nuclear@4: prev_x = x; nuclear@4: prev_y = y; nuclear@2: } nuclear@2: nuclear@2: void game_mouse_motion(int x, int y) nuclear@2: { nuclear@4: int dx = x - prev_x; nuclear@4: int dy = y - prev_y; nuclear@4: prev_x = x; nuclear@4: prev_y = y; nuclear@2: nuclear@4: if(!dx && !dy) { nuclear@4: return; nuclear@4: } nuclear@4: if(bnstate[0]) { nuclear@4: cam_theta += dx * 0.5; nuclear@4: cam_phi += dy * 0.5; nuclear@3: nuclear@4: if(cam_phi < -90) cam_phi = -90; nuclear@4: if(cam_phi > 90) cam_phi = 90; nuclear@4: } nuclear@3: } nuclear@3: nuclear@8: MeshFace dbg_walk_face; nuclear@8: nuclear@2: static void draw_scene() nuclear@2: { nuclear@3: float lpos[][4] = { nuclear@3: {-0.7, 0.7, 1, 0}, nuclear@3: {1, 0, 1, 0} nuclear@3: }; nuclear@3: float lcol[][4] = { nuclear@3: {0.9, 0.7, 0.6, 1}, nuclear@3: {0.3, 0.4, 0.75, 1} nuclear@3: }; nuclear@3: for(int i=0; i<2; i++) { nuclear@3: glEnable(GL_LIGHT0 + i); nuclear@3: glLightfv(GL_LIGHT0 + i, GL_POSITION, lpos[i]); nuclear@3: glLightfv(GL_LIGHT0 + i, GL_DIFFUSE, lcol[i]); nuclear@3: glLightfv(GL_LIGHT0 + i, GL_SPECULAR, lcol[i]); nuclear@3: } nuclear@3: nuclear@3: glMatrixMode(GL_MODELVIEW); nuclear@3: nuclear@13: /*glBindTexture(GL_TEXTURE_2D, chess_tex); nuclear@5: glEnable(GL_TEXTURE_2D); nuclear@3: material(1, 1, 1, 1); nuclear@3: glBegin(GL_QUADS); nuclear@3: glNormal3f(0, 1, 0); nuclear@5: glTexCoord2f(0, 0); glVertex3f(-10, 0, 10); nuclear@5: glTexCoord2f(1, 0); glVertex3f(10, 0, 10); nuclear@5: glTexCoord2f(1, 1); glVertex3f(10, 0, -10); nuclear@5: glTexCoord2f(0, 1); glVertex3f(-10, 0, -10); nuclear@3: glEnd(); nuclear@5: glDisable(GL_TEXTURE_2D); nuclear@3: nuclear@13: material(1, 1, 1, 0.4); nuclear@3: glPushMatrix(); nuclear@3: glTranslatef(0, 1.3, -10); nuclear@3: glFrontFace(GL_CW); nuclear@3: bezier_teapot(2.0); nuclear@3: glFrontFace(GL_CCW); nuclear@8: glPopMatrix();*/ nuclear@8: nuclear@13: glBindBuffer(GL_ARRAY_BUFFER, 0); nuclear@13: glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); nuclear@13: nuclear@8: for(size_t i=0; idraw(); nuclear@8: } nuclear@8: nuclear@10: /*glPushAttrib(GL_ENABLE_BIT); nuclear@8: glDisable(GL_DEPTH_TEST); nuclear@8: glDisable(GL_LIGHTING); nuclear@8: nuclear@8: glBegin(GL_TRIANGLES); nuclear@8: glColor3f(0, 1, 0); nuclear@8: glVertex3fv(&dbg_walk_face.v[0].x); nuclear@8: glVertex3fv(&dbg_walk_face.v[1].x); nuclear@8: glVertex3fv(&dbg_walk_face.v[2].x); nuclear@8: glColor3f(1, 1, 1); nuclear@8: glEnd(); nuclear@8: nuclear@10: glPopAttrib();*/ nuclear@2: } nuclear@2: nuclear@4: static void material(float r, float g, float b, float roughness) nuclear@4: { nuclear@4: float gloss = 1.0 - roughness; nuclear@4: float diffuse[] = {r * roughness, g * roughness, b * roughness, 1.0}; nuclear@4: float specular[] = {gloss, gloss, gloss, 1.0}; nuclear@4: float shin = gloss * 128.0; nuclear@4: nuclear@4: glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, diffuse); nuclear@4: glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specular); nuclear@4: glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, shin); nuclear@4: } nuclear@4: nuclear@4: nuclear@2: static void toggle_hmd_fullscr() nuclear@2: { nuclear@2: static bool fullscr; nuclear@2: static int prev_x, prev_y; nuclear@2: //static int prev_xsz, prev_ysz; nuclear@2: nuclear@2: fullscr = !fullscr; nuclear@2: if(fullscr) { nuclear@2: /* entering fullscreen on the HMD */ nuclear@2: int xoffs = vr_geti_def(VR_WIN_XOFFS, -1); nuclear@2: int yoffs = vr_geti_def(VR_WIN_YOFFS, -1); nuclear@2: if(xoffs != -1) { nuclear@2: get_window_pos(&prev_x, &prev_y); nuclear@2: move_window(xoffs, yoffs); nuclear@2: } nuclear@2: nuclear@2: int xsz = vr_geti_def(VR_DISPLAY_WIDTH, -1); nuclear@2: int ysz = vr_geti_def(VR_DISPLAY_HEIGHT, -1); nuclear@2: if(xsz != -1) { nuclear@2: //prev_xsz = win_width; nuclear@2: //prev_ysz = win_height; nuclear@2: resize_window(xsz, ysz); nuclear@2: } nuclear@2: enter_fullscreen(); nuclear@2: nuclear@2: } else { nuclear@2: /* leaving fullscreen */ nuclear@2: leave_fullscreen(); nuclear@2: /*move_window(prev_x, prev_y); nuclear@2: resize_window(prev_xsz, prev_ysz);*/ nuclear@2: } nuclear@2: } nuclear@2: nuclear@2: static void create_rtarg(int x, int y) nuclear@2: { nuclear@2: if(x == fb_xsz && y == fb_ysz) { nuclear@2: return; // nothing changed nuclear@2: } nuclear@2: nuclear@2: fb_xsz = x; nuclear@2: fb_ysz = y; nuclear@2: fb_tex_xsz = next_pow2(fb_xsz); nuclear@2: fb_tex_ysz = next_pow2(fb_ysz); nuclear@2: nuclear@5: printf("creating %dx%d render target (tex size: %dx%d)\n", fb_xsz, fb_ysz, fb_tex_xsz, fb_tex_ysz); nuclear@5: nuclear@2: if(!fbo) { nuclear@2: glGenFramebuffers(1, &fbo); nuclear@2: nuclear@2: glGenTextures(1, &fb_tex); nuclear@2: glBindTexture(GL_TEXTURE_2D, fb_tex); nuclear@2: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); nuclear@2: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); nuclear@2: nuclear@2: glGenRenderbuffers(1, &fb_depth); nuclear@2: } nuclear@2: nuclear@2: glBindFramebuffer(GL_FRAMEBUFFER, fbo); nuclear@2: nuclear@2: glBindTexture(GL_TEXTURE_2D, fb_tex); nuclear@2: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, fb_tex_xsz, fb_tex_ysz, 0, GL_RGB, GL_UNSIGNED_BYTE, 0); nuclear@2: glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb_tex, 0); nuclear@2: nuclear@2: glBindRenderbuffer(GL_RENDERBUFFER, fb_depth); nuclear@2: glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, fb_tex_xsz, fb_tex_ysz); nuclear@2: glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, fb_depth); nuclear@2: nuclear@2: assert(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE); nuclear@2: glBindFramebuffer(GL_FRAMEBUFFER, 0); nuclear@2: } nuclear@2: nuclear@2: static int next_pow2(int x) nuclear@2: { nuclear@2: x -= 1; nuclear@2: x |= x >> 1; nuclear@2: x |= x >> 2; nuclear@2: x |= x >> 4; nuclear@2: x |= x >> 8; nuclear@2: x |= x >> 16; nuclear@2: return x + 1; nuclear@2: } nuclear@6: nuclear@6: static const char *strip_spaces(const char *str) nuclear@6: { nuclear@6: while(*str && isspace(*str)) str++; nuclear@6: return str; nuclear@6: } nuclear@6: nuclear@7: struct Variable { nuclear@7: const char *var; nuclear@7: enum { BOOL, NUM } type; nuclear@7: bool bool_val; nuclear@7: float num_val; nuclear@7: }; nuclear@7: nuclear@6: static void con_handle(const char *cmd) nuclear@6: { nuclear@8: /* set debug values */ nuclear@8: set_gvar_num("cam-pos-x", cam_pos[0]); nuclear@8: set_gvar_num("cam-pos-y", cam_pos[1]); nuclear@8: set_gvar_num("cam-pos-z", cam_pos[2]); nuclear@8: nuclear@6: std::vector argv; nuclear@6: cmd = strip_spaces(cmd); nuclear@6: nuclear@6: if(!*cmd || *cmd == '#') { nuclear@6: return; nuclear@6: } nuclear@6: nuclear@6: char *line = (char*)alloca(strlen(cmd) + 1); nuclear@6: strcpy(line, cmd); nuclear@6: nuclear@6: char *tok = 0; nuclear@6: while((tok = strtok(tok ? 0 : line, " \t\v\n\r"))) { nuclear@6: argv.push_back(tok); nuclear@6: } nuclear@6: nuclear@6: if(strcmp(argv[0], "set") == 0) { nuclear@6: if(argv.size() < 3) { nuclear@7: con.printf("set must be followed by 2 arguments\n"); nuclear@6: return; nuclear@6: } nuclear@7: if(have_gvar(argv[1])) { nuclear@7: GameVariable &gv = get_gvar(argv[1]); nuclear@7: con.printf("%s->%s (prev: %s)\n", argv[1], argv[2], gv.to_str().c_str()); nuclear@6: } else { nuclear@7: con.printf("%s->%s\n", argv[1], argv[2]); nuclear@6: } nuclear@11: nuclear@11: int type = set_gvar_parse(argv[1], argv[2]); nuclear@11: if(type == GameVariable::NUMBER) { nuclear@11: vr_setf(argv[1], atof(argv[2])); nuclear@11: } else if(type == GameVariable::BOOL) { nuclear@11: vr_seti(argv[1], get_gvar_bool(argv[1]) ? 1 : 0); nuclear@11: } nuclear@11: nuclear@11: // update state vars nuclear@11: cam_pos[0] = get_gvar_num("cam-pos-x"); nuclear@11: cam_pos[1] = get_gvar_num("cam-pos-y"); nuclear@11: cam_pos[2] = get_gvar_num("cam-pos-z"); nuclear@7: nuclear@7: } else if(strcmp(argv[0], "get") == 0) { nuclear@7: if(argv.size() < 2) { nuclear@7: con.printf("get must be followed by the variable name\n"); nuclear@7: return; nuclear@7: } nuclear@7: if(have_gvar(argv[1])) { nuclear@7: GameVariable &gv = get_gvar(argv[1]); nuclear@7: con.printf("%s: %s\n", argv[1], gv.to_str().c_str()); nuclear@7: } nuclear@7: nuclear@7: } else if(strcmp(argv[0], "vars") == 0) { nuclear@7: std::list vars = get_gvar_list(); nuclear@7: std::list::const_iterator it = vars.begin(); nuclear@7: while(it != vars.end()) { nuclear@7: con.printf(" %s\n", it++->c_str()); nuclear@7: } nuclear@7: nuclear@6: } else if(strcmp(argv[0], "exit") == 0 || strcmp(argv[0], "quit") == 0) { nuclear@6: exit_game(); nuclear@7: nuclear@7: } else if(strcmp(argv[0], "help") == 0) { nuclear@7: con.printf("available commands:\n"); nuclear@7: con.printf(" set set value to a variable\n"); nuclear@7: con.printf(" get print the value of a variable\n"); nuclear@7: con.printf(" vars print a list of all variables\n"); nuclear@7: con.printf(" help print this help screen\n"); nuclear@7: con.printf(" exit or quit quit the program\n"); nuclear@7: nuclear@6: } else { nuclear@6: con.printf("invalid command: %s\n", argv[0]); nuclear@6: } nuclear@6: }