nuclear@12: /* nuclear@12: glviewvol is an OpenGL 3D volume data viewer nuclear@12: Copyright (C) 2014 John Tsiombikas nuclear@12: nuclear@12: This program is free software: you can redistribute it and/or modify nuclear@12: it under the terms of the GNU General Public License as published by nuclear@12: the Free Software Foundation, either version 3 of the License, or nuclear@12: (at your option) any later version. nuclear@12: nuclear@12: This program is distributed in the hope that it will be useful, nuclear@12: but WITHOUT ANY WARRANTY; without even the implied warranty of nuclear@12: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the nuclear@12: GNU General Public License for more details. nuclear@12: nuclear@12: You should have received a copy of the GNU General Public License nuclear@12: along with this program. If not, see . nuclear@12: */ nuclear@3: #include nuclear@4: #include nuclear@0: #include "opengl.h" nuclear@11: #include "viewer.h" nuclear@1: #include "rend_fast.h" nuclear@3: #include "opt.h" nuclear@3: #include "volume.h" nuclear@4: #include "xfer_view.h" nuclear@0: nuclear@1: static int win_width, win_height; nuclear@8: static float cam_theta, cam_phi, cam_dist = 4; nuclear@8: static float pre_rot = -90; nuclear@4: static int splitter_y = -1; nuclear@4: nuclear@4: #define SPLITTER_WIDTH 5 nuclear@0: nuclear@1: static Renderer *rend; nuclear@3: static Volume *vol; nuclear@6: static TransferFunc *xfer; nuclear@1: nuclear@0: extern "C" { nuclear@0: nuclear@0: int init() nuclear@0: { nuclear@3: if(!opt.fname) { nuclear@3: fprintf(stderr, "you must specify the volume data filename\n"); nuclear@1: return -1; nuclear@1: } nuclear@1: nuclear@3: switch(opt.rend_type) { nuclear@3: case REND_FAST: nuclear@3: rend = new RendererFast; nuclear@3: break; nuclear@3: default: nuclear@3: return -1; nuclear@3: } nuclear@3: nuclear@3: if(!rend->init()) { nuclear@3: fprintf(stderr, "renderer initialization failed\n"); nuclear@3: return -1; nuclear@3: } nuclear@3: nuclear@3: VoxelVolume *voxvol = new VoxelVolume; nuclear@3: if(!voxvol->load(opt.fname)) { nuclear@3: fprintf(stderr, "failed to load volume data from: %s\n", opt.fname); nuclear@3: return -1; nuclear@3: } nuclear@3: vol = voxvol; nuclear@3: rend->set_volume(vol); nuclear@3: nuclear@6: xfer = new TransferWindow; nuclear@6: rend->set_transfer_function(xfer); nuclear@6: nuclear@6: if(!xfview_init(xfer)) { nuclear@4: return -1; nuclear@4: } nuclear@14: xfview_set_volume(vol); nuclear@4: nuclear@1: return 0; nuclear@0: } nuclear@0: nuclear@0: void cleanup() nuclear@0: { nuclear@4: xfview_destroy(); nuclear@4: nuclear@1: rend->destroy(); nuclear@1: delete rend; nuclear@3: delete vol; nuclear@6: delete xfer; nuclear@0: } nuclear@0: nuclear@0: void ev_display() nuclear@0: { nuclear@0: glClear(GL_COLOR_BUFFER_BIT); nuclear@1: nuclear@4: // render the main view nuclear@4: glViewport(0, win_height - splitter_y, win_width, splitter_y); nuclear@4: nuclear@4: glMatrixMode(GL_PROJECTION); nuclear@4: glLoadIdentity(); nuclear@4: gluPerspective(50.0, (float)win_width / (float)splitter_y, 0.1, 100.0); nuclear@4: nuclear@4: glMatrixMode(GL_MODELVIEW); nuclear@4: glLoadIdentity(); nuclear@4: glTranslatef(0, 0, -cam_dist); nuclear@8: glRotatef(cam_phi + pre_rot, 1, 0, 0); nuclear@4: glRotatef(cam_theta, 0, 1, 0); nuclear@4: nuclear@1: rend->update(0); nuclear@1: rend->render(); nuclear@4: nuclear@4: // draw the transfer function view nuclear@4: glViewport(0, 0, win_width, win_height - splitter_y); nuclear@4: nuclear@4: xfview_draw(); nuclear@4: nuclear@4: // draw the GUI nuclear@4: glViewport(0, 0, win_width, win_height); nuclear@4: nuclear@4: glMatrixMode(GL_PROJECTION); nuclear@4: glLoadIdentity(); nuclear@4: glOrtho(0, win_width, win_height, 0, -1, 1); nuclear@4: nuclear@4: glMatrixMode(GL_MODELVIEW); nuclear@4: glLoadIdentity(); nuclear@4: nuclear@4: glBegin(GL_QUADS); nuclear@4: glColor3f(1, 1, 1); nuclear@4: glVertex2f(0, splitter_y + SPLITTER_WIDTH / 2); nuclear@4: glVertex2f(win_width, splitter_y + SPLITTER_WIDTH / 2); nuclear@4: glVertex2f(win_width, splitter_y - SPLITTER_WIDTH / 2); nuclear@4: glVertex2f(0, splitter_y - SPLITTER_WIDTH / 2); nuclear@4: glEnd(); nuclear@4: nuclear@4: swap_buffers(); nuclear@0: } nuclear@0: nuclear@0: void ev_reshape(int x, int y) nuclear@0: { nuclear@4: if(splitter_y < 0) { // not initialized yet nuclear@4: splitter_y = (int)(y * 0.85); nuclear@4: } else { nuclear@4: // calculate where the splitter was relative to the window height nuclear@4: // and based on that, it's new position nuclear@4: float split = (float)splitter_y / (float)win_height; nuclear@4: splitter_y = (int)(y * split); nuclear@4: } nuclear@4: nuclear@0: win_width = x; nuclear@0: win_height = y; nuclear@0: nuclear@1: glViewport(0, 0, x, y); nuclear@4: if(rend) { nuclear@4: rend->reshape(x, y); nuclear@4: } nuclear@0: } nuclear@0: nuclear@10: static bool zscaling; nuclear@10: nuclear@1: void ev_keyboard(int key, int press, int x, int y) nuclear@1: { nuclear@8: RendererFast *fr; nuclear@8: nuclear@10: switch(key) { nuclear@10: case 27: nuclear@10: if(press) { nuclear@1: quit(); nuclear@10: } nuclear@10: break; nuclear@8: nuclear@10: case 'z': nuclear@10: case 'Z': nuclear@10: zscaling = press; nuclear@10: break; nuclear@10: nuclear@10: case '=': nuclear@10: if(press && (fr = dynamic_cast(rend))) { nuclear@10: int n = fr->get_proxy_count(); nuclear@10: int add = n / 4; nuclear@10: n += add < 1 ? 1 : add; nuclear@10: printf("proxy count: %d\n", n); nuclear@10: fr->set_proxy_count(n); nuclear@8: redisplay(); nuclear@10: } nuclear@10: break; nuclear@8: nuclear@10: case '-': nuclear@10: if(press && (fr = dynamic_cast(rend))) { nuclear@10: int n = fr->get_proxy_count(); nuclear@10: int sub = n / 4; nuclear@10: n -= sub < 1 ? 1 : sub; nuclear@8: nuclear@10: if(n < 1) n = 1; nuclear@8: nuclear@10: printf("proxy count: %d\n", n); nuclear@10: fr->set_proxy_count(n); nuclear@8: redisplay(); nuclear@10: } nuclear@10: break; nuclear@8: nuclear@10: default: nuclear@10: break; nuclear@1: } nuclear@1: } nuclear@1: nuclear@4: static bool bnstate[8]; nuclear@4: static int prev_x, prev_y; nuclear@4: nuclear@4: #define ON_SPLITTER(y) (abs(y - splitter_y) <= SPLITTER_WIDTH / 2) nuclear@4: static bool splitter_dragging; nuclear@4: nuclear@1: void ev_mouse_button(int bn, int press, int x, int y) nuclear@1: { nuclear@4: bnstate[bn] = press != 0; nuclear@4: prev_x = x; nuclear@4: prev_y = y; nuclear@4: nuclear@4: splitter_dragging = bn == 0 && press && ON_SPLITTER(y); nuclear@4: nuclear@4: if(!splitter_dragging && y > splitter_y) { nuclear@4: xfview_button(bn, press, x, y); nuclear@4: } nuclear@1: } nuclear@1: nuclear@1: void ev_mouse_motion(int x, int y) nuclear@1: { nuclear@4: int dx = x - prev_x; nuclear@4: int dy = y - prev_y; nuclear@4: prev_x = x; nuclear@4: prev_y = y; nuclear@4: nuclear@4: if((dx | dy) == 0) return; nuclear@4: nuclear@10: if(bnstate[0] && zscaling) { nuclear@10: float s = rend->get_zscale() + (float)dy / (float)win_height; nuclear@10: rend->set_zscale(s < 0.0 ? 0.0 : s); nuclear@10: redisplay(); nuclear@10: return; nuclear@10: } nuclear@10: nuclear@4: if(splitter_dragging) { nuclear@4: splitter_y += dy; nuclear@4: redisplay(); nuclear@4: return; nuclear@4: } nuclear@4: nuclear@4: if(y > splitter_y) { nuclear@4: xfview_motion(x, y); nuclear@4: return; nuclear@4: } nuclear@4: nuclear@4: // main view motion handling nuclear@4: if(bnstate[0]) { nuclear@4: cam_theta += dx * 0.5; nuclear@4: cam_phi += dy * 0.5; nuclear@4: nuclear@4: if(cam_phi < -90) cam_phi = -90; nuclear@4: if(cam_phi > 90) cam_phi = 90; nuclear@4: redisplay(); nuclear@4: } nuclear@4: if(bnstate[2]) { nuclear@4: cam_dist += dy * 0.1; nuclear@4: nuclear@4: if(cam_dist < 0.0) cam_dist = 0.0; nuclear@4: redisplay(); nuclear@4: } nuclear@1: } nuclear@0: nuclear@0: } // extern "C"