source: git/src/gla-gl.cc @ 4b031c0

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 4b031c0 was b05f516, checked in by Olly Betts <olly@…>, 20 years ago

Define GL_POINT_SIZE_MAX if it isn't already defined.

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

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