[2798] | 1 | /* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */ |
---|
[406] | 2 | /* |
---|
| 3 | * ---------------------------------------------------------------------- |
---|
| 4 | * VolumeRenderer.cpp : VolumeRenderer class for volume visualization |
---|
| 5 | * |
---|
| 6 | * ====================================================================== |
---|
| 7 | * AUTHOR: Wei Qiao <qiaow@purdue.edu> |
---|
| 8 | * Purdue Rendering and Perceptualization Lab (PURPL) |
---|
| 9 | * |
---|
| 10 | * Copyright (c) 2004-2006 Purdue Research Foundation |
---|
| 11 | * |
---|
| 12 | * See the file "license.terms" for information on usage and |
---|
| 13 | * redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES. |
---|
| 14 | * ====================================================================== |
---|
| 15 | */ |
---|
[2822] | 16 | #include <stdlib.h> |
---|
[1328] | 17 | #include <assert.h> |
---|
| 18 | #include <time.h> |
---|
| 19 | #include <sys/time.h> |
---|
[2973] | 20 | #include <float.h> |
---|
[2804] | 21 | |
---|
[2972] | 22 | #include <vector> |
---|
| 23 | |
---|
[2822] | 24 | #include <GL/glew.h> |
---|
| 25 | #include <GL/glut.h> |
---|
| 26 | |
---|
| 27 | #include <tcl.h> |
---|
| 28 | |
---|
[617] | 29 | #include <R2/R2FilePath.h> |
---|
[2804] | 30 | |
---|
| 31 | #include "nanovis.h" |
---|
[2822] | 32 | #include "VolumeRenderer.h" |
---|
| 33 | #include "ConvexPolygon.h" |
---|
[2804] | 34 | |
---|
[617] | 35 | #include "NvStdVertexShader.h" |
---|
[884] | 36 | #include "Trace.h" |
---|
[1028] | 37 | #include "Grid.h" |
---|
[406] | 38 | |
---|
[1028] | 39 | #define NUMDIGITS 6 |
---|
[406] | 40 | |
---|
[2804] | 41 | VolumeRenderer::VolumeRenderer() : |
---|
[2877] | 42 | _sliceMode(false), |
---|
| 43 | _volumeMode(true) |
---|
[406] | 44 | { |
---|
[2877] | 45 | initShaders(); |
---|
[617] | 46 | |
---|
[2972] | 47 | std::string path = R2FilePath::getInstance()->getPath("Font.bmp"); |
---|
| 48 | if (path.empty()) { |
---|
[2822] | 49 | ERROR("can't find Font.bmp\n"); |
---|
[2972] | 50 | assert(!path.empty()); |
---|
[1111] | 51 | } |
---|
[2972] | 52 | initFont(path.c_str()); |
---|
[884] | 53 | _volumeInterpolator = new VolumeInterpolator(); |
---|
[418] | 54 | } |
---|
| 55 | |
---|
[580] | 56 | VolumeRenderer::~VolumeRenderer() |
---|
| 57 | { |
---|
[617] | 58 | delete _zincBlendeShader; |
---|
| 59 | delete _regularVolumeShader; |
---|
| 60 | delete _stdVertexShader; |
---|
[884] | 61 | delete _volumeInterpolator; |
---|
[580] | 62 | } |
---|
[418] | 63 | |
---|
| 64 | //initialize the volume shaders |
---|
[2877] | 65 | void VolumeRenderer::initShaders() |
---|
[2822] | 66 | { |
---|
| 67 | //standard vertex program |
---|
| 68 | _stdVertexShader = new NvStdVertexShader(); |
---|
[406] | 69 | |
---|
[2822] | 70 | //volume rendering shader: one cubic volume |
---|
| 71 | _regularVolumeShader = new NvRegularVolumeShader(); |
---|
[524] | 72 | |
---|
[2822] | 73 | //volume rendering shader: one zincblende orbital volume. |
---|
| 74 | //This shader renders one orbital of the simulation. |
---|
| 75 | //A sim has S, P, D, SS orbitals. thus a full rendering requires 4 zincblende orbital volumes. |
---|
| 76 | //A zincblende orbital volume is decomposed into 2 "interlocking" cubic 4-component volumes and passed to the shader. |
---|
| 77 | //We render each orbital with a independent transfer functions then blend the result. |
---|
| 78 | // |
---|
| 79 | //The engine is already capable of rendering multiple volumes and combine them. Thus, we just invoke this shader on |
---|
| 80 | //S, P, D and SS orbitals with different transfor functions. The result is a multi-orbital rendering. |
---|
| 81 | _zincBlendeShader = new NvZincBlendeVolumeShader(); |
---|
[406] | 82 | } |
---|
| 83 | |
---|
[1258] | 84 | struct SortElement { |
---|
| 85 | float z; |
---|
[2877] | 86 | int volumeId; |
---|
| 87 | int sliceId; |
---|
[2822] | 88 | |
---|
[2804] | 89 | SortElement(float _z, int _v, int _s) : |
---|
[2877] | 90 | z(_z), |
---|
| 91 | volumeId(_v), |
---|
| 92 | sliceId(_s) |
---|
[2804] | 93 | {} |
---|
[415] | 94 | }; |
---|
| 95 | |
---|
[2877] | 96 | static int sliceSort(const void *a, const void *b) |
---|
[2822] | 97 | { |
---|
| 98 | if ((*((SortElement*)a)).z > (*((SortElement*)b)).z) |
---|
[2804] | 99 | return 1; |
---|
| 100 | else |
---|
| 101 | return -1; |
---|
[415] | 102 | } |
---|
| 103 | |
---|
[1258] | 104 | void |
---|
[2877] | 105 | VolumeRenderer::renderAll() |
---|
[580] | 106 | { |
---|
[1478] | 107 | size_t total_rendered_slices = 0; |
---|
[900] | 108 | |
---|
[2877] | 109 | if (_volumeInterpolator->isStarted()) { |
---|
[1478] | 110 | #ifdef notdef |
---|
[884] | 111 | ani_vol = _volumeInterpolator->getVolume(); |
---|
[1478] | 112 | ani_tf = ani_vol->transferFunction(); |
---|
| 113 | #endif |
---|
[2853] | 114 | TRACE("VOLUME INTERPOLATOR IS STARTED ----------------------------"); |
---|
[884] | 115 | } |
---|
[1478] | 116 | // Determine the volumes that are to be rendered. |
---|
[2804] | 117 | std::vector<Volume *> volumes; |
---|
[1493] | 118 | Tcl_HashEntry *hPtr; |
---|
| 119 | Tcl_HashSearch iter; |
---|
| 120 | for (hPtr = Tcl_FirstHashEntry(&NanoVis::volumeTable, &iter); hPtr != NULL; |
---|
[2853] | 121 | hPtr = Tcl_NextHashEntry(&iter)) { |
---|
| 122 | Volume* volPtr; |
---|
| 123 | volPtr = (Volume *)Tcl_GetHashValue(hPtr); |
---|
[2804] | 124 | if (!volPtr->visible()) { |
---|
[2853] | 125 | continue; // Skip this volume |
---|
| 126 | } |
---|
| 127 | // BE CAREFUL: Set the number of slices to something slightly |
---|
| 128 | // different for each volume. If we have identical volumes at exactly |
---|
| 129 | // the same position with exactly the same number of slices, the |
---|
| 130 | // second volume will overwrite the first, so the first won't appear |
---|
| 131 | // at all. |
---|
| 132 | volumes.push_back(volPtr); |
---|
[2877] | 133 | volPtr->numSlices(256 - volumes.size()); |
---|
[1478] | 134 | } |
---|
[884] | 135 | |
---|
[2932] | 136 | glPushAttrib(GL_ENABLE_BIT); |
---|
| 137 | |
---|
[1258] | 138 | //two dimension pointer array |
---|
[2853] | 139 | ConvexPolygon ***polys = new ConvexPolygon**[volumes.size()]; |
---|
[1258] | 140 | //number of actual slices for each volume |
---|
[2932] | 141 | size_t *actual_slices = new size_t[volumes.size()]; |
---|
[884] | 142 | |
---|
[1984] | 143 | TRACE("start loop %d\n", volumes.size()); |
---|
[1478] | 144 | for (size_t i = 0; i < volumes.size(); i++) { |
---|
[2853] | 145 | Volume *volPtr = volumes[i]; |
---|
[1478] | 146 | polys[i] = NULL; |
---|
| 147 | actual_slices[i] = 0; |
---|
[1429] | 148 | |
---|
[2877] | 149 | int n_slices = volPtr->numSlices(); |
---|
[1478] | 150 | if (volPtr->isosurface()) { |
---|
[2853] | 151 | // double the number of slices |
---|
| 152 | n_slices <<= 1; |
---|
| 153 | } |
---|
[2804] | 154 | |
---|
[900] | 155 | //volume start location |
---|
[1478] | 156 | Vector3 loc = volPtr->location(); |
---|
| 157 | Vector4 shift_4d(loc.x, loc.y, loc.z, 0); |
---|
[2804] | 158 | |
---|
[900] | 159 | double x0 = 0; |
---|
| 160 | double y0 = 0; |
---|
| 161 | double z0 = 0; |
---|
[2804] | 162 | |
---|
[900] | 163 | Mat4x4 model_view_no_trans, model_view_trans; |
---|
| 164 | Mat4x4 model_view_no_trans_inverse, model_view_trans_inverse; |
---|
[2804] | 165 | |
---|
[2853] | 166 | //initialize volume plane with world coordinates |
---|
[900] | 167 | Plane volume_planes[6]; |
---|
[2906] | 168 | volume_planes[0].setCoeffs( 1, 0, 0, -x0); |
---|
| 169 | volume_planes[1].setCoeffs(-1, 0, 0, x0+1); |
---|
| 170 | volume_planes[2].setCoeffs( 0, 1, 0, -y0); |
---|
| 171 | volume_planes[3].setCoeffs( 0, -1, 0, y0+1); |
---|
| 172 | volume_planes[4].setCoeffs( 0, 0, 1, -z0); |
---|
| 173 | volume_planes[5].setCoeffs( 0, 0, -1, z0+1); |
---|
[2804] | 174 | |
---|
[2853] | 175 | //get modelview matrix with no translation |
---|
[900] | 176 | glPushMatrix(); |
---|
[2877] | 177 | glScalef(volPtr->aspectRatioWidth, |
---|
| 178 | volPtr->aspectRatioHeight, |
---|
| 179 | volPtr->aspectRatioDepth); |
---|
[2804] | 180 | |
---|
[900] | 181 | glEnable(GL_DEPTH_TEST); |
---|
[2804] | 182 | |
---|
[900] | 183 | GLfloat mv_no_trans[16]; |
---|
| 184 | glGetFloatv(GL_MODELVIEW_MATRIX, mv_no_trans); |
---|
[2804] | 185 | |
---|
[900] | 186 | model_view_no_trans = Mat4x4(mv_no_trans); |
---|
| 187 | model_view_no_trans_inverse = model_view_no_trans.inverse(); |
---|
[2804] | 188 | |
---|
[900] | 189 | glPopMatrix(); |
---|
[2804] | 190 | |
---|
[2853] | 191 | //get modelview matrix with translation |
---|
[900] | 192 | glPushMatrix(); |
---|
| 193 | glTranslatef(shift_4d.x, shift_4d.y, shift_4d.z); |
---|
[2877] | 194 | glScalef(volPtr->aspectRatioWidth, |
---|
| 195 | volPtr->aspectRatioHeight, |
---|
| 196 | volPtr->aspectRatioDepth); |
---|
[900] | 197 | GLfloat mv_trans[16]; |
---|
| 198 | glGetFloatv(GL_MODELVIEW_MATRIX, mv_trans); |
---|
[2804] | 199 | |
---|
[900] | 200 | model_view_trans = Mat4x4(mv_trans); |
---|
| 201 | model_view_trans_inverse = model_view_trans.inverse(); |
---|
[2804] | 202 | |
---|
[2853] | 203 | //draw volume bounding box with translation (the correct location in |
---|
| 204 | //space) |
---|
[1478] | 205 | if (volPtr->outline()) { |
---|
[900] | 206 | float olcolor[3]; |
---|
[2877] | 207 | volPtr->getOutlineColor(olcolor); |
---|
| 208 | drawBoundingBox(x0, y0, z0, x0+1, y0+1, z0+1, |
---|
[2853] | 209 | (double)olcolor[0], (double)olcolor[1], (double)olcolor[2], |
---|
| 210 | 1.5); |
---|
[900] | 211 | } |
---|
| 212 | glPopMatrix(); |
---|
[2804] | 213 | |
---|
[900] | 214 | //draw labels |
---|
| 215 | glPushMatrix(); |
---|
| 216 | glTranslatef(shift_4d.x, shift_4d.y, shift_4d.z); |
---|
[2932] | 217 | if (volPtr->outline()) { |
---|
| 218 | //drawLabel(i); |
---|
[900] | 219 | } |
---|
| 220 | glPopMatrix(); |
---|
[2804] | 221 | |
---|
[2853] | 222 | //transform volume_planes to eye coordinates. |
---|
[2804] | 223 | for (size_t j = 0; j < 6; j++) { |
---|
[1478] | 224 | volume_planes[j].transform(model_view_no_trans); |
---|
[2853] | 225 | } |
---|
[2973] | 226 | double eyeMinX, eyeMaxX, eyeMinY, eyeMaxY, zNear, zFar; |
---|
| 227 | getEyeSpaceBounds(model_view_no_trans, |
---|
| 228 | eyeMinX, eyeMaxX, |
---|
| 229 | eyeMinY, eyeMaxY, |
---|
| 230 | zNear, zFar); |
---|
[2804] | 231 | |
---|
[2853] | 232 | //compute actual rendering slices |
---|
| 233 | float z_step = fabs(zNear-zFar)/n_slices; |
---|
[1478] | 234 | size_t n_actual_slices; |
---|
[2804] | 235 | |
---|
[2877] | 236 | if (volPtr->dataEnabled()) { |
---|
[900] | 237 | n_actual_slices = (int)(fabs(zNear-zFar)/z_step + 1); |
---|
[1478] | 238 | polys[i] = new ConvexPolygon*[n_actual_slices]; |
---|
[900] | 239 | } else { |
---|
| 240 | n_actual_slices = 0; |
---|
[1478] | 241 | polys[i] = NULL; |
---|
[900] | 242 | } |
---|
[1478] | 243 | actual_slices[i] = n_actual_slices; |
---|
[2804] | 244 | |
---|
[2973] | 245 | // These are object coordinates |
---|
[900] | 246 | Vector4 vert1 = (Vector4(-10, -10, -0.5, 1)); |
---|
| 247 | Vector4 vert2 = (Vector4(-10, +10, -0.5, 1)); |
---|
| 248 | Vector4 vert3 = (Vector4(+10, +10, -0.5, 1)); |
---|
| 249 | Vector4 vert4 = (Vector4(+10, -10, -0.5, 1)); |
---|
[2804] | 250 | |
---|
[2853] | 251 | // Render cutplanes first with depth test enabled. They will mark the |
---|
| 252 | // image with their depth values. Then we render other volume slices. |
---|
| 253 | // These volume slices will be occluded correctly by the cutplanes and |
---|
| 254 | // vice versa. |
---|
[1825] | 255 | |
---|
[2853] | 256 | ConvexPolygon static_poly; |
---|
[2877] | 257 | for (int j = 0; j < volPtr->getCutplaneCount(); j++) { |
---|
| 258 | if (!volPtr->isCutplaneEnabled(j)) { |
---|
[2853] | 259 | continue; |
---|
| 260 | } |
---|
[2877] | 261 | float offset = volPtr->getCutplane(j)->offset; |
---|
| 262 | int axis = volPtr->getCutplane(j)->orient; |
---|
[2853] | 263 | |
---|
| 264 | if (axis == 3) { |
---|
| 265 | vert1 = Vector4(-10, -10, offset, 1); |
---|
| 266 | vert2 = Vector4(-10, +10, offset, 1); |
---|
| 267 | vert3 = Vector4(+10, +10, offset, 1); |
---|
| 268 | vert4 = Vector4(+10, -10, offset, 1); |
---|
| 269 | //continue; |
---|
| 270 | } else if (axis == 1) { |
---|
| 271 | vert1 = Vector4(offset, -10, -10, 1); |
---|
| 272 | vert2 = Vector4(offset, +10, -10, 1); |
---|
| 273 | vert3 = Vector4(offset, +10, +10, 1); |
---|
| 274 | vert4 = Vector4(offset, -10, +10, 1); |
---|
| 275 | //continue; |
---|
| 276 | } else if (axis == 2) { |
---|
| 277 | vert1 = Vector4(-10, offset, -10, 1); |
---|
| 278 | vert2 = Vector4(+10, offset, -10, 1); |
---|
| 279 | vert3 = Vector4(+10, offset, +10, 1); |
---|
| 280 | vert4 = Vector4(-10, offset, +10, 1); |
---|
| 281 | //continue; |
---|
| 282 | } |
---|
[2804] | 283 | |
---|
[2853] | 284 | vert1 = model_view_no_trans.transform(vert1); |
---|
| 285 | vert2 = model_view_no_trans.transform(vert2); |
---|
| 286 | vert3 = model_view_no_trans.transform(vert3); |
---|
| 287 | vert4 = model_view_no_trans.transform(vert4); |
---|
[2804] | 288 | |
---|
[2853] | 289 | ConvexPolygon *p = &static_poly; |
---|
| 290 | p->vertices.clear(); |
---|
[2804] | 291 | |
---|
[2877] | 292 | p->appendVertex(vert1); |
---|
| 293 | p->appendVertex(vert2); |
---|
| 294 | p->appendVertex(vert3); |
---|
| 295 | p->appendVertex(vert4); |
---|
[2804] | 296 | |
---|
[2853] | 297 | for (size_t k = 0; k < 6; k++) { |
---|
| 298 | p->clip(volume_planes[k], true); |
---|
| 299 | } |
---|
[2804] | 300 | |
---|
[2853] | 301 | p->transform(model_view_no_trans_inverse); |
---|
| 302 | p->transform(model_view_trans); |
---|
[2804] | 303 | |
---|
[2853] | 304 | glPushMatrix(); |
---|
[2877] | 305 | glScalef(volPtr->aspectRatioWidth, |
---|
| 306 | volPtr->aspectRatioHeight, |
---|
| 307 | volPtr->aspectRatioDepth); |
---|
[2804] | 308 | |
---|
[2877] | 309 | activateVolumeShader(volPtr, true); |
---|
[2853] | 310 | glPopMatrix(); |
---|
[2804] | 311 | |
---|
[2853] | 312 | glEnable(GL_DEPTH_TEST); |
---|
| 313 | glDisable(GL_BLEND); |
---|
[2804] | 314 | |
---|
[2853] | 315 | glBegin(GL_POLYGON); |
---|
[2877] | 316 | p->emit(true); |
---|
[2853] | 317 | glEnd(); |
---|
| 318 | glDisable(GL_DEPTH_TEST); |
---|
[2804] | 319 | |
---|
[2877] | 320 | deactivateVolumeShader(); |
---|
[2853] | 321 | } //done cutplanes |
---|
[2804] | 322 | |
---|
[2853] | 323 | //Now do volume rendering |
---|
| 324 | |
---|
[2973] | 325 | // Initialize view-aligned quads with eye space bounds of |
---|
| 326 | // volume |
---|
| 327 | vert1 = (Vector4(eyeMinX, eyeMinY, -0.5, 1)); |
---|
| 328 | vert2 = (Vector4(eyeMaxX, eyeMinY, -0.5, 1)); |
---|
| 329 | vert3 = (Vector4(eyeMaxX, eyeMaxY, -0.5, 1)); |
---|
| 330 | vert4 = (Vector4(eyeMinX, eyeMaxY, -0.5, 1)); |
---|
[2804] | 331 | |
---|
[1478] | 332 | size_t counter = 0; |
---|
[2804] | 333 | |
---|
[2853] | 334 | //transform slices and store them |
---|
[900] | 335 | float slice_z; |
---|
[1478] | 336 | for (size_t j = 0; j < n_actual_slices; j++) { |
---|
[2853] | 337 | slice_z = zFar + j * z_step; //back to front |
---|
[2804] | 338 | |
---|
[900] | 339 | ConvexPolygon *poly = new ConvexPolygon(); |
---|
[1478] | 340 | polys[i][counter] = poly; |
---|
[900] | 341 | counter++; |
---|
[2804] | 342 | |
---|
[900] | 343 | poly->vertices.clear(); |
---|
[2877] | 344 | poly->setId(i); |
---|
[2804] | 345 | |
---|
[2973] | 346 | // Set eye space Z-coordinate of slice |
---|
[900] | 347 | vert1.z = slice_z; |
---|
| 348 | vert2.z = slice_z; |
---|
| 349 | vert3.z = slice_z; |
---|
| 350 | vert4.z = slice_z; |
---|
[2804] | 351 | |
---|
[2877] | 352 | poly->appendVertex(vert1); |
---|
| 353 | poly->appendVertex(vert2); |
---|
| 354 | poly->appendVertex(vert3); |
---|
| 355 | poly->appendVertex(vert4); |
---|
[2804] | 356 | |
---|
| 357 | for (size_t k = 0; k < 6; k++) { |
---|
[2853] | 358 | poly->clip(volume_planes[k], true); |
---|
| 359 | } |
---|
[2804] | 360 | |
---|
[900] | 361 | poly->transform(model_view_no_trans_inverse); |
---|
| 362 | poly->transform(model_view_trans); |
---|
[2804] | 363 | |
---|
| 364 | if (poly->vertices.size() >= 3) |
---|
[2853] | 365 | total_rendered_slices++; |
---|
[900] | 366 | } |
---|
[1258] | 367 | } //iterate all volumes |
---|
[1984] | 368 | TRACE("end loop\n"); |
---|
[2804] | 369 | |
---|
[1493] | 370 | // We sort all the polygons according to their eye-space depth, from |
---|
| 371 | // farthest to the closest. This step is critical for correct blending |
---|
[2804] | 372 | |
---|
[2853] | 373 | SortElement *slices = (SortElement *) |
---|
| 374 | malloc(sizeof(SortElement) * total_rendered_slices); |
---|
[2804] | 375 | |
---|
[1478] | 376 | size_t counter = 0; |
---|
[2804] | 377 | for (size_t i = 0; i < volumes.size(); i++) { |
---|
| 378 | for (size_t j = 0; j < actual_slices[i]; j++) { |
---|
| 379 | if (polys[i][j]->vertices.size() >= 3) { |
---|
[900] | 380 | slices[counter] = SortElement(polys[i][j]->vertices[0].z, i, j); |
---|
| 381 | counter++; |
---|
| 382 | } |
---|
| 383 | } |
---|
[884] | 384 | } |
---|
[2804] | 385 | |
---|
[1258] | 386 | //sort them |
---|
[2877] | 387 | qsort(slices, total_rendered_slices, sizeof(SortElement), sliceSort); |
---|
[2804] | 388 | |
---|
[1258] | 389 | //Now we are ready to render all the slices from back to front |
---|
[900] | 390 | glEnable(GL_DEPTH_TEST); |
---|
[2932] | 391 | // Non pre-multiplied alpha |
---|
| 392 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); |
---|
[900] | 393 | glEnable(GL_BLEND); |
---|
[2804] | 394 | |
---|
| 395 | for (size_t i = 0; i < total_rendered_slices; i++) { |
---|
[2853] | 396 | Volume *volPtr = NULL; |
---|
[1478] | 397 | |
---|
[2877] | 398 | int volume_index = slices[i].volumeId; |
---|
| 399 | int slice_index = slices[i].sliceId; |
---|
[2853] | 400 | ConvexPolygon *cur = polys[volume_index][slice_index]; |
---|
[2804] | 401 | |
---|
[2853] | 402 | volPtr = volumes[volume_index]; |
---|
| 403 | |
---|
[900] | 404 | glPushMatrix(); |
---|
[2877] | 405 | glScalef(volPtr->aspectRatioWidth, |
---|
| 406 | volPtr->aspectRatioHeight, |
---|
| 407 | volPtr->aspectRatioDepth); |
---|
[2804] | 408 | |
---|
[1493] | 409 | #ifdef notdef |
---|
[2853] | 410 | TRACE("shading slice: volume %s addr=%x slice=%d, volume=%d\n", |
---|
| 411 | volPtr->name(), volPtr, slice_index, volume_index); |
---|
[1493] | 412 | #endif |
---|
[2877] | 413 | activateVolumeShader(volPtr, false); |
---|
[900] | 414 | glPopMatrix(); |
---|
[2804] | 415 | |
---|
[900] | 416 | glBegin(GL_POLYGON); |
---|
[2877] | 417 | cur->emit(true); |
---|
[900] | 418 | glEnd(); |
---|
[884] | 419 | |
---|
[2877] | 420 | deactivateVolumeShader(); |
---|
[884] | 421 | } |
---|
[2804] | 422 | |
---|
[2932] | 423 | glPopAttrib(); |
---|
[2804] | 424 | |
---|
[900] | 425 | //Deallocate all the memory used |
---|
[2804] | 426 | for (size_t i = 0; i < volumes.size(); i++) { |
---|
| 427 | for (size_t j = 0; j <actual_slices[i]; j++) { |
---|
[900] | 428 | delete polys[i][j]; |
---|
| 429 | } |
---|
[1258] | 430 | if (polys[i]) { |
---|
[900] | 431 | delete[] polys[i]; |
---|
| 432 | } |
---|
[452] | 433 | } |
---|
[900] | 434 | delete[] polys; |
---|
| 435 | delete[] actual_slices; |
---|
| 436 | free(slices); |
---|
[415] | 437 | } |
---|
| 438 | |
---|
[1478] | 439 | void |
---|
[2877] | 440 | VolumeRenderer::drawBoundingBox(float x0, float y0, float z0, |
---|
| 441 | float x1, float y1, float z1, |
---|
| 442 | float r, float g, float b, |
---|
| 443 | float line_width) |
---|
[406] | 444 | { |
---|
[2932] | 445 | glPushAttrib(GL_ENABLE_BIT); |
---|
| 446 | |
---|
[1028] | 447 | glEnable(GL_DEPTH_TEST); |
---|
| 448 | glDisable(GL_TEXTURE_2D); |
---|
| 449 | glEnable(GL_BLEND); |
---|
[406] | 450 | |
---|
[2932] | 451 | glMatrixMode(GL_MODELVIEW); |
---|
| 452 | glPushMatrix(); |
---|
| 453 | |
---|
[1028] | 454 | glColor4d(r, g, b, 1.0); |
---|
| 455 | glLineWidth(line_width); |
---|
[2804] | 456 | |
---|
[1028] | 457 | glBegin(GL_LINE_LOOP); |
---|
| 458 | { |
---|
[2853] | 459 | glVertex3d(x0, y0, z0); |
---|
| 460 | glVertex3d(x1, y0, z0); |
---|
| 461 | glVertex3d(x1, y1, z0); |
---|
| 462 | glVertex3d(x0, y1, z0); |
---|
[1028] | 463 | } |
---|
| 464 | glEnd(); |
---|
[2804] | 465 | |
---|
[1028] | 466 | glBegin(GL_LINE_LOOP); |
---|
| 467 | { |
---|
[2853] | 468 | glVertex3d(x0, y0, z1); |
---|
| 469 | glVertex3d(x1, y0, z1); |
---|
| 470 | glVertex3d(x1, y1, z1); |
---|
| 471 | glVertex3d(x0, y1, z1); |
---|
[1028] | 472 | } |
---|
| 473 | glEnd(); |
---|
[2853] | 474 | |
---|
[1028] | 475 | glBegin(GL_LINE_LOOP); |
---|
| 476 | { |
---|
[2853] | 477 | glVertex3d(x0, y0, z0); |
---|
| 478 | glVertex3d(x0, y0, z1); |
---|
| 479 | glVertex3d(x0, y1, z1); |
---|
| 480 | glVertex3d(x0, y1, z0); |
---|
[1028] | 481 | } |
---|
| 482 | glEnd(); |
---|
[2804] | 483 | |
---|
[1028] | 484 | glBegin(GL_LINE_LOOP); |
---|
| 485 | { |
---|
[2853] | 486 | glVertex3d(x1, y0, z0); |
---|
| 487 | glVertex3d(x1, y0, z1); |
---|
| 488 | glVertex3d(x1, y1, z1); |
---|
| 489 | glVertex3d(x1, y1, z0); |
---|
[1028] | 490 | } |
---|
| 491 | glEnd(); |
---|
[406] | 492 | |
---|
[1028] | 493 | #ifdef notdef |
---|
| 494 | /* Rappture doesn't supply axis units yet. So turn labeling off until we |
---|
| 495 | * can display the proper units with the distance of each bounding box |
---|
| 496 | * dimension. |
---|
| 497 | */ |
---|
| 498 | glColor4f(1.0f, 1.0f, 0.0f, 1.0f); |
---|
[406] | 499 | |
---|
[1028] | 500 | if (NanoVis::fonts != NULL) { |
---|
[2853] | 501 | double mv[16], prjm[16]; |
---|
| 502 | int viewport[4]; |
---|
| 503 | double wx, wy, wz; |
---|
| 504 | double dx, dy, dz; |
---|
[406] | 505 | |
---|
[2853] | 506 | glGetDoublev(GL_MODELVIEW_MATRIX, mv); |
---|
| 507 | glGetDoublev(GL_PROJECTION_MATRIX, prjm); |
---|
| 508 | glGetIntegerv(GL_VIEWPORT, viewport); |
---|
[406] | 509 | |
---|
[2853] | 510 | NanoVis::fonts->begin(); |
---|
| 511 | dx = x1 - x0; |
---|
| 512 | dy = y1 - y0; |
---|
| 513 | dz = z1 - z0; |
---|
| 514 | if (gluProject((x0 + x1) * 0.5, y0, z0, mv, prjm, viewport, |
---|
| 515 | &wx, &wy, &wz)) { |
---|
| 516 | char buff[20]; |
---|
| 517 | double min, max; |
---|
[406] | 518 | |
---|
[2853] | 519 | NanoVis::grid->xAxis.GetDataLimits(min, max); |
---|
| 520 | glLoadIdentity(); |
---|
| 521 | glTranslatef((int)wx, viewport[3] - (int) wy, 0.0f); |
---|
| 522 | const char *units; |
---|
| 523 | units = NanoVis::grid->xAxis.GetUnits(); |
---|
| 524 | sprintf(buff, "%.*g %s", NUMDIGITS, max - min, units); |
---|
| 525 | NanoVis::fonts->draw(buff); |
---|
| 526 | } |
---|
| 527 | if (gluProject(x0, (y0 + y1) * 0.5, z0, mv, prjm, viewport, & |
---|
| 528 | wx, &wy, &wz)) { |
---|
| 529 | char buff[20]; |
---|
| 530 | double min, max; |
---|
[406] | 531 | |
---|
[2853] | 532 | NanoVis::grid->yAxis.GetDataLimits(min, max); |
---|
| 533 | glLoadIdentity(); |
---|
| 534 | glTranslatef((int)wx, viewport[3] - (int) wy, 0.0f); |
---|
| 535 | const char *units; |
---|
| 536 | units = NanoVis::grid->yAxis.GetUnits(); |
---|
| 537 | sprintf(buff, "%.*g %s", NUMDIGITS, max - min, units); |
---|
| 538 | NanoVis::fonts->draw(buff); |
---|
| 539 | } |
---|
| 540 | if (gluProject(x0, y0, (z0 + z1) * 0.5, mv, prjm, viewport, |
---|
| 541 | &wx, &wy, &wz)) { |
---|
| 542 | glLoadIdentity(); |
---|
| 543 | glTranslatef((int)wx, viewport[3] - (int) wy, 0.0f); |
---|
| 544 | |
---|
| 545 | double min, max; |
---|
| 546 | NanoVis::grid->zAxis.GetDataLimits(min, max); |
---|
| 547 | const char *units; |
---|
| 548 | units = NanoVis::grid->zAxis.GetUnits(); |
---|
| 549 | char buff[20]; |
---|
| 550 | sprintf(buff, "%.*g %s", NUMDIGITS, max - min, units); |
---|
| 551 | NanoVis::fonts->draw(buff); |
---|
| 552 | } |
---|
| 553 | NanoVis::fonts->end(); |
---|
[1028] | 554 | }; |
---|
| 555 | #endif |
---|
| 556 | glPopMatrix(); |
---|
[2932] | 557 | glPopAttrib(); |
---|
[406] | 558 | } |
---|
| 559 | |
---|
[1478] | 560 | void |
---|
[2877] | 561 | VolumeRenderer::activateVolumeShader(Volume* volPtr, bool sliceMode) |
---|
[617] | 562 | { |
---|
[1478] | 563 | //vertex shader |
---|
| 564 | _stdVertexShader->bind(); |
---|
[1493] | 565 | TransferFunction *tfPtr = volPtr->transferFunction(); |
---|
[2877] | 566 | if (volPtr->volumeType() == Volume::CUBIC) { |
---|
| 567 | _regularVolumeShader->bind(tfPtr->id(), volPtr, sliceMode); |
---|
| 568 | } else if (volPtr->volumeType() == Volume::ZINCBLENDE) { |
---|
| 569 | _zincBlendeShader->bind(tfPtr->id(), volPtr, sliceMode); |
---|
[1478] | 570 | } |
---|
[406] | 571 | } |
---|
[2877] | 572 | |
---|
| 573 | void VolumeRenderer::deactivateVolumeShader() |
---|
[580] | 574 | { |
---|
[617] | 575 | _stdVertexShader->unbind(); |
---|
| 576 | _regularVolumeShader->unbind(); |
---|
| 577 | _zincBlendeShader->unbind(); |
---|
[406] | 578 | } |
---|
| 579 | |
---|
[2973] | 580 | void VolumeRenderer::getEyeSpaceBounds(const Mat4x4& mv, |
---|
| 581 | double& xMin, double& xMax, |
---|
| 582 | double& yMin, double& yMax, |
---|
| 583 | double& zNear, double& zFar) |
---|
[406] | 584 | { |
---|
[2822] | 585 | double x0 = 0; |
---|
| 586 | double y0 = 0; |
---|
| 587 | double z0 = 0; |
---|
| 588 | double x1 = 1; |
---|
| 589 | double y1 = 1; |
---|
| 590 | double z1 = 1; |
---|
[406] | 591 | |
---|
[2822] | 592 | double zMin, zMax; |
---|
[2973] | 593 | xMin = DBL_MAX; |
---|
| 594 | xMax = -DBL_MAX; |
---|
| 595 | yMin = DBL_MAX; |
---|
| 596 | yMax = -DBL_MAX; |
---|
| 597 | zMin = DBL_MAX; |
---|
| 598 | zMax = -DBL_MAX; |
---|
[406] | 599 | |
---|
[2822] | 600 | double vertex[8][4]; |
---|
[406] | 601 | |
---|
[2822] | 602 | vertex[0][0]=x0; vertex[0][1]=y0; vertex[0][2]=z0; vertex[0][3]=1.0; |
---|
| 603 | vertex[1][0]=x1; vertex[1][1]=y0; vertex[1][2]=z0; vertex[1][3]=1.0; |
---|
| 604 | vertex[2][0]=x0; vertex[2][1]=y1; vertex[2][2]=z0; vertex[2][3]=1.0; |
---|
| 605 | vertex[3][0]=x0; vertex[3][1]=y0; vertex[3][2]=z1; vertex[3][3]=1.0; |
---|
| 606 | vertex[4][0]=x1; vertex[4][1]=y1; vertex[4][2]=z0; vertex[4][3]=1.0; |
---|
| 607 | vertex[5][0]=x1; vertex[5][1]=y0; vertex[5][2]=z1; vertex[5][3]=1.0; |
---|
| 608 | vertex[6][0]=x0; vertex[6][1]=y1; vertex[6][2]=z1; vertex[6][3]=1.0; |
---|
| 609 | vertex[7][0]=x1; vertex[7][1]=y1; vertex[7][2]=z1; vertex[7][3]=1.0; |
---|
[406] | 610 | |
---|
[2822] | 611 | for (int i = 0; i < 8; i++) { |
---|
[2973] | 612 | Vector4 eyeVert = mv.transform(Vector4(vertex[i][0], |
---|
| 613 | vertex[i][1], |
---|
| 614 | vertex[i][2], |
---|
| 615 | vertex[i][3])); |
---|
| 616 | if (eyeVert.x < xMin) xMin = eyeVert.x; |
---|
| 617 | if (eyeVert.x > xMax) xMax = eyeVert.x; |
---|
| 618 | if (eyeVert.y < yMin) yMin = eyeVert.y; |
---|
| 619 | if (eyeVert.y > yMax) yMax = eyeVert.y; |
---|
| 620 | if (eyeVert.z < zMin) zMin = eyeVert.z; |
---|
| 621 | if (eyeVert.z > zMax) zMax = eyeVert.z; |
---|
[2822] | 622 | } |
---|
[406] | 623 | |
---|
[2822] | 624 | zNear = zMax; |
---|
| 625 | zFar = zMin; |
---|
[406] | 626 | } |
---|
| 627 | |
---|
[1374] | 628 | bool |
---|
[2877] | 629 | VolumeRenderer::initFont(const char *filename) |
---|
[1374] | 630 | { |
---|
| 631 | FILE *f; |
---|
[448] | 632 | unsigned short int bfType; |
---|
[1177] | 633 | int bfOffBits; |
---|
[448] | 634 | short int biPlanes; |
---|
| 635 | short int biBitCount; |
---|
[1177] | 636 | int biSizeImage; |
---|
[448] | 637 | int width, height; |
---|
| 638 | int i; |
---|
| 639 | unsigned char temp; |
---|
[1374] | 640 | unsigned char* data; |
---|
[448] | 641 | /* make sure the file is there and open it read-only (binary) */ |
---|
[1374] | 642 | f = fopen(filename, "rb"); |
---|
| 643 | if (f == NULL) { |
---|
[2853] | 644 | ERROR("can't open font file \"%s\"\n", filename); |
---|
| 645 | return false; |
---|
[448] | 646 | } |
---|
[2804] | 647 | |
---|
[1374] | 648 | if (fread(&bfType, sizeof(short int), 1, f) != 1) { |
---|
[2853] | 649 | ERROR("can't read %lu bytes from font file \"%s\"\n", |
---|
| 650 | (unsigned long)sizeof(short int), filename); |
---|
| 651 | goto error; |
---|
[448] | 652 | } |
---|
[2804] | 653 | |
---|
[448] | 654 | /* check if file is a bitmap */ |
---|
[1028] | 655 | if (bfType != 19778) { |
---|
[2853] | 656 | ERROR("not a bmp file.\n"); |
---|
| 657 | goto error; |
---|
[448] | 658 | } |
---|
[2804] | 659 | |
---|
[448] | 660 | /* get the file size */ |
---|
| 661 | /* skip file size and reserved fields of bitmap file header */ |
---|
[1374] | 662 | fseek(f, 8, SEEK_CUR); |
---|
[2804] | 663 | |
---|
[448] | 664 | /* get the position of the actual bitmap data */ |
---|
[1374] | 665 | if (fread(&bfOffBits, sizeof(int), 1, f) != 1) { |
---|
[2853] | 666 | ERROR("error reading file.\n"); |
---|
| 667 | goto error; |
---|
[448] | 668 | } |
---|
[2376] | 669 | //TRACE("Data at Offset: %ld\n", bfOffBits); |
---|
[2804] | 670 | |
---|
[448] | 671 | /* skip size of bitmap info header */ |
---|
[1374] | 672 | fseek(f, 4, SEEK_CUR); |
---|
[2804] | 673 | |
---|
[448] | 674 | /* get the width of the bitmap */ |
---|
[1374] | 675 | if (fread(&width, sizeof(int), 1, f) != 1) { |
---|
[2853] | 676 | ERROR("error reading file.\n"); |
---|
| 677 | goto error; |
---|
[1374] | 678 | } |
---|
[2376] | 679 | //TRACE("Width of Bitmap: %d\n", texture->width); |
---|
[2804] | 680 | |
---|
[448] | 681 | /* get the height of the bitmap */ |
---|
[1374] | 682 | if (fread(&height, sizeof(int), 1, f) != 1) { |
---|
[2853] | 683 | ERROR("error reading file.\n"); |
---|
| 684 | goto error; |
---|
[1374] | 685 | } |
---|
[2376] | 686 | //TRACE("Height of Bitmap: %d\n", texture->height); |
---|
[2804] | 687 | |
---|
[448] | 688 | /* get the number of planes (must be set to 1) */ |
---|
[1374] | 689 | if (fread(&biPlanes, sizeof(short int), 1, f) != 1) { |
---|
[2853] | 690 | ERROR("error reading file.\n"); |
---|
| 691 | goto error; |
---|
[448] | 692 | } |
---|
[1374] | 693 | if (biPlanes != 1) { |
---|
[2853] | 694 | ERROR("number of Planes not 1!\n"); |
---|
| 695 | goto error; |
---|
[1374] | 696 | } |
---|
[2804] | 697 | |
---|
[448] | 698 | /* get the number of bits per pixel */ |
---|
[1374] | 699 | if (fread(&biBitCount, sizeof(short int), 1, f) != 1) { |
---|
[2853] | 700 | ERROR("error reading file.\n"); |
---|
| 701 | goto error; |
---|
[448] | 702 | } |
---|
[2804] | 703 | |
---|
[2376] | 704 | //TRACE("Bits per Pixel: %d\n", biBitCount); |
---|
[1028] | 705 | if (biBitCount != 24) { |
---|
[2853] | 706 | ERROR("Bits per Pixel not 24\n"); |
---|
| 707 | goto error; |
---|
[448] | 708 | } |
---|
| 709 | |
---|
| 710 | /* calculate the size of the image in bytes */ |
---|
| 711 | biSizeImage = width * height * 3 * sizeof(unsigned char); |
---|
[1374] | 712 | data = (unsigned char*) malloc(biSizeImage); |
---|
| 713 | if (data == NULL) { |
---|
[2853] | 714 | ERROR("Can't allocate memory for image\n"); |
---|
| 715 | goto error; |
---|
[1374] | 716 | } |
---|
[448] | 717 | |
---|
| 718 | /* seek to the actual data */ |
---|
[1374] | 719 | fseek(f, bfOffBits, SEEK_SET); |
---|
| 720 | if (fread(data, biSizeImage, 1, f) != 1) { |
---|
[2853] | 721 | ERROR("Error loading file!\n"); |
---|
| 722 | goto error; |
---|
[448] | 723 | } |
---|
[1374] | 724 | fclose(f); |
---|
| 725 | |
---|
[448] | 726 | /* swap red and blue (bgr -> rgb) */ |
---|
[1028] | 727 | for (i = 0; i < biSizeImage; i += 3) { |
---|
[448] | 728 | temp = data[i]; |
---|
| 729 | data[i] = data[i + 2]; |
---|
| 730 | data[i + 2] = temp; |
---|
| 731 | } |
---|
| 732 | |
---|
[452] | 733 | //insert alpha channel |
---|
[1374] | 734 | unsigned char* data_with_alpha; |
---|
| 735 | data_with_alpha = (unsigned char*) |
---|
[2853] | 736 | malloc(width*height*4*sizeof(unsigned char)); |
---|
[2804] | 737 | for (int i = 0; i < height; i++) { |
---|
[2853] | 738 | for (int j = 0; j < width; j++) { |
---|
| 739 | unsigned char r, g, b, a; |
---|
| 740 | r = data[3*(i*width+j)]; |
---|
| 741 | g = data[3*(i*width+j)+1]; |
---|
| 742 | b = data[3*(i*width+j)+2]; |
---|
[2804] | 743 | |
---|
[2853] | 744 | if (r==0 && g==0 && b==0) |
---|
| 745 | a = 0; |
---|
| 746 | else |
---|
| 747 | a = 255; |
---|
[2804] | 748 | |
---|
[2853] | 749 | data_with_alpha[4*(i*width+j)] = r; |
---|
| 750 | data_with_alpha[4*(i*width+j) + 1] = g; |
---|
| 751 | data_with_alpha[4*(i*width+j) + 2] = b; |
---|
| 752 | data_with_alpha[4*(i*width+j) + 3] = a; |
---|
| 753 | } |
---|
[452] | 754 | } |
---|
| 755 | free(data); |
---|
[2804] | 756 | |
---|
[448] | 757 | //create opengl texture |
---|
[2877] | 758 | glGenTextures(1, &_fontTexture); |
---|
| 759 | glBindTexture(GL_TEXTURE_2D, _fontTexture); |
---|
[452] | 760 | //glTexImage2D(GL_TEXTURE_2D, 0, 3, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data); |
---|
| 761 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data_with_alpha); |
---|
[448] | 762 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
---|
| 763 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
---|
[452] | 764 | glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); |
---|
[448] | 765 | |
---|
[452] | 766 | free(data_with_alpha); |
---|
[448] | 767 | |
---|
[2877] | 768 | buildFont(); |
---|
[1374] | 769 | return (glGetError()==0); |
---|
| 770 | |
---|
| 771 | error: |
---|
| 772 | fclose(f); |
---|
| 773 | return false; |
---|
[448] | 774 | } |
---|
| 775 | |
---|
[1374] | 776 | void |
---|
[2877] | 777 | VolumeRenderer::drawLabel(Volume* vol) |
---|
[1374] | 778 | { |
---|
[1478] | 779 | //glEnable(GL_TEXTURE_2D); |
---|
| 780 | glDisable(GL_TEXTURE_2D); |
---|
| 781 | glEnable(GL_DEPTH_TEST); |
---|
[2804] | 782 | |
---|
[1478] | 783 | //x |
---|
| 784 | glColor3f(0.5, 0.5, 0.5); |
---|
[2804] | 785 | |
---|
[1478] | 786 | int length = vol->label[0].size(); |
---|
| 787 | glPushMatrix(); |
---|
[2804] | 788 | |
---|
[2877] | 789 | glTranslatef(.5 * vol->aspectRatioWidth, |
---|
| 790 | vol->aspectRatioHeight, |
---|
| 791 | -0.1 * vol->aspectRatioDepth); |
---|
[452] | 792 | glRotatef(180, 0, 0, 1); |
---|
| 793 | glRotatef(90, 1, 0, 0); |
---|
[2804] | 794 | |
---|
[455] | 795 | glScalef(0.0008, 0.0008, 0.0008); |
---|
[2822] | 796 | for (int i = 0; i < length; i++) { |
---|
[2853] | 797 | glutStrokeCharacter(GLUT_STROKE_ROMAN, vol->label[0].c_str()[i]); |
---|
| 798 | glTranslatef(0.04, 0., 0.); |
---|
[452] | 799 | } |
---|
[1478] | 800 | glPopMatrix(); |
---|
[2804] | 801 | |
---|
[1478] | 802 | //y |
---|
| 803 | length = vol->label[1].size(); |
---|
| 804 | glPushMatrix(); |
---|
[2877] | 805 | glTranslatef(vol->aspectRatioWidth, |
---|
| 806 | 0.5*vol->aspectRatioHeight, |
---|
| 807 | -0.1*vol->aspectRatioDepth); |
---|
[452] | 808 | glRotatef(90, 0, 1, 0); |
---|
| 809 | glRotatef(90, 0, 0, 1); |
---|
[2822] | 810 | |
---|
[455] | 811 | glScalef(0.0008, 0.0008, 0.0008); |
---|
[2822] | 812 | for (int i = 0; i < length; i++) { |
---|
[2853] | 813 | glutStrokeCharacter(GLUT_STROKE_ROMAN, vol->label[1].c_str()[i]); |
---|
| 814 | glTranslatef(0.04, 0., 0.); |
---|
[452] | 815 | } |
---|
[1478] | 816 | glPopMatrix(); |
---|
[2804] | 817 | |
---|
[1478] | 818 | //z |
---|
| 819 | length = vol->label[2].size(); |
---|
| 820 | glPushMatrix(); |
---|
[2877] | 821 | glTranslatef(0., |
---|
| 822 | 1. * vol->aspectRatioHeight, |
---|
| 823 | 0.5 * vol->aspectRatioDepth); |
---|
[452] | 824 | glRotatef(90, 0, 1, 0); |
---|
[2804] | 825 | |
---|
[455] | 826 | glScalef(0.0008, 0.0008, 0.0008); |
---|
[2822] | 827 | for (int i = 0; i < length; i++) { |
---|
[2853] | 828 | glutStrokeCharacter(GLUT_STROKE_ROMAN, vol->label[2].c_str()[i]); |
---|
| 829 | glTranslatef(0.04, 0., 0.); |
---|
[452] | 830 | } |
---|
[1478] | 831 | glPopMatrix(); |
---|
[2804] | 832 | |
---|
[1478] | 833 | glDisable(GL_TEXTURE_2D); |
---|
[448] | 834 | } |
---|
| 835 | |
---|
[1478] | 836 | void |
---|
[2877] | 837 | VolumeRenderer::buildFont() |
---|
[1478] | 838 | { |
---|
[448] | 839 | GLfloat cx, cy; /* the character coordinates in our texture */ |
---|
[2877] | 840 | _fontBase = glGenLists(256); |
---|
| 841 | glBindTexture(GL_TEXTURE_2D, _fontTexture); |
---|
[1478] | 842 | for (int loop = 0; loop < 256; loop++) { |
---|
[448] | 843 | cx = (float) (loop % 16) / 16.0f; |
---|
| 844 | cy = (float) (loop / 16) / 16.0f; |
---|
[2877] | 845 | glNewList(_fontBase + loop, GL_COMPILE); |
---|
[2822] | 846 | glBegin(GL_QUADS); |
---|
| 847 | glTexCoord2f(cx, 1 - cy - 0.0625f); |
---|
| 848 | glVertex3f(0, 0, 0); |
---|
| 849 | glTexCoord2f(cx + 0.0625f, 1 - cy - 0.0625f); |
---|
| 850 | glVertex3f(0.04, 0, 0); |
---|
| 851 | glTexCoord2f(cx + 0.0625f, 1 - cy); |
---|
| 852 | glVertex3f(0.04, 0.04, 0); |
---|
| 853 | glTexCoord2f(cx, 1 - cy); |
---|
| 854 | glVertex3f(0, 0.04, 0); |
---|
| 855 | glEnd(); |
---|
| 856 | glTranslated(0.04, 0, 0); |
---|
[448] | 857 | glEndList(); |
---|
| 858 | } |
---|
| 859 | } |
---|
| 860 | |
---|
[2804] | 861 | void |
---|
[1478] | 862 | VolumeRenderer::glPrint(char* string, int set) |
---|
| 863 | { |
---|
[2804] | 864 | if (set > 1) { |
---|
[2822] | 865 | set = 1; |
---|
[1478] | 866 | } |
---|
[2877] | 867 | glBindTexture(GL_TEXTURE_2D, _fontTexture); |
---|
| 868 | glListBase(_fontBase - 32 + (128 * set)); |
---|
[448] | 869 | glCallLists(strlen(string), GL_BYTE, string); |
---|
| 870 | } |
---|