eleni@0: #include eleni@0: #include elene@9: #include eleni@0: elene@17: #include eleni@0: #include eleni@0: #include eleni@0: elene@9: #include elene@9: elene@9: #include "mesh.h" elene@6: #include "volume.h" eleni@1: elene@10: static bool init(void); elene@10: static GLUI *create_ui(void); elene@10: static void display(void); elene@10: static void reshape(int x, int y); elene@10: static void keyboard(unsigned char key, int x, int y); elene@10: static void keyboard_up(unsigned char key, int x, int y); elene@10: static void mouse(int button, int state, int x, int y); elene@10: static void motion(int x, int y); eleni@0: elene@16: static bool init_xfer(void); elene@16: static void display_xfer(void); elene@16: static void reshape_xfer(int x, int y); elene@17: static void mouse_xfer(int button, int state, int x, int y); elene@16: static void motion_xfer(int x, int y); elene@17: //todo keyb esc elene@16: elene@16: static int mainwin_id, xferwin_id; elene@16: eleni@0: static int win_xsz, win_ysz; elene@9: static float cam_phi, cam_theta, cam_dist = 6; elene@9: static std::vector key_state(256); eleni@0: elene@11: static const char *vol_fname = "data/test1.vol"; elene@6: elene@6: static Volume *vol; elene@9: static Mesh *mesh; elene@12: static float cur_z, thres = 0.5, thres2 = 1.0; elene@9: elene@9: static int use_orig_vol_res = 1; elene@9: static int vol_res[3]; // volume sampling resolution x/y/z elene@9: elene@9: static GLUI *ui; eleni@1: eleni@0: int main(int argc, char **argv) eleni@0: { eleni@0: glutInit(&argc, argv); elene@16: if(argv[1]) elene@16: vol_fname = argv[1]; elene@16: elene@16: if(!init()) { elene@16: fprintf(stderr, "Failed to initialize program.\n"); elene@16: return 1; elene@16: } elene@16: elene@16: init_xfer(); elene@16: elene@16: glutMainLoop(); elene@16: return 0; elene@16: } elene@16: elene@16: static bool init() elene@16: { elene@7: glutInitWindowSize(512, 512); elene@11: glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE); eleni@0: elene@16: mainwin_id = glutCreateWindow("CT scan"); eleni@0: glutDisplayFunc(display); eleni@0: glutReshapeFunc(reshape); eleni@0: glutKeyboardFunc(keyboard); elene@9: glutKeyboardUpFunc(keyboard_up); eleni@0: glutMouseFunc(mouse); eleni@0: glutMotionFunc(motion); eleni@0: eleni@0: glewInit(); eleni@0: elene@11: glEnable(GL_DEPTH_TEST); elene@11: glEnable(GL_NORMALIZE); elene@11: elene@11: glEnable(GL_LIGHTING); //TODO: shaders elene@11: glEnable(GL_LIGHT0); elene@11: glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 1); elene@11: elene@9: vol = new Volume; elene@11: if(!vol->load(vol_fname)) { elene@11: fprintf(stderr, "Failed to load %s", vol_fname); elene@9: return false; elene@9: } elene@9: mesh = new Mesh; elene@9: cur_z = 0.5; elene@9: elene@9: vol_res[0] = vol->get_slice(0)->get_width(); elene@9: vol_res[1] = vol->get_slice(0)->get_height(); elene@9: vol_res[2] = vol->get_slice_count(); elene@9: elene@9: if(!(ui = create_ui())) { elene@9: return false; elene@9: } elene@9: elene@9: return true; elene@9: } elene@9: elene@9: static GLUI_Spinner *res_spin[3]; elene@9: static void toggle_use_orig(int id) elene@9: { elene@9: for(int i=0; i<3; i++) { elene@9: if(use_orig_vol_res) { elene@9: res_spin[i]->disable(); elene@9: } else { elene@9: res_spin[i]->enable(); elene@9: } elene@9: } elene@9: } elene@9: static void thres_change(int id) elene@9: { elene@9: static float prev_thres = thres; elene@12: static float prev_thres2 = thres2; elene@9: elene@12: if(prev_thres != thres || prev_thres2 != thres2) { elene@9: prev_thres = thres; elene@12: prev_thres2 = thres2; elene@12: mesh->clear(); elene@12: } elene@16: elene@16: glutSetWindow(xferwin_id); elene@16: glutPostRedisplay(); elene@16: glutSetWindow(mainwin_id); elene@16: glutPostRedisplay(); elene@12: } elene@12: static void res_change(int id) elene@12: { elene@12: static float prev_resx = vol_res[0]; elene@12: static float prev_resy = vol_res[1]; elene@12: static float prev_resz = vol_res[2]; elene@12: elene@12: if(prev_resx != vol_res[0] || prev_resy != vol_res[1] || prev_resz != vol_res[2]) { elene@12: prev_resx = vol_res[0]; elene@12: prev_resy = vol_res[1]; elene@12: prev_resz = vol_res[2]; elene@9: mesh->clear(); elene@9: } elene@9: } elene@10: static GLUI *create_ui() elene@9: { elene@9: GLUI *ui = GLUI_Master.create_glui("ui"); elene@9: assert(ui); elene@9: elene@9: ui->set_main_gfx_window(glutGetWindow()); elene@9: elene@12: GLUI_Panel *thres_panel = ui->add_panel("iso thresholds"); elene@12: elene@12: GLUI_Spinner *thres_spin = ui->add_spinner_to_panel(thres_panel, "T1", GLUI_SPINNER_FLOAT, &thres, 0, thres_change); elene@9: thres_spin->set_float_limits(0, 1); elene@9: elene@12: GLUI_Spinner *thres2_spin = ui->add_spinner_to_panel(thres_panel, "T2", GLUI_SPINNER_FLOAT, &thres2, 0, thres_change); elene@12: thres2_spin->set_float_limits(0, 1); elene@12: elene@9: GLUI_Panel *res_panel = ui->add_panel("volume resolution"); elene@9: elene@9: ui->add_checkbox_to_panel(res_panel, "original resolution", &use_orig_vol_res, 0, toggle_use_orig); elene@9: elene@9: static const char *res_spin_name[] = {"x", "y", "z"}; elene@9: for(int i=0; i<3; i++) { elene@12: res_spin[i] = ui->add_spinner_to_panel(res_panel, res_spin_name[i], GLUI_SPINNER_INT, vol_res + i, 0, res_change); elene@9: res_spin[i]->set_int_limits(8, 256); // TODO limits as arguments or config elene@9: res_spin[i]->disable(); elene@9: } elene@9: elene@9: return ui; elene@9: } elene@9: elene@10: static void display(void) eleni@0: { elene@9: glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); elene@9: elene@9: glMatrixMode(GL_MODELVIEW); elene@9: glLoadIdentity(); elene@9: glTranslatef(0, 0, -cam_dist); elene@9: glRotatef(cam_phi, 1, 0, 0); elene@9: glRotatef(cam_theta, 0, 1, 0); elene@9: elene@9: /* elene@6: glBindTexture(GL_TEXTURE_3D, vol->get_texture()); elene@6: glEnable(GL_TEXTURE_3D); eleni@1: glBegin(GL_QUADS); elene@6: glTexCoord3f(0, 0, cur_z); glVertex3f(-1, -1, 0); elene@6: glTexCoord3f(0, 1, cur_z); glVertex3f(-1, 1, 0); elene@6: glTexCoord3f(1, 1, cur_z); glVertex3f(1, 1, 0); elene@6: glTexCoord3f(1, 0, cur_z); glVertex3f(1, -1, 0); eleni@1: glEnd(); elene@6: glDisable(GL_TEXTURE_3D); elene@9: */ eleni@0: elene@9: if(mesh->is_empty()) { elene@9: printf("recalculating isosurface ... "); elene@9: fflush(stdout); elene@12: vol->create_mesh(mesh, thres, thres2, vol_res[0], vol_res[1], vol_res[2]); elene@9: printf("done.\n"); elene@9: } elene@9: mesh->draw(); elene@9: elene@9: //TODO: draw threshold eleni@0: glutSwapBuffers(); eleni@0: assert(glGetError() == GL_NO_ERROR); eleni@0: } eleni@0: elene@10: static void reshape(int x, int y) eleni@0: { eleni@0: glViewport(0, 0, x, y); elene@9: glMatrixMode(GL_PROJECTION); elene@9: glLoadIdentity(); elene@9: gluPerspective(45, (float)x / (float)y, 0.5, 500); elene@9: eleni@0: if(x != win_xsz || y != win_ysz) { eleni@0: win_xsz = x; eleni@0: win_ysz = y; eleni@0: } eleni@0: } eleni@0: elene@10: static void keyboard(unsigned char key, int x, int y) eleni@0: { elene@9: key_state[(int)key] = true; elene@9: eleni@0: switch(key) { eleni@0: case 27: eleni@0: exit(0); elene@9: case 'w': elene@9: { elene@9: static bool wire; elene@9: wire = !wire; elene@9: glPolygonMode(GL_FRONT_AND_BACK, wire ? GL_LINE : GL_FILL); elene@9: } elene@9: break; eleni@0: default: eleni@0: break; eleni@0: } eleni@0: } eleni@0: elene@10: static void keyboard_up(unsigned char key, int x, int y) elene@9: { elene@9: key_state[(int) key] = false; elene@9: } elene@9: eleni@0: static int prev_x, prev_y; elene@9: static bool bn_state[32]; elene@12: static float prev_thres, prev_thres2; elene@9: elene@10: static void mouse(int bn, int state, int x, int y) eleni@0: { eleni@0: prev_x = x; eleni@0: prev_y = y; elene@9: elene@9: bn_state[bn - GLUT_LEFT_BUTTON] = (state == GLUT_DOWN); elene@9: elene@9: if(state == GLUT_DOWN) { elene@9: prev_thres = thres; elene@12: prev_thres2 = thres2; elene@9: } elene@9: else { elene@12: if(thres != prev_thres || thres2 != prev_thres2) { elene@9: mesh->clear(); elene@9: } elene@9: } eleni@0: } eleni@0: elene@10: static void motion(int x, int y) eleni@0: { eleni@0: int dx = x - prev_x; eleni@0: int dy = y - prev_y; elene@9: elene@9: if(!dx && !dy) elene@9: return; elene@9: eleni@0: prev_x = x; eleni@0: prev_y = y; elene@6: elene@9: if(key_state[(int)'t']) { elene@9: thres = (float)x / (float)win_xsz; elene@9: printf("threshold: %f\n", thres); elene@9: elene@9: glutPostRedisplay(); elene@9: return; elene@9: } elene@9: elene@9: // camera elene@9: if(bn_state[0]) { elene@9: if(key_state[(int)'z']) { elene@9: //zoom the camera elene@9: elene@9: cam_dist += dy * 0.1; elene@9: elene@9: if(cam_dist < 0) elene@9: cam_dist = 0; elene@9: } elene@9: else { elene@9: //rotate the camera elene@9: elene@9: cam_phi += dy * 0.5; elene@9: cam_theta += dx * 0.5; elene@9: elene@9: if(cam_phi > 90) elene@9: cam_phi = 90; elene@9: if(cam_phi < -90) elene@9: cam_phi = -90; elene@9: } elene@9: elene@6: glutPostRedisplay(); elene@6: } eleni@0: } elene@16: elene@16: static bool init_xfer(void) elene@16: { elene@16: glutSetWindow(mainwin_id); elene@16: int x = glutGet(GLUT_WINDOW_X); elene@16: int y = glutGet(GLUT_WINDOW_Y) + glutGet(GLUT_WINDOW_HEIGHT); elene@16: elene@16: glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE); elene@16: glutInitWindowSize(512, 100); elene@16: glutInitWindowPosition(x, y); elene@16: elene@16: xferwin_id = glutCreateWindow("Transfer Function"); elene@16: glutDisplayFunc(display_xfer); elene@16: glutReshapeFunc(reshape_xfer); elene@17: glutMouseFunc(mouse_xfer); elene@16: glutMotionFunc(motion_xfer); elene@16: elene@16: return true; elene@16: } elene@16: elene@16: static void display_xfer(void) elene@16: { elene@16: glClear(GL_COLOR_BUFFER_BIT); elene@16: elene@16: int num_val = glutGet(GLUT_WINDOW_WIDTH) / 4.0; elene@16: float x = 0; elene@16: float dx = 1.0 / num_val; elene@16: float low, high; elene@16: if(thres < thres2) { elene@16: low = thres; elene@16: high = thres2; elene@16: } elene@16: else { elene@16: low = thres2; elene@16: high = thres; elene@16: } elene@16: elene@17: //drawing the transfer function curve elene@17: elene@17: glLineWidth(3); elene@17: glBegin(GL_LINE_STRIP); elene@17: glColor3f(0.8, 0.3, 0.8); elene@17: for(int i=0; i 0.001) { elene@17: mesh->clear(); elene@17: ui->sync_live(); elene@17: } elene@17: select_thres = 0; elene@17: } elene@17: } elene@17: } elene@17: elene@16: static void motion_xfer(int x, int y) elene@16: { elene@17: if(!select_thres) elene@17: return; elene@17: elene@17: int width = glutGet(GLUT_WINDOW_WIDTH); elene@17: float xpos = (float)x / (float)width; elene@17: elene@17: *select_thres = xpos; elene@17: elene@17: glutPostRedisplay(); elene@17: glutSetWindow(mainwin_id); elene@17: glutPostRedisplay(); elene@16: }