source: git/src/gla-gl.cc @ ecbc6c18

RELEASE/1.1RELEASE/1.2debug-cidebug-ci-sanitisersstereowalls-data
Last change on this file since ecbc6c18 was ecbc6c18, checked in by Olly Betts <olly@…>, 14 years ago

src/: Update FSF address in licence notices.

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

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