volume.c
上传用户:xk288cn
上传日期:2007-05-28
资源大小:4876k
文件大小:16k
源码类别:

GIS编程

开发平台:

Visual C++

  1. #include <GL/glut.h>
  2. #include <math.h>
  3. #include "texture.h"
  4. #include <stdio.h>
  5. #include <string.h>
  6. #include <stdlib.h>
  7. /* nonzero if not power of 2 */ 
  8. #define NOTPOW2(num) ((num) & (num - 1))
  9. int
  10. makepow2(int val)
  11. {
  12.     int power = 0;
  13.     if(!val)
  14. return 0;
  15.     while(val >>= 1)
  16. power++;
  17.     return(1 << power);
  18. }
  19. #define CHECK_ERROR(str)                                           
  20. {                                                                  
  21.     GLenum error;                                                  
  22.     if(error = glGetError())                                       
  23.        printf("GL Error: %s (%s)n", gluErrorString(error), str);  
  24. }
  25. enum {X, Y, Z, W};
  26. enum {R, G, B, A};
  27. enum {OVER, ATTENUATE, NONE, LASTOP}; /* blend modes */
  28. /* mouse modes */
  29. enum {OBJ_ANGLE, SLICES, CUTTING, GEOMXY, GEOMZ, MINBOOST, BOOSTWID, BOOST}; 
  30. enum {NOLIST, SPHERE}; /* display list */
  31. /* window dimensions */
  32. int winWidth = 512;
  33. int winHeight = 512;
  34. int active;
  35. int operator = OVER;
  36. GLboolean texture = GL_TRUE;
  37. GLboolean dblbuf = GL_TRUE;
  38. GLboolean cut = GL_FALSE;
  39. GLboolean geom = GL_FALSE;
  40. GLboolean map = GL_FALSE;
  41. GLint cutbias = 50;
  42. int hasBlendColor = 0;
  43. #if defined(_WIN32) && !defined(MESA)
  44. #include <windows.h>
  45. PFNGLBLENDCOLOREXTPROC glBlendColorEXT;
  46. #endif
  47. GLfloat objangle[2] = {0.f, 0.f};
  48. GLfloat objpos[3] = {0.f, 0.f, 0.f};
  49. GLfloat minboost = 0.f, boostwid = .03f, boost = 3.f; /* transfer function */
  50. /* 3d texture data that's read in */
  51. /* XXX TODO; make command line arguments */
  52. int Texwid = 128; /* dimensions of each 2D texture */
  53. int Texht = 128;
  54. int Texdepth = 69; /* number of 2D textures */
  55. /* Actual dimensions of the texture (restricted to max 3d texture size) */
  56. int texwid, texht, texdepth;
  57. int slices;
  58. GLubyte *tex3ddata; /* pointer to 3D texture data */
  59. GLfloat *lighttex = 0;
  60. GLfloat lightpos[4] = {0.f, 0.f, 1.f, 0.f};
  61. GLboolean lightchanged[2] = {GL_TRUE, GL_TRUE};
  62. void
  63. reshape(int wid, int ht)
  64. {
  65.     winWidth = wid;
  66.     winHeight = ht;
  67.     glViewport(0, 0, wid, ht);
  68. }
  69. void
  70. motion(int x, int y)
  71. {
  72.     switch(active)
  73.     {
  74.     case OBJ_ANGLE:
  75. objangle[X] = (x - winWidth/2) * 360./winWidth;
  76. objangle[Y] = (y - winHeight/2) * 360./winHeight;
  77. glutPostRedisplay();
  78. break;
  79.     case SLICES:
  80. slices = x * texwid/winWidth;
  81. glutPostRedisplay();
  82. break;
  83.     case CUTTING:
  84. cutbias = (x - winWidth/2) * 300/winWidth;
  85. glutPostRedisplay();
  86. break;
  87.     case GEOMXY:
  88. objpos[X] = (x - winWidth/2) * 300/winWidth;
  89. objpos[Y] = (winHeight/2 - y) * 300/winHeight;
  90. glutPostRedisplay();
  91. break;
  92.     case GEOMZ:
  93. objpos[Z] = (x - winWidth/2) * 300/winWidth;
  94. glutPostRedisplay();
  95. break;
  96.     case MINBOOST:
  97. minboost = x * .25f/winWidth;
  98. glutPostRedisplay();
  99. break;
  100.     case BOOSTWID:
  101. boostwid = x * .5f/winWidth;
  102. glutPostRedisplay();
  103. break;
  104.     case BOOST:
  105. boost = x * 20.f/winWidth;
  106. glutPostRedisplay();
  107. break;
  108.     }
  109. }
  110. void
  111. mouse(int button, int state, int x, int y)
  112. {
  113.     if(state == GLUT_DOWN)
  114. switch(button)
  115. {
  116. case GLUT_LEFT_BUTTON: /* rotate the data volume */
  117.     if(map)
  118. active = MINBOOST;
  119.     else
  120. active = OBJ_ANGLE;
  121.     motion(x, y);
  122.     break;
  123. case GLUT_MIDDLE_BUTTON:
  124.     if(map)
  125. active = BOOSTWID;
  126.     else
  127. if(cut)
  128.     active = CUTTING; /* move cutting plane */
  129. else
  130.     active = GEOMXY; /* move geometry */
  131.     motion(x, y);
  132.     break;
  133. case GLUT_RIGHT_BUTTON: /* move the polygon */
  134.     if(map)
  135. active = BOOST;
  136.     else
  137. if(geom)
  138.     active = GEOMZ;
  139. else
  140.     active = SLICES;
  141.     motion(x, y);
  142.     break;
  143. }
  144. }
  145. /* use pixel path to remap 3D texture data */
  146. void
  147. remaptex(void)
  148. {
  149.     int i, size;
  150.     GLfloat *map;
  151.     glPixelTransferi(GL_MAP_COLOR, GL_TRUE);
  152.     glGetIntegerv(GL_MAX_PIXEL_MAP_TABLE, &size);
  153.     map = (GLfloat *)malloc(sizeof(GLfloat) * size);
  154.     for(i = 0; i < size;i++)
  155.     {
  156. map[i] = (GLfloat)i/(size - 1);
  157. if(((GLfloat)i/size > minboost) &&
  158.    ((GLfloat)i/size < minboost + boostwid))
  159. {
  160.     map[i] *= boost;
  161. }
  162. else
  163.     map[i] /= boost;
  164.     }
  165.     glPixelMapfv(GL_PIXEL_MAP_R_TO_R, size, map);
  166.     glPixelMapfv(GL_PIXEL_MAP_G_TO_G, size, map);
  167.     glPixelMapfv(GL_PIXEL_MAP_B_TO_B, size, map);
  168.     glPixelMapfv(GL_PIXEL_MAP_A_TO_A, size, map);
  169. #ifdef GL_EXT_texture3D
  170.     glTexImage3DEXT(GL_TEXTURE_3D_EXT, 0, GL_LUMINANCE_ALPHA,
  171.     texwid, texht, texdepth,
  172.     0,
  173.     GL_RGBA, GL_UNSIGNED_BYTE, tex3ddata);
  174. #endif
  175.     glPixelTransferi(GL_MAP_COLOR, GL_FALSE);
  176.     free(map);
  177.     CHECK_ERROR("OpenGL Error in remaptex()");
  178. }
  179. GLdouble clipplane0[] = {-1.,  0.,  0., 100.}; /* x < 100 out */
  180. GLdouble clipplane1[] = { 1.,  0.,  0., 100.}; /* x > 100 out */
  181. GLdouble clipplane2[] = { 0., -1.,  0., 100.}; /* y < 100 out */
  182. GLdouble clipplane3[] = { 0.,  1.,  0., 100.}; /* y > 100 out */
  183. GLdouble clipplane4[] = { 0.,  0., -1., 100.}; /* z < 100 out */
  184. GLdouble clipplane5[] = { 0.,  0.,  1., 100.}; /* z > 100 out */
  185. /* define a cutting plane */
  186. GLdouble cutplane[] = {0.f, -.5f, -2.f, 50.f};
  187. /* draw the object unlit without surface texture */
  188. void redraw(void)
  189. {
  190.     int i;
  191.     GLfloat offS, offT, offR; /* mapping texture to planes */
  192.     offS = 200.f/texwid;
  193.     offT = 200.f/texht;
  194.     offR = 200.f/texdepth;
  195.     
  196.     clipplane0[W] = 100.f - offS;
  197.     clipplane1[W] = 100.f - offS;
  198.     clipplane2[W] = 100.f - offT;
  199.     clipplane3[W] = 100.f - offT;
  200.     clipplane4[W] = 100.f - offR;
  201.     clipplane5[W] = 100.f - offR;
  202.     glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
  203.     if(map)
  204. remaptex();
  205.     /* GL_MODELVIEW */
  206.     if(cut)
  207.     {
  208. cutplane[W] = cutbias;
  209. glClipPlane(GL_CLIP_PLANE5, cutplane);
  210.     }
  211.     glPushMatrix(); /* identity */
  212.     glRotatef(objangle[X], 0.f, 1.f, 0.f);
  213.     glRotatef(objangle[Y], 1.f, 0.f, 0.f);
  214.     glClipPlane(GL_CLIP_PLANE0, clipplane0);
  215.     glClipPlane(GL_CLIP_PLANE1, clipplane1);
  216.     glClipPlane(GL_CLIP_PLANE2, clipplane2);
  217.     glClipPlane(GL_CLIP_PLANE3, clipplane3);
  218.     glClipPlane(GL_CLIP_PLANE4, clipplane4);
  219.     if(!cut)
  220. glClipPlane(GL_CLIP_PLANE5, clipplane5);
  221.     glPopMatrix(); /* back to identity */
  222.     /* draw opaque geometry here */
  223.     glDisable(GL_CLIP_PLANE0);
  224.     glDisable(GL_CLIP_PLANE1);
  225.     glDisable(GL_CLIP_PLANE2);
  226.     glDisable(GL_CLIP_PLANE3);
  227.     glDisable(GL_CLIP_PLANE4);
  228.     if(geom)
  229.     {
  230. if(!cut)
  231.     glDisable(GL_CLIP_PLANE5);
  232. glPushMatrix();
  233. glTranslatef(objpos[X], objpos[Y], objpos[Z]);
  234. glCallList(SPHERE);
  235. glPopMatrix();
  236.     }
  237.     glMatrixMode(GL_TEXTURE);
  238.     glEnable(GL_CLIP_PLANE0);
  239.     glEnable(GL_CLIP_PLANE1);
  240.     glEnable(GL_CLIP_PLANE2);
  241.     glEnable(GL_CLIP_PLANE3);
  242.     glEnable(GL_CLIP_PLANE4);
  243.     glEnable(GL_CLIP_PLANE5);
  244.     glMatrixMode(GL_TEXTURE);
  245.     glPushMatrix(); /* identity */
  246.     glTranslatef( .5f,  .5f, .5f);
  247.     glRotatef(objangle[Y], 1.f, 0.f, 0.f);
  248.     glRotatef(objangle[X], 0.f, 0.f, 1.f);
  249.     glTranslatef( -.5f,  -.5f, -.5f);
  250.     switch(operator)
  251.     {
  252.     case OVER:
  253. glEnable(GL_BLEND);
  254. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  255. break;
  256.     case ATTENUATE:
  257. #ifdef GL_EXT_blend_color
  258.         if (hasBlendColor){
  259.     glEnable(GL_BLEND);
  260.     glBlendFunc(GL_CONSTANT_ALPHA_EXT, GL_ONE);
  261.     glBlendColorEXT(1.f, 1.f, 1.f, 1.f/slices);
  262.         } else
  263. #endif
  264.         {
  265.             fprintf(stderr, "volume: attenuate not supported!n");
  266.         }
  267.         break;
  268.     case NONE:
  269. /* don't blend */
  270. break;
  271.     }
  272.     if(texture) {
  273. #ifdef GL_EXT_texture3D
  274.        glEnable(GL_TEXTURE_3D_EXT);
  275. #endif
  276.     } else {
  277. #ifdef GL_EXT_texture3D
  278.        glDisable(GL_TEXTURE_3D_EXT);
  279. #endif
  280.        glEnable(GL_LIGHTING);
  281.        glEnable(GL_LIGHT0);
  282.     }
  283.     
  284.     for(i = 0; i < slices; i++)
  285.     {
  286. glBegin(GL_QUADS);
  287. glVertex3f(-100.f, -100.f, 
  288.    -100.f + offR + i * (200.f - 2 * offR)/(slices - 1));
  289. glVertex3f( 100.f, -100.f,
  290.    -100.f + offR + i * (200.f - 2 * offR)/(slices - 1));
  291. glVertex3f( 100.f,  100.f,
  292.    -100.f + offR + i * (200.f - 2 * offR)/(slices - 1));
  293. glVertex3f(-100.f,  100.f,
  294.    -100.f + offR + i * (200.f - 2 * offR)/(slices - 1));
  295. glEnd();
  296.     }
  297. #ifdef GL_EXT_texture3D
  298.     glDisable(GL_TEXTURE_3D_EXT);
  299. #endif
  300.     if(!texture)
  301.     {
  302.        glDisable(GL_LIGHTING);
  303.     }
  304.     glDisable(GL_BLEND);
  305.     glPopMatrix(); /* back to identity */
  306.     glMatrixMode(GL_MODELVIEW);
  307.     if(operator == ATTENUATE)
  308.     {
  309. glPixelTransferf(GL_RED_SCALE, 3.f); /* brighten image */
  310. glPixelTransferf(GL_GREEN_SCALE, 3.f);
  311. glPixelTransferf(GL_BLUE_SCALE, 3.f);
  312. glCopyPixels(0, 0, winWidth, winHeight, GL_COLOR);
  313.     }
  314.     if(dblbuf)
  315. glutSwapBuffers(); 
  316.     else
  317. glFlush(); 
  318.     CHECK_ERROR("OpenGL Error in redraw()");
  319. }
  320. /* ARGSUSED1 */
  321. void key(unsigned char key, int x, int y)
  322. {
  323.     switch(key)
  324.     {
  325.     case 'm': /* remap texture values */
  326. if(map)
  327. {
  328.     fprintf(stderr, "remapping offn");
  329.     map = GL_FALSE;
  330. }
  331. else
  332. {
  333.     fprintf(stderr, "remapping on:n"
  334.             "left mouse moves emphasize valuen"
  335.             "middle mouse moves emphasize widthn"
  336.             "right mouse adjusts gainn");
  337.     map = GL_TRUE;
  338. }
  339. remaptex();
  340. glutPostRedisplay();
  341. break;
  342.     case 'o':
  343. operator++;
  344. if(operator == LASTOP)
  345.     operator = OVER;
  346. glutPostRedisplay();
  347. break;
  348.     case 't':
  349. if(texture)
  350.     texture = GL_FALSE;
  351. else
  352.     texture = GL_TRUE;
  353. glutPostRedisplay();
  354. break;
  355.     case 'c':
  356. if(cut)
  357. {
  358.     fprintf(stderr, "cutting plane offn");
  359.     cut = GL_FALSE;
  360. }
  361. else
  362. {
  363.     fprintf(stderr, 
  364.     "Cutting plane on: "
  365.     "middle mouse (horizontal) moves cutting planen");
  366.     cut = GL_TRUE;
  367. }
  368. glutPostRedisplay();
  369. break;
  370.     case 'g': /* toggle geometry */
  371. if(geom)
  372.     geom = GL_FALSE;
  373. else
  374.     geom = GL_TRUE;
  375. glutPostRedisplay();
  376. break;
  377.     case '33':
  378. exit(0);
  379. break;
  380.     case '?':
  381.     case 'h':
  382.     default:
  383. fprintf(stderr, 
  384. "Keyboard Commandsn"
  385. "m - toggle transfer function (remapping)n"
  386. "o - toggle operatorn"
  387. "t - toggle 3D texturingn"
  388. "c - toggle cutting planen"
  389. "g - toggle geometryn");
  390. break;
  391.     }
  392. }
  393. GLubyte *
  394. loadtex3d(int *texwid, int *texht, int *texdepth, int *texcomps)
  395. {
  396.     char *filename;
  397.     GLubyte *tex3ddata;
  398.     GLuint *texslice; /* 2D slice of 3D texture */
  399.     GLint max3dtexdims; /* maximum allowed 3d texture dimension */
  400.     GLint newval;
  401.     int i;
  402.     /* load 3D texture data */
  403.     filename = (char*)malloc(sizeof(char) * strlen("../data/skull/skullXX.la"));
  404.     tex3ddata = (GLubyte *)malloc(Texwid * Texht * Texdepth * 
  405.   4 * sizeof(GLubyte));
  406.     for(i = 0; i < Texdepth; i++)
  407.     {
  408. sprintf(filename, "../data/skull/skull%d.la", i);
  409. /* read_texture reads as RGBA */
  410. texslice = read_texture(filename, texwid, texht, texcomps);
  411. memcpy(&tex3ddata[i * Texwid * Texht * 4],  /* copy in a slice */
  412.        texslice, 
  413.        Texwid * Texht * 4 * sizeof(GLubyte));
  414. free(texslice);
  415.     }
  416.     free(filename);
  417.     *texdepth = Texdepth;
  418.     max3dtexdims = 0;
  419. #ifdef GL_EXT_texture3D
  420.     glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE_EXT, &max3dtexdims);
  421. #endif
  422.     /* adjust width */
  423.     newval = *texwid;
  424.     if(*texwid > max3dtexdims)
  425. newval = max3dtexdims;
  426.     if(NOTPOW2(*texwid))
  427.         newval = makepow2(*texwid);
  428.     if(newval != *texwid)
  429.     {
  430. glPixelStorei(GL_UNPACK_ROW_LENGTH, *texwid);
  431. glPixelStorei(GL_UNPACK_SKIP_PIXELS, (*texwid - newval)/2);
  432. *texwid = newval;
  433.     }
  434.     /* adjust height */
  435.     newval = *texht;
  436.     if(*texht > max3dtexdims)
  437. newval = max3dtexdims;
  438.     if(NOTPOW2(*texht))
  439.         newval = makepow2(*texht);
  440.     if(*texht > newval)
  441.     {
  442. #ifdef GL_EXT_texture3D
  443. glPixelStorei(GL_UNPACK_IMAGE_HEIGHT_EXT, *texht);
  444. #endif
  445. glPixelStorei(GL_UNPACK_SKIP_ROWS, (*texht - newval)/2);
  446. *texht = newval;
  447.     }
  448.     /* adjust depth */
  449.     newval = *texdepth;
  450.     if(*texdepth > max3dtexdims)
  451. newval = max3dtexdims;
  452.     if(NOTPOW2(*texdepth))
  453.         newval = makepow2(*texdepth);
  454.     if(*texdepth > newval)
  455.     {
  456. *texdepth = newval;
  457.     }
  458.     return tex3ddata;
  459. }
  460. main(int argc, char *argv[])
  461. {
  462.     int texcomps;
  463.     static GLfloat splane[4] = {1.f/200.f, 0.f, 0.f, .5f};
  464.     static GLfloat rplane[4] = {0, 1.f/200.f, 0, .5f};
  465.     static GLfloat tplane[4] = {0, 0, 1.f/200.f, .5f};
  466.     static GLfloat lightpos[4] = {150., 150., 150., 1.f};
  467.     glutInit(&argc, argv);
  468.     glutInitWindowSize(winWidth, winHeight);
  469.     if(argc > 1)
  470.     {
  471. char *args = argv[1];
  472. GLboolean done = GL_FALSE;
  473. while(!done)
  474. {
  475.     switch(*args)
  476.     {
  477.     case 's': /* single buffer */
  478. printf("Single Bufferedn");
  479. dblbuf = GL_FALSE;
  480. break;
  481.     case '-': /* do nothing */
  482. break;
  483.     case 0:
  484. done = GL_TRUE;
  485. break;
  486.     }
  487.     args++;
  488. }
  489.     }
  490.     if(dblbuf)
  491. glutInitDisplayMode(GLUT_RGBA|GLUT_DEPTH|GLUT_DOUBLE);
  492.     else
  493. glutInitDisplayMode(GLUT_RGBA|GLUT_DEPTH);
  494.     (void)glutCreateWindow("volume rendering demo");
  495.     glutDisplayFunc(redraw);
  496.     glutReshapeFunc(reshape);
  497.     glutMouseFunc(mouse);
  498.     glutMotionFunc(motion);
  499.     glutKeyboardFunc(key);
  500.     /* Initialize OpenGL State */
  501.     /* draw a perspective scene */
  502. #if 0
  503.     glMatrixMode(GL_PROJECTION);
  504.     /* cube, 300 on a side */
  505.     glFrustum(-150., 150., -150., 150., 300., 600.);
  506.     glMatrixMode(GL_MODELVIEW);
  507.     /* look at scene from (0, 0, 450) */
  508.     gluLookAt(0., 0., 450., 0., 0., 0., 0., 1., 0.);
  509. #else
  510.     glMatrixMode(GL_PROJECTION);
  511.     /* cube, 300 on a side */
  512.     glOrtho(-150., 150., -150., 150., -150., 150.);
  513.     glMatrixMode(GL_MODELVIEW);
  514. #endif
  515.     glEnable(GL_DEPTH_TEST);
  516. #ifdef GL_EXT_texture3D
  517.     glEnable(GL_TEXTURE_3D_EXT);
  518. #endif
  519.     glEnable(GL_TEXTURE_GEN_S);
  520.     glEnable(GL_TEXTURE_GEN_T);
  521.     glEnable(GL_TEXTURE_GEN_R);
  522.     glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
  523.     glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
  524.     glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
  525.     glTexGenfv(GL_S, GL_OBJECT_PLANE, splane);
  526.     glTexGenfv(GL_T, GL_OBJECT_PLANE, tplane);
  527.     glTexGenfv(GL_R, GL_OBJECT_PLANE, rplane);
  528. #ifdef GL_EXT_texture3D
  529.     /* to avoid boundary problems */
  530.     glTexParameteri(GL_TEXTURE_3D_EXT, GL_TEXTURE_WRAP_S, GL_CLAMP);
  531.     glTexParameteri(GL_TEXTURE_3D_EXT, GL_TEXTURE_WRAP_T, GL_CLAMP);
  532.     glTexParameteri(GL_TEXTURE_3D_EXT, GL_TEXTURE_WRAP_R_EXT, GL_CLAMP);
  533. #endif
  534.     glEnable(GL_CLIP_PLANE0);
  535.     glEnable(GL_CLIP_PLANE1);
  536.     glEnable(GL_CLIP_PLANE2);
  537.     glEnable(GL_CLIP_PLANE3);
  538.     glEnable(GL_CLIP_PLANE4);
  539.     glEnable(GL_CLIP_PLANE5);
  540.     glDisable(GL_LIGHT0);
  541.     glLightfv(GL_LIGHT0, GL_POSITION, lightpos);
  542.     tex3ddata = loadtex3d(&texwid, &texht, &texdepth, &texcomps);
  543.     slices = texht;
  544. #ifdef GL_EXT_texture3D
  545.     glTexParameteri(GL_TEXTURE_3D_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  546.     glTexImage3DEXT(GL_TEXTURE_3D_EXT, 0, GL_LUMINANCE_ALPHA,
  547.     texwid, texht, texdepth,
  548.     0,
  549.     GL_RGBA, GL_UNSIGNED_BYTE, tex3ddata);
  550. #endif
  551.     /* make a display list containing a sphere */
  552.     glNewList(SPHERE, GL_COMPILE);
  553.     {
  554. static GLfloat lightpos[] = {150.f, 150.f, 150.f, 1.f};
  555. static GLfloat material[] = {1.f, .5f, 1.f, 1.f};
  556. GLUquadricObj *qobj = gluNewQuadric();
  557. glPushAttrib(GL_LIGHTING_BIT);
  558. glEnable(GL_LIGHTING);
  559. glEnable(GL_LIGHT0);
  560. glLightfv(GL_LIGHT0, GL_POSITION, lightpos);
  561. glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, material);
  562. gluSphere(qobj, 20.f, 20, 20);
  563. gluDeleteQuadric(qobj);
  564. glPopAttrib();
  565.     }
  566.     glEndList();
  567.     key('?', 0, 0); /* print usage message */
  568.     CHECK_ERROR("end of main");
  569.     if(!glutExtensionSupported("GL_EXT_texture3d")) {
  570.       fprintf(stderr,
  571.         "volume: requires OpenGL texture 3D extension to operate correctly.n");
  572.     }
  573.     hasBlendColor = glutExtensionSupported("GL_EXT_blend_color");
  574.     if(!hasBlendColor) {
  575.       fprintf(stderr,
  576.         "volume: needs OpenGL blend color extension to attenuate.n");
  577. #if defined(_WIN32) && !defined(MESA)
  578.       glBlendColorEXT = (PFNGLBLENDCOLOREXTPROC) wglGetProcAddress("glBlendColorEXT");
  579.       if (glBlendColorEXT == NULL) {
  580.         hasBlendColor = 0;
  581.       }
  582. #endif
  583.     }
  584.     glutMainLoop();
  585.     return 0;             /* ANSI C requires main to return int. */
  586. }