source: git/src/gla-gl.cc @ 9baa53a

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 9baa53a was 9baa53a, checked in by Olly Betts <olly@…>, 18 years ago

Add missing CHECK_GL_ERROR() calls and correct some which had the wrong
descriptions.

Use GL_POINT_SPRITE and GL_COORD_REPLACE instead of GL_POINT_SPRITE_ARB
and GL_COORD_REPLACE_ARB. They have the same numeric values, so the
shorter (now official) names are better.

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

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