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

Last change on this file since 2827 was 2827, checked in by ldelgass, 8 years ago

Move Vector3/4Array typdefs to the Vector3/4.h headers and remove TypeDefs?.h.
Also misc. cleanups

  • Property svn:eol-style set to native
File size: 18.8 KB
Line 
1 /* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
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 "Texture1D.h"
15#include "R2/R2FilePath.h"
16#include "RpField1D.h"
17#include "RenderContext.h"
18
19bool HeightMap::update_pending = false;
20double HeightMap::valueMin = 0.0;
21double HeightMap::valueMax = 1.0;
22
23#define TOPCONTOUR      0
24//#define TOPCONTOUR    1
25HeightMap::HeightMap() :
26    _vertexBufferObjectID(0),
27    _textureBufferObjectID(0),
28    _vertexCount(0),
29    _contour(0),
30    _topContour(0),
31    _tfPtr(0),
32    _opacity(0.5f),
33    _indexBuffer(0),
34    _indexCount(0),
35    _contourColor(1.0f, 0.0f, 0.0f),
36    _contourVisible(false),
37    _topContourVisible(true),
38    _visible(false),
39    _scale(1.0f, 1.0f, 1.0f),
40    _centerPoint(0.0f, 0.0f, 0.0f),
41    _heights(NULL)
42{
43    _shader = new NvShader();
44    _shader->loadFragmentProgram("heightcolor.cg", "main");
45    _tfParam      = _shader->getNamedParameterFromFP("tf");
46    _opacityParam = _shader->getNamedParameterFromFP("opacity");
47}
48
49HeightMap::~HeightMap()
50{
51    reset();
52
53    if (_shader) {
54        delete _shader;
55    }
56    if (_heights != NULL) {
57        free(_heights);
58    }
59}
60
61void
62HeightMap::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    glShadeModel(GL_FLAT);
141    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
142   
143    if (_contour != NULL) {
144        if (_contourVisible) {
145            glDisable(GL_BLEND);
146            glDisable(GL_TEXTURE_2D);
147            glColor4f(_contourColor.x, _contourColor.y, _contourColor.z,
148                _opacity /*1.0f*/);
149            glDepthRange (0.0, 0.999);
150            _contour->render();
151            glDepthRange (0.0, 1.0);
152        }
153
154#if TOPCONTOUR
155        if (_topContourVisible) {
156            glDisable(GL_BLEND);
157            glDisable(GL_TEXTURE_2D);
158            glColor4f(_contourColor.x, _contourColor.y, _contourColor.z,
159                _opacity /*1.0f*/);
160            //glDepthRange (0.0, 0.999);
161            _topContour->render();
162            //glDepthRange (0.0, 1.0);
163        }
164#endif
165    }
166    glPopMatrix();
167}
168
169void
170HeightMap::createIndexBuffer(int xCount, int zCount, float* heights)
171{
172    if (_indexBuffer != NULL) {
173        delete [] _indexBuffer;
174        _indexBuffer = NULL;
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    if (_indexBuffer != NULL) {
254        delete [] _indexBuffer;
255        _indexBuffer = NULL;
256    }
257}
258
259void
260HeightMap::setHeight(int xCount, int yCount, Vector3* heights)
261{
262    _vertexCount = xCount * yCount;
263    reset();
264   
265    _heights = (float *)heights;
266    float min, max;
267    min = heights[0].y, max = heights[0].y;
268
269    int count = xCount * yCount;
270    for (int i = 0; i < count; ++i) {
271        if (min > heights[i].y) {
272            min = heights[i].y;
273        }
274        if (max < heights[i].y) {
275            max = heights[i].y;
276        }
277    }
278
279    _scale.x = 1.0f;
280    _scale.z = max - min;
281    _scale.y = 1.0f;
282
283    xAxis.SetRange(0.0, 1.0);
284    yAxis.SetRange(0.0, 1.0);
285    zAxis.SetRange(0.0, 1.0);
286    wAxis.SetRange(min, max);
287    update_pending = true;
288   
289    _centerPoint.set(_scale.x * 0.5, _scale.z * 0.5 + min, _scale.y * 0.5);
290
291    Vector3* texcoord = new Vector3[count];
292    for (int i = 0; i < count; ++i) {
293        texcoord[i].set(0, 0, heights[i].y);
294    }
295   
296    glGenBuffers(1, &_vertexBufferObjectID);
297    glBindBuffer(GL_ARRAY_BUFFER, _vertexBufferObjectID);
298    glBufferData(GL_ARRAY_BUFFER, _vertexCount * sizeof( Vector3 ), heights,
299        GL_STATIC_DRAW);
300    glGenBuffers(1, &_textureBufferObjectID);
301    glBindBuffer(GL_ARRAY_BUFFER, _textureBufferObjectID);
302    glBufferData(GL_ARRAY_BUFFER, _vertexCount * sizeof(float) * 3, texcoord,
303        GL_STATIC_DRAW);
304    glBindBuffer(GL_ARRAY_BUFFER, 0);
305   
306    delete [] texcoord;
307   
308    if (_contour != NULL) {
309        delete _contour;
310        _contour = NULL;
311    }
312    ContourLineFilter lineFilter;
313    _contour = lineFilter.create(0.0f, 1.0f, 10, heights, xCount, yCount);
314
315#if TOPCONTOUR
316    ContourLineFilter topLineFilter;
317    topLineFilter.setHeightTop(true);
318    _topContour = topLineFilter.create(0.0f, 1.0f, 10, heights, xCount, yCount);
319#endif
320
321    //if (heightMap)
322    //{
323    //  VertexBuffer* vertexBuffer = new VertexBuffer(VertexBuffer::POSITION3, xCount * yCount, sizeof(Vector3) * xCount * yCount, heightMap, false);
324    this->createIndexBuffer(xCount, yCount, 0);
325    //}
326    //else
327    //{
328    //ERROR("HeightMap::setHeight\n");
329    //}
330}
331
332void
333HeightMap::setHeight(float xMin, float yMin, float xMax, float yMax,
334                     int xNum, int yNum, float* heights)
335{
336    _vertexCount = xNum * yNum;
337    _xNum = xNum, _yNum = yNum;
338    _heights = heights;
339    reset();
340   
341    // Get the min/max of the heights. */
342    float min, max;
343    min = max = heights[0];
344    for (int i = 0; i < _vertexCount; ++i) {
345        if (min > heights[i]) {
346            min = heights[i];
347        } else if (max < heights[i]) {
348            max = heights[i];
349        }
350    }
351#ifdef notdef
352    if (retainScale_) {
353        // Check the units of each axis.  If they are the same, we want to
354        // retain the surface's aspect ratio when transforming coordinates to
355        // the grid. Use the range of the longest axis when the units are the
356        // same. 
357        if (xAxis.units() != NULL) && (xAxis.units() == yAxis.units()) {
358        }
359        if (yAxis.units() != NULL) && (yAxis.units() == zAxis.units()) {
360        }
361    }
362#endif
363
364    wAxis.SetRange(min, max);
365    yAxis.SetRange(min, max);
366    xAxis.SetRange(xMin, xMax);
367    zAxis.SetRange(yMin, yMax);
368   
369   
370    min = 0.0, max = 1.0;
371    xMin = yMin = min = 0.0;
372    xMax = yMax = max = 1.0;
373    // Save the scales.
374    _scale.x = _scale.y = _scale.z = 1.0;
375
376    update_pending = true;
377
378    _centerPoint.set(0.5, 0.5, 0.5);
379   
380#ifndef notdef
381    Vector3* texcoord = new Vector3[_vertexCount];
382    for (int i = 0; i < _vertexCount; ++i) {
383        texcoord[i].set(0, 0, heights[i]);
384    }
385   
386    Vector3* map = createHeightVertices(xMin, yMin, xMax, yMax, xNum, yNum,
387                                        heights);
388   
389    glGenBuffers(1, &_vertexBufferObjectID);
390    glBindBuffer(GL_ARRAY_BUFFER, _vertexBufferObjectID);
391    glBufferData(GL_ARRAY_BUFFER, _vertexCount * sizeof(Vector3), map,
392        GL_STATIC_DRAW);
393    glGenBuffers(1, &_textureBufferObjectID);
394    glBindBuffer(GL_ARRAY_BUFFER, _textureBufferObjectID);
395    glBufferData(GL_ARRAY_BUFFER, _vertexCount * sizeof(float) * 3, texcoord,
396        GL_STATIC_DRAW);
397    glBindBuffer(GL_ARRAY_BUFFER, 0);
398   
399    delete [] texcoord;
400   
401   
402    if (_contour != NULL) {
403        delete _contour;
404        _contour = NULL;
405    }
406    ContourLineFilter lineFilter;
407    //lineFilter.transferFunction(_tfPtr);
408    _contour = lineFilter.create(0.0f, 1.0f, 10, map, xNum, yNum);
409   
410#if TOPCONTOUR
411    ContourLineFilter topLineFilter;
412    topLineFilter.setHeightTop(true);
413    _topContour = topLineFilter.create(0.0f, 1.0f, 10, map, xNum, yNum);
414#endif
415    this->createIndexBuffer(xNum, yNum, heights);
416    delete [] map;
417#endif
418}
419
420Vector3*
421HeightMap::createHeightVertices(float xMin, float yMin, float xMax,
422                                float yMax, int xNum, int yNum, float* height)
423{
424    Vector3* vertices = new Vector3[xNum * yNum];
425
426    Vector3* dstDataPtr = vertices;
427    float* srcDataPtr = height;
428   
429    for (int y = 0; y < yNum; ++y) {
430        float yCoord;
431
432        yCoord = yMin + ((yMax - yMin) * y) / (yNum - 1);
433        for (int x = 0; x < xNum; ++x) {
434            float xCoord;
435
436            xCoord = xMin + ((xMax - xMin) * x) / (xNum - 1);
437            dstDataPtr->set(xCoord, *srcDataPtr, yCoord);
438
439            ++dstDataPtr;
440            ++srcDataPtr;
441        }
442    }
443    return vertices;
444}
445
446// Maps the data coordinates of the surface into the grid's axes.
447void
448HeightMap::MapToGrid(Grid *gridPtr)
449{
450    int count = _xNum * _yNum;
451
452    reset();
453
454    // The range of the grid's y-axis 0..1 represents the distance between the
455    // smallest and largest major ticks for all surfaces plotted.  Translate
456    // this surface's y-values (heights) into the grid's axis coordinates.
457
458    float yScale = 1.0 / (gridPtr->yAxis.max() - gridPtr->yAxis.min());
459    float *p, *q, *pend;
460    float *normHeights = new float[count];
461    for (p = _heights, pend = p + count, q = normHeights; p < pend; p++, q++) {
462        *q = (*p - gridPtr->yAxis.min()) * yScale;
463    }
464    Vector3 *t, *texcoord;
465    texcoord = new Vector3[count];
466    for (t = texcoord, p = normHeights, pend = p + count; p < pend; p++, t++) {
467        t->set(0, 0, *p);
468    }
469
470    // Normalize the mesh coordinates (x and z min/max) the range of the major
471    // ticks for the x and z grid axes as well.
472
473    float xScale, zScale;
474    float xMin, xMax, zMin, zMax;
475
476    xScale = 1.0 / (gridPtr->xAxis.max() - gridPtr->xAxis.min());
477    xMin = (xAxis.min() - gridPtr->xAxis.min()) * xScale;
478    xMax = (xAxis.max() - gridPtr->xAxis.min()) * xScale;
479    zScale = 1.0 / (gridPtr->zAxis.max() - gridPtr->zAxis.min());
480    zMin = (zAxis.min() - gridPtr->zAxis.min()) * zScale;
481    zMax = (zAxis.max() - gridPtr->zAxis.min()) * zScale;
482
483    Vector3* vertices;
484    vertices = createHeightVertices(xMin, zMin, xMax, zMax, _xNum, _yNum,
485        normHeights);
486   
487    glGenBuffers(1, &_vertexBufferObjectID);
488    glBindBuffer(GL_ARRAY_BUFFER, _vertexBufferObjectID);
489    glBufferData(GL_ARRAY_BUFFER, _vertexCount * sizeof(Vector3), vertices,
490        GL_STATIC_DRAW);
491    glGenBuffers(1, &_textureBufferObjectID);
492    glBindBuffer(GL_ARRAY_BUFFER, _textureBufferObjectID);
493    glBufferData(GL_ARRAY_BUFFER, _vertexCount * sizeof(float) * 3, texcoord,
494        GL_STATIC_DRAW);
495    glBindBuffer(GL_ARRAY_BUFFER, 0);
496    delete [] texcoord;
497
498    if (_contour != NULL) {
499        delete _contour;
500        _contour = NULL;
501    }
502    ContourLineFilter lineFilter;
503    //lineFilter.transferFunction(_tfPtr);
504    _contour = lineFilter.create(0.0f, 1.0f, 10, vertices, _xNum, _yNum);
505   
506#if TOPCONTOUR
507    ContourLineFilter topLineFilter;
508    topLineFilter.setHeightTop(true);
509    _topContour = topLineFilter.create(0.0f, 1.0f, 10, vertices, _xNum, _yNum);
510#endif
511    this->createIndexBuffer(_xNum, _yNum, normHeights);
512    delete [] normHeights;
513    delete [] vertices;
514}
515
516void
517HeightMap::render_topview(graphics::RenderContext* renderContext,
518                          int render_width, int render_height)
519{
520   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
521    glPushAttrib(GL_VIEWPORT_BIT);
522    glViewport(0, 0, render_width, render_height);
523    glMatrixMode(GL_PROJECTION);
524    glPushMatrix();
525    glLoadIdentity();
526    //gluOrtho2D(0, render_width, 0, render_height);
527    glOrtho(-.5, .5, -.5, .5, -50, 50);
528    glMatrixMode(GL_MODELVIEW);
529    glPushMatrix();
530    glLoadIdentity();
531
532    glTranslatef(0.0, 0.0, -10.0);
533
534    // put camera rotation and traslation
535    //glScalef(1 / _scale.x, 1 / _scale.y , 1 / _scale.z);
536
537    if (renderContext->getCullMode() == graphics::RenderContext::NO_CULL) {
538        glDisable(GL_CULL_FACE);
539    } else {
540        glEnable(GL_CULL_FACE);
541        glCullFace((GLuint) renderContext->getCullMode());
542    }
543
544    glPolygonMode(GL_FRONT_AND_BACK, (GLuint) renderContext->getPolygonMode());
545    glShadeModel((GLuint) renderContext->getShadingModel());
546
547    glPushMatrix();
548
549    //glTranslatef(-_centerPoint.x, -_centerPoint.y, -_centerPoint.z);
550
551    //glScalef(0.01, 0.01, 0.01f);
552    glRotatef(90.0f, 1.0f, 0.0f, 0.0f);
553    glTranslatef(-_centerPoint.x, -_centerPoint.y, -_centerPoint.z);
554    if (_contour != NULL) {
555        glDepthRange (0.001, 1.0);
556    }
557       
558    glEnable(GL_DEPTH_TEST);
559
560    glEnable(GL_BLEND);
561    glEnable(GL_TEXTURE_2D);
562    if (_vertexBufferObjectID)
563    {
564        TRACE("TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT\n");
565        glColor3f(1.0f, 1.0f, 1.0f);
566        glShadeModel(GL_SMOOTH);
567        glEnable(GL_BLEND);
568        glEnableClientState(GL_VERTEX_ARRAY);
569        glDisableClientState(GL_COLOR_ARRAY);
570        glDisableClientState(GL_INDEX_ARRAY);
571        glDisableClientState(GL_NORMAL_ARRAY);
572       
573        if (_tfPtr != NULL) {
574            cgGLBindProgram(_shader->getFP());
575            cgGLEnableProfile(CG_PROFILE_FP30);
576           
577            cgGLSetTextureParameter(_tfParam, _tfPtr->id());
578            cgGLEnableTextureParameter(_tfParam);
579           
580            glEnable(GL_TEXTURE_1D);
581            _tfPtr->getTexture()->activate();
582           
583            glEnableClientState(GL_TEXTURE_COORD_ARRAY);
584        }
585        else {
586            glDisableClientState(GL_TEXTURE_COORD_ARRAY);
587        }
588       
589        glBindBuffer(GL_ARRAY_BUFFER, _vertexBufferObjectID);
590        glVertexPointer(3, GL_FLOAT, 12, 0);
591       
592        glBindBuffer(GL_ARRAY_BUFFER, _textureBufferObjectID);
593        ::glTexCoordPointer(3, GL_FLOAT, 12, 0);
594       
595#define _TRIANGLES_
596#ifdef _TRIANGLES_
597        glDrawElements(GL_TRIANGLES, _indexCount, GL_UNSIGNED_INT,
598                       _indexBuffer);
599#else                   
600        glDrawElements(GL_QUADS, _indexCount, GL_UNSIGNED_INT,
601                       _indexBuffer);
602#endif
603
604        glBindBuffer(GL_ARRAY_BUFFER, 0);
605       
606        glDisableClientState(GL_VERTEX_ARRAY);
607        if (_tfPtr != NULL) {
608            _tfPtr->getTexture()->deactivate();
609            glDisableClientState(GL_TEXTURE_COORD_ARRAY);
610           
611            cgGLDisableProfile(CG_PROFILE_FP30);
612        }
613    }
614   
615    glShadeModel(GL_FLAT);
616    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
617   
618    if (_contour != NULL) {
619        if (_contourVisible) {
620            glDisable(GL_BLEND);
621            glDisable(GL_TEXTURE_2D);
622            glColor4f(_contourColor.x, _contourColor.y, _contourColor.z, 1.0f);
623            glDepthRange (0.0, 0.999);
624            _contour->render();
625            glDepthRange (0.0, 1.0);
626        }
627
628#if TOPCONTOUR
629        if (_topContourVisible) {
630            glDisable(GL_BLEND);
631            glDisable(GL_TEXTURE_2D);
632            glColor4f(_contourColor.x, _contourColor.y, _contourColor.z, 1.0f);
633            //glDepthRange (0.0, 0.999);
634            _topContour->render();
635            //glDepthRange (0.0, 1.0);
636        }
637#endif
638    }
639   
640    glPopMatrix();
641    glPopMatrix();
642
643    glMatrixMode(GL_PROJECTION);
644    glPushMatrix();
645    glMatrixMode(GL_MODELVIEW);
646
647    glPopAttrib();
648}
649
Note: See TracBrowser for help on using the repository browser.