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

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

If we can't create an OpenGL list, fall back to rendering directly.

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

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