source: git/src/gla-gl.cc @ 1f2bcc2

RELEASE/1.1RELEASE/1.2debug-cidebug-ci-sanitisersfaster-cavernloglog-selectstereostereo-2025walls-datawalls-data-hanging-as-warningwarn-only-for-hanging-survey
Last change on this file since 1f2bcc2 was 6662d02, checked in by Olly Betts <olly@…>, 20 years ago

Restore GL_DEPTH_TEST state after plotting cross or blobs the slow way.

git-svn-id: file:///home/survex-svn/survex/branches/survex-1_1@3093 4b37db11-9a0c-4f06-9ece-9ab7cdaee568

  • Property mode set to 100644
File size: 35.1 KB
Line 
1//
2//  gla-gl.cc
3//
4//  OpenGL implementation for the GLA abstraction layer.
5//
6//  Copyright (C) 2002-2003,2005 Mark R. Shinwell
7//  Copyright (C) 2003,2004,2005 Olly Betts
8//
9//  This program is free software; you can redistribute it and/or modify
10//  it under the terms of the GNU General Public License as published by
11//  the Free Software Foundation; either version 2 of the License, or
12//  (at your option) any later version.
13//
14//  This program is distributed in the hope that it will be useful,
15//  but WITHOUT ANY WARRANTY; without even the implied warranty of
16//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17//  GNU General Public License for more details.
18//
19//  You should have received a copy of the GNU General Public License
20//  along with this program; if not, write to the Free Software
21//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22//
23
24#ifdef HAVE_CONFIG_H
25#include <config.h>
26#endif
27
28#include <wx/confbase.h>
29#include <wx/image.h>
30
31#include <algorithm>
32
33#include "gla.h"
34#include "message.h"
35#include "useful.h"
36
37#include <GL/glext.h>
38
39#ifndef GL_POINT_SPRITE_ARB
40#define GL_POINT_SPRITE_ARB 0x8861
41#endif
42#ifndef GL_COORD_REPLACE_ARB
43#define GL_COORD_REPLACE_ARB 0x8862
44#endif
45
46#ifndef USE_FNT
47// Some WIN32 stupidity which causes mingw to fail to link for some reason;
48// doing this probably means we can't safely use atexit() - see the comments in
49// glut.h if you can take the full horror...
50#define GLUT_DISABLE_ATEXIT_HACK
51// For glutBitmapLength()
52#define GLUT_API_VERSION 4
53#include <GL/glut.h>
54#ifdef FREEGLUT
55#include <GL/freeglut_ext.h>
56#endif
57#endif
58
59using namespace std;
60
61const double BLOB_DIAMETER = 5.0;
62
63wxString GetGLSystemDescription()
64{
65    const char *p = (const char*)glGetString(GL_VERSION);
66    // If OpenGL isn't initialised, p may be NULL.
67    if (!p) return "No OpenGL information available!";
68
69    wxString info;
70    info += "OpenGL ";
71    info += p;
72    info += '\n';
73    info += (const char*)glGetString(GL_VENDOR);
74    info += '\n';
75    info += (const char*)glGetString(GL_RENDERER);
76    info += '\n';
77
78    const GLubyte* gl_extensions = glGetString(GL_EXTENSIONS);
79    if (*gl_extensions) {
80        info += gl_extensions;
81        info += '\n';
82    }
83    GLint red, green, blue;
84    glGetIntegerv(GL_RED_BITS, &red);
85    glGetIntegerv(GL_GREEN_BITS, &green);
86    glGetIntegerv(GL_BLUE_BITS, &blue);
87    GLint max_texture_size;
88    glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size);
89    GLint max_viewport[2];
90    glGetIntegerv(GL_MAX_VIEWPORT_DIMS, max_viewport);
91    GLdouble point_size_range[2];
92    glGetDoublev(GL_POINT_SIZE_RANGE, point_size_range);
93    GLdouble point_size_granularity;
94    glGetDoublev(GL_POINT_SIZE_GRANULARITY, &point_size_granularity);
95    wxString s;
96    s.Printf("R%dG%dB%d\n"
97             "Max Texture size: %dx%d\n"
98             "Max Viewport size: %dx%d\n"
99             "Point Size %.3f-%.3f (granularity %.3f)",
100             (int)red, (int)green, (int)blue,
101             (int)max_texture_size, (int)max_texture_size,
102             (int)max_viewport[0], (int)max_viewport[1],
103             point_size_range[0], point_size_range[1],
104             point_size_granularity);
105    info += s;
106    return info;
107}
108
109#ifdef GLA_DEBUG
110// Important: CHECK_GL_ERROR must not be called within a glBegin()/glEnd() pair
111//            (thus it must not be called from BeginLines(), etc., or within a
112//             BeginLines()/EndLines() block etc.)
113#define CHECK_GL_ERROR(f, m) \
114    do { \
115        GLenum err = glGetError(); \
116        if (err != GL_NO_ERROR) { \
117            fprintf(stderr, "OpenGL error: %s: call %s in function %s\n", \
118                    gluErrorString(err), m, f); \
119            /* abort();*/ \
120        } \
121    } \
122    while (0)
123#else
124#define CHECK_GL_ERROR(f, m) do {} while (0)
125#endif
126
127//
128//  GLAPen
129//
130
131GLAPen::GLAPen()
132{
133    components[0] = components[1] = components[2] = 0.0;
134}
135
136GLAPen::~GLAPen()
137{
138}
139
140void GLAPen::SetColour(double red, double green, double blue)
141{
142    components[0] = red;
143    components[1] = green;
144    components[2] = blue;
145}
146
147double GLAPen::GetRed() const
148{
149    return components[0];
150}
151
152double GLAPen::GetGreen() const
153{
154    return components[1];
155}
156
157double GLAPen::GetBlue() const
158{
159    return components[2];
160}
161
162void GLAPen::Interpolate(const GLAPen& pen, double how_far)
163{
164    components[0] = how_far * (pen.GetRed() - components[0]) + components[0];
165    components[1] = how_far * (pen.GetGreen() - components[1]) + components[1];
166    components[2] = how_far * (pen.GetBlue() - components[2]) + components[2];
167}
168
169struct ColourTriple {
170    // RGB triple: values are from 0-255 inclusive for each component.
171    unsigned char r, g, b;
172};
173
174// These must be in the same order as the entries in COLOURS[] below.
175const ColourTriple COLOURS[] = {
176    { 0, 0, 0 },       // black
177    { 100, 100, 100 }, // grey
178    { 180, 180, 180 }, // light grey
179    { 140, 140, 140 }, // light grey 2
180    { 90, 90, 90 },    // dark grey
181    { 255, 255, 255 }, // white
182    { 0, 100, 255},    // turquoise
183    { 0, 255, 40 },    // green
184    { 150, 205, 224 }, // indicator 1
185    { 114, 149, 160 }, // indicator 2
186    { 255, 255, 0 },   // yellow
187    { 255, 0, 0 },     // red
188    { 40, 40, 255 },   // blue
189};
190
191void GLAList::DrawList() const {
192    glCallList(gl_list);
193    CHECK_GL_ERROR("GLAList::DrawList", "glCallList");
194}
195
196void GLAList::InvalidateList() {
197    glDeleteLists(gl_list, 1);
198    CHECK_GL_ERROR("GLAList::InvalidateList", "glDeleteLists");
199
200    // And flag this list as requiring generation before use.
201    gl_list = 0;
202}
203
204//
205//  GLACanvas
206//
207
208#ifndef USE_FNT
209void* const GLACanvas::m_Font = GLUT_BITMAP_HELVETICA_10;
210const int GLACanvas::m_FontSize = 10;
211#endif
212
213GLACanvas::GLACanvas(wxWindow* parent, int id, const wxPoint& posn, wxSize size) :
214    wxGLCanvas(parent, id, posn, size)
215{
216    // Constructor.
217
218    m_Quadric = NULL;
219    m_Rotation.setFromSphericalPolars(0.0, 0.0, 0.0);
220    m_Scale = 0.0;
221    m_Translation.x = m_Translation.y = m_Translation.z = 0.0;
222    m_VolumeDiameter = 1.0;
223    m_Texture = 0;
224    m_Textured = false;
225    m_Perspective = false;
226    m_Fog = false;
227    m_AntiAlias = false;
228    list_flags = 0;
229}
230
231GLACanvas::~GLACanvas()
232{
233    // Destructor.
234
235    if (m_Quadric) {
236        gluDeleteQuadric(m_Quadric);
237        CHECK_GL_ERROR("~GLACanvas", "gluDeleteQuadric");
238    }
239}
240
241void GLACanvas::FirstShow()
242{
243    SetCurrent();
244
245    // Clear any cached OpenGL lists.
246    vector<GLAList>::iterator i;
247    for (i = drawing_lists.begin(); i != drawing_lists.end(); ++i) {
248        if (*i) i->InvalidateList();
249    }
250    drawing_lists.resize(0);
251
252    static bool once_only = true;
253    if (!once_only) return;
254    once_only = false;
255
256    m_Quadric = gluNewQuadric();
257    CHECK_GL_ERROR("FirstShow", "gluNewQuadric");
258    if (!m_Quadric) {
259        abort(); // FIXME need to cope somehow
260    }
261
262    glShadeModel(GL_FLAT);
263    CHECK_GL_ERROR("FirstShow", "glShadeModel");
264    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
265    CHECK_GL_ERROR("FirstShow", "glPolygonMode");
266    //glAlphaFunc(GL_GREATER, 0.5f);
267    //CHECK_GL_ERROR("FirstShow", "glAlphaFunc");
268
269    // Grey fog effect.
270    GLfloat fogcolour[4] = { 0.5, 0.5, 0.5, 1.0 };
271    glFogfv(GL_FOG_COLOR, fogcolour);
272
273    // Linear fogging.
274    glFogi(GL_FOG_MODE, GL_LINEAR);
275
276    // Optimise for speed (compute fog per vertex).
277    glHint(GL_FOG_HINT, GL_FASTEST);
278
279    // No padding on pixel packing and unpacking (default is to pad each
280    // line to a multiple of 4 bytes).
281    glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // For setting texture maps.
282    CHECK_GL_ERROR("FirstShow", "glPixelStorei GL_UNPACK_ALIGNMENT");
283    glPixelStorei(GL_PACK_ALIGNMENT, 1); // For screengrabs and movies.
284    CHECK_GL_ERROR("FirstShow", "glPixelStorei GL_PACK_ALIGNMENT");
285
286#ifdef USE_FNT
287    // Load font
288    wxString path(msg_cfgpth());
289    path += wxCONFIG_PATH_SEPARATOR;
290    path += "aven.txf";
291    m_Font.load(path.c_str());
292#endif
293
294    // Check if we can use GL_POINTS to plot blobs at stations.
295    GLdouble point_size_range[2];
296    glGetDoublev(GL_POINT_SIZE_RANGE, point_size_range);
297    CHECK_GL_ERROR("FirstShow", "glGetDoublev GL_POINT_SIZE_RANGE");
298    glpoint_ok = (point_size_range[0] <= BLOB_DIAMETER &&
299                  point_size_range[1] >= BLOB_DIAMETER);
300    if (glpoint_ok) {
301        glPointSize(BLOB_DIAMETER);
302        CHECK_GL_ERROR("FirstShow", "glPointSize");
303    }
304
305    // See if we have GL_POINT_SPRITE_ARB - if so use it to draw crosses
306    // by texture mapping GL points.
307    const char * gl_extensions = (const char *)glGetString(GL_EXTENSIONS);
308    glpoint_sprite = strstr(gl_extensions, "GL_ARB_point_sprite");
309    if (glpoint_sprite) {
310        float maxSize = 0.0f;
311        glGetFloatv(GL_POINT_SIZE_MAX_ARB, &maxSize);
312        if (maxSize < 8) glpoint_sprite = false;
313    }
314    if (glpoint_sprite) {
315        glGenTextures(1, &m_CrossTexture);
316        glBindTexture(GL_TEXTURE_2D, m_CrossTexture);
317        const unsigned char crossteximage[128] = {
318            255,255,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,255,255,
319              0,  0,255,255,  0,  0,  0,  0,  0,  0,  0,  0,255,255,  0,  0,
320              0,  0,  0,  0,255,255,  0,  0,  0,  0,255,255,  0,  0,  0,  0,
321              0,  0,  0,  0,  0,  0,255,255,255,255,  0,  0,  0,  0,  0,  0,
322              0,  0,  0,  0,  0,  0,255,255,255,255,  0,  0,  0,  0,  0,  0,
323              0,  0,  0,  0,255,255,  0,  0,  0,  0,255,255,  0,  0,  0,  0,
324              0,  0,255,255,  0,  0,  0,  0,  0,  0,  0,  0,255,255,  0,  0,
325            255,255,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,255,255
326        };
327        glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
328        glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
329        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
330        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
331        glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, 8, 8, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, (GLvoid *)crossteximage);
332        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
333        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
334    }
335}
336
337void GLACanvas::Clear()
338{
339    // Clear the canvas.
340
341    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
342    CHECK_GL_ERROR("Clear", "glClear");
343}
344
345void GLACanvas::SetRotation(const Quaternion& q)
346{
347    m_Rotation = q;
348}
349
350void GLACanvas::SetScale(Double scale)
351{
352    if (scale != m_Scale) {
353        vector<GLAList>::iterator i;
354        for (i = drawing_lists.begin(); i != drawing_lists.end(); ++i) {
355            if (*i && i->test_flag(INVALIDATE_ON_SCALE)) i->InvalidateList();
356        }
357
358        m_Scale = scale;
359    }
360}
361
362void GLACanvas::SetTranslation(Double x, Double y, Double z)
363{
364    m_Translation.x = x;
365    m_Translation.y = y;
366    m_Translation.z = z;
367}
368
369void GLACanvas::AddTranslation(Double x, Double y, Double z)
370{
371    m_Translation.x += x;
372    m_Translation.y += y;
373    m_Translation.z += z;
374}
375
376void GLACanvas::AddTranslationScreenCoordinates(int dx, int dy)
377{
378    // Translate the data by a given amount, specified in screen coordinates.
379
380    // Find out how far the translation takes us in data coordinates.
381    SetDataTransform();
382
383    Double x0, y0, z0;
384    Double x, y, z;
385    gluUnProject(0.0, 0.0, 0.0, modelview_matrix, projection_matrix, viewport,
386                 &x0, &y0, &z0);
387    CHECK_GL_ERROR("AddTranslationScreenCoordinates", "gluUnProject");
388    gluUnProject(dx, -dy, 0.0, modelview_matrix, projection_matrix, viewport,
389                 &x, &y, &z);
390    CHECK_GL_ERROR("AddTranslationScreenCoordinates", "gluUnProject (2)");
391
392    // Apply the translation.
393    AddTranslation(x - x0, y - y0, z - z0);
394}
395
396void GLACanvas::SetVolumeDiameter(glaCoord diameter)
397{
398    // Set the size of the data drawing volume by giving the diameter of the
399    // smallest sphere containing it.
400
401    m_VolumeDiameter = max(1.0, diameter);
402}
403
404void GLACanvas::StartDrawing()
405{
406    // Prepare for a redraw operation.
407
408    SetCurrent();
409    glDepthMask(true);
410}
411
412void GLACanvas::EnableSmoothPolygons()
413{
414    // Prepare for drawing smoothly-shaded polygons.
415    // Only use this when required (in particular lines in lists may not be
416    // coloured correctly when this is enabled).
417
418    glShadeModel(GL_SMOOTH);
419    //glEnable(GL_COLOR_MATERIAL);
420
421    //GLfloat diffuseMaterial[] = { 0.3, 0.3, 0.3, 1.0 };
422    //GLfloat mat_specular[] = { 0.5, 0.5, 0.5, 1.0 };
423    //wxSize size = GetSize();
424    //double aspect = double(size.GetWidth()) / double(size.GetHeight());
425#if 0
426    GLfloat light_position[] = { m_VolumeDiameter * 0.5 - 5.0,
427                                 m_VolumeDiameter * 0.5 / aspect - 5.0,
428                                 m_VolumeDiameter * 0.5 + 5.0,
429                                 0.0 };
430#endif
431
432  //  glMaterialfv(GL_FRONT, GL_AMBIENT, diffuseMaterial);
433//    glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
434//    glMaterialf(GL_FRONT, GL_SHININESS, 10.0);
435/*
436    GLfloat light_ambient[] = { 1.0, 1.0, 1.0, 1.0 };
437    glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
438    glLightfv(GL_LIGHT0, GL_POSITION, light_position);
439    glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
440    //glColorMaterial(GL_BACK, GL_AMBIENT);
441    glEnable(GL_LIGHTING);
442    glEnable(GL_LIGHT0);*/
443}
444
445void GLACanvas::DisableSmoothPolygons()
446{
447    glShadeModel(GL_FLAT);
448  //  glDisable(GL_LIGHT0);
449//    glDisable(GL_LIGHTING);
450//    glDisable(GL_COLOR_MATERIAL);
451}
452
453void GLACanvas::PlaceNormal(glaCoord x, glaCoord y, glaCoord z)
454{
455    // Add a normal (for polygons etc.)
456
457    glNormal3d(x, y, z);
458}
459
460void GLACanvas::SetDataTransform()
461{
462    // Set viewport.  The width and height go to zero when the panel is dragged
463    // right across so we clamp them to be at least 1 to avoid errors from the
464    // opengl calls below.
465    int window_width;
466    int window_height;
467    GetSize(&window_width, &window_height);
468    if (window_height < 1) window_height = 1;
469    if (window_width < 1) window_width = 1;
470    double aspect = double(window_height) / double(window_width);
471
472    glViewport(0, 0, window_width, window_height);
473    CHECK_GL_ERROR("SetDataTransform", "glViewport");
474
475    // Set projection.
476    glMatrixMode(GL_PROJECTION);
477    CHECK_GL_ERROR("SetDataTransform", "glMatrixMode");
478    glLoadIdentity();
479    CHECK_GL_ERROR("SetDataTransform", "glLoadIdentity");
480
481    Double near_plane = 1.0;
482    if (m_Perspective) {
483        Double lr = near_plane * tan(rad(25.0));
484        Double far_plane = m_VolumeDiameter * 5 + near_plane; // FIXME: work out properly
485        Double tb = lr * aspect;
486        glFrustum(-lr, lr, -tb, tb, near_plane, far_plane);
487        CHECK_GL_ERROR("SetViewportAndProjection", "glFrustum");
488    } else {
489        near_plane = 0.0;
490        assert(m_Scale != 0.0);
491        Double lr = m_VolumeDiameter / m_Scale * 0.5;
492        Double far_plane = m_VolumeDiameter + near_plane;
493        Double tb = lr * aspect;
494        glOrtho(-lr, lr, -tb, tb, near_plane, far_plane);
495        CHECK_GL_ERROR("SetViewportAndProjection", "glOrtho");
496    }
497
498    // Set the modelview transform for drawing data.
499    glMatrixMode(GL_MODELVIEW);
500    CHECK_GL_ERROR("SetDataTransform", "glMatrixMode");
501    glLoadIdentity();
502    CHECK_GL_ERROR("SetDataTransform", "glLoadIdentity");
503    if (m_Perspective) {
504        glTranslated(0.0, 0.0, -near_plane);
505    } else {
506        glTranslated(0.0, 0.0, -0.5 * m_VolumeDiameter);
507    }
508    CHECK_GL_ERROR("SetDataTransform", "glTranslated");
509    // Get axes the correct way around (z upwards, y into screen)
510    glRotated(-90.0, 1.0, 0.0, 0.0);
511    CHECK_GL_ERROR("SetDataTransform", "glRotated");
512    m_Rotation.CopyToOpenGL();
513    CHECK_GL_ERROR("SetDataTransform", "CopyToOpenGL");
514    if (m_Perspective) {
515        glTranslated(m_Translation.x, m_Translation.y, m_Translation.z);
516        CHECK_GL_ERROR("SetDataTransform", "glTranslated");
517    }
518
519    // Save projection matrix.
520    glGetDoublev(GL_PROJECTION_MATRIX, projection_matrix);
521    CHECK_GL_ERROR("SetDataTransform", "glGetDoublev");
522
523    // Save viewport coordinates.
524    glGetIntegerv(GL_VIEWPORT, viewport);
525    CHECK_GL_ERROR("SetDataTransform", "glGetIntegerv");
526
527    // Save modelview matrix.
528    glGetDoublev(GL_MODELVIEW_MATRIX, modelview_matrix);
529    CHECK_GL_ERROR("SetDataTransform", "glGetDoublev");
530
531    if (!m_Perspective) {
532        // Adjust the translation so we don't change the Z position of the model
533        Double X, Y, Z;
534        gluProject(m_Translation.x, m_Translation.y, m_Translation.z,
535                   modelview_matrix, projection_matrix, viewport,
536                   &X, &Y, &Z);
537        Double Tx, Ty, Tz;
538        gluUnProject(X, Y, 0.5, modelview_matrix, projection_matrix, viewport,
539                     &Tx, &Ty, &Tz);
540        glMatrixMode(GL_MODELVIEW);
541        glTranslated(Tx, Ty, Tz);
542        CHECK_GL_ERROR("SetDataTransform", "glTranslated");
543        glGetDoublev(GL_MODELVIEW_MATRIX, modelview_matrix);
544    }
545
546    glEnable(GL_DEPTH_TEST);
547    CHECK_GL_ERROR("SetDataTransform", "glEnable GL_DEPTH_TEST");
548
549    if (m_Textured) {
550        glBindTexture(GL_TEXTURE_2D, m_Texture);
551        glEnable(GL_TEXTURE_2D);
552        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
553        CHECK_GL_ERROR("ToggleTextured", "glTexParameteri GL_TEXTURE_WRAP_S");
554        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
555        CHECK_GL_ERROR("ToggleTextured", "glTexParameteri GL_TEXTURE_WRAP_T");
556        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
557        CHECK_GL_ERROR("ToggleTextured", "glTexParameteri GL_TEXTURE_MAG_FILTER");
558        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
559                        GL_LINEAR_MIPMAP_LINEAR);
560        CHECK_GL_ERROR("ToggleTextured", "glTexParameteri GL_TEXTURE_MIN_FILTER");
561        glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
562    } else {
563        glDisable(GL_TEXTURE_2D);
564    }
565    if (m_Fog) {
566        glFogf(GL_FOG_START, near_plane);
567        glFogf(GL_FOG_END, near_plane + m_VolumeDiameter);
568        glEnable(GL_FOG);
569    } else {
570        glDisable(GL_FOG);
571    }
572
573    if (m_AntiAlias) {
574        glEnable(GL_LINE_SMOOTH);
575        glEnable(GL_BLEND);
576        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
577    } else {
578        glDisable(GL_LINE_SMOOTH);
579    }
580}
581
582void GLACanvas::SetIndicatorTransform()
583{
584    list_flags |= NEVER_CACHE;
585
586    // Set the modelview transform and projection for drawing indicators.
587    wxSize size = GetSize();
588    int window_width = max(size.GetWidth(), 1);
589    int window_height = max(size.GetHeight(), 1);
590
591    // No modelview transform
592    glMatrixMode(GL_MODELVIEW);
593    CHECK_GL_ERROR("SetIndicatorTransform", "glMatrixMode");
594    glLoadIdentity();
595    CHECK_GL_ERROR("SetIndicatorTransform", "glLoadIdentity");
596
597    glDisable(GL_DEPTH_TEST);
598    CHECK_GL_ERROR("SetIndicatorTransform", "glDisable GL_DEPTH_TEST");
599    glDisable(GL_FOG);
600    CHECK_GL_ERROR("SetIndicatorTransform", "glDisable GL_FOG");
601
602    // And just a simple 2D projection
603    glMatrixMode(GL_PROJECTION);
604    CHECK_GL_ERROR("SetIndicatorTransform", "glMatrixMode");
605    glLoadIdentity();
606    CHECK_GL_ERROR("SetIndicatorTransform", "glLoadIdentity (2)");
607    gluOrtho2D(0, window_width, 0, window_height);
608    CHECK_GL_ERROR("SetIndicatorTransform", "gluOrtho2D");
609
610    glDisable(GL_TEXTURE_2D);
611    CHECK_GL_ERROR("SetIndicatorTransform", "glDisable GL_TEXTURE_2D");
612    glDisable(GL_BLEND);
613    CHECK_GL_ERROR("SetIndicatorTransform", "glDisable GL_BLEND");
614    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
615    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
616    glAlphaFunc(GL_GREATER, 0.5f);
617    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
618    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
619    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
620    CHECK_GL_ERROR("SetIndicatorTransform", "various");
621}
622
623void GLACanvas::FinishDrawing()
624{
625    // Complete a redraw operation.
626
627//    glFlush();
628//    CHECK_GL_ERROR("FinishDrawing", "glFlush");
629    SwapBuffers();
630}
631
632void GLACanvas::DrawList(unsigned int l)
633{
634    // FIXME: uncomment to disable use of lists for debugging:
635    // GenerateList(l); return;
636    if (l >= drawing_lists.size()) drawing_lists.resize(l + 1);
637
638    // We generate the OpenGL lists lazily to minimise delays on startup.
639    // So check if we need to generate the OpenGL list now.
640    if (!drawing_lists[l] && !drawing_lists[l].test_flag(NEVER_CACHE)) {
641        // Create a new OpenGL list to hold this sequence of drawing
642        // operations.
643        GLuint list = glGenLists(1);
644#ifdef GLA_DEBUG
645        printf("new list #%d: %d... ", l, list);
646        m_Vertices = 0;
647#endif
648        CHECK_GL_ERROR("CreateList", "glGenLists");
649        if (list == 0) {
650            // If we can't create a list, fall back to just drawing directly.
651            GenerateList(l);
652            return;
653        }
654
655        // We should have 256 lists for font drawing and a dozen or so for 2D
656        // and 3D lists.  So something is amiss if we've generated 1000 lists,
657        // probably a infinite loop in the lazy list mechanism.
658        assert(list < 1000);
659
660        // http://www.opengl.org/resources/faq/technical/displaylist.htm
661        // advises:
662        // "Stay away from GL_COMPILE_AND_EXECUTE mode. Instead, create the
663        // list using GL_COMPILE mode, then execute it with glCallList()."
664        glNewList(list, GL_COMPILE);
665        CHECK_GL_ERROR("CreateList", "glNewList");
666        // Clear list_flags so that we can note what conditions to invalidate
667        // the cached OpenGL list on.
668        list_flags = 0;
669        GenerateList(l);
670        glEndList();
671#ifdef GLA_DEBUG
672        printf("done (%d vertices)\n", m_Vertices);
673#endif
674        CHECK_GL_ERROR("CreateList", "glEndList");
675        drawing_lists[l] = GLAList(list, list_flags);
676    }
677
678    if (drawing_lists[l].test_flag(NEVER_CACHE)) {
679        // That list can't be cached.
680        GenerateList(l);
681    } else {
682        // Perform the operations specified by the OpenGL display list.
683        drawing_lists[l].DrawList();
684    }
685}
686
687void GLACanvas::DrawList2D(unsigned int l, glaCoord x, glaCoord y, Double rotation)
688{
689    glMatrixMode(GL_PROJECTION);
690    CHECK_GL_ERROR("DrawList2D", "glMatrixMode");
691    glPushMatrix();
692    CHECK_GL_ERROR("DrawList2D", "glPushMatrix");
693    glTranslated(x, y, 0);
694    CHECK_GL_ERROR("DrawList2D", "glTranslated");
695    if (rotation != 0.0) {
696        glRotated(rotation, 0, 0, -1);
697        CHECK_GL_ERROR("DrawList2D", "glRotated");
698    }
699    DrawList(l);
700    glMatrixMode(GL_PROJECTION);
701    CHECK_GL_ERROR("DrawList2D", "glMatrixMode 2");
702    glPopMatrix();
703    CHECK_GL_ERROR("DrawList2D", "glPopMatrix");
704}
705
706void GLACanvas::InvalidateList(unsigned int l)
707{
708    if (l < drawing_lists.size() && drawing_lists[l]) {
709        // Delete any existing OpenGL list.
710        drawing_lists[l].InvalidateList();
711    }
712}
713
714void GLACanvas::SetBackgroundColour(float red, float green, float blue)
715{
716    // Set the background colour of the canvas.
717
718    glClearColor(red, green, blue, 1.0);
719    CHECK_GL_ERROR("SetBackgroundColour", "glClearColor");
720}
721
722void GLACanvas::SetColour(const GLAPen& pen, double rgb_scale)
723{
724    // Set the colour for subsequent operations.
725    glColor3f(pen.GetRed() * rgb_scale, pen.GetGreen() * rgb_scale,
726              pen.GetBlue() * rgb_scale);
727}
728
729void GLACanvas::SetColour(const GLAPen& pen)
730{
731    // Set the colour for subsequent operations.
732    glColor3dv(pen.components);
733}
734
735void GLACanvas::SetColour(gla_colour colour)
736{
737    // Set the colour for subsequent operations.
738    glColor3ubv(&COLOURS[colour].r);
739}
740
741void GLACanvas::DrawText(glaCoord x, glaCoord y, glaCoord z, const wxString& str)
742{
743    // Draw a text string on the current buffer in the current font.
744#ifdef USE_FNT
745    GLdouble X, Y, Z;
746    if (!gluProject(x, y, z, modelview_matrix, projection_matrix, viewport,
747                    &X, &Y, &Z)) return;
748    // Only draw text which is in front of the viewer.
749    if (Z > 0) DrawIndicatorText((int)X, (int)Y, str);
750#else
751    glRasterPos3d(x, y, z);
752    CHECK_GL_ERROR("DrawText", "glRasterPos3d");
753
754#ifdef FREEGLUT
755    glutBitmapString(m_Font, (const unsigned char *)str.c_str());
756    CHECK_GL_ERROR("DrawText", "glutBitmapString");
757#else
758    for (size_t pos = 0; pos < str.length(); pos++) {
759        glutBitmapCharacter(m_Font, int((unsigned char)str[pos]));
760        CHECK_GL_ERROR("DrawText", "glutBitmapCharacter");
761    }
762#endif
763#endif
764}
765
766void GLACanvas::DrawIndicatorText(int x, int y, const wxString& str)
767{
768#ifdef USE_FNT
769    glPushAttrib(GL_ENABLE_BIT);
770    glEnable(GL_ALPHA_TEST);
771    glEnable(GL_TEXTURE_2D);
772    glDisable(GL_DEPTH_TEST);
773    m_Font.puts(x, y, str.c_str());
774    glPopAttrib();
775#else
776    DrawText(x, y, 0.0, str);
777#endif
778}
779
780void GLACanvas::GetTextExtent(const wxString& str, int * x_ext, int * y_ext)
781{
782#ifdef USE_FNT
783    m_Font.getTextExtent(str.c_str(), x_ext, y_ext);
784#else
785    if (x_ext) {
786#if 1
787        *x_ext = glutBitmapLength(m_Font, (const unsigned char *)str.c_str());
788        CHECK_GL_ERROR("GetTextExtent", "glutBitmapLength");
789#else
790        *x_ext = 0;
791        for (size_t pos = 0; pos < str.length(); pos++) {
792            x_ext += glutBitmapWidth(m_Font, int((unsigned char)str[pos]));
793            CHECK_GL_ERROR("GetTextExtent", "glutBitmapWidth");
794        }
795#endif
796    }
797    if (y_ext) *y_ext = m_FontSize + 2;
798#endif
799}
800
801void GLACanvas::BeginQuadrilaterals()
802{
803    // Commence drawing of quadrilaterals.
804
805    glBegin(GL_QUADS);
806}
807
808void GLACanvas::EndQuadrilaterals()
809{
810    // Finish drawing of quadrilaterals.
811
812    glEnd();
813}
814
815void GLACanvas::BeginLines()
816{
817    // Commence drawing of a set of lines.
818
819    glBegin(GL_LINES);
820}
821
822void GLACanvas::EndLines()
823{
824    // Finish drawing of a set of lines.
825
826    glEnd();
827}
828
829void GLACanvas::BeginTriangles()
830{
831    // Commence drawing of a set of triangles.
832
833    glBegin(GL_TRIANGLES);
834}
835
836void GLACanvas::EndTriangles()
837{
838    // Finish drawing of a set of triangles.
839
840    glEnd();
841}
842
843void GLACanvas::BeginTriangleStrip()
844{
845    // Commence drawing of a triangle strip.
846
847    glBegin(GL_TRIANGLE_STRIP);
848}
849
850void GLACanvas::EndTriangleStrip()
851{
852    // Finish drawing of a triangle strip.
853
854    glEnd();
855}
856
857void GLACanvas::BeginPolyline()
858{
859    // Commence drawing of a polyline.
860
861    glBegin(GL_LINE_STRIP);
862}
863
864void GLACanvas::EndPolyline()
865{
866    // Finish drawing of a polyline.
867
868    glEnd();
869}
870
871void GLACanvas::BeginPolygon()
872{
873    // Commence drawing of a polygon.
874
875    glBegin(GL_POLYGON);
876}
877
878void GLACanvas::EndPolygon()
879{
880    // Finish drawing of a polygon.
881
882    glEnd();
883}
884
885void GLACanvas::PlaceVertex(glaCoord x, glaCoord y, glaCoord z)
886{
887    // Place a vertex for the current object being drawn.
888
889#ifdef GLA_DEBUG
890    m_Vertices++;
891#endif
892    glVertex3d(x, y, z);
893}
894
895void GLACanvas::PlaceIndicatorVertex(glaCoord x, glaCoord y)
896{
897    // Place a vertex for the current indicator object being drawn.
898
899    PlaceVertex(x, y, 0.0);
900}
901
902void GLACanvas::BeginBlobs()
903{
904    if (glpoint_ok) {
905        // Commence drawing of a set of blobs.
906        glEnable(GL_ALPHA_TEST);
907        CHECK_GL_ERROR("BeginBlobs", "glEnable GL_ALPHA_TEST");
908        glEnable(GL_POINT_SMOOTH);
909        CHECK_GL_ERROR("BeginBlobs", "glEnable GL_POINT_SMOOTH");
910        glBegin(GL_POINTS);
911    } else {
912        glPushAttrib(GL_TRANSFORM_BIT|GL_VIEWPORT_BIT|GL_ENABLE_BIT);
913        CHECK_GL_ERROR("BeginBlobs", "glPushAttrib");
914        SetIndicatorTransform();
915        glEnable(GL_DEPTH_TEST);
916        CHECK_GL_ERROR("BeginBlobs", "glEnable GL_DEPTH_TEST");
917    }
918}
919
920void GLACanvas::EndBlobs()
921{
922    if (glpoint_ok) {
923        // Finish drawing of a set of blobs.
924        glEnd();
925        CHECK_GL_ERROR("EndBlobs", "GL_POINTS");
926        glDisable(GL_POINT_SMOOTH);
927        CHECK_GL_ERROR("EndBlobs", "glDisable GL_POINT_SMOOTH");
928        glDisable(GL_ALPHA_TEST);
929        CHECK_GL_ERROR("EndBlobs", "glDisable GL_ALPHA_TEST");
930    } else {
931        glPopAttrib();
932        CHECK_GL_ERROR("EndBlobs", "glPopAttrib");
933    }
934}
935
936void GLACanvas::DrawBlob(glaCoord x, glaCoord y, glaCoord z)
937{
938    if (glpoint_ok) {
939        // Draw a marker.
940        PlaceVertex(x, y, z);
941    } else {
942        Double X, Y, Z;
943        if (!Transform(x, y, z, &X, &Y, &Z)) {
944            printf("bad transform\n");
945            return;
946        }
947        // Stuff behind us (in perspective view) will get clipped,
948        // but we can save effort with a cheap check here.
949        if (Z <= 0) return;
950
951        // Draw an filled circle.
952        assert(m_Quadric);
953        glTranslated(X, Y, Z);
954        CHECK_GL_ERROR("glTranslated 1", "DrawBlob");
955        gluDisk(m_Quadric, 0, BLOB_DIAMETER * 0.5, 8, 1);
956        CHECK_GL_ERROR("gluDisk", "DrawBlob");
957        glTranslated(-X, -Y, -Z);
958        CHECK_GL_ERROR("glTranslated 2", "DrawBlob");
959#ifdef GLA_DEBUG
960        m_Vertices++;
961#endif
962    }
963}
964
965void GLACanvas::BeginCrosses()
966{
967    // Plot crosses.
968    if (glpoint_sprite) {
969        glPushAttrib(GL_ENABLE_BIT|GL_POINT_BIT);
970        glBindTexture(GL_TEXTURE_2D, m_CrossTexture);
971        glEnable(GL_ALPHA_TEST);
972        glPointSize(8);
973        glTexEnvf(GL_POINT_SPRITE_ARB, GL_COORD_REPLACE_ARB, GL_TRUE);
974        glEnable(GL_TEXTURE_2D);
975        glEnable(GL_POINT_SPRITE_ARB);
976        glBegin(GL_POINTS);
977    } else {
978        // To get the crosses to appear at a constant size and orientation on
979        // screen, we plot them in the Indicator transform coordinates (which
980        // unfortunately means they can't be usefully put in an opengl display
981        // list).
982        glPushAttrib(GL_TRANSFORM_BIT|GL_VIEWPORT_BIT|GL_ENABLE_BIT);
983        CHECK_GL_ERROR("BeginCrosses", "glPushAttrib");
984        SetIndicatorTransform();
985        glEnable(GL_DEPTH_TEST);
986        CHECK_GL_ERROR("BeginBlobs", "glEnable GL_DEPTH_TEST");
987        glBegin(GL_LINES);
988    }
989}
990
991void GLACanvas::EndCrosses()
992{
993    glEnd();
994    glPopAttrib();
995    CHECK_GL_ERROR("EndCrosses", "glPopAttrib");
996}
997
998void GLACanvas::DrawCross(glaCoord x, glaCoord y, glaCoord z)
999{
1000    if (glpoint_sprite) {
1001        PlaceVertex(x, y, z);
1002    } else {
1003        Double X, Y, Z;
1004        if (!Transform(x, y, z, &X, &Y, &Z)) {
1005            printf("bad transform\n");
1006            return;
1007        }
1008        // Stuff behind us (in perspective view) will get clipped,
1009        // but we can save effort with a cheap check here.
1010        if (Z > 0) {
1011            // Round to integers before adding on the offsets for the
1012            // cross arms to avoid uneven crosses.
1013            X = rint(X);
1014            Y = rint(Y);
1015            PlaceVertex(X - 3, Y - 3, Z);
1016            PlaceVertex(X + 3, Y + 3, Z);
1017            PlaceVertex(X - 3, Y + 3, Z);
1018            PlaceVertex(X + 3, Y - 3, Z);
1019        }
1020    }
1021}
1022
1023void GLACanvas::DrawRing(glaCoord x, glaCoord y)
1024{
1025    // Draw an unfilled circle
1026    const Double radius = 4;
1027    assert(m_Quadric);
1028    glTranslated(x, y, 0.0);
1029    CHECK_GL_ERROR("DrawRing", "glTranslated");
1030    gluDisk(m_Quadric, radius - 1.0, radius, 12, 1);
1031    CHECK_GL_ERROR("DrawRing", "gluDisk");
1032    glTranslated(-x, -y, 0.0);
1033    CHECK_GL_ERROR("DrawRing", "glTranslated");
1034}
1035
1036void GLACanvas::DrawRectangle(gla_colour edge, gla_colour fill,
1037                              glaCoord x0, glaCoord y0, glaCoord w, glaCoord h)
1038{
1039    // Draw a filled rectangle with an edge in the indicator plane.
1040    // (x0, y0) specify the bottom-left corner of the rectangle and (w, h) the
1041    // size.
1042
1043    SetColour(fill);
1044    BeginQuadrilaterals();
1045    PlaceIndicatorVertex(x0, y0);
1046    PlaceIndicatorVertex(x0 + w, y0);
1047    PlaceIndicatorVertex(x0 + w, y0 + h);
1048    PlaceIndicatorVertex(x0, y0 + h);
1049    EndQuadrilaterals();
1050
1051    if (edge != fill) {
1052        SetColour(edge);
1053        BeginLines();
1054        PlaceIndicatorVertex(x0, y0);
1055        PlaceIndicatorVertex(x0 + w, y0);
1056        PlaceIndicatorVertex(x0 + w, y0 + h);
1057        PlaceIndicatorVertex(x0, y0 + h);
1058        EndLines();
1059    }
1060}
1061
1062void
1063GLACanvas::DrawShadedRectangle(const GLAPen & fill_bot, const GLAPen & fill_top,
1064                               glaCoord x0, glaCoord y0,
1065                               glaCoord w, glaCoord h)
1066{
1067    // Draw a graduated filled rectangle in the indicator plane.
1068    // (x0, y0) specify the bottom-left corner of the rectangle and (w, h) the
1069    // size.
1070
1071    glShadeModel(GL_SMOOTH);
1072    BeginQuadrilaterals();
1073    SetColour(fill_bot);
1074    PlaceIndicatorVertex(x0, y0);
1075    PlaceIndicatorVertex(x0 + w, y0);
1076    SetColour(fill_top);
1077    PlaceIndicatorVertex(x0 + w, y0 + h);
1078    PlaceIndicatorVertex(x0, y0 + h);
1079    EndQuadrilaterals();
1080    glShadeModel(GL_FLAT);
1081}
1082
1083void GLACanvas::DrawCircle(gla_colour edge, gla_colour fill,
1084                           glaCoord cx, glaCoord cy, glaCoord radius)
1085{
1086    // Draw a filled circle with an edge.
1087    SetColour(fill);
1088    glTranslated(cx, cy, 0.0);
1089    CHECK_GL_ERROR("DrawCircle", "glTranslated");
1090    assert(m_Quadric);
1091    gluDisk(m_Quadric, 0.0, radius, 36, 1);
1092    CHECK_GL_ERROR("DrawCircle", "gluDisk");
1093    SetColour(edge);
1094    gluDisk(m_Quadric, radius - 1.0, radius, 36, 1);
1095    CHECK_GL_ERROR("DrawCircle", "gluDisk (2)");
1096    glTranslated(-cx, -cy, 0.0);
1097    CHECK_GL_ERROR("DrawCircle", "glTranslated (2)");
1098}
1099
1100void GLACanvas::DrawSemicircle(gla_colour edge, gla_colour fill,
1101                               glaCoord cx, glaCoord cy,
1102                               glaCoord radius, glaCoord start)
1103{
1104    // Draw a filled semicircle with an edge.
1105    // The semicircle extends from "start" deg to "start"+180 deg (increasing
1106    // clockwise, 0 deg upwards).
1107    SetColour(fill);
1108    glTranslated(cx, cy, 0.0);
1109    CHECK_GL_ERROR("DrawCircle", "glTranslated");
1110    assert(m_Quadric);
1111    gluPartialDisk(m_Quadric, 0.0, radius, 36, 1, start, 180.0);
1112    CHECK_GL_ERROR("DrawCircle", "gluPartialDisk");
1113    SetColour(edge);
1114    gluPartialDisk(m_Quadric, radius - 1.0, radius, 36, 1, start, 180.0);
1115    CHECK_GL_ERROR("DrawCircle", "gluPartialDisk (2)");
1116    glTranslated(-cx, -cy, 0.0);
1117    CHECK_GL_ERROR("DrawCircle", "glTranslated (2)");
1118}
1119
1120void GLACanvas::DrawTriangle(gla_colour edge, gla_colour fill, GLAPoint* points)
1121{
1122    // Draw a filled triangle with an edge.
1123
1124    SetColour(fill);
1125    BeginTriangles();
1126    PlaceIndicatorVertex(points[0].GetX(), points[0].GetY());
1127    PlaceIndicatorVertex(points[1].GetX(), points[1].GetY());
1128    PlaceIndicatorVertex(points[2].GetX(), points[2].GetY());
1129    EndTriangles();
1130
1131    SetColour(edge);
1132    BeginPolyline();
1133    PlaceIndicatorVertex(points[0].GetX(), points[0].GetY());
1134    PlaceIndicatorVertex(points[1].GetX(), points[1].GetY());
1135    PlaceIndicatorVertex(points[2].GetX(), points[2].GetY());
1136    EndPolyline();
1137}
1138
1139void GLACanvas::EnableDashedLines()
1140{
1141    // Enable dashed lines, and start drawing in them.
1142
1143    glLineStipple(1, 0x3333);
1144    CHECK_GL_ERROR("EnableDashedLines", "glLineStipple");
1145    glEnable(GL_LINE_STIPPLE);
1146    CHECK_GL_ERROR("EnableDashedLines", "glEnable GL_LINE_STIPPLE");
1147}
1148
1149void GLACanvas::DisableDashedLines()
1150{
1151    glDisable(GL_LINE_STIPPLE);
1152    CHECK_GL_ERROR("DisableDashedLines", "glDisable GL_LINE_STIPPLE");
1153}
1154
1155bool GLACanvas::Transform(Double x, Double y, Double z,
1156                          Double* x_out, Double* y_out, Double* z_out) const
1157{
1158    // Convert from data coordinates to screen coordinates.
1159
1160    // Perform the projection.
1161    return gluProject(x, y, z, modelview_matrix, projection_matrix, viewport,
1162                      x_out, y_out, z_out);
1163}
1164
1165void GLACanvas::ReverseTransform(Double x, Double y,
1166                                 Double* x_out, Double* y_out, Double* z_out) const
1167{
1168    // Convert from screen coordinates to data coordinates.
1169
1170    // Perform the projection.
1171    gluUnProject(x, y, 0.0, modelview_matrix, projection_matrix, viewport,
1172                 x_out, y_out, z_out);
1173    CHECK_GL_ERROR("ReverseTransform", "gluUnProject");
1174}
1175
1176Double GLACanvas::SurveyUnitsAcrossViewport() const
1177{
1178    // Measure the current viewport in survey units, taking into account the
1179    // current display scale.
1180
1181    assert(m_Scale != 0.0);
1182    list_flags |= INVALIDATE_ON_SCALE;
1183    return m_VolumeDiameter / m_Scale;
1184}
1185
1186void GLACanvas::ToggleTextured()
1187{
1188    m_Textured = !m_Textured;
1189    if (m_Textured && m_Texture == 0) {
1190        glGenTextures(1, &m_Texture);
1191        CHECK_GL_ERROR("ToggleTextured", "glGenTextures");
1192
1193        glBindTexture(GL_TEXTURE_2D, m_Texture);
1194        CHECK_GL_ERROR("ToggleTextured", "glBindTexture");
1195
1196        ::wxInitAllImageHandlers();
1197        wxImage img;
1198        if (!img.LoadFile(wxString(msg_cfgpth()) + wxCONFIG_PATH_SEPARATOR +
1199                          wxString("icons") + wxCONFIG_PATH_SEPARATOR +
1200                          wxString("texture.png"), wxBITMAP_TYPE_PNG)) {
1201            // FIXME
1202            fprintf(stderr, "Couldn't load image.\n");
1203            exit(1);
1204        }
1205        // Generate mipmaps.
1206        gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, // was GL_LUMINANCE
1207                          img.GetWidth(), img.GetHeight(),
1208                          GL_RGB, GL_UNSIGNED_BYTE, img.GetData());
1209        CHECK_GL_ERROR("ToggleTextured", "gluBuild2DMipmaps");
1210
1211        glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
1212        CHECK_GL_ERROR("ToggleTextured", "glTexEnvi");
1213    }
1214}
1215
1216bool GLACanvas::SaveScreenshot(const wxString & fnm, int type) const
1217{
1218    int width;
1219    int height;
1220    GetSize(&width, &height);
1221    unsigned char *pixels = (unsigned char *)malloc(width * height * 3);
1222    if (!pixels) return false;
1223    glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, (GLvoid *)pixels);
1224    wxImage grab(width, height, pixels);
1225    // FIXME: might be slow to create new image, and uses twice the memory.
1226    // Perhaps flip image inplace ourselves using memcpy?
1227    return grab.Mirror(false).SaveFile(fnm, type);
1228}
Note: See TracBrowser for help on using the repository browser.