nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include "app.h" nuclear@0: #include "opengl.h" nuclear@0: #include "texture.h" nuclear@0: #include "mesh.h" nuclear@0: #include "meshgen.h" nuclear@0: nuclear@0: static void draw_scene(); // both near and infinite parts nuclear@0: static void draw_scene_near(); // near scene: regular objects affected by parallax shift and translation nuclear@0: // infinity scene: objects conceptually at infinity, not affected by parallax shift and translation nuclear@0: static void draw_scene_inf(); nuclear@0: static bool parse_args(int argc, char **argv); nuclear@0: nuclear@0: static const char *img_fname; nuclear@0: static float cam_theta, cam_phi; nuclear@0: nuclear@0: static Texture *pano_tex; nuclear@0: static Mesh *pano_mesh; nuclear@0: nuclear@0: static int win_width, win_height; nuclear@0: nuclear@0: nuclear@0: bool app_init(int argc, char **argv) nuclear@0: { nuclear@0: if(!parse_args(argc, argv)) { nuclear@0: return false; nuclear@0: } nuclear@0: if(!img_fname) { nuclear@0: fprintf(stderr, "please specify an equilateral panoramic image\n"); nuclear@0: return false; nuclear@0: } nuclear@0: nuclear@0: if(!init_opengl()) { nuclear@0: return false; nuclear@0: } nuclear@0: nuclear@0: glEnable(GL_CULL_FACE); nuclear@0: nuclear@0: if(GLEW_ARB_framebuffer_sRGB) { nuclear@0: glGetError(); // discard previous errors nuclear@0: glEnable(GL_FRAMEBUFFER_SRGB); nuclear@0: if(glGetError() != GL_NO_ERROR) { nuclear@0: fprintf(stderr, "failed to enable sRGB framebuffer\n"); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: Mesh::use_custom_sdr_attr = false; nuclear@0: pano_mesh = new Mesh; nuclear@0: gen_sphere(pano_mesh, 1.0, 80, 40); nuclear@0: pano_mesh->flip(); nuclear@0: Mat4 xform; nuclear@0: xform.rotation_y(-M_PI / 2.0); // rotate the sphere to face the "front" part of the image nuclear@0: pano_mesh->apply_xform(xform, xform); nuclear@0: nuclear@0: xform.scaling(-1, 1, 1); // flip horizontal texcoord since we're inside the sphere nuclear@0: pano_mesh->texcoord_apply_xform(xform); nuclear@0: nuclear@0: pano_tex = new Texture; nuclear@0: if(!pano_tex->load(img_fname)) { nuclear@0: return false; nuclear@0: } nuclear@0: return true; nuclear@0: } nuclear@0: nuclear@0: void app_cleanup() nuclear@0: { nuclear@0: delete pano_mesh; nuclear@0: delete pano_tex; nuclear@0: } nuclear@0: nuclear@0: void app_draw() nuclear@0: { nuclear@0: glClear(GL_COLOR_BUFFER_BIT); nuclear@0: nuclear@0: Mat4 view_matrix; nuclear@0: view_matrix.pre_rotate_x(deg_to_rad(cam_phi)); nuclear@0: view_matrix.pre_rotate_y(deg_to_rad(cam_theta)); nuclear@0: nuclear@0: glMatrixMode(GL_MODELVIEW); nuclear@0: glLoadMatrixf(view_matrix[0]); nuclear@0: nuclear@0: draw_scene(); nuclear@0: nuclear@0: app_swap_buffers(); nuclear@0: assert(glGetError() == GL_NO_ERROR); nuclear@0: } nuclear@0: nuclear@0: void render_cubemap() nuclear@0: { nuclear@0: int fbsize = win_width < win_height ? win_width : win_height; nuclear@0: float *pixels = new float[fbsize * fbsize * 3]; nuclear@0: nuclear@0: glViewport(0, 0, fbsize, fbsize); nuclear@0: nuclear@0: Mat4 viewmat[6]; nuclear@0: viewmat[0].rotation_y(deg_to_rad(90)); // +X nuclear@0: viewmat[1].rotation_x(deg_to_rad(-90)); // +Y nuclear@0: viewmat[2].rotation_y(deg_to_rad(180)); // +Z nuclear@0: viewmat[3].rotation_y(deg_to_rad(-90)); // -X nuclear@0: viewmat[4].rotation_x(deg_to_rad(90)); // -Y nuclear@0: nuclear@0: static const char *fname[] = { nuclear@0: "cubemap_px.jpg", nuclear@0: "cubemap_py.jpg", nuclear@0: "cubemap_pz.jpg", nuclear@0: "cubemap_nx.jpg", nuclear@0: "cubemap_ny.jpg", nuclear@0: "cubemap_nz.jpg" nuclear@0: }; nuclear@0: nuclear@0: glMatrixMode(GL_PROJECTION); nuclear@0: glLoadIdentity(); nuclear@0: gluPerspective(45, 1.0, 0.5, 500.0); nuclear@0: nuclear@0: for(int i=0; i<6; i++) { nuclear@0: glClear(GL_COLOR_BUFFER_BIT); nuclear@0: nuclear@0: glMatrixMode(GL_MODELVIEW); nuclear@0: glLoadMatrixf(viewmat[i][0]); nuclear@0: nuclear@0: draw_scene(); nuclear@0: nuclear@0: glReadPixels(0, 0, fbsize, fbsize, GL_RGB, GL_FLOAT, pixels); nuclear@0: if(img_save_pixels(fname[i], pixels, fbsize, fbsize, IMG_FMT_RGBF) == -1) { nuclear@0: fprintf(stderr, "failed to save %dx%d image: %s\n", fbsize, fbsize, fname[i]); nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: glViewport(0, 0, win_width, win_height); nuclear@0: nuclear@0: delete [] pixels; nuclear@0: } nuclear@0: nuclear@0: // both near and infinite parts (see below) nuclear@0: static void draw_scene() nuclear@0: { nuclear@0: draw_scene_inf(); nuclear@0: draw_scene_near(); nuclear@0: } nuclear@0: nuclear@0: // infinity scene: objects conceptually at infinity, not affected by parallax shift and translation nuclear@0: static void draw_scene_inf() nuclear@0: { nuclear@0: pano_tex->bind(); nuclear@0: glEnable(GL_TEXTURE_2D); nuclear@0: pano_mesh->draw(); nuclear@0: glDisable(GL_TEXTURE_2D); nuclear@0: } nuclear@0: nuclear@0: // near scene: regular objects affected by parallax shift and translation nuclear@0: static void draw_scene_near() nuclear@0: { nuclear@0: } nuclear@0: nuclear@0: void app_reshape(int x, int y) nuclear@0: { nuclear@0: glViewport(0, 0, x, y); nuclear@0: nuclear@0: glMatrixMode(GL_PROJECTION); nuclear@0: glLoadIdentity(); nuclear@0: gluPerspective(50.0, (float)x / (float)y, 0.5, 500.0); nuclear@0: nuclear@0: win_width = x; nuclear@0: win_height = y; nuclear@0: } nuclear@0: nuclear@0: void app_keyboard(int key, bool press) nuclear@0: { nuclear@0: if(press) { nuclear@0: switch(key) { nuclear@0: case 27: nuclear@0: app_quit(); nuclear@0: break; nuclear@0: nuclear@0: case 's': nuclear@0: printf("rendering cubemap\n"); nuclear@0: render_cubemap(); nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: static float prev_x, prev_y; nuclear@0: static bool bnstate[16]; nuclear@0: nuclear@0: void app_mouse_button(int bn, bool press, int x, int y) nuclear@0: { nuclear@0: if(bn < (int)(sizeof bnstate / sizeof *bnstate)) { nuclear@0: bnstate[bn] = press; nuclear@0: } nuclear@0: prev_x = x; nuclear@0: prev_y = y; nuclear@0: } nuclear@0: nuclear@0: void app_mouse_motion(int x, int y) nuclear@0: { nuclear@0: float dx = x - prev_x; nuclear@0: float dy = y - prev_y; nuclear@0: prev_x = x; nuclear@0: prev_y = y; nuclear@0: nuclear@0: if(!dx && !dy) return; nuclear@0: nuclear@0: if(bnstate[0]) { nuclear@0: cam_theta += dx * 0.5; nuclear@0: cam_phi += dy * 0.5; nuclear@0: nuclear@0: if(cam_phi < -90) cam_phi = -90; nuclear@0: if(cam_phi > 90) cam_phi = 90; nuclear@0: app_redisplay(); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: static bool parse_args(int argc, char **argv) nuclear@0: { nuclear@0: for(int i=1; i