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

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

src/gla-gl.cc: Fix non-USE_FNT case to work again (it's limited to
ISO-8859-1 characters though).

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

  • Property mode set to 100644
File size: 40.6 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
[4ba80e0]87const double BLOB_DIAMETER = 5.0;
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];
339    glGetDoublev(GL_POINT_SIZE_RANGE, point_size_range);
340    CHECK_GL_ERROR("FirstShow", "glGetDoublev GL_POINT_SIZE_RANGE");
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");
416    //if (glpoint_sprite) printf("Using GL_POINT_SPRITE* for crosses");
[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
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
[5eab797]464    m_VolumeDiameter = max(1.0, diameter);
[56da40e]465}
466
467void GLACanvas::StartDrawing()
468{
469    // Prepare for a redraw operation.
[096e56c]470
[203d2a7]471    SetCurrent();
472    glDepthMask(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
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);
604        Double Tx, Ty, Tz;
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");
[4ba80e0]1001    }
[e633bb1]1002}
[d9b3270]1003
[e633bb1]1004void GLACanvas::EndBlobs()
1005{
[4ba80e0]1006    if (glpoint_ok) {
1007        // Finish drawing of a set of blobs.
1008        glEnd();
1009        CHECK_GL_ERROR("EndBlobs", "GL_POINTS");
1010    }
[78924eb]1011    glPopAttrib();
1012    CHECK_GL_ERROR("EndBlobs", "glPopAttrib");
[e633bb1]1013}
[d9b3270]1014
[e633bb1]1015void GLACanvas::DrawBlob(glaCoord x, glaCoord y, glaCoord z)
1016{
[4ba80e0]1017    if (glpoint_ok) {
1018        // Draw a marker.
1019        PlaceVertex(x, y, z);
1020    } else {
1021        Double X, Y, Z;
[d67450e]1022        if (!Transform(Vector3(x, y, z), &X, &Y, &Z)) {
[4ba80e0]1023            printf("bad transform\n");
1024            return;
1025        }
1026        // Stuff behind us (in perspective view) will get clipped,
1027        // but we can save effort with a cheap check here.
1028        if (Z <= 0) return;
1029
1030        // Draw an filled circle.
1031        assert(m_Quadric);
1032        glTranslated(X, Y, Z);
[9baa53a]1033        CHECK_GL_ERROR("DrawBlob", "glTranslated 1");
[4ba80e0]1034        gluDisk(m_Quadric, 0, BLOB_DIAMETER * 0.5, 8, 1);
[9baa53a]1035        CHECK_GL_ERROR("DrawBlob", "gluDisk");
[4ba80e0]1036        glTranslated(-X, -Y, -Z);
[9baa53a]1037        CHECK_GL_ERROR("DrawBlob", "glTranslated 2");
[78924eb]1038    }
[95ce35f]1039#ifdef GLA_DEBUG
[78924eb]1040    m_Vertices++;
[95ce35f]1041#endif
[429465a]1042}
1043
[81aea4e]1044void GLACanvas::DrawBlob(glaCoord x, glaCoord y)
1045{
1046    if (glpoint_ok) {
1047        // Draw a marker.
1048        PlaceVertex(x, y, 0);
1049    } else {
1050        // Draw an filled circle.
1051        assert(m_Quadric);
1052        glTranslated(x, y, 0);
[9baa53a]1053        CHECK_GL_ERROR("DrawBlob 2", "glTranslated 1");
[81aea4e]1054        gluDisk(m_Quadric, 0, BLOB_DIAMETER * 0.5, 8, 1);
[9baa53a]1055        CHECK_GL_ERROR("DrawBlob 2", "gluDisk");
[81aea4e]1056        glTranslated(-x, -y, 0);
[9baa53a]1057        CHECK_GL_ERROR("DrawBlob 2", "glTranslated 2");
[81aea4e]1058    }
1059#ifdef GLA_DEBUG
1060    m_Vertices++;
1061#endif
1062}
1063
[86fe6e4]1064void GLACanvas::BeginCrosses()
1065{
[95ce35f]1066    // Plot crosses.
1067    if (glpoint_sprite) {
1068        glPushAttrib(GL_ENABLE_BIT|GL_POINT_BIT);
[9baa53a]1069        CHECK_GL_ERROR("BeginCrosses", "glPushAttrib");
[95ce35f]1070        glBindTexture(GL_TEXTURE_2D, m_CrossTexture);
[9baa53a]1071        CHECK_GL_ERROR("BeginCrosses", "glBindTexture");
[95ce35f]1072        glEnable(GL_ALPHA_TEST);
[9baa53a]1073        CHECK_GL_ERROR("BeginCrosses", "glEnable GL_ALPHA_TEST");
[95ce35f]1074        glPointSize(8);
[9baa53a]1075        CHECK_GL_ERROR("BeginCrosses", "glPointSize");
1076        glTexEnvi(GL_POINT_SPRITE, GL_COORD_REPLACE, GL_TRUE);
1077        CHECK_GL_ERROR("BeginCrosses", "glTexEnvi GL_POINT_SPRITE");
[95ce35f]1078        glEnable(GL_TEXTURE_2D);
[9baa53a]1079        CHECK_GL_ERROR("BeginCrosses", "glEnable GL_TEXTURE_2D");
1080        glEnable(GL_POINT_SPRITE);
1081        CHECK_GL_ERROR("BeginCrosses", "glEnable GL_POINT_SPRITE");
[95ce35f]1082        glBegin(GL_POINTS);
1083    } else {
1084        // To get the crosses to appear at a constant size and orientation on
1085        // screen, we plot them in the Indicator transform coordinates (which
1086        // unfortunately means they can't be usefully put in an opengl display
1087        // list).
[6662d02]1088        glPushAttrib(GL_TRANSFORM_BIT|GL_VIEWPORT_BIT|GL_ENABLE_BIT);
[9baa53a]1089        CHECK_GL_ERROR("BeginCrosses", "glPushAttrib 2");
[95ce35f]1090        SetIndicatorTransform();
[6f5f9e5]1091        glEnable(GL_DEPTH_TEST);
[9baa53a]1092        CHECK_GL_ERROR("BeginCrosses", "glEnable GL_DEPTH_TEST");
[95ce35f]1093        glBegin(GL_LINES);
1094    }
[86fe6e4]1095}
1096
1097void GLACanvas::EndCrosses()
1098{
[95ce35f]1099    glEnd();
[9baa53a]1100    if (glpoint_sprite) {
1101        CHECK_GL_ERROR("EndCrosses", "glEnd GL_POINTS");
1102    } else {
1103        CHECK_GL_ERROR("EndCrosses", "glEnd GL_LINES");
1104    }
[86fe6e4]1105    glPopAttrib();
1106    CHECK_GL_ERROR("EndCrosses", "glPopAttrib");
1107}
1108
1109void GLACanvas::DrawCross(glaCoord x, glaCoord y, glaCoord z)
1110{
[95ce35f]1111    if (glpoint_sprite) {
1112        PlaceVertex(x, y, z);
1113    } else {
1114        Double X, Y, Z;
[d67450e]1115        if (!Transform(Vector3(x, y, z), &X, &Y, &Z)) {
[95ce35f]1116            printf("bad transform\n");
1117            return;
1118        }
1119        // Stuff behind us (in perspective view) will get clipped,
1120        // but we can save effort with a cheap check here.
1121        if (Z > 0) {
1122            // Round to integers before adding on the offsets for the
1123            // cross arms to avoid uneven crosses.
1124            X = rint(X);
1125            Y = rint(Y);
1126            PlaceVertex(X - 3, Y - 3, Z);
1127            PlaceVertex(X + 3, Y + 3, Z);
1128            PlaceVertex(X - 3, Y + 3, Z);
1129            PlaceVertex(X + 3, Y - 3, Z);
1130        }
[86fe6e4]1131    }
[78924eb]1132#ifdef GLA_DEBUG
1133    m_Vertices++;
1134#endif
[86fe6e4]1135}
1136
[e633bb1]1137void GLACanvas::DrawRing(glaCoord x, glaCoord y)
[429465a]1138{
1139    // Draw an unfilled circle
[096e56c]1140    const Double radius = 4;
[429465a]1141    assert(m_Quadric);
[78924eb]1142    glMatrixMode(GL_MODELVIEW);
1143    CHECK_GL_ERROR("DrawRing", "glMatrixMode");
1144    glPushMatrix();
1145    CHECK_GL_ERROR("DrawRing", "glPushMatrix");
[429465a]1146    glTranslated(x, y, 0.0);
1147    CHECK_GL_ERROR("DrawRing", "glTranslated");
1148    gluDisk(m_Quadric, radius - 1.0, radius, 12, 1);
1149    CHECK_GL_ERROR("DrawRing", "gluDisk");
[78924eb]1150    glPopMatrix();
1151    CHECK_GL_ERROR("DrawRing", "glPopMatrix");
[d9b3270]1152}
1153
[aa048c3]1154void GLACanvas::DrawRectangle(gla_colour edge, gla_colour fill,
1155                              glaCoord x0, glaCoord y0, glaCoord w, glaCoord h)
[56da40e]1156{
1157    // Draw a filled rectangle with an edge in the indicator plane.
[ca8c864]1158    // (x0, y0) specify the bottom-left corner of the rectangle and (w, h) the
1159    // size.
[56da40e]1160
[c10dd28]1161    SetColour(fill);
[aa048c3]1162    BeginQuadrilaterals();
[56da40e]1163    PlaceIndicatorVertex(x0, y0);
1164    PlaceIndicatorVertex(x0 + w, y0);
1165    PlaceIndicatorVertex(x0 + w, y0 + h);
1166    PlaceIndicatorVertex(x0, y0 + h);
1167    EndQuadrilaterals();
1168
[aa048c3]1169    if (edge != fill) {
[c10dd28]1170        SetColour(edge);
1171        BeginLines();
1172        PlaceIndicatorVertex(x0, y0);
1173        PlaceIndicatorVertex(x0 + w, y0);
1174        PlaceIndicatorVertex(x0 + w, y0 + h);
1175        PlaceIndicatorVertex(x0, y0 + h);
1176        EndLines();
1177    }
[56da40e]1178}
1179
[aa048c3]1180void
1181GLACanvas::DrawShadedRectangle(const GLAPen & fill_bot, const GLAPen & fill_top,
1182                               glaCoord x0, glaCoord y0,
1183                               glaCoord w, glaCoord h)
1184{
1185    // Draw a graduated filled rectangle in the indicator plane.
1186    // (x0, y0) specify the bottom-left corner of the rectangle and (w, h) the
1187    // size.
1188
[02aa4d4]1189    glShadeModel(GL_SMOOTH);
[9baa53a]1190    CHECK_GL_ERROR("DrawShadedRectangle", "glShadeModel GL_SMOOTH");
[aa048c3]1191    BeginQuadrilaterals();
1192    SetColour(fill_bot);
1193    PlaceIndicatorVertex(x0, y0);
1194    PlaceIndicatorVertex(x0 + w, y0);
1195    SetColour(fill_top);
1196    PlaceIndicatorVertex(x0 + w, y0 + h);
1197    PlaceIndicatorVertex(x0, y0 + h);
1198    EndQuadrilaterals();
[02aa4d4]1199    glShadeModel(GL_FLAT);
[9baa53a]1200    CHECK_GL_ERROR("DrawShadedRectangle", "glShadeModel GL_FLAT");
[aa048c3]1201}
1202
1203void GLACanvas::DrawCircle(gla_colour edge, gla_colour fill,
1204                           glaCoord cx, glaCoord cy, glaCoord radius)
[56da40e]1205{
[087bc72]1206    // Draw a filled circle with an edge.
[56da40e]1207    SetColour(fill);
[78924eb]1208    glMatrixMode(GL_MODELVIEW);
1209    CHECK_GL_ERROR("DrawCircle", "glMatrixMode");
1210    glPushMatrix();
1211    CHECK_GL_ERROR("DrawCircle", "glPushMatrix");
[56da40e]1212    glTranslated(cx, cy, 0.0);
[8326157]1213    CHECK_GL_ERROR("DrawCircle", "glTranslated");
[1b12b82]1214    assert(m_Quadric);
1215    gluDisk(m_Quadric, 0.0, radius, 36, 1);
[8326157]1216    CHECK_GL_ERROR("DrawCircle", "gluDisk");
[56da40e]1217    SetColour(edge);
[1b12b82]1218    gluDisk(m_Quadric, radius - 1.0, radius, 36, 1);
[8326157]1219    CHECK_GL_ERROR("DrawCircle", "gluDisk (2)");
[78924eb]1220    glPopMatrix();
1221    CHECK_GL_ERROR("DrawCircle", "glPopMatrix");
[56da40e]1222}
1223
[aa048c3]1224void GLACanvas::DrawSemicircle(gla_colour edge, gla_colour fill,
[1b12b82]1225                               glaCoord cx, glaCoord cy,
1226                               glaCoord radius, glaCoord start)
[56da40e]1227{
1228    // Draw a filled semicircle with an edge.
[ca8c864]1229    // The semicircle extends from "start" deg to "start"+180 deg (increasing
1230    // clockwise, 0 deg upwards).
[56da40e]1231    SetColour(fill);
[78924eb]1232    glMatrixMode(GL_MODELVIEW);
1233    CHECK_GL_ERROR("DrawSemicircle", "glMatrixMode");
1234    glPushMatrix();
1235    CHECK_GL_ERROR("DrawSemicircle", "glPushMatrix");
[56da40e]1236    glTranslated(cx, cy, 0.0);
[78924eb]1237    CHECK_GL_ERROR("DrawSemicircle", "glTranslated");
[1b12b82]1238    assert(m_Quadric);
1239    gluPartialDisk(m_Quadric, 0.0, radius, 36, 1, start, 180.0);
[78924eb]1240    CHECK_GL_ERROR("DrawSemicircle", "gluPartialDisk");
[56da40e]1241    SetColour(edge);
[1b12b82]1242    gluPartialDisk(m_Quadric, radius - 1.0, radius, 36, 1, start, 180.0);
[78924eb]1243    CHECK_GL_ERROR("DrawSemicircle", "gluPartialDisk (2)");
1244    glPopMatrix();
1245    CHECK_GL_ERROR("DrawSemicircle", "glPopMatrix");
[56da40e]1246}
1247
[d67450e]1248void
1249GLACanvas::DrawTriangle(gla_colour edge, gla_colour fill,
1250                        const Vector3 &p0, const Vector3 &p1, const Vector3 &p2)
[56da40e]1251{
[ca8c864]1252    // Draw a filled triangle with an edge.
[096e56c]1253
[56da40e]1254    SetColour(fill);
1255    BeginTriangles();
[d67450e]1256    PlaceIndicatorVertex(p0.GetX(), p0.GetY());
1257    PlaceIndicatorVertex(p1.GetX(), p1.GetY());
1258    PlaceIndicatorVertex(p2.GetX(), p2.GetY());
[56da40e]1259    EndTriangles();
1260
1261    SetColour(edge);
[d67450e]1262    glBegin(GL_LINE_STRIP);
1263    PlaceIndicatorVertex(p0.GetX(), p0.GetY());
1264    PlaceIndicatorVertex(p1.GetX(), p1.GetY());
1265    PlaceIndicatorVertex(p2.GetX(), p2.GetY());
1266    glEnd();
[9baa53a]1267    CHECK_GL_ERROR("DrawTriangle", "glEnd GL_LINE_STRIP");
[56da40e]1268}
1269
1270void GLACanvas::EnableDashedLines()
1271{
1272    // Enable dashed lines, and start drawing in them.
1273
[9eb58d0]1274    glLineStipple(1, 0x3333);
[8326157]1275    CHECK_GL_ERROR("EnableDashedLines", "glLineStipple");
[56da40e]1276    glEnable(GL_LINE_STIPPLE);
[bb1f293]1277    CHECK_GL_ERROR("EnableDashedLines", "glEnable GL_LINE_STIPPLE");
[56da40e]1278}
1279
1280void GLACanvas::DisableDashedLines()
1281{
1282    glDisable(GL_LINE_STIPPLE);
[bb1f293]1283    CHECK_GL_ERROR("DisableDashedLines", "glDisable GL_LINE_STIPPLE");
[56da40e]1284}
1285
[d67450e]1286bool GLACanvas::Transform(const Vector3 & v,
[3c3ca1d]1287                          Double* x_out, Double* y_out, Double* z_out) const
[56da40e]1288{
1289    // Convert from data coordinates to screen coordinates.
[096e56c]1290
[087bc72]1291    // Perform the projection.
[d67450e]1292    return gluProject(v.GetX(), v.GetY(), v.GetZ(),
1293                      modelview_matrix, projection_matrix, viewport,
[6adffadf]1294                      x_out, y_out, z_out);
[56da40e]1295}
1296
[1b12b82]1297void GLACanvas::ReverseTransform(Double x, Double y,
[3c3ca1d]1298                                 Double* x_out, Double* y_out, Double* z_out) const
[a2b3d62]1299{
1300    // Convert from screen coordinates to data coordinates.
[096e56c]1301
[a2b3d62]1302    // Perform the projection.
[1b12b82]1303    gluUnProject(x, y, 0.0, modelview_matrix, projection_matrix, viewport,
1304                 x_out, y_out, z_out);
[8326157]1305    CHECK_GL_ERROR("ReverseTransform", "gluUnProject");
[a2b3d62]1306}
1307
[e7f9e99]1308Double GLACanvas::SurveyUnitsAcrossViewport() const
[087bc72]1309{
[8326157]1310    // Measure the current viewport in survey units, taking into account the
1311    // current display scale.
[087bc72]1312
[9eb58d0]1313    assert(m_Scale != 0.0);
[db59b02]1314    list_flags |= INVALIDATE_ON_SCALE;
[c5fc8eb]1315    return m_VolumeDiameter / m_Scale;
[087bc72]1316}
[045e2af]1317
[d67450e]1318void GLACanvas::ToggleSmoothShading()
1319{
1320    m_SmoothShading = !m_SmoothShading;
1321}
1322
[a517825]1323void GLACanvas::ToggleTextured()
1324{
1325    m_Textured = !m_Textured;
1326    if (m_Textured && m_Texture == 0) {
1327        glGenTextures(1, &m_Texture);
1328        CHECK_GL_ERROR("ToggleTextured", "glGenTextures");
1329
1330        glBindTexture(GL_TEXTURE_2D, m_Texture);
1331        CHECK_GL_ERROR("ToggleTextured", "glBindTexture");
1332
1333        ::wxInitAllImageHandlers();
[5627cbb]1334
[a517825]1335        wxImage img;
[8a05a7a]1336        wxString texture(wmsg_cfgpth());
[5627cbb]1337        texture += wxCONFIG_PATH_SEPARATOR;
1338        texture += wxT("icons");
1339        texture += wxCONFIG_PATH_SEPARATOR;
1340        texture += wxT("texture.png");
1341        if (!img.LoadFile(texture, wxBITMAP_TYPE_PNG)) {
[a517825]1342            // FIXME
1343            fprintf(stderr, "Couldn't load image.\n");
1344            exit(1);
1345        }
[5627cbb]1346
[ecbdd96]1347        // Generate mipmaps.
[7c18431]1348        gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, // was GL_LUMINANCE
[ecbdd96]1349                          img.GetWidth(), img.GetHeight(),
1350                          GL_RGB, GL_UNSIGNED_BYTE, img.GetData());
1351        CHECK_GL_ERROR("ToggleTextured", "gluBuild2DMipmaps");
[a517825]1352
1353        glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
1354        CHECK_GL_ERROR("ToggleTextured", "glTexEnvi");
1355    }
1356}
1357
[045e2af]1358bool GLACanvas::SaveScreenshot(const wxString & fnm, int type) const
1359{
1360    int width;
1361    int height;
1362    GetSize(&width, &height);
[1bd4841]1363    unsigned char *pixels = (unsigned char *)malloc(3 * width * (height + 1));
[045e2af]1364    if (!pixels) return false;
1365    glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, (GLvoid *)pixels);
[1bd4841]1366    unsigned char * tmp_row = pixels + 3 * width * height;
1367    // We need to flip the image vertically - this approach should be more
1368    // efficient than using wxImage::Mirror(false) as that creates a new
1369    // wxImage object.
1370    for (int y = height / 2 - 1; y >= 0; --y) {
1371        unsigned char * upper = pixels + 3 * width * y;
1372        unsigned char * lower = pixels + 3 * width * (height - y - 1);
1373        memcpy(tmp_row, upper, 3 * width);
1374        memcpy(upper, lower, 3 * width);
1375        memcpy(lower, tmp_row, 3 * width);
1376    }
1377    // NB wxImage constructor calls free(pixels) for us.
[045e2af]1378    wxImage grab(width, height, pixels);
[1bd4841]1379    return grab.SaveFile(fnm, type);
[045e2af]1380}
Note: See TracBrowser for help on using the repository browser.