source: git/src/gla-gl.cc @ 5ed907b0

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

src/gfxcore.cc,src/gla-gl.cc,src/gla.h: Use col_WHITE to specify we
want to draw something in white.

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