source: git/src/gla-gl.cc @ 6b088f6

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 6b088f6 was 185d793, checked in by Olly Betts <olly@…>, 20 years ago

aven: OpenGL 2.0 always includes support for point sprites so rework
our for them to include that knowledge.

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

  • Property mode set to 100644
File size: 35.9 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    // Point sprites provide an easy, fast way for us to draw crosses by
306    // texture mapping GL points.
307    //
308    // If we have OpenGL > 2.0 then we definitely have GL_POINT_SPRITE.
309    // Otherwise see if we have the GL_ARB_point_sprite or GL_NV_point_sprite
310    // extensions.
311    //
312    // The symbolic constants GL_POINT_SPRITE, GL_POINT_SPRITE_ARB, and
313    // GL_POINT_SPRITE_NV all give the same number so it doesn't matter
314    // which we use.
315    float maxSize = 0.0f;
316    glGetFloatv(GL_POINT_SIZE_MAX, &maxSize);
317    if (maxSize >= 8) {
318        glpoint_sprite = (atoi((const char *)glGetString(GL_VERSION)) >= 2);
319        if (!glpoint_sprite) {
320            const char * p = (const char *)glGetString(GL_EXTENSIONS);
321            while (true) {
322                if (memcmp(p, "GL_ARB_point_sprite", 19) == 0) {
323                    p += 19;
324                } else if (memcmp(p, "GL_NV_point_sprite", 18) == 0) {
325                    p += 18;
326                } else {
327                    continue;
328                }
329                if (*p == '\0' || *p == ' ') {
330                    glpoint_sprite = true;
331                    break;
332                }
333                p = strchr(p + 1, ' ');
334                if (!p) break;
335                ++p;
336            }
337        }
338    }
339
340    if (glpoint_sprite) {
341        glGenTextures(1, &m_CrossTexture);
342        glBindTexture(GL_TEXTURE_2D, m_CrossTexture);
343        const unsigned char crossteximage[128] = {
344            255,255,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,255,255,
345              0,  0,255,255,  0,  0,  0,  0,  0,  0,  0,  0,255,255,  0,  0,
346              0,  0,  0,  0,255,255,  0,  0,  0,  0,255,255,  0,  0,  0,  0,
347              0,  0,  0,  0,  0,  0,255,255,255,255,  0,  0,  0,  0,  0,  0,
348              0,  0,  0,  0,  0,  0,255,255,255,255,  0,  0,  0,  0,  0,  0,
349              0,  0,  0,  0,255,255,  0,  0,  0,  0,255,255,  0,  0,  0,  0,
350              0,  0,255,255,  0,  0,  0,  0,  0,  0,  0,  0,255,255,  0,  0,
351            255,255,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,255,255
352        };
353        glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
354        glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
355        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
356        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
357        glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, 8, 8, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, (GLvoid *)crossteximage);
358        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
359        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
360    }
361    //if (glpoint_ok) printf("Using GL_POINTS for blobs\n");
362    //if (glpoint_sprite) printf("Using GL_POINT_SPRITE* for crosses");
363}
364
365void GLACanvas::Clear()
366{
367    // Clear the canvas.
368
369    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
370    CHECK_GL_ERROR("Clear", "glClear");
371}
372
373void GLACanvas::SetRotation(const Quaternion& q)
374{
375    m_Rotation = q;
376}
377
378void GLACanvas::SetScale(Double scale)
379{
380    if (scale != m_Scale) {
381        vector<GLAList>::iterator i;
382        for (i = drawing_lists.begin(); i != drawing_lists.end(); ++i) {
383            if (*i && i->test_flag(INVALIDATE_ON_SCALE)) i->InvalidateList();
384        }
385
386        m_Scale = scale;
387    }
388}
389
390void GLACanvas::SetTranslation(Double x, Double y, Double z)
391{
392    m_Translation.x = x;
393    m_Translation.y = y;
394    m_Translation.z = z;
395}
396
397void GLACanvas::AddTranslation(Double x, Double y, Double z)
398{
399    m_Translation.x += x;
400    m_Translation.y += y;
401    m_Translation.z += z;
402}
403
404void GLACanvas::AddTranslationScreenCoordinates(int dx, int dy)
405{
406    // Translate the data by a given amount, specified in screen coordinates.
407
408    // Find out how far the translation takes us in data coordinates.
409    SetDataTransform();
410
411    Double x0, y0, z0;
412    Double x, y, z;
413    gluUnProject(0.0, 0.0, 0.0, modelview_matrix, projection_matrix, viewport,
414                 &x0, &y0, &z0);
415    CHECK_GL_ERROR("AddTranslationScreenCoordinates", "gluUnProject");
416    gluUnProject(dx, -dy, 0.0, modelview_matrix, projection_matrix, viewport,
417                 &x, &y, &z);
418    CHECK_GL_ERROR("AddTranslationScreenCoordinates", "gluUnProject (2)");
419
420    // Apply the translation.
421    AddTranslation(x - x0, y - y0, z - z0);
422}
423
424void GLACanvas::SetVolumeDiameter(glaCoord diameter)
425{
426    // Set the size of the data drawing volume by giving the diameter of the
427    // smallest sphere containing it.
428
429    m_VolumeDiameter = max(1.0, diameter);
430}
431
432void GLACanvas::StartDrawing()
433{
434    // Prepare for a redraw operation.
435
436    SetCurrent();
437    glDepthMask(true);
438}
439
440void GLACanvas::EnableSmoothPolygons()
441{
442    // Prepare for drawing smoothly-shaded polygons.
443    // Only use this when required (in particular lines in lists may not be
444    // coloured correctly when this is enabled).
445
446    glShadeModel(GL_SMOOTH);
447    //glEnable(GL_COLOR_MATERIAL);
448
449    //GLfloat diffuseMaterial[] = { 0.3, 0.3, 0.3, 1.0 };
450    //GLfloat mat_specular[] = { 0.5, 0.5, 0.5, 1.0 };
451    //wxSize size = GetSize();
452    //double aspect = double(size.GetWidth()) / double(size.GetHeight());
453#if 0
454    GLfloat light_position[] = { m_VolumeDiameter * 0.5 - 5.0,
455                                 m_VolumeDiameter * 0.5 / aspect - 5.0,
456                                 m_VolumeDiameter * 0.5 + 5.0,
457                                 0.0 };
458#endif
459
460  //  glMaterialfv(GL_FRONT, GL_AMBIENT, diffuseMaterial);
461//    glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
462//    glMaterialf(GL_FRONT, GL_SHININESS, 10.0);
463/*
464    GLfloat light_ambient[] = { 1.0, 1.0, 1.0, 1.0 };
465    glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
466    glLightfv(GL_LIGHT0, GL_POSITION, light_position);
467    glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
468    //glColorMaterial(GL_BACK, GL_AMBIENT);
469    glEnable(GL_LIGHTING);
470    glEnable(GL_LIGHT0);*/
471}
472
473void GLACanvas::DisableSmoothPolygons()
474{
475    glShadeModel(GL_FLAT);
476  //  glDisable(GL_LIGHT0);
477//    glDisable(GL_LIGHTING);
478//    glDisable(GL_COLOR_MATERIAL);
479}
480
481void GLACanvas::PlaceNormal(glaCoord x, glaCoord y, glaCoord z)
482{
483    // Add a normal (for polygons etc.)
484
485    glNormal3d(x, y, z);
486}
487
488void GLACanvas::SetDataTransform()
489{
490    // Set viewport.  The width and height go to zero when the panel is dragged
491    // right across so we clamp them to be at least 1 to avoid errors from the
492    // opengl calls below.
493    int window_width;
494    int window_height;
495    GetSize(&window_width, &window_height);
496    if (window_height < 1) window_height = 1;
497    if (window_width < 1) window_width = 1;
498    double aspect = double(window_height) / double(window_width);
499
500    glViewport(0, 0, window_width, window_height);
501    CHECK_GL_ERROR("SetDataTransform", "glViewport");
502
503    // Set projection.
504    glMatrixMode(GL_PROJECTION);
505    CHECK_GL_ERROR("SetDataTransform", "glMatrixMode");
506    glLoadIdentity();
507    CHECK_GL_ERROR("SetDataTransform", "glLoadIdentity");
508
509    Double near_plane = 1.0;
510    if (m_Perspective) {
511        Double lr = near_plane * tan(rad(25.0));
512        Double far_plane = m_VolumeDiameter * 5 + near_plane; // FIXME: work out properly
513        Double tb = lr * aspect;
514        glFrustum(-lr, lr, -tb, tb, near_plane, far_plane);
515        CHECK_GL_ERROR("SetViewportAndProjection", "glFrustum");
516    } else {
517        near_plane = 0.0;
518        assert(m_Scale != 0.0);
519        Double lr = m_VolumeDiameter / m_Scale * 0.5;
520        Double far_plane = m_VolumeDiameter + near_plane;
521        Double tb = lr * aspect;
522        glOrtho(-lr, lr, -tb, tb, near_plane, far_plane);
523        CHECK_GL_ERROR("SetViewportAndProjection", "glOrtho");
524    }
525
526    // Set the modelview transform for drawing data.
527    glMatrixMode(GL_MODELVIEW);
528    CHECK_GL_ERROR("SetDataTransform", "glMatrixMode");
529    glLoadIdentity();
530    CHECK_GL_ERROR("SetDataTransform", "glLoadIdentity");
531    if (m_Perspective) {
532        glTranslated(0.0, 0.0, -near_plane);
533    } else {
534        glTranslated(0.0, 0.0, -0.5 * m_VolumeDiameter);
535    }
536    CHECK_GL_ERROR("SetDataTransform", "glTranslated");
537    // Get axes the correct way around (z upwards, y into screen)
538    glRotated(-90.0, 1.0, 0.0, 0.0);
539    CHECK_GL_ERROR("SetDataTransform", "glRotated");
540    m_Rotation.CopyToOpenGL();
541    CHECK_GL_ERROR("SetDataTransform", "CopyToOpenGL");
542    if (m_Perspective) {
543        glTranslated(m_Translation.x, m_Translation.y, m_Translation.z);
544        CHECK_GL_ERROR("SetDataTransform", "glTranslated");
545    }
546
547    // Save projection matrix.
548    glGetDoublev(GL_PROJECTION_MATRIX, projection_matrix);
549    CHECK_GL_ERROR("SetDataTransform", "glGetDoublev");
550
551    // Save viewport coordinates.
552    glGetIntegerv(GL_VIEWPORT, viewport);
553    CHECK_GL_ERROR("SetDataTransform", "glGetIntegerv");
554
555    // Save modelview matrix.
556    glGetDoublev(GL_MODELVIEW_MATRIX, modelview_matrix);
557    CHECK_GL_ERROR("SetDataTransform", "glGetDoublev");
558
559    if (!m_Perspective) {
560        // Adjust the translation so we don't change the Z position of the model
561        Double X, Y, Z;
562        gluProject(m_Translation.x, m_Translation.y, m_Translation.z,
563                   modelview_matrix, projection_matrix, viewport,
564                   &X, &Y, &Z);
565        Double Tx, Ty, Tz;
566        gluUnProject(X, Y, 0.5, modelview_matrix, projection_matrix, viewport,
567                     &Tx, &Ty, &Tz);
568        glMatrixMode(GL_MODELVIEW);
569        glTranslated(Tx, Ty, Tz);
570        CHECK_GL_ERROR("SetDataTransform", "glTranslated");
571        glGetDoublev(GL_MODELVIEW_MATRIX, modelview_matrix);
572    }
573
574    glEnable(GL_DEPTH_TEST);
575    CHECK_GL_ERROR("SetDataTransform", "glEnable GL_DEPTH_TEST");
576
577    if (m_Textured) {
578        glBindTexture(GL_TEXTURE_2D, m_Texture);
579        glEnable(GL_TEXTURE_2D);
580        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
581        CHECK_GL_ERROR("ToggleTextured", "glTexParameteri GL_TEXTURE_WRAP_S");
582        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
583        CHECK_GL_ERROR("ToggleTextured", "glTexParameteri GL_TEXTURE_WRAP_T");
584        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
585        CHECK_GL_ERROR("ToggleTextured", "glTexParameteri GL_TEXTURE_MAG_FILTER");
586        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
587                        GL_LINEAR_MIPMAP_LINEAR);
588        CHECK_GL_ERROR("ToggleTextured", "glTexParameteri GL_TEXTURE_MIN_FILTER");
589        glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
590    } else {
591        glDisable(GL_TEXTURE_2D);
592    }
593    if (m_Fog) {
594        glFogf(GL_FOG_START, near_plane);
595        glFogf(GL_FOG_END, near_plane + m_VolumeDiameter);
596        glEnable(GL_FOG);
597    } else {
598        glDisable(GL_FOG);
599    }
600
601    if (m_AntiAlias) {
602        glEnable(GL_LINE_SMOOTH);
603        glEnable(GL_BLEND);
604        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
605    } else {
606        glDisable(GL_LINE_SMOOTH);
607    }
608}
609
610void GLACanvas::SetIndicatorTransform()
611{
612    list_flags |= NEVER_CACHE;
613
614    // Set the modelview transform and projection for drawing indicators.
615    wxSize size = GetSize();
616    int window_width = max(size.GetWidth(), 1);
617    int window_height = max(size.GetHeight(), 1);
618
619    // No modelview transform
620    glMatrixMode(GL_MODELVIEW);
621    CHECK_GL_ERROR("SetIndicatorTransform", "glMatrixMode");
622    glLoadIdentity();
623    CHECK_GL_ERROR("SetIndicatorTransform", "glLoadIdentity");
624
625    glDisable(GL_DEPTH_TEST);
626    CHECK_GL_ERROR("SetIndicatorTransform", "glDisable GL_DEPTH_TEST");
627    glDisable(GL_FOG);
628    CHECK_GL_ERROR("SetIndicatorTransform", "glDisable GL_FOG");
629
630    // And just a simple 2D projection
631    glMatrixMode(GL_PROJECTION);
632    CHECK_GL_ERROR("SetIndicatorTransform", "glMatrixMode");
633    glLoadIdentity();
634    CHECK_GL_ERROR("SetIndicatorTransform", "glLoadIdentity (2)");
635    gluOrtho2D(0, window_width, 0, window_height);
636    CHECK_GL_ERROR("SetIndicatorTransform", "gluOrtho2D");
637
638    glDisable(GL_TEXTURE_2D);
639    CHECK_GL_ERROR("SetIndicatorTransform", "glDisable GL_TEXTURE_2D");
640    glDisable(GL_BLEND);
641    CHECK_GL_ERROR("SetIndicatorTransform", "glDisable GL_BLEND");
642    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
643    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
644    glAlphaFunc(GL_GREATER, 0.5f);
645    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
646    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
647    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
648    CHECK_GL_ERROR("SetIndicatorTransform", "various");
649}
650
651void GLACanvas::FinishDrawing()
652{
653    // Complete a redraw operation.
654
655//    glFlush();
656//    CHECK_GL_ERROR("FinishDrawing", "glFlush");
657    SwapBuffers();
658}
659
660void GLACanvas::DrawList(unsigned int l)
661{
662    // FIXME: uncomment to disable use of lists for debugging:
663    // GenerateList(l); return;
664    if (l >= drawing_lists.size()) drawing_lists.resize(l + 1);
665
666    // We generate the OpenGL lists lazily to minimise delays on startup.
667    // So check if we need to generate the OpenGL list now.
668    if (!drawing_lists[l] && !drawing_lists[l].test_flag(NEVER_CACHE)) {
669        // Create a new OpenGL list to hold this sequence of drawing
670        // operations.
671        GLuint list = glGenLists(1);
672#ifdef GLA_DEBUG
673        printf("new list #%d: %d... ", l, list);
674        m_Vertices = 0;
675#endif
676        CHECK_GL_ERROR("CreateList", "glGenLists");
677        if (list == 0) {
678            // If we can't create a list, fall back to just drawing directly.
679            GenerateList(l);
680            return;
681        }
682
683        // We should have 256 lists for font drawing and a dozen or so for 2D
684        // and 3D lists.  So something is amiss if we've generated 1000 lists,
685        // probably a infinite loop in the lazy list mechanism.
686        assert(list < 1000);
687
688        // http://www.opengl.org/resources/faq/technical/displaylist.htm
689        // advises:
690        // "Stay away from GL_COMPILE_AND_EXECUTE mode. Instead, create the
691        // list using GL_COMPILE mode, then execute it with glCallList()."
692        glNewList(list, GL_COMPILE);
693        CHECK_GL_ERROR("CreateList", "glNewList");
694        // Clear list_flags so that we can note what conditions to invalidate
695        // the cached OpenGL list on.
696        list_flags = 0;
697        GenerateList(l);
698        glEndList();
699#ifdef GLA_DEBUG
700        printf("done (%d vertices)\n", m_Vertices);
701#endif
702        CHECK_GL_ERROR("CreateList", "glEndList");
703        drawing_lists[l] = GLAList(list, list_flags);
704    }
705
706    if (drawing_lists[l].test_flag(NEVER_CACHE)) {
707        // That list can't be cached.
708        GenerateList(l);
709    } else {
710        // Perform the operations specified by the OpenGL display list.
711        drawing_lists[l].DrawList();
712    }
713}
714
715void GLACanvas::DrawList2D(unsigned int l, glaCoord x, glaCoord y, Double rotation)
716{
717    glMatrixMode(GL_PROJECTION);
718    CHECK_GL_ERROR("DrawList2D", "glMatrixMode");
719    glPushMatrix();
720    CHECK_GL_ERROR("DrawList2D", "glPushMatrix");
721    glTranslated(x, y, 0);
722    CHECK_GL_ERROR("DrawList2D", "glTranslated");
723    if (rotation != 0.0) {
724        glRotated(rotation, 0, 0, -1);
725        CHECK_GL_ERROR("DrawList2D", "glRotated");
726    }
727    DrawList(l);
728    glMatrixMode(GL_PROJECTION);
729    CHECK_GL_ERROR("DrawList2D", "glMatrixMode 2");
730    glPopMatrix();
731    CHECK_GL_ERROR("DrawList2D", "glPopMatrix");
732}
733
734void GLACanvas::InvalidateList(unsigned int l)
735{
736    if (l < drawing_lists.size() && drawing_lists[l]) {
737        // Delete any existing OpenGL list.
738        drawing_lists[l].InvalidateList();
739    }
740}
741
742void GLACanvas::SetBackgroundColour(float red, float green, float blue)
743{
744    // Set the background colour of the canvas.
745
746    glClearColor(red, green, blue, 1.0);
747    CHECK_GL_ERROR("SetBackgroundColour", "glClearColor");
748}
749
750void GLACanvas::SetColour(const GLAPen& pen, double rgb_scale)
751{
752    // Set the colour for subsequent operations.
753    glColor3f(pen.GetRed() * rgb_scale, pen.GetGreen() * rgb_scale,
754              pen.GetBlue() * rgb_scale);
755}
756
757void GLACanvas::SetColour(const GLAPen& pen)
758{
759    // Set the colour for subsequent operations.
760    glColor3dv(pen.components);
761}
762
763void GLACanvas::SetColour(gla_colour colour)
764{
765    // Set the colour for subsequent operations.
766    glColor3ubv(&COLOURS[colour].r);
767}
768
769void GLACanvas::DrawText(glaCoord x, glaCoord y, glaCoord z, const wxString& str)
770{
771    // Draw a text string on the current buffer in the current font.
772#ifdef USE_FNT
773    GLdouble X, Y, Z;
774    if (!gluProject(x, y, z, modelview_matrix, projection_matrix, viewport,
775                    &X, &Y, &Z)) return;
776    // Only draw text which is in front of the viewer.
777    if (Z > 0) DrawIndicatorText((int)X, (int)Y, str);
778#else
779    glRasterPos3d(x, y, z);
780    CHECK_GL_ERROR("DrawText", "glRasterPos3d");
781
782#ifdef FREEGLUT
783    glutBitmapString(m_Font, (const unsigned char *)str.c_str());
784    CHECK_GL_ERROR("DrawText", "glutBitmapString");
785#else
786    for (size_t pos = 0; pos < str.length(); pos++) {
787        glutBitmapCharacter(m_Font, int((unsigned char)str[pos]));
788        CHECK_GL_ERROR("DrawText", "glutBitmapCharacter");
789    }
790#endif
791#endif
792}
793
794void GLACanvas::DrawIndicatorText(int x, int y, const wxString& str)
795{
796#ifdef USE_FNT
797    glPushAttrib(GL_ENABLE_BIT);
798    glEnable(GL_ALPHA_TEST);
799    glEnable(GL_TEXTURE_2D);
800    glDisable(GL_DEPTH_TEST);
801    m_Font.puts(x, y, str.c_str());
802    glPopAttrib();
803#else
804    DrawText(x, y, 0.0, str);
805#endif
806}
807
808void GLACanvas::GetTextExtent(const wxString& str, int * x_ext, int * y_ext)
809{
810#ifdef USE_FNT
811    m_Font.getTextExtent(str.c_str(), x_ext, y_ext);
812#else
813    if (x_ext) {
814#if 1
815        *x_ext = glutBitmapLength(m_Font, (const unsigned char *)str.c_str());
816        CHECK_GL_ERROR("GetTextExtent", "glutBitmapLength");
817#else
818        *x_ext = 0;
819        for (size_t pos = 0; pos < str.length(); pos++) {
820            x_ext += glutBitmapWidth(m_Font, int((unsigned char)str[pos]));
821            CHECK_GL_ERROR("GetTextExtent", "glutBitmapWidth");
822        }
823#endif
824    }
825    if (y_ext) *y_ext = m_FontSize + 2;
826#endif
827}
828
829void GLACanvas::BeginQuadrilaterals()
830{
831    // Commence drawing of quadrilaterals.
832
833    glBegin(GL_QUADS);
834}
835
836void GLACanvas::EndQuadrilaterals()
837{
838    // Finish drawing of quadrilaterals.
839
840    glEnd();
841}
842
843void GLACanvas::BeginLines()
844{
845    // Commence drawing of a set of lines.
846
847    glBegin(GL_LINES);
848}
849
850void GLACanvas::EndLines()
851{
852    // Finish drawing of a set of lines.
853
854    glEnd();
855}
856
857void GLACanvas::BeginTriangles()
858{
859    // Commence drawing of a set of triangles.
860
861    glBegin(GL_TRIANGLES);
862}
863
864void GLACanvas::EndTriangles()
865{
866    // Finish drawing of a set of triangles.
867
868    glEnd();
869}
870
871void GLACanvas::BeginTriangleStrip()
872{
873    // Commence drawing of a triangle strip.
874
875    glBegin(GL_TRIANGLE_STRIP);
876}
877
878void GLACanvas::EndTriangleStrip()
879{
880    // Finish drawing of a triangle strip.
881
882    glEnd();
883}
884
885void GLACanvas::BeginPolyline()
886{
887    // Commence drawing of a polyline.
888
889    glBegin(GL_LINE_STRIP);
890}
891
892void GLACanvas::EndPolyline()
893{
894    // Finish drawing of a polyline.
895
896    glEnd();
897}
898
899void GLACanvas::BeginPolygon()
900{
901    // Commence drawing of a polygon.
902
903    glBegin(GL_POLYGON);
904}
905
906void GLACanvas::EndPolygon()
907{
908    // Finish drawing of a polygon.
909
910    glEnd();
911}
912
913void GLACanvas::PlaceVertex(glaCoord x, glaCoord y, glaCoord z)
914{
915    // Place a vertex for the current object being drawn.
916
917#ifdef GLA_DEBUG
918    m_Vertices++;
919#endif
920    glVertex3d(x, y, z);
921}
922
923void GLACanvas::PlaceIndicatorVertex(glaCoord x, glaCoord y)
924{
925    // Place a vertex for the current indicator object being drawn.
926
927    PlaceVertex(x, y, 0.0);
928}
929
930void GLACanvas::BeginBlobs()
931{
932    if (glpoint_ok) {
933        // Commence drawing of a set of blobs.
934        glEnable(GL_ALPHA_TEST);
935        CHECK_GL_ERROR("BeginBlobs", "glEnable GL_ALPHA_TEST");
936        glEnable(GL_POINT_SMOOTH);
937        CHECK_GL_ERROR("BeginBlobs", "glEnable GL_POINT_SMOOTH");
938        glBegin(GL_POINTS);
939    } else {
940        glPushAttrib(GL_TRANSFORM_BIT|GL_VIEWPORT_BIT|GL_ENABLE_BIT);
941        CHECK_GL_ERROR("BeginBlobs", "glPushAttrib");
942        SetIndicatorTransform();
943        glEnable(GL_DEPTH_TEST);
944        CHECK_GL_ERROR("BeginBlobs", "glEnable GL_DEPTH_TEST");
945    }
946}
947
948void GLACanvas::EndBlobs()
949{
950    if (glpoint_ok) {
951        // Finish drawing of a set of blobs.
952        glEnd();
953        CHECK_GL_ERROR("EndBlobs", "GL_POINTS");
954        glDisable(GL_POINT_SMOOTH);
955        CHECK_GL_ERROR("EndBlobs", "glDisable GL_POINT_SMOOTH");
956        glDisable(GL_ALPHA_TEST);
957        CHECK_GL_ERROR("EndBlobs", "glDisable GL_ALPHA_TEST");
958    } else {
959        glPopAttrib();
960        CHECK_GL_ERROR("EndBlobs", "glPopAttrib");
961    }
962}
963
964void GLACanvas::DrawBlob(glaCoord x, glaCoord y, glaCoord z)
965{
966    if (glpoint_ok) {
967        // Draw a marker.
968        PlaceVertex(x, y, z);
969    } else {
970        Double X, Y, Z;
971        if (!Transform(x, y, z, &X, &Y, &Z)) {
972            printf("bad transform\n");
973            return;
974        }
975        // Stuff behind us (in perspective view) will get clipped,
976        // but we can save effort with a cheap check here.
977        if (Z <= 0) return;
978
979        // Draw an filled circle.
980        assert(m_Quadric);
981        glTranslated(X, Y, Z);
982        CHECK_GL_ERROR("glTranslated 1", "DrawBlob");
983        gluDisk(m_Quadric, 0, BLOB_DIAMETER * 0.5, 8, 1);
984        CHECK_GL_ERROR("gluDisk", "DrawBlob");
985        glTranslated(-X, -Y, -Z);
986        CHECK_GL_ERROR("glTranslated 2", "DrawBlob");
987#ifdef GLA_DEBUG
988        m_Vertices++;
989#endif
990    }
991}
992
993void GLACanvas::BeginCrosses()
994{
995    // Plot crosses.
996    if (glpoint_sprite) {
997        glPushAttrib(GL_ENABLE_BIT|GL_POINT_BIT);
998        glBindTexture(GL_TEXTURE_2D, m_CrossTexture);
999        glEnable(GL_ALPHA_TEST);
1000        glPointSize(8);
1001        glTexEnvf(GL_POINT_SPRITE_ARB, GL_COORD_REPLACE_ARB, GL_TRUE);
1002        glEnable(GL_TEXTURE_2D);
1003        glEnable(GL_POINT_SPRITE_ARB);
1004        glBegin(GL_POINTS);
1005    } else {
1006        // To get the crosses to appear at a constant size and orientation on
1007        // screen, we plot them in the Indicator transform coordinates (which
1008        // unfortunately means they can't be usefully put in an opengl display
1009        // list).
1010        glPushAttrib(GL_TRANSFORM_BIT|GL_VIEWPORT_BIT|GL_ENABLE_BIT);
1011        CHECK_GL_ERROR("BeginCrosses", "glPushAttrib");
1012        SetIndicatorTransform();
1013        glEnable(GL_DEPTH_TEST);
1014        CHECK_GL_ERROR("BeginBlobs", "glEnable GL_DEPTH_TEST");
1015        glBegin(GL_LINES);
1016    }
1017}
1018
1019void GLACanvas::EndCrosses()
1020{
1021    glEnd();
1022    glPopAttrib();
1023    CHECK_GL_ERROR("EndCrosses", "glPopAttrib");
1024}
1025
1026void GLACanvas::DrawCross(glaCoord x, glaCoord y, glaCoord z)
1027{
1028    if (glpoint_sprite) {
1029        PlaceVertex(x, y, z);
1030    } else {
1031        Double X, Y, Z;
1032        if (!Transform(x, y, z, &X, &Y, &Z)) {
1033            printf("bad transform\n");
1034            return;
1035        }
1036        // Stuff behind us (in perspective view) will get clipped,
1037        // but we can save effort with a cheap check here.
1038        if (Z > 0) {
1039            // Round to integers before adding on the offsets for the
1040            // cross arms to avoid uneven crosses.
1041            X = rint(X);
1042            Y = rint(Y);
1043            PlaceVertex(X - 3, Y - 3, Z);
1044            PlaceVertex(X + 3, Y + 3, Z);
1045            PlaceVertex(X - 3, Y + 3, Z);
1046            PlaceVertex(X + 3, Y - 3, Z);
1047        }
1048    }
1049}
1050
1051void GLACanvas::DrawRing(glaCoord x, glaCoord y)
1052{
1053    // Draw an unfilled circle
1054    const Double radius = 4;
1055    assert(m_Quadric);
1056    glTranslated(x, y, 0.0);
1057    CHECK_GL_ERROR("DrawRing", "glTranslated");
1058    gluDisk(m_Quadric, radius - 1.0, radius, 12, 1);
1059    CHECK_GL_ERROR("DrawRing", "gluDisk");
1060    glTranslated(-x, -y, 0.0);
1061    CHECK_GL_ERROR("DrawRing", "glTranslated");
1062}
1063
1064void GLACanvas::DrawRectangle(gla_colour edge, gla_colour fill,
1065                              glaCoord x0, glaCoord y0, glaCoord w, glaCoord h)
1066{
1067    // Draw a filled rectangle with an edge in the indicator plane.
1068    // (x0, y0) specify the bottom-left corner of the rectangle and (w, h) the
1069    // size.
1070
1071    SetColour(fill);
1072    BeginQuadrilaterals();
1073    PlaceIndicatorVertex(x0, y0);
1074    PlaceIndicatorVertex(x0 + w, y0);
1075    PlaceIndicatorVertex(x0 + w, y0 + h);
1076    PlaceIndicatorVertex(x0, y0 + h);
1077    EndQuadrilaterals();
1078
1079    if (edge != fill) {
1080        SetColour(edge);
1081        BeginLines();
1082        PlaceIndicatorVertex(x0, y0);
1083        PlaceIndicatorVertex(x0 + w, y0);
1084        PlaceIndicatorVertex(x0 + w, y0 + h);
1085        PlaceIndicatorVertex(x0, y0 + h);
1086        EndLines();
1087    }
1088}
1089
1090void
1091GLACanvas::DrawShadedRectangle(const GLAPen & fill_bot, const GLAPen & fill_top,
1092                               glaCoord x0, glaCoord y0,
1093                               glaCoord w, glaCoord h)
1094{
1095    // Draw a graduated filled rectangle in the indicator plane.
1096    // (x0, y0) specify the bottom-left corner of the rectangle and (w, h) the
1097    // size.
1098
1099    glShadeModel(GL_SMOOTH);
1100    BeginQuadrilaterals();
1101    SetColour(fill_bot);
1102    PlaceIndicatorVertex(x0, y0);
1103    PlaceIndicatorVertex(x0 + w, y0);
1104    SetColour(fill_top);
1105    PlaceIndicatorVertex(x0 + w, y0 + h);
1106    PlaceIndicatorVertex(x0, y0 + h);
1107    EndQuadrilaterals();
1108    glShadeModel(GL_FLAT);
1109}
1110
1111void GLACanvas::DrawCircle(gla_colour edge, gla_colour fill,
1112                           glaCoord cx, glaCoord cy, glaCoord radius)
1113{
1114    // Draw a filled circle with an edge.
1115    SetColour(fill);
1116    glTranslated(cx, cy, 0.0);
1117    CHECK_GL_ERROR("DrawCircle", "glTranslated");
1118    assert(m_Quadric);
1119    gluDisk(m_Quadric, 0.0, radius, 36, 1);
1120    CHECK_GL_ERROR("DrawCircle", "gluDisk");
1121    SetColour(edge);
1122    gluDisk(m_Quadric, radius - 1.0, radius, 36, 1);
1123    CHECK_GL_ERROR("DrawCircle", "gluDisk (2)");
1124    glTranslated(-cx, -cy, 0.0);
1125    CHECK_GL_ERROR("DrawCircle", "glTranslated (2)");
1126}
1127
1128void GLACanvas::DrawSemicircle(gla_colour edge, gla_colour fill,
1129                               glaCoord cx, glaCoord cy,
1130                               glaCoord radius, glaCoord start)
1131{
1132    // Draw a filled semicircle with an edge.
1133    // The semicircle extends from "start" deg to "start"+180 deg (increasing
1134    // clockwise, 0 deg upwards).
1135    SetColour(fill);
1136    glTranslated(cx, cy, 0.0);
1137    CHECK_GL_ERROR("DrawCircle", "glTranslated");
1138    assert(m_Quadric);
1139    gluPartialDisk(m_Quadric, 0.0, radius, 36, 1, start, 180.0);
1140    CHECK_GL_ERROR("DrawCircle", "gluPartialDisk");
1141    SetColour(edge);
1142    gluPartialDisk(m_Quadric, radius - 1.0, radius, 36, 1, start, 180.0);
1143    CHECK_GL_ERROR("DrawCircle", "gluPartialDisk (2)");
1144    glTranslated(-cx, -cy, 0.0);
1145    CHECK_GL_ERROR("DrawCircle", "glTranslated (2)");
1146}
1147
1148void GLACanvas::DrawTriangle(gla_colour edge, gla_colour fill, GLAPoint* points)
1149{
1150    // Draw a filled triangle with an edge.
1151
1152    SetColour(fill);
1153    BeginTriangles();
1154    PlaceIndicatorVertex(points[0].GetX(), points[0].GetY());
1155    PlaceIndicatorVertex(points[1].GetX(), points[1].GetY());
1156    PlaceIndicatorVertex(points[2].GetX(), points[2].GetY());
1157    EndTriangles();
1158
1159    SetColour(edge);
1160    BeginPolyline();
1161    PlaceIndicatorVertex(points[0].GetX(), points[0].GetY());
1162    PlaceIndicatorVertex(points[1].GetX(), points[1].GetY());
1163    PlaceIndicatorVertex(points[2].GetX(), points[2].GetY());
1164    EndPolyline();
1165}
1166
1167void GLACanvas::EnableDashedLines()
1168{
1169    // Enable dashed lines, and start drawing in them.
1170
1171    glLineStipple(1, 0x3333);
1172    CHECK_GL_ERROR("EnableDashedLines", "glLineStipple");
1173    glEnable(GL_LINE_STIPPLE);
1174    CHECK_GL_ERROR("EnableDashedLines", "glEnable GL_LINE_STIPPLE");
1175}
1176
1177void GLACanvas::DisableDashedLines()
1178{
1179    glDisable(GL_LINE_STIPPLE);
1180    CHECK_GL_ERROR("DisableDashedLines", "glDisable GL_LINE_STIPPLE");
1181}
1182
1183bool GLACanvas::Transform(Double x, Double y, Double z,
1184                          Double* x_out, Double* y_out, Double* z_out) const
1185{
1186    // Convert from data coordinates to screen coordinates.
1187
1188    // Perform the projection.
1189    return gluProject(x, y, z, modelview_matrix, projection_matrix, viewport,
1190                      x_out, y_out, z_out);
1191}
1192
1193void GLACanvas::ReverseTransform(Double x, Double y,
1194                                 Double* x_out, Double* y_out, Double* z_out) const
1195{
1196    // Convert from screen coordinates to data coordinates.
1197
1198    // Perform the projection.
1199    gluUnProject(x, y, 0.0, modelview_matrix, projection_matrix, viewport,
1200                 x_out, y_out, z_out);
1201    CHECK_GL_ERROR("ReverseTransform", "gluUnProject");
1202}
1203
1204Double GLACanvas::SurveyUnitsAcrossViewport() const
1205{
1206    // Measure the current viewport in survey units, taking into account the
1207    // current display scale.
1208
1209    assert(m_Scale != 0.0);
1210    list_flags |= INVALIDATE_ON_SCALE;
1211    return m_VolumeDiameter / m_Scale;
1212}
1213
1214void GLACanvas::ToggleTextured()
1215{
1216    m_Textured = !m_Textured;
1217    if (m_Textured && m_Texture == 0) {
1218        glGenTextures(1, &m_Texture);
1219        CHECK_GL_ERROR("ToggleTextured", "glGenTextures");
1220
1221        glBindTexture(GL_TEXTURE_2D, m_Texture);
1222        CHECK_GL_ERROR("ToggleTextured", "glBindTexture");
1223
1224        ::wxInitAllImageHandlers();
1225        wxImage img;
1226        if (!img.LoadFile(wxString(msg_cfgpth()) + wxCONFIG_PATH_SEPARATOR +
1227                          wxString("icons") + wxCONFIG_PATH_SEPARATOR +
1228                          wxString("texture.png"), wxBITMAP_TYPE_PNG)) {
1229            // FIXME
1230            fprintf(stderr, "Couldn't load image.\n");
1231            exit(1);
1232        }
1233        // Generate mipmaps.
1234        gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, // was GL_LUMINANCE
1235                          img.GetWidth(), img.GetHeight(),
1236                          GL_RGB, GL_UNSIGNED_BYTE, img.GetData());
1237        CHECK_GL_ERROR("ToggleTextured", "gluBuild2DMipmaps");
1238
1239        glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
1240        CHECK_GL_ERROR("ToggleTextured", "glTexEnvi");
1241    }
1242}
1243
1244bool GLACanvas::SaveScreenshot(const wxString & fnm, int type) const
1245{
1246    int width;
1247    int height;
1248    GetSize(&width, &height);
1249    unsigned char *pixels = (unsigned char *)malloc(width * height * 3);
1250    if (!pixels) return false;
1251    glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, (GLvoid *)pixels);
1252    wxImage grab(width, height, pixels);
1253    // FIXME: might be slow to create new image, and uses twice the memory.
1254    // Perhaps flip image inplace ourselves using memcpy?
1255    return grab.Mirror(false).SaveFile(fnm, type);
1256}
Note: See TracBrowser for help on using the repository browser.