source: git/src/gla-gl.cc @ 17c483d

RELEASE/1.2debug-cidebug-ci-sanitisersstereowalls-datawalls-data-hanging-as-warning
Last change on this file since 17c483d was 17c483d, checked in by Olly Betts <olly@…>, 11 years ago

src/gla-gl.cc,src/glbitmapfont.cc: Add checks for errors when reading
the font file.

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