source: git/src/gla-gl.cc @ 226b599e

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

src/gla-gl.cc: Add \n to commented out debug message.

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

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