source: git/src/gla-gl.cc @ 78924eb

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 78924eb was 78924eb, checked in by Olly Betts <olly@…>, 19 years ago

Add "error log" window which only appears if there are OpenGL errors and
logs them all so they can be saved and sent in.

Use glPushMatrix and glPopMatrix rather than applying the reverse
transformation to undo.

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

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