source: git/src/gla-gl.cc @ 04bf822

RELEASE/1.2debug-cidebug-ci-sanitisersfaster-cavernloglog-selectstereostereo-2025walls-datawalls-data-hanging-as-warningwarn-only-for-hanging-survey
Last change on this file since 04bf822 was 04bf822, checked in by Olly Betts <olly@…>, 15 years ago

src/gla-gl.cc: Use GL_SMOOTH_POINT_SIZE_RANGE and
GL_SMOOTH_POINT_SIZE_GRANULARITY instead of GL_POINT_SIZE_RANGE and
GL_POINT_SIZE_GRANULARITY (unless they aren't defined) as the latter
two are deprecated in OpenGL >= 1.2. If GL_ALIASED_POINT_SIZE_RANGE
is available (OpenGL >= 1.2 again) then report its value in the
about box.

git-svn-id: file:///home/survex-svn/survex/trunk@3520 4b37db11-9a0c-4f06-9ece-9ab7cdaee568

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