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

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

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

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

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

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