nuclear@35: #include "opengl.h" eleni@0: elene@17: #include eleni@0: #include eleni@0: #include eleni@0: elene@9: #include elene@9: elene@9: #include "mesh.h" elene@22: #include "sdr.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); 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@25: static void volume_preview(); elene@25: static void draw_iso(); elene@25: static void draw_volume(); elene@16: elene@16: static int mainwin_id, xferwin_id; elene@18: //todo keyb esc 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@22: static const char *vsdr_path = "data/shaders/transfer.v.glsl"; elene@22: static const char *fsdr_path = "data/shaders/transfer.f.glsl"; elene@25: static const char *vsdr_vol_path = "data/shaders/vol.v.glsl"; elene@25: static const char *fsdr_vol_path = "data/shaders/vol.f.glsl"; elene@25: elene@25: static unsigned int sprog; elene@25: static unsigned int sprog_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@25: static float slice_z = 0.5; 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@26: static float bound_scale = 1.42; elene@9: elene@9: static GLUI *ui; eleni@1: elene@25: static int num_poly = 128; elene@22: eleni@0: int main(int argc, char **argv) eleni@0: { elene@31: glutInitWindowSize(512, 512); 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@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@25: glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); elene@25: elene@22: //FIXME shaders setup elene@22: if(!(sprog = sdr_getprog(vsdr_path, fsdr_path))) elene@22: { elene@22: fprintf(stderr, "Failed to create shader program!\n"); elene@22: return false; elene@22: } elene@22: elene@25: if(!(sprog_vol = sdr_getprog(vsdr_vol_path, fsdr_vol_path))) elene@25: { elene@25: fprintf(stderr, "Failed to create shader program!\n"); elene@25: return false; elene@25: } elene@25: 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@26: num_poly = std::max(vol_res[0], std::max(vol_res[1], vol_res[2])) * bound_scale; elene@26: 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@23: elene@12: mesh->clear(); elene@23: elene@23: glutSetWindow(xferwin_id); elene@23: glutPostRedisplay(); elene@23: glutSetWindow(mainwin_id); elene@23: glutPostRedisplay(); elene@12: } elene@23: } elene@16: 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@29: #ifdef ISO 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@29: #endif elene@20: GLUI_Panel *preview_panel = ui->add_panel("volume preview"); elene@20: elene@22: GLUI_Spinner *preview_spin = ui->add_spinner_to_panel(preview_panel, "slice z", GLUI_SPINNER_FLOAT, &slice_z, 0); elene@20: preview_spin->set_float_limits(0, 1); elene@20: elene@9: return ui; elene@9: } elene@9: elene@18: static void volume_preview () elene@18: { elene@18: float aspect = win_xsz / win_ysz; elene@18: elene@21: glDisable(GL_DEPTH_TEST); elene@18: elene@22: glUseProgram(sprog); elene@23: int tmin_loc = glGetUniformLocation(sprog, "tmin"); elene@23: if(tmin_loc != -1) elene@23: glUniform1f(tmin_loc, std::min(thres, thres2)); elene@23: int tmax_loc = glGetUniformLocation(sprog, "tmax"); elene@23: if(tmax_loc != -1) elene@23: glUniform1f(tmax_loc, std::max(thres, thres2)); elene@22: elene@18: glMatrixMode(GL_MODELVIEW); elene@18: glPushMatrix(); elene@18: glLoadIdentity(); elene@18: elene@18: glMatrixMode(GL_PROJECTION); elene@18: glPushMatrix(); elene@18: glLoadIdentity(); elene@25: glOrtho(0, aspect, 0, 1, -1, 1); elene@18: elene@18: glBindTexture(GL_TEXTURE_3D, vol->get_texture()); elene@18: glEnable(GL_TEXTURE_3D); elene@18: glBegin(GL_QUADS); elene@21: glColor3f(1.0, 1.0, 1.0); elene@25: glTexCoord3f(0, 0, slice_z); glVertex3f(0, 1, 0); elene@25: glTexCoord3f(0, 1, slice_z); glVertex3f(0, 0.75, 0); elene@25: glTexCoord3f(1, 1, slice_z); glVertex3f(0.25, 0.75, 0); elene@25: glTexCoord3f(1, 0, slice_z); glVertex3f(0.25, 1, 0); elene@18: glEnd(); elene@18: glDisable(GL_TEXTURE_3D); elene@18: elene@30: glUseProgram(0); elene@30: elene@32: glLineWidth(2); elene@21: glBegin(GL_LINE_LOOP); elene@32: glColor3f(0.8, 0.3, 0.8); elene@30: glVertex3f(0, 1, 0); elene@30: glVertex3f(0, 0.75, 0); elene@30: glVertex3f(0.25, 0.75, 0); elene@30: glVertex3f(0.25, 1, 0); elene@21: glEnd(); elene@21: elene@18: glMatrixMode(GL_PROJECTION); elene@18: glPopMatrix(); elene@18: elene@18: glMatrixMode(GL_MODELVIEW); elene@18: glPopMatrix(); elene@18: elene@21: glEnable(GL_DEPTH_TEST); elene@25: } elene@25: elene@25: static void draw_iso() elene@25: { elene@25: if(mesh->is_empty()) { elene@25: printf("recalculating isosurface ... "); elene@25: fflush(stdout); elene@25: vol->create_mesh(mesh, thres, thres2, vol_res[0], vol_res[1], vol_res[2]); elene@25: printf("done.\n"); elene@25: } elene@25: mesh->draw(); elene@25: } elene@25: elene@25: static void draw_volume() elene@25: { elene@25: glUseProgram(sprog_vol); elene@25: elene@25: int tmin_loc = glGetUniformLocation(sprog_vol, "tmin"); elene@25: if(tmin_loc != -1) elene@25: glUniform1f(tmin_loc, std::min(thres, thres2)); elene@25: int tmax_loc = glGetUniformLocation(sprog_vol, "tmax"); elene@25: if(tmax_loc != -1) elene@25: glUniform1f(tmax_loc, std::max(thres, thres2)); elene@25: elene@25: int res_loc = glGetUniformLocation(sprog_vol, "res"); elene@25: if(res_loc != -1) elene@26: glUniform3f(res_loc, (float)vol_res[0], (float)vol_res[1], (float)vol_res[2]); elene@25: elene@30: glRotatef(-vol->get_volume_rotation(), 1, 0, 0); elene@30: elene@25: glDisable(GL_DEPTH_TEST); elene@25: glBindTexture(GL_TEXTURE_3D, vol->get_texture()); elene@25: glEnable(GL_TEXTURE_3D); elene@25: glEnable(GL_BLEND); elene@25: glBegin(GL_QUADS); elene@25: for(int i=0; iclear(); 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@34: elene@34: //drawing the histogram elene@34: float hmax = 1 / (float)vol->max_histogram_value; elene@34: elene@34: glBegin(GL_QUADS); elene@34: for (int i=0; ihistogram[i + 1], 0); elene@34: glVertex3f(x0, hmax * vol->histogram[i], 0); elene@34: } elene@34: glEnd(); elene@34: 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@23: thres_change(0); elene@17: elene@17: glutPostRedisplay(); elene@17: glutSetWindow(mainwin_id); elene@17: glutPostRedisplay(); elene@16: }