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

RELEASE/1.2debug-cidebug-ci-sanitiserswalls-data
Last change on this file since 9fcc81a was 127435f, checked in by Olly Betts <olly@…>, 5 years ago

Draw the measuring line ring with an even shape

Previously the exact shape of the ring varied slightly depending on
the exact coordinates, which could be visually distracting once you
noticed it.

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