00001 #include "volume_canvas.h"
00002
00003 #include <wx/dcclient.h>
00004 #include <wx/image.h>
00005
00006 #include <fstream>
00007 #include <vector>
00008 #include <algorithm>
00009 #include <stdexcept>
00010 #include <utility>
00011
00012 BEGIN_EVENT_TABLE(VolumeCanvas, wxGLCanvas)
00013 EVT_PAINT(VolumeCanvas::OnPaint)
00014 EVT_SIZE(VolumeCanvas::OnSize)
00015 EVT_ERASE_BACKGROUND(VolumeCanvas::OnEraseBackground)
00016 END_EVENT_TABLE()
00017
00018 namespace
00019 {
00020 std::pair<float, float> ComputeScaleFactors(float a1, float a2)
00021 {
00022 const float x = a2 / a1;
00023 if (x > 1.0f) return std::make_pair(1.0f, 1.0f / x);
00024 else return std::make_pair(x, 1.0f);
00025 }
00026
00027 int attrib_list[] = { WX_GL_RGBA, WX_GL_DOUBLEBUFFER, 0 };
00028 }
00029
00030 VolumeCanvas::VolumeCanvas(wxWindow * parent, wxWindowID id)
00031 : wxGLCanvas(parent, id, wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE, wxGLCanvasName,attrib_list)
00032 , m_volume_texture(0)
00033 , m_transfer_texture(0)
00034 , m_glstatus(GLS_INIT_PENDING)
00035 , m_volprog(0 )
00036 , m_viewmode(VM_SLICE_X)
00037 , m_sizex(0)
00038 , m_sizey(0)
00039 , m_sizez(0)
00040 , m_slice(0)
00041 , m_rotx(0)
00042 , m_roty(0)
00043 , m_rotz(0)
00044 {
00045 }
00046
00047 void VolumeCanvas::UpdateTransferFunction(TransferFunctionSampler *sampler)
00048 {
00049 std::vector<float> data;
00050
00051
00052 data.push_back(0.0f);
00053 data.push_back(0.0f);
00054 data.push_back(0.0f);
00055 data.push_back(0.0f);
00056
00057 for (int i = 1; i < 4096; ++i) {
00058 const TransferFunctionSampler::Color c = sampler->MapDensity(i / 4095.0f);
00059 data.push_back(c.r);
00060 data.push_back(c.g);
00061 data.push_back(c.b);
00062 data.push_back(c.a);
00063 }
00064
00065 glBindTexture(GL_TEXTURE_1D, m_transfer_texture);
00066 glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, 4096, 0, GL_RGBA, GL_FLOAT, &data[0]);
00067
00068 Refresh();
00069 }
00070
00071 void VolumeCanvas::Render3dView() const
00072 {
00073
00074 static const GLfloat vdata[] =
00075 {
00076 1.0f, 1.0f, -1.0f,
00077 1.0f, -1.0f, -1.0f,
00078 -1.0f, -1.0f, -1.0f,
00079 -1.0f, 1.0f, -1.0f,
00080 1.0f, 1.0f, 1.0f,
00081 1.0f, -1.0f, 1.0f,
00082 -1.0f, -1.0f, 1.0f,
00083 -1.0f, 1.0f, 1.0f
00084 };
00085
00086
00087 static const GLushort idata[] =
00088 {
00089 0, 1, 2, 3,
00090 4, 7, 6, 5,
00091 0, 4, 5, 1,
00092 1, 5, 6, 2,
00093 2, 6, 7, 3,
00094 4, 0, 3, 7
00095 };
00096
00097 const double aspect = static_cast<double>(m_viewport_width) / m_viewport_height;
00098
00099 glMatrixMode(GL_PROJECTION);
00100 glLoadIdentity();
00101
00102 gluPerspective(54.0, aspect, 0.1, 100.0);
00103 glMatrixMode(GL_MODELVIEW);
00104 glLoadIdentity();
00105
00106
00107 glTranslatef(0.0f, 0.0f, -3.0f);
00108
00109
00110 glRotatef(m_rotx, 1.0f, 0.0f, 0.0f);
00111 glRotatef(m_roty, 0.0f, 1.0f, 0.0f);
00112 glRotatef(m_rotz, 0.0f, 0.0f, 1.0f);
00113
00114
00115 const float ms = std::max(std::max(GetSizeX(), GetSizeY()), GetSizeZ());
00116 const float sx = GetSizeX() / ms;
00117 const float sy = GetSizeY() / ms;
00118 const float sz = GetSizeZ() / ms;
00119 glScalef(sx, sy, sz);
00120
00121 m_volprog->SetMode(VolProgram::M_3D_COLOR_CUBE);
00122 m_volprog->Use();
00123
00124 GLuint tex[2];
00125 glGenTextures(2, tex);
00126
00127
00128
00129 glEnable(GL_CULL_FACE);
00130 glEnableClientState(GL_VERTEX_ARRAY);
00131 glVertexPointer(3, GL_FLOAT, 0, vdata);
00132
00133
00134 glCullFace(GL_FRONT);
00135 glDrawElements(GL_QUADS, 24, GL_UNSIGNED_SHORT, idata);
00136 glBindTexture(GL_TEXTURE_2D, tex[0]);
00137 glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, m_viewport_width, m_viewport_height, 0);
00138 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
00139 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
00140 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
00141 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
00142
00143
00144 glCullFace(GL_BACK);
00145 glDrawElements(GL_QUADS, 24, GL_UNSIGNED_SHORT, idata);
00146 glBindTexture(GL_TEXTURE_2D, tex[1]);
00147 glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, m_viewport_width, m_viewport_height, 0);
00148 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
00149 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
00150 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
00151 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
00152
00153 glDisableClientState(GL_VERTEX_ARRAY);
00154 glDisable(GL_CULL_FACE);
00155
00156 m_volprog->SetMode(VolProgram::M_3D_RAYCAST);
00157 m_volprog->SetBackAndFrontTextures(tex[0], tex[1]);
00158 m_volprog->SetVolumeTexture(m_volume_texture);
00159 m_volprog->SetTransferFunctionTexture(m_transfer_texture);
00160 m_volprog->Use();
00161
00162
00163
00164 glMatrixMode(GL_PROJECTION);
00165 glLoadIdentity();
00166 glMatrixMode(GL_MODELVIEW);
00167 glLoadIdentity();
00168
00169 glBegin(GL_QUADS);
00170 glVertex2f(-1.0f, -1.0f);
00171 glVertex2f( 1.0f, -1.0f);
00172 glVertex2f( 1.0f, 1.0f);
00173 glVertex2f(-1.0f, 1.0f);
00174 glEnd();
00175
00176 m_volprog->SetBackAndFrontTextures(0, 0);
00177
00178 glDeleteTextures(2, tex);
00179 }
00180
00181 void VolumeCanvas::RenderSliceView() const
00182 {
00183 glMatrixMode(GL_PROJECTION);
00184 glLoadIdentity();
00185 glMatrixMode(GL_MODELVIEW);
00186 glLoadIdentity();
00187
00188 const float aspect = static_cast<float> (m_viewport_width) / m_viewport_height;
00189 float slice;
00190 std::pair<float, float> scale;
00191
00192 m_volprog->SetMode(VolProgram::M_SLICE);
00193 m_volprog->SetVolumeTexture(m_volume_texture);
00194 m_volprog->SetTransferFunctionTexture(m_transfer_texture);
00195 m_volprog->Use();
00196
00197 switch (GetViewmode()) {
00198 case VM_SLICE_X:
00199 slice = static_cast<float> (GetSlice()) / GetSizeX();
00200 scale = ComputeScaleFactors(aspect, static_cast<float> (GetSizeY()) / GetSizeZ());
00201 glScalef(scale.first, scale.second, 1.0f);
00202 glBegin(GL_QUADS);
00203 glTexCoord3f(slice, 0.0f, 0.0f);
00204 glVertex2f(-1.0f, -1.0f);
00205 glTexCoord3f(slice, 1.0f, 0.0f);
00206 glVertex2f(1.0f, -1.0f);
00207 glTexCoord3f(slice, 1.0f, 1.0f);
00208 glVertex2f(1.0f, 1.0f);
00209 glTexCoord3f(slice, 0.0f, 1.0f);
00210 glVertex2f(-1.0f, 1.0f);
00211 glEnd();
00212 break;
00213 case VM_SLICE_Y:
00214 slice = static_cast<float> (GetSlice()) / GetSizeY();
00215 scale = ComputeScaleFactors(aspect, static_cast<float> (GetSizeX()) / GetSizeZ());
00216 glScalef(scale.first, scale.second, 1.0f);
00217 glBegin(GL_QUADS);
00218 glTexCoord3f(0.0f, slice, 0.0f);
00219 glVertex2f(-1.0f, -1.0f);
00220 glTexCoord3f(1.0f, slice, 0.0f);
00221 glVertex2f(1.0f, -1.0f);
00222 glTexCoord3f(1.0f, slice, 1.0f);
00223 glVertex2f(1.0f, 1.0f);
00224 glTexCoord3f(0.0f, slice, 1.0f);
00225 glVertex2f(-1.0f, 1.0f);
00226 glEnd();
00227 break;
00228 case VM_SLICE_Z:
00229 slice = static_cast<float> (GetSlice()) / GetSizeZ();
00230 scale = ComputeScaleFactors(aspect, static_cast<float> (GetSizeX()) / GetSizeY());
00231 glScalef(scale.first, scale.second, 1.0f);
00232 glBegin(GL_QUADS);
00233 glTexCoord3f(0.0f, 0.0f, slice);
00234 glVertex2f(-1.0f, -1.0f);
00235 glTexCoord3f(1.0f, 0.0f, slice);
00236 glVertex2f(1.0f, -1.0f);
00237 glTexCoord3f(1.0f, 1.0f, slice);
00238 glVertex2f(1.0f, 1.0f);
00239 glTexCoord3f(0.0f, 1.0f, slice);
00240 glVertex2f(-1.0f, 1.0f);
00241 glEnd();
00242 break;
00243 }
00244 }
00245
00246 void VolumeCanvas::RenderToFile(const wxString& file_name, int width, int height)
00247 {
00248
00249
00250 if (!CheckGL()) return;
00251
00252 GLuint id_rbf;
00253 GLuint id_fbo;
00254
00255
00256 glGenFramebuffersEXT(1, &id_fbo);
00257 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, id_fbo);
00258
00259
00260 glGenRenderbuffersEXT(1, &id_rbf);
00261 glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGBA, width, height);
00262 glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, id_rbf);
00263
00264
00265 glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, id_rbf);
00266
00267
00268 if (GL_FRAMEBUFFER_COMPLETE_EXT == glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT))
00269 {
00270 m_viewport_width = width;
00271 m_viewport_height = height;
00272
00273 Render();
00274
00275 unsigned char *rgb = (unsigned char*)malloc(width * height * 3);
00276 unsigned char *alpha = (unsigned char*)malloc(width * height);
00277
00278 glPixelStorei(GL_PACK_ALIGNMENT, 1);
00279 glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, rgb);
00280 glReadPixels(0, 0, width, height, GL_ALPHA, GL_UNSIGNED_BYTE, alpha);
00281 glPixelStorei(GL_PACK_ALIGNMENT, 4);
00282
00283 wxImage img(width, height);
00284 img.SetData(rgb);
00285 img.SetAlpha(alpha);
00286 img = img.Mirror(false);
00287
00288 if (!img.SaveFile(file_name)) {
00289
00290 }
00291 } else {
00292
00293 }
00294
00295
00296 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
00297
00298
00299 glDeleteRenderbuffersEXT(1, &id_rbf);
00300
00301
00302 glDeleteFramebuffersEXT(1, &id_fbo);
00303 }
00304
00305 void VolumeCanvas::Render() const
00306 {
00307 glViewport(0, 0, m_viewport_width, m_viewport_height);
00308 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
00309 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
00310
00311 if (!m_volume_texture) return;
00312
00313 if (GetViewmode() == VM_3D) {
00314 Render3dView();
00315 } else {
00316 RenderSliceView();
00317 }
00318 }
00319
00320 bool VolumeCanvas::InitGL()
00321 {
00322 if (glewInit() != GLEW_NO_ERROR) return false;
00323 if (!GLEW_VERSION_2_0) return false;
00324
00325 try {
00326 m_volprog = new VolProgram("data/volprog");
00327 } catch (...) {
00328 return false;
00329 }
00330
00331 glGenTextures(1, &m_transfer_texture);
00332 glBindTexture(GL_TEXTURE_1D, m_transfer_texture);
00333 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
00334 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
00335 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
00336
00337 return true;
00338 }
00339
00340 bool VolumeCanvas::CheckGL()
00341 {
00342 #ifndef __WXMOTIF__
00343 if (!GetContext()) return false;
00344 #endif
00345
00346 SetCurrent();
00347 if (m_glstatus == GLS_INIT_PENDING) m_glstatus = InitGL() ? GLS_OK : GLS_INIT_FAILED;
00348 if (m_glstatus != GLS_OK) return false;
00349 return true;
00350 }
00351
00352 void VolumeCanvas::OnPaint(wxPaintEvent & event)
00353 {
00354 wxPaintDC dc(this);
00355 if (!CheckGL()) return;
00356
00357
00358 GetClientSize(&m_viewport_width, &m_viewport_height);
00359 Render();
00360 SwapBuffers();
00361 }
00362
00363 void VolumeCanvas::OnSize(wxSizeEvent &event)
00364 {
00365 wxGLCanvas::OnSize(event);
00366 Update();
00367 }
00368
00369 void VolumeCanvas::OnEraseBackground(wxEraseEvent& event)
00370 {
00371
00372 }
00373
00374 namespace
00375 {
00376 struct VolumeData {
00377 unsigned short grad_x;
00378 unsigned short grad_y;
00379 unsigned short grad_z;
00380 unsigned short value;
00381 };
00382 }
00383
00384 void VolumeCanvas::OpenFile(const char *filename)
00385 {
00386 if (!CheckGL()) throw std::runtime_error("failed to initialise OpenGL");
00387
00388 std::ifstream in(filename, std::ios::in | std::ios::binary);
00389 in.exceptions(std::ios::failbit | std::ios::badbit);
00390
00391 unsigned short size_x, size_y, size_z;
00392
00393
00394 in.read(reinterpret_cast<char*> (&size_x), sizeof(size_x));
00395 in.read(reinterpret_cast<char*> (&size_y), sizeof(size_y));
00396 in.read(reinterpret_cast<char*> (&size_z), sizeof(size_z));
00397
00398
00399 wxUINT16_SWAP_ON_BE(size_x);
00400 wxUINT16_SWAP_ON_BE(size_y);
00401 wxUINT16_SWAP_ON_BE(size_z);
00402
00403 GLint max_size;
00404 glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, &max_size);
00405
00406 if (size_x < 16 || size_y < 16 || size_z < 16 || size_x > max_size || size_y > max_size || size_z > max_size) {
00407 throw std::runtime_error("dataset size exceeds hardware limits");
00408 }
00409
00410 const unsigned count = size_x * size_y * size_z;
00411 std::vector<VolumeData> volume_data(count);
00412
00413 std::auto_ptr<HistogramData> histogram(new HistogramData());
00414
00415
00416 for (unsigned int z = 0; z < size_z; ++z) {
00417 for (unsigned int y = 0; y < size_y; ++y) {
00418 for (unsigned int x = 0; x < size_x; ++x) {
00419 unsigned short value;
00420 in.read(reinterpret_cast<char*> (&value), sizeof(value));
00421 wxUINT16_SWAP_ON_BE(value);
00422
00423
00424
00425 value <<= 4;
00426
00427 volume_data[x + y * size_x + z * size_x * size_y].value = value;
00428
00429 histogram->AddData(value);
00430 }
00431 }
00432 }
00433
00434
00435 struct ArrayAccess3d {
00436 std::vector<VolumeData> &data;
00437 unsigned short sx, sy, sz;
00438
00439 ArrayAccess3d(std::vector<VolumeData> &data, unsigned short sx, unsigned short sy, unsigned short sz) :
00440 data(data), sx(sx), sy(sy), sz(sz)
00441 {
00442 }
00443
00444 VolumeData& operator ()(int x, int y, int z)
00445 {
00446
00447 x = std::min(std::max(x, 0), sx - 1);
00448 y = std::min(std::max(y, 0), sy - 1);
00449 z = std::min(std::max(z, 0), sz - 1);
00450
00451
00452 return data[x + y * sx + z * sx * sy];
00453 }
00454 } volume(volume_data, size_x, size_y, size_z);
00455
00456
00457 for (unsigned short z = 0; z < size_z; ++z) {
00458 for (unsigned short y = 0; y < size_y; ++y) {
00459 for (unsigned short x = 0; x < size_x; ++x) {
00460 volume(x, y, z).grad_x = 0.5 * (volume(x + 1, y, z).value - volume(x - 1, y, z).value);
00461 volume(x, y, z).grad_y = 0.5 * (volume(x, y + 1, z).value - volume(x, y - 1, z).value);
00462 volume(x, y, z).grad_z = 0.5 * (volume(x, y, z + 1).value - volume(x, y, z - 1).value);
00463 }
00464 }
00465 }
00466
00467
00468 while (glGetError() != GL_NO_ERROR)
00469 ;
00470
00471
00472
00473
00474 GLuint id = 0;
00475 glGenTextures(1, &id);
00476 glBindTexture(GL_TEXTURE_3D, id);
00477 glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA16, size_x, size_y, size_z, 0, GL_RGBA, GL_UNSIGNED_SHORT,
00478 &volume_data[0]);
00479 glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
00480 glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
00481 glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
00482 glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
00483 glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
00484
00485 if (glGetError() != GL_NO_ERROR) {
00486 glDeleteTextures(1, &id);
00487 throw std::runtime_error("OpenGL error");
00488 }
00489
00490
00491 m_histogramdata = histogram;
00492
00493
00494 glDeleteTextures(1, &m_volume_texture);
00495 m_volume_texture = id;
00496
00497 m_sizex = size_x;
00498 m_sizey = size_y;
00499 m_sizez = size_z;
00500
00501
00502 Refresh();
00503 }