source: trunk/packages/vizservers/nanovis/HeightMap.cpp @ 1699

Last change on this file since 1699 was 1546, checked in by gah, 15 years ago

add transparency to heightmap

File size: 18.8 KB
Line 
1 
2#include <memory.h>
3#include <stdlib.h>
4#include <sys/time.h>
5#include <sys/types.h>
6#include <unistd.h>
7#include <fcntl.h>
8#include <stdlib.h>
9#include <GL/glew.h>
10#include <GL/gl.h>
11#include "Grid.h"
12#include "HeightMap.h"
13#include "ContourLineFilter.h"
14#include "TypeDefs.h"
15#include "Texture1D.h"
16#include "R2/R2FilePath.h"
17#include "RpField1D.h"
18#include "RenderContext.h"
19
20bool HeightMap::update_pending = false;
21double HeightMap::valueMin = 0.0;
22double HeightMap::valueMax = 1.0;
23
24#define TOPCONTOUR      0
25//#define TOPCONTOUR    1
26HeightMap::HeightMap() :
27    _vertexBufferObjectID(0),
28    _textureBufferObjectID(0),
29    _vertexCount(0),
30    _contour(0),
31    _topContour(0),
32    _tfPtr(0),
33    _opacity(0.5f),
34    _indexBuffer(0),
35    _indexCount(0),
36    _contourColor(1.0f, 0.0f, 0.0f),
37    _contourVisible(false),
38    _topContourVisible(true),
39    _visible(false),
40    _scale(1.0f, 1.0f, 1.0f),
41    _centerPoint(0.0f, 0.0f, 0.0f),
42    heights_(NULL)
43{
44    _shader = new NvShader();
45    _shader->loadFragmentProgram("heightcolor.cg", "main");
46    _tfParam      = _shader->getNamedParameterFromFP("tf");
47    _opacityParam = _shader->getNamedParameterFromFP("opacity");
48}
49
50HeightMap::~HeightMap()
51{
52    reset();
53
54    if (_shader) {
55        delete _shader;
56    }
57
58    // TMP
59    //if (_tfPtr) delete _tfPtr;
60}
61
62void HeightMap::render(graphics::RenderContext* renderContext)
63{
64    if (renderContext->getCullMode() == graphics::RenderContext::NO_CULL) {
65        glDisable(GL_CULL_FACE);
66    } else {
67        glEnable(GL_CULL_FACE);
68        glCullFace((GLuint) renderContext->getCullMode());
69    }
70    glPolygonMode(GL_FRONT_AND_BACK, (GLuint) renderContext->getPolygonMode());
71    glShadeModel((GLuint) renderContext->getShadingModel());
72
73    glPushMatrix();
74
75#ifndef notdef
76    if (_scale.x != 0.0) {
77        glScalef(1 / _scale.x, 1 / _scale.y , 1 / _scale.z);
78    }
79#endif
80    glTranslatef(-_centerPoint.x, -_centerPoint.y, -_centerPoint.z);
81
82    if (_contour != NULL) {
83        glDepthRange (0.001, 1.0);
84    }
85       
86    glEnable(GL_DEPTH_TEST);
87
88    if (_vertexBufferObjectID) {
89        glColor3f(1.0f, 1.0f, 1.0f);
90        glShadeModel(GL_SMOOTH);
91        glEnable(GL_BLEND);
92        glEnableClientState(GL_VERTEX_ARRAY);
93        glDisableClientState(GL_COLOR_ARRAY);
94        glDisableClientState(GL_INDEX_ARRAY);
95        glDisableClientState(GL_NORMAL_ARRAY);
96       
97        if (_tfPtr) {
98            // PUT vertex program here
99            //
100            //
101           
102            cgGLBindProgram(_shader->getFP());
103            cgGLEnableProfile(CG_PROFILE_FP30);
104           
105            cgGLSetTextureParameter(_tfParam, _tfPtr->id());
106            cgGLEnableTextureParameter(_tfParam);
107            cgGLSetParameter1f(_opacityParam, _opacity);
108           
109            glEnable(GL_TEXTURE_1D);
110            _tfPtr->getTexture()->activate();
111           
112            glEnableClientState(GL_TEXTURE_COORD_ARRAY);
113        }
114       
115        glBindBuffer(GL_ARRAY_BUFFER, _vertexBufferObjectID);
116        glVertexPointer(3, GL_FLOAT, 12, 0);
117       
118        glBindBuffer(GL_ARRAY_BUFFER, _textureBufferObjectID);
119        ::glTexCoordPointer(3, GL_FLOAT, 12, 0);
120       
121#define _TRIANGLES_
122#ifdef _TRIANGLES_
123        glDrawElements(GL_TRIANGLES, _indexCount, GL_UNSIGNED_INT,
124                       _indexBuffer);
125#else                   
126        glDrawElements(GL_QUADS, _indexCount, GL_UNSIGNED_INT,
127                       _indexBuffer);
128#endif
129
130        glBindBuffer(GL_ARRAY_BUFFER, 0);
131       
132        glDisableClientState(GL_VERTEX_ARRAY);
133        if (_tfPtr != NULL) {
134            _tfPtr->getTexture()->deactivate();
135            glDisableClientState(GL_TEXTURE_COORD_ARRAY);
136           
137            cgGLDisableProfile(CG_PROFILE_FP30);
138        }
139    }
140   
141    glShadeModel(GL_FLAT);
142    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
143   
144    if (_contour != NULL) {
145        if (_contourVisible) {
146            glDisable(GL_BLEND);
147            glDisable(GL_TEXTURE_2D);
148            glColor4f(_contourColor.x, _contourColor.y, _contourColor.z,
149                _opacity /*1.0f*/);
150            glDepthRange (0.0, 0.999);
151            _contour->render();
152            glDepthRange (0.0, 1.0);
153        }
154
155#if TOPCONTOUR
156        if (_topContourVisible) {
157            glDisable(GL_BLEND);
158            glDisable(GL_TEXTURE_2D);
159            glColor4f(_contourColor.x, _contourColor.y, _contourColor.z,
160                _opacity /*1.0f*/);
161            //glDepthRange (0.0, 0.999);
162            _topContour->render();
163            //glDepthRange (0.0, 1.0);
164        }
165#endif
166    }
167    glPopMatrix();
168}
169
170void
171HeightMap::createIndexBuffer(int xCount, int zCount, float* heights)
172{
173    if (_indexBuffer != NULL) {
174        delete [] _indexBuffer;
175    }
176    _indexCount = (xCount - 1) * (zCount - 1) * 6;
177    _indexBuffer = new int[_indexCount];
178   
179    int i, j;
180    int boundaryWidth = xCount - 1;
181    int boundaryHeight = zCount - 1;
182    int* ptr = _indexBuffer;
183    int index1, index2, index3, index4;
184    bool index1Valid, index2Valid, index3Valid, index4Valid;
185    index1Valid = index2Valid = index3Valid = index4Valid = true;
186
187    if (heights) {
188        int ic = 0;
189        for (i = 0; i < boundaryHeight; ++i) {
190            for (j = 0; j < boundaryWidth; ++j) {
191                index1 = i * xCount +j;
192                if (isnan(heights[index1])) index1Valid = false;
193                index2 = (i + 1) * xCount + j;
194                if (isnan(heights[index2])) index2Valid = false;
195                index3 = (i + 1) * xCount + j + 1;
196                if (isnan(heights[index3])) index3Valid = false;
197                index4 = i * xCount + j + 1;
198                if (isnan(heights[index4])) index4Valid = false;
199           
200#ifdef _TRIANGLES_
201                if (index1Valid && index2Valid && index3Valid) {
202                    *ptr = index1; ++ptr;
203                    *ptr = index2; ++ptr;
204                    *ptr = index3; ++ptr;
205                    ++ic;
206                }
207                if (index1Valid && index3Valid && index4Valid) {
208                    *ptr = index1; ++ptr;
209                    *ptr = index3; ++ptr;
210                    *ptr = index4; ++ptr;
211                    ++ic;
212                }
213#else
214                if (index1Valid && index2Valid && index3Valid && index4Valid) {
215                    *ptr = index1; ++ptr;
216                    *ptr = index2; ++ptr;
217                    *ptr = index3; ++ptr;
218                    *ptr = index4; ++ptr;
219                    ++ic;
220                }
221#endif
222            }
223        }
224    } else {
225        for (i = 0; i < boundaryHeight; ++i) {
226            for (j = 0; j < boundaryWidth; ++j) {
227                *ptr = i * xCount + j; ++ptr;
228                *ptr = (i + 1) * xCount + j; ++ptr;
229                *ptr = (i + 1) * xCount + j + 1; ++ptr;
230                *ptr = i * xCount + j; ++ptr;
231                *ptr = (i + 1) * xCount + j + 1; ++ptr;
232                *ptr = i * xCount + j + 1; ++ptr;
233            }
234        }
235    }
236}
237
238void
239HeightMap::reset()
240{
241    if (_vertexBufferObjectID) {
242        glDeleteBuffers(1, &_vertexBufferObjectID);
243        _vertexBufferObjectID = 0;
244    }
245    if (_textureBufferObjectID) {
246        glDeleteBuffers(1, &_textureBufferObjectID);
247        _textureBufferObjectID = 0;
248    }
249    if (_contour != NULL) {
250        delete _contour;
251        _contour = NULL;
252    }
253}
254
255void
256HeightMap::setHeight(int xCount, int yCount, Vector3* heights)
257{
258    _vertexCount = xCount * yCount;
259    reset();
260   
261    heights_ = (float *)heights;
262    float min, max;
263    min = heights[0].y, max = heights[0].y;
264
265    int count = xCount * yCount;
266    for (int i = 0; i < count; ++i) {
267        if (min > heights[i].y) {
268            min = heights[i].y;
269        }
270        if (max < heights[i].y) {
271            max = heights[i].y;
272        }
273    }
274
275    _scale.x = 1.0f;
276    _scale.z = max - min;
277    _scale.y = 1.0f;
278
279    xAxis.SetRange(0.0, 1.0);
280    yAxis.SetRange(0.0, 1.0);
281    zAxis.SetRange(0.0, 1.0);
282    wAxis.SetRange(min, max);
283    update_pending = true;
284   
285    _centerPoint.set(_scale.x * 0.5, _scale.z * 0.5 + min, _scale.y * 0.5);
286
287    Vector3* texcoord = new Vector3[count];
288    for (int i = 0; i < count; ++i) {
289        texcoord[i].set(0, 0, heights[i].y);
290    }
291   
292    glGenBuffers(1, &_vertexBufferObjectID);
293    glBindBuffer(GL_ARRAY_BUFFER, _vertexBufferObjectID);
294    glBufferData(GL_ARRAY_BUFFER, _vertexCount * sizeof( Vector3 ), heights,
295        GL_STATIC_DRAW);
296    glGenBuffers(1, &_textureBufferObjectID);
297    glBindBuffer(GL_ARRAY_BUFFER, _textureBufferObjectID);
298    glBufferData(GL_ARRAY_BUFFER, _vertexCount * sizeof(float) * 3, texcoord,
299        GL_STATIC_DRAW);
300    glBindBuffer(GL_ARRAY_BUFFER, 0);
301   
302    delete [] texcoord;
303   
304   
305    ContourLineFilter lineFilter;
306    _contour = lineFilter.create(0.0f, 1.0f, 10, heights, xCount, yCount);
307
308#if TOPCONTOUR
309    ContourLineFilter topLineFilter;
310    topLineFilter.setHeightTop(true);
311    _topContour = topLineFilter.create(0.0f, 1.0f, 10, heights, xCount, yCount);
312#endif
313
314    //if (heightMap)
315    //{
316    //  VertexBuffer* vertexBuffer = new VertexBuffer(VertexBuffer::POSITION3, xCount * yCount, sizeof(Vector3) * xCount * yCount, heightMap, false);
317    this->createIndexBuffer(xCount, yCount, 0);
318    //}
319    //else
320    //{
321    //printf("ERROR - HeightMap::setHeight\n");
322    //}
323}
324
325void
326HeightMap::setHeight(float xMin, float yMin, float xMax, float yMax,
327                     int xNum, int yNum, float* heights)
328{
329    _vertexCount = xNum * yNum;
330    xNum_ = xNum, yNum_ = yNum;
331    heights_ = heights;
332    reset();
333   
334    // Get the min/max of the heights. */
335    float min, max;
336    min = max = heights[0];
337    for (int i = 0; i < _vertexCount; ++i) {
338        if (min > heights[i]) {
339            min = heights[i];
340        } else if (max < heights[i]) {
341            max = heights[i];
342        }
343    }
344#ifdef notdef
345    if (retainScale_) {
346        // Check the units of each axis.  If they are the same, we want to
347        // retain the surface's aspect ratio when transforming coordinates to
348        // the grid. Use the range of the longest axis when the units are the
349        // same. 
350        if (xAxis.units() != NULL) && (xAxis.units() == yAxis.units()) {
351        }
352        if (yAxis.units() != NULL) && (yAxis.units() == zAxis.units()) {
353        }
354    }
355#endif
356
357    wAxis.SetRange(min, max);
358    yAxis.SetRange(min, max);
359    xAxis.SetRange(xMin, xMax);
360    zAxis.SetRange(yMin, yMax);
361   
362   
363    min = 0.0, max = 1.0;
364    xMin = yMin = min = 0.0;
365    xMax = yMax = max = 1.0;
366    // Save the scales.
367    _scale.x = _scale.y = _scale.z = 1.0;
368
369    update_pending = true;
370
371    _centerPoint.set(0.5, 0.5, 0.5);
372   
373#ifndef notdef
374    Vector3* texcoord = new Vector3[_vertexCount];
375    for (int i = 0; i < _vertexCount; ++i) {
376        texcoord[i].set(0, 0, heights[i]);
377    }
378   
379    Vector3* heightMap = createHeightVertices(xMin, yMin, xMax, yMax, xNum, yNum, heights);
380   
381    glGenBuffers(1, &_vertexBufferObjectID);
382    glBindBuffer(GL_ARRAY_BUFFER, _vertexBufferObjectID);
383    glBufferData(GL_ARRAY_BUFFER, _vertexCount * sizeof( Vector3 ), heightMap,
384        GL_STATIC_DRAW);
385    glGenBuffers(1, &_textureBufferObjectID);
386    glBindBuffer(GL_ARRAY_BUFFER, _textureBufferObjectID);
387    glBufferData(GL_ARRAY_BUFFER, _vertexCount * sizeof(float) * 3, texcoord,
388        GL_STATIC_DRAW);
389    glBindBuffer(GL_ARRAY_BUFFER, 0);
390   
391    delete [] texcoord;
392   
393   
394    ContourLineFilter lineFilter;
395    //lineFilter.transferFunction(_tfPtr);
396    _contour = lineFilter.create(0.0f, 1.0f, 10, heightMap, xNum, yNum);
397   
398#if TOPCONTOUR
399    ContourLineFilter topLineFilter;
400    topLineFilter.setHeightTop(true);
401    _topContour = topLineFilter.create(0.0f, 1.0f, 10, heightMap, xNum, yNum);
402#endif
403
404
405    //if (heightMap)
406    //{
407    //  VertexBuffer* vertexBuffer = new VertexBuffer(VertexBuffer::POSITION3, xNum * yNum,
408    // sizeof(Vector3) * xNum * yNum, heightMap, false);
409    if (_indexBuffer != NULL) {
410        free(_indexBuffer);
411        _indexBuffer = NULL;
412    }
413    this->createIndexBuffer(xNum, yNum, heights);
414    //}
415    //else
416    //{
417    //printf("ERROR - HeightMap::setHeight\n");
418    //}
419#endif
420}
421
422Vector3*
423HeightMap::createHeightVertices(float xMin, float yMin, float xMax,
424                                float yMax, int xNum, int yNum, float* height)
425{
426    Vector3* vertices = (Vector3*) malloc(sizeof(Vector3) * xNum * yNum);
427
428    Vector3* dstDataPtr = vertices;
429    float* srcDataPtr = height;
430   
431    for (int y = 0; y < yNum; ++y) {
432        float yCoord;
433
434        yCoord = yMin + ((yMax - yMin) * y) / (yNum - 1);
435        for (int x = 0; x < xNum; ++x) {
436            float xCoord;
437
438            xCoord = xMin + ((xMax - xMin) * x) / (xNum - 1);
439            dstDataPtr->set(xCoord, *srcDataPtr, yCoord);
440
441            ++dstDataPtr;
442            ++srcDataPtr;
443        }
444    }
445    return vertices;
446}
447
448// Maps the data coordinates of the surface into the grid's axes.
449void
450HeightMap::MapToGrid(Grid *gridPtr)
451{
452    int count = xNum_ * yNum_;
453
454    reset();
455
456    // The range of the grid's y-axis 0..1 represents the distance between the
457    // smallest and largest major ticks for all surfaces plotted.  Translate
458    // this surface's y-values (heights) into the grid's axis coordinates.
459
460    float yScale = 1.0 / (gridPtr->yAxis.max() - gridPtr->yAxis.min());
461    float *p, *q, *pend;
462    float *normHeights = new float[count];
463    for (p = heights_, pend = p + count, q = normHeights; p < pend; p++, q++) {
464        *q = (*p - gridPtr->yAxis.min()) * yScale;
465    }
466    Vector3 *t, *texcoord;
467    texcoord = new Vector3[count];
468    for (t = texcoord, p = normHeights, pend = p + count; p < pend; p++, t++) {
469        t->set(0, 0, *p);
470    }
471
472    // Normalize the mesh coordinates (x and z min/max) the range of the major
473    // ticks for the x and z grid axes as well.
474
475    float xScale, zScale;
476    float xMin, xMax, zMin, zMax;
477
478    xScale = 1.0 / (gridPtr->xAxis.max() - gridPtr->xAxis.min());
479    xMin = (xAxis.min() - gridPtr->xAxis.min()) * xScale;
480    xMax = (xAxis.max() - gridPtr->xAxis.min()) * xScale;
481    zScale = 1.0 / (gridPtr->zAxis.max() - gridPtr->zAxis.min());
482    zMin = (zAxis.min() - gridPtr->zAxis.min()) * zScale;
483    zMax = (zAxis.max() - gridPtr->zAxis.min()) * zScale;
484
485    Vector3* vertices;
486    vertices = createHeightVertices(xMin, zMin, xMax, zMax, xNum_, yNum_,
487        normHeights);
488   
489    glGenBuffers(1, &_vertexBufferObjectID);
490    glBindBuffer(GL_ARRAY_BUFFER, _vertexBufferObjectID);
491    glBufferData(GL_ARRAY_BUFFER, _vertexCount * sizeof(Vector3), vertices,
492        GL_STATIC_DRAW);
493    glGenBuffers(1, &_textureBufferObjectID);
494    glBindBuffer(GL_ARRAY_BUFFER, _textureBufferObjectID);
495    glBufferData(GL_ARRAY_BUFFER, _vertexCount * sizeof(float) * 3, texcoord,
496        GL_STATIC_DRAW);
497    glBindBuffer(GL_ARRAY_BUFFER, 0);
498    delete [] texcoord;
499
500    ContourLineFilter lineFilter;
501    //lineFilter.transferFunction(_tfPtr);
502    _contour = lineFilter.create(0.0f, 1.0f, 10, vertices, xNum_, yNum_);
503   
504#if TOPCONTOUR
505    ContourLineFilter topLineFilter;
506    topLineFilter.setHeightTop(true);
507    _topContour = topLineFilter.create(0.0f, 1.0f, 10, vertices, xNum_, yNum_);
508#endif
509    this->createIndexBuffer(xNum_, yNum_, normHeights);
510    delete [] normHeights;
511}
512
513void HeightMap::render_topview(graphics::RenderContext* renderContext, int render_width, int render_height)
514{
515   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
516    glPushAttrib(GL_VIEWPORT_BIT);
517    glViewport(0, 0, render_width, render_height);
518    glMatrixMode(GL_PROJECTION);
519    glPushMatrix();
520    glLoadIdentity();
521    //gluOrtho2D(0, render_width, 0, render_height);
522    glOrtho(-.5, .5, -.5, .5, -50, 50);
523    glMatrixMode(GL_MODELVIEW);
524    glPushMatrix();
525    glLoadIdentity();
526
527    glTranslatef(0.0, 0.0, -10.0);
528
529    // put camera rotation and traslation
530    //glScalef(1 / _scale.x, 1 / _scale.y , 1 / _scale.z);
531
532    if (renderContext->getCullMode() == graphics::RenderContext::NO_CULL) {
533        glDisable(GL_CULL_FACE);
534    } else {
535        glEnable(GL_CULL_FACE);
536        glCullFace((GLuint) renderContext->getCullMode());
537    }
538
539    glPolygonMode(GL_FRONT_AND_BACK, (GLuint) renderContext->getPolygonMode());
540    glShadeModel((GLuint) renderContext->getShadingModel());
541
542    glPushMatrix();
543
544    //glTranslatef(-_centerPoint.x, -_centerPoint.y, -_centerPoint.z);
545
546    //glScalef(0.01, 0.01, 0.01f);
547    glRotatef(90.0f, 1.0f, 0.0f, 0.0f);
548    glTranslatef(-_centerPoint.x, -_centerPoint.y, -_centerPoint.z);
549    if (_contour != NULL) {
550        glDepthRange (0.001, 1.0);
551    }
552       
553    glEnable(GL_DEPTH_TEST);
554
555    glEnable(GL_BLEND);
556    glEnable(GL_TEXTURE_2D);
557    if (_vertexBufferObjectID)
558    {
559        printf("TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT\n");
560        glColor3f(1.0f, 1.0f, 1.0f);
561        glShadeModel(GL_SMOOTH);
562        glEnable(GL_BLEND);
563        glEnableClientState(GL_VERTEX_ARRAY);
564        glDisableClientState(GL_COLOR_ARRAY);
565        glDisableClientState(GL_INDEX_ARRAY);
566        glDisableClientState(GL_NORMAL_ARRAY);
567       
568        if (_tfPtr != NULL) {
569            cgGLBindProgram(_shader->getFP());
570            cgGLEnableProfile(CG_PROFILE_FP30);
571           
572            cgGLSetTextureParameter(_tfParam, _tfPtr->id());
573            cgGLEnableTextureParameter(_tfParam);
574           
575            glEnable(GL_TEXTURE_1D);
576            _tfPtr->getTexture()->activate();
577           
578            glEnableClientState(GL_TEXTURE_COORD_ARRAY);
579        }
580        else {
581            glDisableClientState(GL_TEXTURE_COORD_ARRAY);
582        }
583       
584        glBindBuffer(GL_ARRAY_BUFFER, _vertexBufferObjectID);
585        glVertexPointer(3, GL_FLOAT, 12, 0);
586       
587        glBindBuffer(GL_ARRAY_BUFFER, _textureBufferObjectID);
588        ::glTexCoordPointer(3, GL_FLOAT, 12, 0);
589       
590#define _TRIANGLES_
591#ifdef _TRIANGLES_
592        glDrawElements(GL_TRIANGLES, _indexCount, GL_UNSIGNED_INT,
593                       _indexBuffer);
594#else                   
595        glDrawElements(GL_QUADS, _indexCount, GL_UNSIGNED_INT,
596                       _indexBuffer);
597#endif
598
599        glBindBuffer(GL_ARRAY_BUFFER, 0);
600       
601        glDisableClientState(GL_VERTEX_ARRAY);
602        if (_tfPtr != NULL) {
603            _tfPtr->getTexture()->deactivate();
604            glDisableClientState(GL_TEXTURE_COORD_ARRAY);
605           
606            cgGLDisableProfile(CG_PROFILE_FP30);
607        }
608    }
609   
610    glShadeModel(GL_FLAT);
611    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
612   
613    if (_contour != NULL) {
614        if (_contourVisible) {
615            glDisable(GL_BLEND);
616            glDisable(GL_TEXTURE_2D);
617            glColor4f(_contourColor.x, _contourColor.y, _contourColor.z, 1.0f);
618            glDepthRange (0.0, 0.999);
619            _contour->render();
620            glDepthRange (0.0, 1.0);
621        }
622
623#if TOPCONTOUR
624        if (_topContourVisible) {
625            glDisable(GL_BLEND);
626            glDisable(GL_TEXTURE_2D);
627            glColor4f(_contourColor.x, _contourColor.y, _contourColor.z, 1.0f);
628            //glDepthRange (0.0, 0.999);
629            _topContour->render();
630            //glDepthRange (0.0, 1.0);
631        }
632#endif
633    }
634   
635    glPopMatrix();
636    glPopMatrix();
637
638    glMatrixMode(GL_PROJECTION);
639    glPushMatrix();
640    glMatrixMode(GL_MODELVIEW);
641
642    glPopAttrib();
643}
644
Note: See TracBrowser for help on using the repository browser.