volmetrics

annotate src/main.cc @ 23:930c063ae346

preview works - transfer function on sdr
author Eleni Maria Stea <elene.mst@gmail.com>
date Thu, 24 Apr 2014 21:32:28 +0300
parents 4e120dcd55ec
children 4b6c952a83bd
rev   line source
eleni@0 1 #include <GL/glew.h>
eleni@0 2 #include <GL/glut.h>
elene@9 3 #include <GL/glui.h>
eleni@0 4
elene@17 5 #include <math.h>
eleni@0 6 #include <stdio.h>
eleni@0 7 #include <assert.h>
eleni@0 8
elene@9 9 #include <vector>
elene@9 10
elene@9 11 #include "mesh.h"
elene@22 12 #include "sdr.h"
elene@6 13 #include "volume.h"
eleni@1 14
elene@10 15 static bool init(void);
elene@10 16 static GLUI *create_ui(void);
elene@10 17 static void display(void);
elene@10 18 static void reshape(int x, int y);
elene@10 19 static void keyboard(unsigned char key, int x, int y);
elene@10 20 static void keyboard_up(unsigned char key, int x, int y);
elene@10 21 static void mouse(int button, int state, int x, int y);
elene@10 22 static void motion(int x, int y);
elene@16 23 static bool init_xfer(void);
elene@16 24 static void display_xfer(void);
elene@16 25 static void reshape_xfer(int x, int y);
elene@17 26 static void mouse_xfer(int button, int state, int x, int y);
elene@16 27 static void motion_xfer(int x, int y);
elene@18 28 static void volume_preview ();
elene@16 29
elene@16 30 static int mainwin_id, xferwin_id;
elene@18 31 //todo keyb esc
elene@16 32
eleni@0 33 static int win_xsz, win_ysz;
elene@9 34 static float cam_phi, cam_theta, cam_dist = 6;
elene@9 35 static std::vector<bool> key_state(256);
eleni@0 36
elene@11 37 static const char *vol_fname = "data/test1.vol";
elene@22 38 static const char *vsdr_path = "data/shaders/transfer.v.glsl";
elene@22 39 static const char *fsdr_path = "data/shaders/transfer.f.glsl";
elene@6 40
elene@6 41 static Volume *vol;
elene@9 42 static Mesh *mesh;
elene@12 43 static float cur_z, thres = 0.5, thres2 = 1.0;
elene@22 44 static float slice_z;
elene@9 45
elene@9 46 static int use_orig_vol_res = 1;
elene@9 47 static int vol_res[3]; // volume sampling resolution x/y/z
elene@9 48
elene@9 49 static GLUI *ui;
eleni@1 50
elene@22 51 static unsigned int sprog;
elene@22 52
eleni@0 53 int main(int argc, char **argv)
eleni@0 54 {
eleni@0 55 glutInit(&argc, argv);
elene@16 56 if(argv[1])
elene@16 57 vol_fname = argv[1];
elene@16 58
elene@16 59 if(!init()) {
elene@16 60 fprintf(stderr, "Failed to initialize program.\n");
elene@16 61 return 1;
elene@16 62 }
elene@16 63
elene@16 64 init_xfer();
elene@16 65
elene@16 66 glutMainLoop();
elene@16 67 return 0;
elene@16 68 }
elene@16 69
elene@16 70 static bool init()
elene@16 71 {
elene@7 72 glutInitWindowSize(512, 512);
elene@11 73 glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE);
eleni@0 74
elene@16 75 mainwin_id = glutCreateWindow("CT scan");
eleni@0 76 glutDisplayFunc(display);
eleni@0 77 glutReshapeFunc(reshape);
eleni@0 78 glutKeyboardFunc(keyboard);
elene@9 79 glutKeyboardUpFunc(keyboard_up);
eleni@0 80 glutMouseFunc(mouse);
eleni@0 81 glutMotionFunc(motion);
eleni@0 82
eleni@0 83 glewInit();
eleni@0 84
elene@11 85 glEnable(GL_DEPTH_TEST);
elene@11 86 glEnable(GL_NORMALIZE);
elene@11 87
elene@22 88 glEnable(GL_LIGHTING);
elene@11 89 glEnable(GL_LIGHT0);
elene@11 90 glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 1);
elene@11 91
elene@22 92 //FIXME shaders setup
elene@22 93 if(!(sprog = sdr_getprog(vsdr_path, fsdr_path)))
elene@22 94 {
elene@22 95 fprintf(stderr, "Failed to create shader program!\n");
elene@22 96 return false;
elene@22 97 }
elene@22 98
elene@9 99 vol = new Volume;
elene@11 100 if(!vol->load(vol_fname)) {
elene@11 101 fprintf(stderr, "Failed to load %s", vol_fname);
elene@9 102 return false;
elene@9 103 }
elene@9 104 mesh = new Mesh;
elene@9 105 cur_z = 0.5;
elene@9 106
elene@9 107 vol_res[0] = vol->get_slice(0)->get_width();
elene@9 108 vol_res[1] = vol->get_slice(0)->get_height();
elene@9 109 vol_res[2] = vol->get_slice_count();
elene@9 110
elene@9 111 if(!(ui = create_ui())) {
elene@9 112 return false;
elene@9 113 }
elene@9 114
elene@9 115 return true;
elene@9 116 }
elene@9 117
elene@9 118 static GLUI_Spinner *res_spin[3];
elene@9 119 static void toggle_use_orig(int id)
elene@9 120 {
elene@9 121 for(int i=0; i<3; i++) {
elene@9 122 if(use_orig_vol_res) {
elene@9 123 res_spin[i]->disable();
elene@9 124 } else {
elene@9 125 res_spin[i]->enable();
elene@9 126 }
elene@9 127 }
elene@9 128 }
elene@9 129 static void thres_change(int id)
elene@9 130 {
elene@9 131 static float prev_thres = thres;
elene@12 132 static float prev_thres2 = thres2;
elene@9 133
elene@12 134 if(prev_thres != thres || prev_thres2 != thres2) {
elene@9 135 prev_thres = thres;
elene@12 136 prev_thres2 = thres2;
elene@23 137
elene@12 138 mesh->clear();
elene@23 139
elene@23 140 glutSetWindow(xferwin_id);
elene@23 141 glutPostRedisplay();
elene@23 142 glutSetWindow(mainwin_id);
elene@23 143 glutPostRedisplay();
elene@12 144 }
elene@23 145 }
elene@16 146
elene@12 147 static void res_change(int id)
elene@12 148 {
elene@12 149 static float prev_resx = vol_res[0];
elene@12 150 static float prev_resy = vol_res[1];
elene@12 151 static float prev_resz = vol_res[2];
elene@12 152
elene@12 153 if(prev_resx != vol_res[0] || prev_resy != vol_res[1] || prev_resz != vol_res[2]) {
elene@12 154 prev_resx = vol_res[0];
elene@12 155 prev_resy = vol_res[1];
elene@12 156 prev_resz = vol_res[2];
elene@9 157 mesh->clear();
elene@9 158 }
elene@9 159 }
elene@10 160 static GLUI *create_ui()
elene@9 161 {
elene@9 162 GLUI *ui = GLUI_Master.create_glui("ui");
elene@9 163 assert(ui);
elene@9 164
elene@9 165 ui->set_main_gfx_window(glutGetWindow());
elene@9 166
elene@12 167 GLUI_Panel *thres_panel = ui->add_panel("iso thresholds");
elene@12 168
elene@12 169 GLUI_Spinner *thres_spin = ui->add_spinner_to_panel(thres_panel, "T1", GLUI_SPINNER_FLOAT, &thres, 0, thres_change);
elene@9 170 thres_spin->set_float_limits(0, 1);
elene@9 171
elene@12 172 GLUI_Spinner *thres2_spin = ui->add_spinner_to_panel(thres_panel, "T2", GLUI_SPINNER_FLOAT, &thres2, 0, thres_change);
elene@12 173 thres2_spin->set_float_limits(0, 1);
elene@12 174
elene@9 175 GLUI_Panel *res_panel = ui->add_panel("volume resolution");
elene@9 176
elene@9 177 ui->add_checkbox_to_panel(res_panel, "original resolution", &use_orig_vol_res, 0, toggle_use_orig);
elene@9 178
elene@9 179 static const char *res_spin_name[] = {"x", "y", "z"};
elene@9 180 for(int i=0; i<3; i++) {
elene@12 181 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 182 res_spin[i]->set_int_limits(8, 256); // TODO limits as arguments or config
elene@9 183 res_spin[i]->disable();
elene@9 184 }
elene@9 185
elene@20 186 GLUI_Panel *preview_panel = ui->add_panel("volume preview");
elene@20 187
elene@22 188 GLUI_Spinner *preview_spin = ui->add_spinner_to_panel(preview_panel, "slice z", GLUI_SPINNER_FLOAT, &slice_z, 0);
elene@20 189 preview_spin->set_float_limits(0, 1);
elene@20 190
elene@9 191 return ui;
elene@9 192 }
elene@9 193
elene@18 194 static void volume_preview ()
elene@18 195 {
elene@18 196 float aspect = win_xsz / win_ysz;
elene@18 197
elene@18 198 glDisable(GL_LIGHTING);
elene@21 199 glDisable(GL_DEPTH_TEST);
elene@18 200
elene@22 201 glUseProgram(sprog);
elene@23 202 int tmin_loc = glGetUniformLocation(sprog, "tmin");
elene@23 203 if(tmin_loc != -1)
elene@23 204 glUniform1f(tmin_loc, std::min(thres, thres2));
elene@23 205 int tmax_loc = glGetUniformLocation(sprog, "tmax");
elene@23 206 if(tmax_loc != -1)
elene@23 207 glUniform1f(tmax_loc, std::max(thres, thres2));
elene@22 208
elene@18 209 glMatrixMode(GL_MODELVIEW);
elene@18 210 glPushMatrix();
elene@18 211 glLoadIdentity();
elene@18 212
elene@18 213 glMatrixMode(GL_PROJECTION);
elene@18 214 glPushMatrix();
elene@18 215 glLoadIdentity();
elene@18 216 glOrtho(-aspect, aspect, -1.0, 1.0, -1.0, 1.0);
elene@18 217
elene@18 218 glBindTexture(GL_TEXTURE_3D, vol->get_texture());
elene@18 219 glEnable(GL_TEXTURE_3D);
elene@18 220 glBegin(GL_QUADS);
elene@21 221 glColor3f(1.0, 1.0, 1.0);
elene@22 222 glTexCoord3f(0, 0, slice_z); glVertex3f(-1.0, 1.0, 0.0);
elene@22 223 glTexCoord3f(0, 1, slice_z); glVertex3f(-1.0, 0.5, 0.0);
elene@22 224 glTexCoord3f(1, 1, slice_z); glVertex3f(-0.5, 0.5, 0.0);
elene@22 225 glTexCoord3f(1, 0, slice_z); glVertex3f(-0.5, 1.0, 0.0);
elene@18 226 glEnd();
elene@18 227 glDisable(GL_TEXTURE_3D);
elene@18 228
elene@21 229 glLineWidth(3);
elene@21 230 glBegin(GL_LINE_LOOP);
elene@21 231 glColor3f(0.4, 0.8, 0.4);
elene@21 232 glVertex3f(-1.0, 1.0, 0.0);
elene@21 233 glVertex3f(-1.0, 0.5, 0.0);
elene@21 234 glVertex3f(-0.5, 0.5, 0.0);
elene@21 235 glVertex3f(-0.5, 1.0, 0.0);
elene@21 236 glEnd();
elene@21 237
elene@18 238 glMatrixMode(GL_PROJECTION);
elene@18 239 glPopMatrix();
elene@18 240
elene@18 241 glMatrixMode(GL_MODELVIEW);
elene@18 242 glPopMatrix();
elene@18 243
elene@22 244 glUseProgram(0);
elene@22 245
elene@21 246 glEnable(GL_DEPTH_TEST);
elene@18 247 glEnable(GL_LIGHTING);
elene@18 248 }
elene@18 249
elene@10 250 static void display(void)
eleni@0 251 {
elene@9 252 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
elene@9 253
elene@9 254 glMatrixMode(GL_MODELVIEW);
elene@9 255 glLoadIdentity();
elene@9 256 glTranslatef(0, 0, -cam_dist);
elene@9 257 glRotatef(cam_phi, 1, 0, 0);
elene@9 258 glRotatef(cam_theta, 0, 1, 0);
elene@9 259
elene@9 260 /*
elene@6 261 glBindTexture(GL_TEXTURE_3D, vol->get_texture());
elene@6 262 glEnable(GL_TEXTURE_3D);
eleni@1 263 glBegin(GL_QUADS);
elene@6 264 glTexCoord3f(0, 0, cur_z); glVertex3f(-1, -1, 0);
elene@6 265 glTexCoord3f(0, 1, cur_z); glVertex3f(-1, 1, 0);
elene@6 266 glTexCoord3f(1, 1, cur_z); glVertex3f(1, 1, 0);
elene@6 267 glTexCoord3f(1, 0, cur_z); glVertex3f(1, -1, 0);
eleni@1 268 glEnd();
elene@6 269 glDisable(GL_TEXTURE_3D);
elene@9 270 */
eleni@0 271
elene@23 272 /* if(mesh->is_empty()) {
elene@9 273 printf("recalculating isosurface ... ");
elene@9 274 fflush(stdout);
elene@12 275 vol->create_mesh(mesh, thres, thres2, vol_res[0], vol_res[1], vol_res[2]);
elene@9 276 printf("done.\n");
elene@9 277 }
elene@9 278 mesh->draw();
elene@23 279 */
elene@9 280
elene@18 281 volume_preview();
eleni@0 282 glutSwapBuffers();
eleni@0 283 assert(glGetError() == GL_NO_ERROR);
eleni@0 284 }
eleni@0 285
elene@10 286 static void reshape(int x, int y)
eleni@0 287 {
eleni@0 288 glViewport(0, 0, x, y);
elene@9 289 glMatrixMode(GL_PROJECTION);
elene@9 290 glLoadIdentity();
elene@9 291 gluPerspective(45, (float)x / (float)y, 0.5, 500);
elene@9 292
eleni@0 293 if(x != win_xsz || y != win_ysz) {
eleni@0 294 win_xsz = x;
eleni@0 295 win_ysz = y;
eleni@0 296 }
eleni@0 297 }
eleni@0 298
elene@10 299 static void keyboard(unsigned char key, int x, int y)
eleni@0 300 {
elene@9 301 key_state[(int)key] = true;
elene@9 302
eleni@0 303 switch(key) {
eleni@0 304 case 27:
eleni@0 305 exit(0);
elene@9 306 case 'w':
elene@9 307 {
elene@9 308 static bool wire;
elene@9 309 wire = !wire;
elene@9 310 glPolygonMode(GL_FRONT_AND_BACK, wire ? GL_LINE : GL_FILL);
elene@9 311 }
elene@9 312 break;
eleni@0 313 default:
eleni@0 314 break;
eleni@0 315 }
eleni@0 316 }
eleni@0 317
elene@10 318 static void keyboard_up(unsigned char key, int x, int y)
elene@9 319 {
elene@9 320 key_state[(int) key] = false;
elene@9 321 }
elene@9 322
eleni@0 323 static int prev_x, prev_y;
elene@9 324 static bool bn_state[32];
elene@12 325 static float prev_thres, prev_thres2;
elene@9 326
elene@10 327 static void mouse(int bn, int state, int x, int y)
eleni@0 328 {
eleni@0 329 prev_x = x;
eleni@0 330 prev_y = y;
elene@9 331
elene@9 332 bn_state[bn - GLUT_LEFT_BUTTON] = (state == GLUT_DOWN);
elene@9 333
elene@9 334 if(state == GLUT_DOWN) {
elene@9 335 prev_thres = thres;
elene@12 336 prev_thres2 = thres2;
elene@9 337 }
elene@9 338 else {
elene@12 339 if(thres != prev_thres || thres2 != prev_thres2) {
elene@9 340 mesh->clear();
elene@9 341 }
elene@9 342 }
eleni@0 343 }
eleni@0 344
elene@10 345 static void motion(int x, int y)
eleni@0 346 {
eleni@0 347 int dx = x - prev_x;
eleni@0 348 int dy = y - prev_y;
elene@9 349
elene@9 350 if(!dx && !dy)
elene@9 351 return;
elene@9 352
eleni@0 353 prev_x = x;
eleni@0 354 prev_y = y;
elene@6 355
elene@9 356 if(key_state[(int)'t']) {
elene@9 357 thres = (float)x / (float)win_xsz;
elene@9 358 printf("threshold: %f\n", thres);
elene@9 359
elene@9 360 glutPostRedisplay();
elene@9 361 return;
elene@9 362 }
elene@9 363
elene@9 364 // camera
elene@9 365 if(bn_state[0]) {
elene@9 366 if(key_state[(int)'z']) {
elene@9 367 //zoom the camera
elene@9 368
elene@9 369 cam_dist += dy * 0.1;
elene@9 370
elene@9 371 if(cam_dist < 0)
elene@9 372 cam_dist = 0;
elene@9 373 }
elene@9 374 else {
elene@9 375 //rotate the camera
elene@9 376
elene@9 377 cam_phi += dy * 0.5;
elene@9 378 cam_theta += dx * 0.5;
elene@9 379
elene@9 380 if(cam_phi > 90)
elene@9 381 cam_phi = 90;
elene@9 382 if(cam_phi < -90)
elene@9 383 cam_phi = -90;
elene@9 384 }
elene@9 385
elene@6 386 glutPostRedisplay();
elene@6 387 }
eleni@0 388 }
elene@16 389
elene@16 390 static bool init_xfer(void)
elene@16 391 {
elene@16 392 glutSetWindow(mainwin_id);
elene@16 393 int x = glutGet(GLUT_WINDOW_X);
elene@16 394 int y = glutGet(GLUT_WINDOW_Y) + glutGet(GLUT_WINDOW_HEIGHT);
elene@16 395
elene@16 396 glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
elene@16 397 glutInitWindowSize(512, 100);
elene@16 398 glutInitWindowPosition(x, y);
elene@16 399
elene@16 400 xferwin_id = glutCreateWindow("Transfer Function");
elene@16 401 glutDisplayFunc(display_xfer);
elene@16 402 glutReshapeFunc(reshape_xfer);
elene@17 403 glutMouseFunc(mouse_xfer);
elene@16 404 glutMotionFunc(motion_xfer);
elene@16 405
elene@16 406 return true;
elene@16 407 }
elene@16 408
elene@16 409 static void display_xfer(void)
elene@16 410 {
elene@16 411 glClear(GL_COLOR_BUFFER_BIT);
elene@16 412
elene@16 413 int num_val = glutGet(GLUT_WINDOW_WIDTH) / 4.0;
elene@16 414 float x = 0;
elene@16 415 float dx = 1.0 / num_val;
elene@16 416 float low, high;
elene@16 417 if(thres < thres2) {
elene@16 418 low = thres;
elene@16 419 high = thres2;
elene@16 420 }
elene@16 421 else {
elene@16 422 low = thres2;
elene@16 423 high = thres;
elene@16 424 }
elene@16 425
elene@17 426 //drawing the transfer function curve
elene@17 427
elene@17 428 glLineWidth(3);
elene@17 429 glBegin(GL_LINE_STRIP);
elene@17 430 glColor3f(0.8, 0.3, 0.8);
elene@17 431 for(int i=0; i<num_val; i++) {
elene@17 432 float val = transfer_function(x, low, high);
elene@17 433 glVertex2f(x, val);
elene@17 434 x += dx;
elene@17 435 }
elene@17 436 glEnd();
elene@17 437
elene@17 438 //threshold bars
elene@17 439
elene@17 440 glLineWidth(2);
elene@17 441 glBegin(GL_LINES);
elene@17 442 glColor3f(0.4, 0.4, 0.8);
elene@17 443 glVertex2f(low, 0);
elene@17 444 glVertex2f(low, 1);
elene@17 445 glColor3f(0.4, 0.8, 0.4);
elene@17 446 glVertex2f(high, 0);
elene@17 447 glVertex2f(high, 1);
elene@17 448 glEnd();
elene@17 449
elene@17 450 /*
elene@17 451 * gradient
elene@17 452 */
elene@17 453 /* glBegin(GL_QUADS);
elene@16 454 for(int i=0; i<num_val; i++) {
elene@16 455 float val = transfer_function(x, low, high);
elene@16 456 glColor3f(val, val, val);
elene@16 457 glVertex3f(x, 1.0, 0.0);
elene@16 458 glVertex3f(x, 0.0, 0.0);
elene@16 459
elene@16 460 val = transfer_function(x + dx, low, high);
elene@16 461 glColor3f(val, val, val);
elene@16 462 glVertex3f(x + dx, 0.0, 0.0);
elene@16 463 glVertex3f(x + dx, 1.0, 0.0);
elene@16 464 x += dx;
elene@16 465 }
elene@17 466 glEnd(); */
elene@16 467
elene@16 468 glutSwapBuffers();
elene@16 469 assert(glGetError() == GL_NO_ERROR);
elene@16 470 }
elene@16 471
elene@16 472 static void reshape_xfer(int x, int y)
elene@16 473 {
elene@16 474 glViewport(0.0, 0.0, x, y);
elene@16 475 glMatrixMode(GL_PROJECTION);
elene@16 476 glLoadIdentity();
elene@16 477 glOrtho(0.0, 1.0, 0.0, 1.0, -1.0, 1.0);
elene@16 478 }
elene@16 479
elene@17 480 static float *select_thres;
elene@17 481 static float prev_select_thres;
elene@17 482 static void mouse_xfer(int button, int state, int x, int y)
elene@17 483 {
elene@17 484 if(button == GLUT_LEFT_BUTTON) {
elene@17 485 if(state == GLUT_DOWN) {
elene@17 486 int width = glutGet(GLUT_WINDOW_WIDTH);
elene@17 487 float xpos = (float)x / (float)width;
elene@17 488 if(fabs(xpos - thres) <= fabs(xpos - thres2)) {
elene@17 489 select_thres = &thres;
elene@17 490 }
elene@17 491 else {
elene@17 492 select_thres = &thres2;
elene@17 493 }
elene@17 494 prev_select_thres = *select_thres;
elene@17 495 }
elene@17 496 else {
elene@17 497 if(fabs(*select_thres - prev_select_thres) > 0.001) {
elene@17 498 mesh->clear();
elene@17 499 ui->sync_live();
elene@17 500 }
elene@17 501 select_thres = 0;
elene@17 502 }
elene@17 503 }
elene@17 504 }
elene@17 505
elene@16 506 static void motion_xfer(int x, int y)
elene@16 507 {
elene@17 508 if(!select_thres)
elene@17 509 return;
elene@17 510
elene@17 511 int width = glutGet(GLUT_WINDOW_WIDTH);
elene@17 512 float xpos = (float)x / (float)width;
elene@17 513
elene@17 514 *select_thres = xpos;
elene@23 515 thres_change(0);
elene@17 516
elene@17 517 glutPostRedisplay();
elene@17 518 glutSetWindow(mainwin_id);
elene@17 519 glutPostRedisplay();
elene@16 520 }