nuclear@1: #include nuclear@1: #include nuclear@1: #include nuclear@1: #include "opengl.h" nuclear@46: #include "psys/psys.h" nuclear@1: #include "level.h" nuclear@48: #include "texture.h" nuclear@1: #include "camera.h" nuclear@5: #include "datapath.h" nuclear@5: #include "tileset.h" nuclear@15: #include "renderer.h" nuclear@41: #include "renderer_deferred.h" nuclear@60: #include "renderer_multipass.h" nuclear@24: #include "cmdcon.h" nuclear@18: #include "cfg.h" nuclear@38: #include "timer.h" nuclear@48: #include "audio/audio.h" nuclear@48: #include "audio/source.h" nuclear@55: #include "audio/ovstream.h" nuclear@1: nuclear@17: bool init(int xsz, int ysz); nuclear@5: void cleanup(); nuclear@7: void idle(); nuclear@1: void disp(); nuclear@15: void draw(); nuclear@18: void view_matrix(int eye); nuclear@18: void proj_matrix(int eye); nuclear@7: void update(unsigned long msec); nuclear@1: void reshape(int x, int y); nuclear@1: void keyb(unsigned char key, int x, int y); nuclear@7: void key_release(unsigned char key, int x, int y); nuclear@24: void keyb_con(unsigned char key, int x, int y); nuclear@1: void mouse(int bn, int state, int x, int y); nuclear@1: void motion(int x, int y); nuclear@46: unsigned int load_psys_tex(const char *fname, void *cls); nuclear@1: nuclear@5: static TileSet *tileset; nuclear@1: static Level *level; nuclear@7: nuclear@7: static FpsCamera cam; nuclear@7: static bool keystate[256]; nuclear@1: nuclear@18: static float stereo_focus_dist = 0.25; nuclear@18: static float stereo_eye_sep = stereo_focus_dist / 30.0; nuclear@18: nuclear@24: static bool show_con; nuclear@5: nuclear@48: static AudioSource *move_sound; nuclear@55: static OggVorbisStream *music; nuclear@48: nuclear@1: int main(int argc, char **argv) nuclear@1: { nuclear@1: glutInit(&argc, argv); nuclear@5: nuclear@18: if(!cfg.parse_args(argc, argv)) { nuclear@18: return 1; nuclear@5: } nuclear@5: nuclear@18: glutInitWindowSize(cfg.width, cfg.height); nuclear@18: glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE | (cfg.stereo ? GLUT_STEREO : 0)); nuclear@18: glutCreateWindow("dungeon crawler prototype"); nuclear@1: nuclear@7: glutIdleFunc(idle); nuclear@1: glutDisplayFunc(disp); nuclear@1: glutReshapeFunc(reshape); nuclear@1: glutKeyboardFunc(keyb); nuclear@7: glutKeyboardUpFunc(key_release); nuclear@1: glutMouseFunc(mouse); nuclear@1: glutMotionFunc(motion); nuclear@1: nuclear@1: glewInit(); nuclear@1: nuclear@18: if(!init(cfg.width, cfg.height)) { nuclear@5: return 1; nuclear@5: } nuclear@39: atexit(cleanup); nuclear@5: nuclear@5: glutMainLoop(); nuclear@5: } nuclear@5: nuclear@17: bool init(int xsz, int ysz) nuclear@5: { nuclear@41: // backup light for the forward crappy renderer nuclear@1: glEnable(GL_LIGHTING); nuclear@1: glEnable(GL_LIGHT0); nuclear@41: nuclear@41: float ldir[] = {0, 0, -0.5, 1}; nuclear@1: glLightfv(GL_LIGHT0, GL_POSITION, ldir); nuclear@41: float lcol[] = {1, 1, 1, 1}; nuclear@41: glLightfv(GL_LIGHT0, GL_DIFFUSE, lcol); nuclear@41: glLightfv(GL_LIGHT0, GL_SPECULAR, lcol); nuclear@1: nuclear@1: glEnable(GL_DEPTH_TEST); nuclear@1: glEnable(GL_CULL_FACE); nuclear@1: glEnable(GL_MULTISAMPLE); nuclear@1: nuclear@25: add_data_path("."); nuclear@5: add_data_path("data"); nuclear@48: add_data_path("data/audio"); nuclear@16: add_data_path("sdr"); nuclear@5: nuclear@48: if(cfg.sound && !init_audio()) { nuclear@48: fprintf(stderr, "failed to initialize audio, continuing silently\n"); nuclear@48: cfg.sound = false; nuclear@48: } nuclear@48: if(cfg.sound) { nuclear@48: move_sound = new AudioSource; nuclear@52: move_sound->set_volume(0.4); nuclear@55: nuclear@55: music = new OggVorbisStream; nuclear@63: if(music->open(datafile_path("bgtrack.ogg").c_str())) { nuclear@56: music->set_volume(0.6); nuclear@55: music->play(PlayMode::loop); nuclear@55: } else { nuclear@55: delete music; nuclear@55: music = 0; nuclear@55: } nuclear@48: } nuclear@48: nuclear@60: switch(cfg.rend) { nuclear@60: case Config::Renderer::mrt: nuclear@60: rend = new DeferredRenderer; nuclear@60: if(!rend->init(xsz, ysz)) { nuclear@60: fprintf(stderr, "failed to initialize deferred mrt renderer\n"); nuclear@60: delete rend; nuclear@60: return false; nuclear@60: } nuclear@60: break; nuclear@41: nuclear@60: case Config::Renderer::multipass: nuclear@60: rend = new MultipassRenderer; nuclear@41: if(!rend->init(xsz, ysz)) { nuclear@60: fprintf(stderr, "failed to initialize multipass deferred renderer\n"); nuclear@60: delete rend; nuclear@41: return false; nuclear@41: } nuclear@60: break; nuclear@60: nuclear@60: case Config::Renderer::fwd: nuclear@60: rend = new FwdRenderer; nuclear@60: if(!rend->init(xsz, ysz)) { nuclear@60: fprintf(stderr, "failed to initialize forward renderer\n"); nuclear@60: delete rend; nuclear@60: return false; nuclear@60: } nuclear@60: break; nuclear@60: nuclear@60: default: nuclear@60: // try each in turn falling back to progressively worse renderers nuclear@60: rend = new DeferredRenderer; nuclear@60: if(!rend->init(xsz, ysz)) { nuclear@60: printf("falling back to multipass renderer...\n"); nuclear@60: nuclear@60: delete rend; nuclear@60: rend = new MultipassRenderer(); nuclear@60: if(!rend->init(xsz, ysz)) { nuclear@60: printf("falling back to crappy renderer...\n"); nuclear@60: nuclear@60: rend = new FwdRenderer(); nuclear@60: if(!rend->init(xsz, ysz)) { nuclear@60: fprintf(stderr, "failed to create renderer\n"); nuclear@60: return false; nuclear@60: } nuclear@60: } nuclear@60: } nuclear@15: } nuclear@15: nuclear@26: if(!init_cmdcon()) { nuclear@26: return false; nuclear@26: } nuclear@26: nuclear@46: psys_texture_loader(load_psys_tex, 0, 0); nuclear@46: nuclear@5: // load a tileset nuclear@5: tileset = new TileSet; nuclear@18: printf("loading tileset: %s\n", cfg.tileset_file); nuclear@63: if(!tileset->load(datafile_path(cfg.tileset_file).c_str())) { nuclear@5: return false; nuclear@5: } nuclear@5: set_active_tileset(tileset); nuclear@5: nuclear@1: level = new Level; nuclear@18: printf("loading level: %s\n", cfg.level_file); nuclear@63: if(!level->load(datafile_path(cfg.level_file).c_str())) { nuclear@5: return false; nuclear@1: } nuclear@1: nuclear@7: cam.input_move(0, 0.5, 0); nuclear@7: nuclear@5: return true; nuclear@1: } nuclear@1: nuclear@15: void cleanup() nuclear@15: { nuclear@15: delete level; nuclear@15: delete tileset; nuclear@41: delete rend; nuclear@26: nuclear@26: cleanup_cmdcon(); nuclear@51: nuclear@51: if(cfg.sound) { nuclear@55: delete music; nuclear@51: delete move_sound; nuclear@51: destroy_audio(); nuclear@51: } nuclear@15: } nuclear@15: nuclear@7: void idle() nuclear@7: { nuclear@7: glutPostRedisplay(); nuclear@7: } nuclear@7: nuclear@1: void disp() nuclear@1: { nuclear@38: update(get_time_msec()); nuclear@7: nuclear@18: if(cfg.stereo) { nuclear@18: glDrawBuffer(GL_BACK_LEFT); nuclear@1: nuclear@18: glMatrixMode(GL_PROJECTION); nuclear@18: glLoadIdentity(); nuclear@18: proj_matrix(-1); nuclear@18: glMatrixMode(GL_MODELVIEW); nuclear@18: glLoadIdentity(); nuclear@18: view_matrix(-1); nuclear@1: nuclear@24: draw(); nuclear@18: nuclear@18: glDrawBuffer(GL_BACK_RIGHT); nuclear@18: nuclear@18: glMatrixMode(GL_PROJECTION); nuclear@18: glLoadIdentity(); nuclear@18: proj_matrix(1); nuclear@18: glMatrixMode(GL_MODELVIEW); nuclear@18: glLoadIdentity(); nuclear@18: view_matrix(1); nuclear@18: nuclear@24: draw(); nuclear@18: } else { nuclear@18: glMatrixMode(GL_PROJECTION); nuclear@18: glLoadIdentity(); nuclear@18: proj_matrix(0); nuclear@18: glMatrixMode(GL_MODELVIEW); nuclear@18: glLoadIdentity(); nuclear@18: view_matrix(0); nuclear@18: nuclear@24: draw(); nuclear@18: } nuclear@1: nuclear@1: glutSwapBuffers(); nuclear@1: assert(glGetError() == GL_NO_ERROR); nuclear@7: } nuclear@7: nuclear@24: void draw() nuclear@24: { nuclear@41: glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); nuclear@29: nuclear@41: rend->render(level); nuclear@24: nuclear@24: if(show_con) { nuclear@24: draw_cmdcon(); nuclear@24: } nuclear@24: } nuclear@24: nuclear@18: void view_matrix(int eye) nuclear@18: { nuclear@18: float offs = stereo_eye_sep * eye * 0.5; nuclear@18: glTranslatef(-offs, 0, 0); nuclear@18: cam.use_inverse(); nuclear@18: } nuclear@18: nuclear@18: void proj_matrix(int eye) nuclear@18: { nuclear@18: static const float fov = M_PI / 4.0; nuclear@18: static const float near_clip = 0.1; nuclear@18: static const float far_clip = 100.0; nuclear@18: nuclear@18: float top = near_clip * tan(fov * 0.5); nuclear@18: float right = top * (float)cfg.width / (float)cfg.height; nuclear@18: nuclear@18: float frust_shift = -(float)eye * (stereo_eye_sep * 0.5 * near_clip / stereo_focus_dist); nuclear@18: glFrustum(-right + frust_shift, right + frust_shift, -top, top, near_clip, far_clip); nuclear@18: } nuclear@18: nuclear@18: nuclear@7: void update(unsigned long msec) nuclear@7: { nuclear@7: static unsigned long last_upd; nuclear@7: nuclear@7: if(last_upd == 0) { nuclear@7: last_upd = msec; nuclear@7: } nuclear@7: float dt = (float)(msec - last_upd) / 1000.0; nuclear@7: nuclear@56: float offs = 2.0 * dt; nuclear@7: float dx = 0, dy = 0; nuclear@7: nuclear@7: // handle key input nuclear@48: bool did_move = false; nuclear@7: if(keystate['w'] || keystate['W']) { nuclear@7: dy -= offs; nuclear@48: did_move = true; nuclear@7: } nuclear@7: if(keystate['s'] || keystate['S']) { nuclear@7: dy += offs; nuclear@48: did_move = true; nuclear@7: } nuclear@7: if(keystate['d'] || keystate['D']) { nuclear@7: dx += offs; nuclear@48: did_move = true; nuclear@7: } nuclear@7: if(keystate['a'] || keystate['A']) { nuclear@7: dx -= offs; nuclear@48: did_move = true; nuclear@7: } nuclear@7: nuclear@7: cam.input_move(dx, 0, dy); nuclear@7: nuclear@46: tileset->update_tiles(msec); nuclear@49: nuclear@49: level->set_player_position(cam.get_position()); nuclear@38: level->update(msec, dt); nuclear@38: nuclear@49: if(cfg.sound) { nuclear@50: // set the listener matrix nuclear@50: set_audio_listener(cam.matrix()); nuclear@50: nuclear@50: // play the walking sound if we're walking nuclear@49: int cellx, celly; nuclear@49: level->get_cell_coords_at(cam.get_position(), &cellx, &celly); nuclear@48: nuclear@49: const AudioSample *move_sample; nuclear@49: if(did_move && (move_sample = level->get_sample(cellx, celly, TILE_SAMPLE_WALK))) { nuclear@49: if(move_sample != move_sound->get_sample()) { nuclear@49: move_sound->stop(); nuclear@49: move_sound->set_sample(move_sample); nuclear@49: move_sound->play(); nuclear@49: } nuclear@49: } else { nuclear@49: if(move_sound->get_sample()) { nuclear@49: move_sound->stop(); nuclear@49: move_sound->set_sample(0); nuclear@49: } nuclear@48: } nuclear@48: } nuclear@48: nuclear@7: last_upd = msec; nuclear@1: } nuclear@1: nuclear@1: void reshape(int x, int y) nuclear@1: { nuclear@1: glViewport(0, 0, x, y); nuclear@18: cfg.width = x; nuclear@18: cfg.height = y; nuclear@30: nuclear@41: rend->resize(x, y); nuclear@1: } nuclear@1: nuclear@18: static bool stereo_shift_pressed; nuclear@18: nuclear@1: void keyb(unsigned char key, int x, int y) nuclear@1: { nuclear@1: switch(key) { nuclear@1: case 27: nuclear@1: exit(0); nuclear@18: nuclear@24: case '`': nuclear@24: show_con = true; nuclear@24: glutKeyboardFunc(keyb_con); nuclear@24: glutKeyboardUpFunc(0); nuclear@24: glutPostRedisplay(); nuclear@24: break; nuclear@24: nuclear@18: case 'z': nuclear@18: stereo_shift_pressed = true; nuclear@18: break; nuclear@18: nuclear@48: case 'p': nuclear@48: { nuclear@48: Vector3 pos = cam.get_position(); nuclear@48: int cell_x, cell_y; nuclear@48: level->get_cell_coords_at(pos, &cell_x, &cell_y); nuclear@48: printf("Current position: [%.2f %.2f %.2f] cell: [%d %d]\n", pos.x, pos.y, pos.z, nuclear@48: cell_x, cell_y); nuclear@48: } nuclear@48: break; nuclear@48: nuclear@48: case 'P': nuclear@48: { nuclear@48: Vector3 pos = cam.get_position(); nuclear@48: int cell_x, cell_y; nuclear@48: level->get_cell_coords_at(pos, &cell_x, &cell_y); nuclear@48: AudioSample *sample = level->get_sample(cell_x, cell_y, TILE_SAMPLE_WALK); nuclear@48: printf("walk sample: %p\n", (void*)sample); nuclear@48: } nuclear@48: break; nuclear@48: nuclear@48: nuclear@18: case '\n': nuclear@18: case '\r': nuclear@18: { nuclear@18: static bool fullscr; nuclear@18: if(glutGetModifiers() & GLUT_ACTIVE_ALT) { nuclear@18: fullscr = !fullscr; nuclear@18: if(fullscr) { nuclear@18: glutFullScreen(); nuclear@18: } else { nuclear@18: glutPositionWindow(20, 20); nuclear@18: } nuclear@18: } nuclear@18: } nuclear@18: break; nuclear@18: nuclear@18: default: nuclear@18: break; nuclear@1: } nuclear@7: nuclear@7: keystate[key] = true; nuclear@7: } nuclear@7: nuclear@7: void key_release(unsigned char key, int x, int y) nuclear@7: { nuclear@18: switch(key) { nuclear@18: case 'z': nuclear@18: stereo_shift_pressed = false; nuclear@18: break; nuclear@18: nuclear@18: default: nuclear@18: break; nuclear@18: } nuclear@18: nuclear@7: keystate[key] = false; nuclear@1: } nuclear@1: nuclear@24: void keyb_con(unsigned char key, int x, int y) nuclear@24: { nuclear@24: if(key == '`' || key == 27) { nuclear@24: show_con = false; nuclear@24: glutKeyboardFunc(keyb); nuclear@24: glutKeyboardUpFunc(key_release); nuclear@24: glutPostRedisplay(); nuclear@24: } else { nuclear@24: cmdcon_keypress(key); nuclear@24: } nuclear@24: } nuclear@24: nuclear@1: static int prev_x, prev_y; nuclear@1: static bool bnstate[32]; nuclear@1: nuclear@1: void mouse(int bn, int state, int x, int y) nuclear@1: { nuclear@1: prev_x = x; nuclear@1: prev_y = y; nuclear@1: bnstate[bn - GLUT_LEFT_BUTTON] = state == GLUT_DOWN; nuclear@1: } nuclear@1: nuclear@1: void motion(int x, int y) nuclear@1: { nuclear@1: int dx = x - prev_x; nuclear@1: int dy = y - prev_y; nuclear@1: prev_x = x; nuclear@1: prev_y = y; nuclear@1: nuclear@18: if(stereo_shift_pressed) { nuclear@18: if(dy != 0) { nuclear@18: stereo_focus_dist += dy * 0.01; nuclear@18: stereo_eye_sep = stereo_focus_dist / 30.0; nuclear@18: printf("foc: %f, sep: %f\n", stereo_focus_dist, stereo_eye_sep); nuclear@18: glutPostRedisplay(); nuclear@18: } nuclear@18: return; nuclear@18: } nuclear@18: nuclear@1: if(bnstate[0]) { nuclear@56: cam.input_rotate(dy * 0.0075, dx * 0.0075, 0); nuclear@1: glutPostRedisplay(); nuclear@1: } nuclear@1: if(bnstate[2]) { nuclear@1: cam.input_zoom(dy * 0.1); nuclear@1: glutPostRedisplay(); nuclear@1: } nuclear@1: } nuclear@46: nuclear@46: unsigned int load_psys_tex(const char *fname, void *cls) nuclear@46: { nuclear@46: TextureSet *texset = tileset->get_textures(); nuclear@48: return texset->get(fname); nuclear@46: }