source: git/src/gla-gl.cc @ 618b01f

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 618b01f was ff8de60, checked in by Olly Betts <olly@…>, 20 years ago

Fixed infinite loop.

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