volmetrics

annotate src/main.cc @ 17:0f4fff558737

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