source: git/src/gla-gl.cc @ 78beaf1

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 78beaf1 was 3c3ca1d, checked in by Olly Betts <olly@…>, 20 years ago

Make GLACanvas::Transform and GLACanvas::ReverseTransform? const.

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

  • Property mode set to 100644
File size: 34.6 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 = 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        assert(list != 0);
650
651        // http://www.opengl.org/resources/faq/technical/displaylist.htm
652        // advises:
653        // "Stay away from GL_COMPILE_AND_EXECUTE mode. Instead, create the
654        // list using GL_COMPILE mode, then execute it with glCallList()."
655        glNewList(list, GL_COMPILE);
656        CHECK_GL_ERROR("CreateList", "glNewList");
657        // Clear list_flags so that we can note what conditions to invalidate
658        // the cached OpenGL list on.
659        list_flags = 0;
660        GenerateList(l);
661        glEndList();
662#ifdef GLA_DEBUG
663        printf("done (%d vertices)\n", m_Vertices);
664#endif
665        CHECK_GL_ERROR("CreateList", "glEndList");
666        drawing_lists[l] = GLAList(list, list_flags);
667    }
668
669    if (drawing_lists[l].test_flag(NEVER_CACHE)) {
670        // That list can't be cached.
671        GenerateList(l);
672    } else {
673        // Perform the operations specified by the OpenGL display list.
674        drawing_lists[l].DrawList();
675    }
676}
677
678void GLACanvas::DrawList2D(unsigned int l, glaCoord x, glaCoord y, Double rotation)
679{
680    glMatrixMode(GL_PROJECTION);
681    CHECK_GL_ERROR("DrawList2D", "glMatrixMode");
682    glPushMatrix();
683    CHECK_GL_ERROR("DrawList2D", "glPushMatrix");
684    glTranslated(x, y, 0);
685    CHECK_GL_ERROR("DrawList2D", "glTranslated");
686    if (rotation != 0.0) {
687        glRotated(rotation, 0, 0, -1);
688        CHECK_GL_ERROR("DrawList2D", "glRotated");
689    }
690    DrawList(l);
691    glMatrixMode(GL_PROJECTION);
692    CHECK_GL_ERROR("DrawList2D", "glMatrixMode 2");
693    glPopMatrix();
694    CHECK_GL_ERROR("DrawList2D", "glPopMatrix");
695}
696
697void GLACanvas::InvalidateList(unsigned int l)
698{
699    if (l < drawing_lists.size() && drawing_lists[l]) {
700        // Delete any existing OpenGL list.
701        drawing_lists[l].InvalidateList();
702    }
703}
704
705void GLACanvas::SetBackgroundColour(float red, float green, float blue)
706{
707    // Set the background colour of the canvas.
708
709    glClearColor(red, green, blue, 1.0);
710    CHECK_GL_ERROR("SetBackgroundColour", "glClearColor");
711}
712
713void GLACanvas::SetColour(const GLAPen& pen, double rgb_scale)
714{
715    // Set the colour for subsequent operations.
716    glColor3f(pen.GetRed() * rgb_scale, pen.GetGreen() * rgb_scale,
717              pen.GetBlue() * rgb_scale);
718}
719
720void GLACanvas::SetColour(const GLAPen& pen)
721{
722    // Set the colour for subsequent operations.
723    glColor3dv(pen.components);
724}
725
726void GLACanvas::SetColour(gla_colour colour)
727{
728    // Set the colour for subsequent operations.
729    glColor3ubv(&COLOURS[colour].r);
730}
731
732void GLACanvas::DrawText(glaCoord x, glaCoord y, glaCoord z, const wxString& str)
733{
734    // Draw a text string on the current buffer in the current font.
735#ifdef USE_FNT
736    GLdouble X, Y, Z;
737    if (!gluProject(x, y, z, modelview_matrix, projection_matrix, viewport,
738                    &X, &Y, &Z)) return;
739    // Only draw text which is in front of the viewer.
740    if (Z > 0) DrawIndicatorText((int)X, (int)Y, str);
741#else
742    glRasterPos3d(x, y, z);
743    CHECK_GL_ERROR("DrawText", "glRasterPos3d");
744
745#ifdef FREEGLUT
746    glutBitmapString(m_Font, (const unsigned char *)str.c_str());
747    CHECK_GL_ERROR("DrawText", "glutBitmapString");
748#else
749    for (size_t pos = 0; pos < str.Length(); pos++) {
750        glutBitmapCharacter(m_Font, int((unsigned char)str[pos]));
751        CHECK_GL_ERROR("DrawText", "glutBitmapCharacter");
752    }
753#endif
754#endif
755}
756
757void GLACanvas::DrawIndicatorText(int x, int y, const wxString& str)
758{
759#ifdef USE_FNT
760    glPushAttrib(GL_ENABLE_BIT);
761    glEnable(GL_ALPHA_TEST);
762    glEnable(GL_TEXTURE_2D);
763    glDisable(GL_DEPTH_TEST);
764    m_Font.puts(x, y, str.c_str());
765    glPopAttrib();
766#else
767    DrawText(x, y, 0.0, str);
768#endif
769}
770
771void GLACanvas::GetTextExtent(const wxString& str, int * x_ext, int * y_ext)
772{
773#ifdef USE_FNT
774    m_Font.getTextExtent(str.c_str(), x_ext, y_ext);
775#else
776    if (x_ext) {
777#if 1
778        *x_ext = glutBitmapLength(m_Font, (const unsigned char *)str.c_str());
779        CHECK_GL_ERROR("GetTextExtent", "glutBitmapLength");
780#else
781        *x_ext = 0;
782        for (size_t pos = 0; pos < str.Length(); pos++) {
783            x_ext += glutBitmapWidth(m_Font, int((unsigned char)str[pos]));
784            CHECK_GL_ERROR("GetTextExtent", "glutBitmapWidth");
785        }
786#endif
787    }
788    if (y_ext) *y_ext = m_FontSize + 2;
789#endif
790}
791
792void GLACanvas::BeginQuadrilaterals()
793{
794    // Commence drawing of quadrilaterals.
795
796    glBegin(GL_QUADS);
797}
798
799void GLACanvas::EndQuadrilaterals()
800{
801    // Finish drawing of quadrilaterals.
802
803    glEnd();
804}
805
806void GLACanvas::BeginLines()
807{
808    // Commence drawing of a set of lines.
809
810    glBegin(GL_LINES);
811}
812
813void GLACanvas::EndLines()
814{
815    // Finish drawing of a set of lines.
816
817    glEnd();
818}
819
820void GLACanvas::BeginTriangles()
821{
822    // Commence drawing of a set of triangles.
823
824    glBegin(GL_TRIANGLES);
825}
826
827void GLACanvas::EndTriangles()
828{
829    // Finish drawing of a set of triangles.
830
831    glEnd();
832}
833
834void GLACanvas::BeginTriangleStrip()
835{
836    // Commence drawing of a triangle strip.
837
838    glBegin(GL_TRIANGLE_STRIP);
839}
840
841void GLACanvas::EndTriangleStrip()
842{
843    // Finish drawing of a triangle strip.
844
845    glEnd();
846}
847
848void GLACanvas::BeginPolyline()
849{
850    // Commence drawing of a polyline.
851
852    glBegin(GL_LINE_STRIP);
853}
854
855void GLACanvas::EndPolyline()
856{
857    // Finish drawing of a polyline.
858
859    glEnd();
860}
861
862void GLACanvas::BeginPolygon()
863{
864    // Commence drawing of a polygon.
865
866    glBegin(GL_POLYGON);
867}
868
869void GLACanvas::EndPolygon()
870{
871    // Finish drawing of a polygon.
872
873    glEnd();
874}
875
876void GLACanvas::PlaceVertex(glaCoord x, glaCoord y, glaCoord z)
877{
878    // Place a vertex for the current object being drawn.
879
880#ifdef GLA_DEBUG
881    m_Vertices++;
882#endif
883    glVertex3d(x, y, z);
884}
885
886void GLACanvas::PlaceIndicatorVertex(glaCoord x, glaCoord y)
887{
888    // Place a vertex for the current indicator object being drawn.
889
890    PlaceVertex(x, y, 0.0);
891}
892
893void GLACanvas::BeginBlobs()
894{
895    if (glpoint_ok) {
896        // Commence drawing of a set of blobs.
897        glEnable(GL_ALPHA_TEST);
898        CHECK_GL_ERROR("BeginBlobs", "glEnable GL_ALPHA_TEST");
899        glEnable(GL_POINT_SMOOTH);
900        CHECK_GL_ERROR("BeginBlobs", "glEnable GL_POINT_SMOOTH");
901        glBegin(GL_POINTS);
902    } else {
903        glPushAttrib(GL_TRANSFORM_BIT|GL_VIEWPORT_BIT);
904        CHECK_GL_ERROR("BeginBlobs", "glPushAttrib");
905        SetIndicatorTransform();
906    }
907}
908
909void GLACanvas::EndBlobs()
910{
911    if (glpoint_ok) {
912        // Finish drawing of a set of blobs.
913        glEnd();
914        CHECK_GL_ERROR("EndBlobs", "GL_POINTS");
915        glDisable(GL_POINT_SMOOTH);
916        CHECK_GL_ERROR("EndBlobs", "glDisable GL_POINT_SMOOTH");
917        glDisable(GL_ALPHA_TEST);
918        CHECK_GL_ERROR("EndBlobs", "glDisable GL_ALPHA_TEST");
919    } else {
920        glPopAttrib();
921        CHECK_GL_ERROR("EndBlobs", "glPopAttrib");
922    }
923}
924
925void GLACanvas::DrawBlob(glaCoord x, glaCoord y, glaCoord z)
926{
927    if (glpoint_ok) {
928        // Draw a marker.
929        PlaceVertex(x, y, z);
930    } else {
931        Double X, Y, Z;
932        if (!Transform(x, y, z, &X, &Y, &Z)) {
933            printf("bad transform\n");
934            return;
935        }
936        // Stuff behind us (in perspective view) will get clipped,
937        // but we can save effort with a cheap check here.
938        if (Z <= 0) return;
939
940        // Draw an filled circle.
941        assert(m_Quadric);
942        glTranslated(X, Y, Z);
943        CHECK_GL_ERROR("glTranslated 1", "DrawBlob");
944        gluDisk(m_Quadric, 0, BLOB_DIAMETER * 0.5, 8, 1);
945        CHECK_GL_ERROR("gluDisk", "DrawBlob");
946        glTranslated(-X, -Y, -Z);
947        CHECK_GL_ERROR("glTranslated 2", "DrawBlob");
948#ifdef GLA_DEBUG
949        m_Vertices++;
950#endif
951    }
952}
953
954void GLACanvas::BeginCrosses()
955{
956    // Plot crosses.
957    if (glpoint_sprite) {
958        glPushAttrib(GL_ENABLE_BIT|GL_POINT_BIT);
959        glBindTexture(GL_TEXTURE_2D, m_CrossTexture);
960        glEnable(GL_ALPHA_TEST);
961        glPointSize(8);
962        glTexEnvf(GL_POINT_SPRITE_ARB, GL_COORD_REPLACE_ARB, GL_TRUE);
963        glEnable(GL_TEXTURE_2D);
964        glEnable(GL_POINT_SPRITE_ARB);
965        glBegin(GL_POINTS);
966    } else {
967        // To get the crosses to appear at a constant size and orientation on
968        // screen, we plot them in the Indicator transform coordinates (which
969        // unfortunately means they can't be usefully put in an opengl display
970        // list).
971        glPushAttrib(GL_TRANSFORM_BIT|GL_VIEWPORT_BIT);
972        CHECK_GL_ERROR("BeginCrosses", "glPushAttrib");
973        SetIndicatorTransform();
974        glBegin(GL_LINES);
975    }
976}
977
978void GLACanvas::EndCrosses()
979{
980    glEnd();
981    glPopAttrib();
982    CHECK_GL_ERROR("EndCrosses", "glPopAttrib");
983}
984
985void GLACanvas::DrawCross(glaCoord x, glaCoord y, glaCoord z)
986{
987    if (glpoint_sprite) {
988        PlaceVertex(x, y, z);
989    } else {
990        Double X, Y, Z;
991        if (!Transform(x, y, z, &X, &Y, &Z)) {
992            printf("bad transform\n");
993            return;
994        }
995        // Stuff behind us (in perspective view) will get clipped,
996        // but we can save effort with a cheap check here.
997        if (Z > 0) {
998            // Round to integers before adding on the offsets for the
999            // cross arms to avoid uneven crosses.
1000            X = rint(X);
1001            Y = rint(Y);
1002            PlaceVertex(X - 3, Y - 3, Z);
1003            PlaceVertex(X + 3, Y + 3, Z);
1004            PlaceVertex(X - 3, Y + 3, Z);
1005            PlaceVertex(X + 3, Y - 3, Z);
1006        }
1007    }
1008}
1009
1010void GLACanvas::DrawRing(glaCoord x, glaCoord y)
1011{
1012    // Draw an unfilled circle
1013    const Double radius = 4;
1014    assert(m_Quadric);
1015    glTranslated(x, y, 0.0);
1016    CHECK_GL_ERROR("DrawRing", "glTranslated");
1017    gluDisk(m_Quadric, radius - 1.0, radius, 12, 1);
1018    CHECK_GL_ERROR("DrawRing", "gluDisk");
1019    glTranslated(-x, -y, 0.0);
1020    CHECK_GL_ERROR("DrawRing", "glTranslated");
1021}
1022
1023void GLACanvas::DrawRectangle(gla_colour edge, gla_colour fill,
1024                              glaCoord x0, glaCoord y0, glaCoord w, glaCoord h)
1025{
1026    // Draw a filled rectangle with an edge in the indicator plane.
1027    // (x0, y0) specify the bottom-left corner of the rectangle and (w, h) the
1028    // size.
1029
1030    SetColour(fill);
1031    BeginQuadrilaterals();
1032    PlaceIndicatorVertex(x0, y0);
1033    PlaceIndicatorVertex(x0 + w, y0);
1034    PlaceIndicatorVertex(x0 + w, y0 + h);
1035    PlaceIndicatorVertex(x0, y0 + h);
1036    EndQuadrilaterals();
1037
1038    if (edge != fill) {
1039        SetColour(edge);
1040        BeginLines();
1041        PlaceIndicatorVertex(x0, y0);
1042        PlaceIndicatorVertex(x0 + w, y0);
1043        PlaceIndicatorVertex(x0 + w, y0 + h);
1044        PlaceIndicatorVertex(x0, y0 + h);
1045        EndLines();
1046    }
1047}
1048
1049void
1050GLACanvas::DrawShadedRectangle(const GLAPen & fill_bot, const GLAPen & fill_top,
1051                               glaCoord x0, glaCoord y0,
1052                               glaCoord w, glaCoord h)
1053{
1054    // Draw a graduated filled rectangle in the indicator plane.
1055    // (x0, y0) specify the bottom-left corner of the rectangle and (w, h) the
1056    // size.
1057
1058    glShadeModel(GL_SMOOTH);
1059    BeginQuadrilaterals();
1060    SetColour(fill_bot);
1061    PlaceIndicatorVertex(x0, y0);
1062    PlaceIndicatorVertex(x0 + w, y0);
1063    SetColour(fill_top);
1064    PlaceIndicatorVertex(x0 + w, y0 + h);
1065    PlaceIndicatorVertex(x0, y0 + h);
1066    EndQuadrilaterals();
1067    glShadeModel(GL_FLAT);
1068}
1069
1070void GLACanvas::DrawCircle(gla_colour edge, gla_colour fill,
1071                           glaCoord cx, glaCoord cy, glaCoord radius)
1072{
1073    // Draw a filled circle with an edge.
1074    SetColour(fill);
1075    glTranslated(cx, cy, 0.0);
1076    CHECK_GL_ERROR("DrawCircle", "glTranslated");
1077    assert(m_Quadric);
1078    gluDisk(m_Quadric, 0.0, radius, 36, 1);
1079    CHECK_GL_ERROR("DrawCircle", "gluDisk");
1080    SetColour(edge);
1081    gluDisk(m_Quadric, radius - 1.0, radius, 36, 1);
1082    CHECK_GL_ERROR("DrawCircle", "gluDisk (2)");
1083    glTranslated(-cx, -cy, 0.0);
1084    CHECK_GL_ERROR("DrawCircle", "glTranslated (2)");
1085}
1086
1087void GLACanvas::DrawSemicircle(gla_colour edge, gla_colour fill,
1088                               glaCoord cx, glaCoord cy,
1089                               glaCoord radius, glaCoord start)
1090{
1091    // Draw a filled semicircle with an edge.
1092    // The semicircle extends from "start" deg to "start"+180 deg (increasing
1093    // clockwise, 0 deg upwards).
1094    SetColour(fill);
1095    glTranslated(cx, cy, 0.0);
1096    CHECK_GL_ERROR("DrawCircle", "glTranslated");
1097    assert(m_Quadric);
1098    gluPartialDisk(m_Quadric, 0.0, radius, 36, 1, start, 180.0);
1099    CHECK_GL_ERROR("DrawCircle", "gluPartialDisk");
1100    SetColour(edge);
1101    gluPartialDisk(m_Quadric, radius - 1.0, radius, 36, 1, start, 180.0);
1102    CHECK_GL_ERROR("DrawCircle", "gluPartialDisk (2)");
1103    glTranslated(-cx, -cy, 0.0);
1104    CHECK_GL_ERROR("DrawCircle", "glTranslated (2)");
1105}
1106
1107void GLACanvas::DrawTriangle(gla_colour edge, gla_colour fill, GLAPoint* points)
1108{
1109    // Draw a filled triangle with an edge.
1110
1111    SetColour(fill);
1112    BeginTriangles();
1113    PlaceIndicatorVertex(points[0].GetX(), points[0].GetY());
1114    PlaceIndicatorVertex(points[1].GetX(), points[1].GetY());
1115    PlaceIndicatorVertex(points[2].GetX(), points[2].GetY());
1116    EndTriangles();
1117
1118    SetColour(edge);
1119    BeginPolyline();
1120    PlaceIndicatorVertex(points[0].GetX(), points[0].GetY());
1121    PlaceIndicatorVertex(points[1].GetX(), points[1].GetY());
1122    PlaceIndicatorVertex(points[2].GetX(), points[2].GetY());
1123    EndPolyline();
1124}
1125
1126void GLACanvas::EnableDashedLines()
1127{
1128    // Enable dashed lines, and start drawing in them.
1129
1130    glLineStipple(1, 0x3333);
1131    CHECK_GL_ERROR("EnableDashedLines", "glLineStipple");
1132    glEnable(GL_LINE_STIPPLE);
1133    CHECK_GL_ERROR("EnableDashedLines", "glEnable GL_LINE_STIPPLE");
1134}
1135
1136void GLACanvas::DisableDashedLines()
1137{
1138    glDisable(GL_LINE_STIPPLE);
1139    CHECK_GL_ERROR("DisableDashedLines", "glDisable GL_LINE_STIPPLE");
1140}
1141
1142bool GLACanvas::Transform(Double x, Double y, Double z,
1143                          Double* x_out, Double* y_out, Double* z_out) const
1144{
1145    // Convert from data coordinates to screen coordinates.
1146
1147    // Perform the projection.
1148    return gluProject(x, y, z, modelview_matrix, projection_matrix, viewport,
1149                      x_out, y_out, z_out);
1150}
1151
1152void GLACanvas::ReverseTransform(Double x, Double y,
1153                                 Double* x_out, Double* y_out, Double* z_out) const
1154{
1155    // Convert from screen coordinates to data coordinates.
1156
1157    // Perform the projection.
1158    gluUnProject(x, y, 0.0, modelview_matrix, projection_matrix, viewport,
1159                 x_out, y_out, z_out);
1160    CHECK_GL_ERROR("ReverseTransform", "gluUnProject");
1161}
1162
1163Double GLACanvas::SurveyUnitsAcrossViewport() const
1164{
1165    // Measure the current viewport in survey units, taking into account the
1166    // current display scale.
1167
1168    assert(m_Scale != 0.0);
1169    list_flags |= INVALIDATE_ON_SCALE;
1170    return m_VolumeDiameter / m_Scale;
1171}
1172
1173void GLACanvas::ToggleTextured()
1174{
1175    m_Textured = !m_Textured;
1176    if (m_Textured && m_Texture == 0) {
1177        glGenTextures(1, &m_Texture);
1178        CHECK_GL_ERROR("ToggleTextured", "glGenTextures");
1179
1180        glBindTexture(GL_TEXTURE_2D, m_Texture);
1181        CHECK_GL_ERROR("ToggleTextured", "glBindTexture");
1182
1183        ::wxInitAllImageHandlers();
1184        wxImage img;
1185        if (!img.LoadFile(wxString(msg_cfgpth()) + wxCONFIG_PATH_SEPARATOR +
1186                          wxString("icons") + wxCONFIG_PATH_SEPARATOR +
1187                          wxString("texture.png"), wxBITMAP_TYPE_PNG)) {
1188            // FIXME
1189            fprintf(stderr, "Couldn't load image.\n");
1190            exit(1);
1191        }
1192        // Generate mipmaps.
1193        gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, // was GL_LUMINANCE
1194                          img.GetWidth(), img.GetHeight(),
1195                          GL_RGB, GL_UNSIGNED_BYTE, img.GetData());
1196        CHECK_GL_ERROR("ToggleTextured", "gluBuild2DMipmaps");
1197
1198        glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
1199        CHECK_GL_ERROR("ToggleTextured", "glTexEnvi");
1200    }
1201}
1202
1203bool GLACanvas::SaveScreenshot(const wxString & fnm, int type) const
1204{
1205    int width;
1206    int height;
1207    GetSize(&width, &height);
1208    unsigned char *pixels = (unsigned char *)malloc(width * height * 3);
1209    if (!pixels) return false;
1210    glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, (GLvoid *)pixels);
1211    wxImage grab(width, height, pixels);
1212    // FIXME: might be slow to create new image, and uses twice the memory.
1213    // Perhaps flip image inplace ourselves using memcpy?
1214    return grab.Mirror(false).SaveFile(fnm, type);
1215}
Note: See TracBrowser for help on using the repository browser.