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

RELEASE/1.2debug-cidebug-ci-sanitisersstereowalls-data
Last change on this file since a72ed95 was 09dfd18, checked in by Olly Betts <olly@…>, 9 years ago

buildmacosx.sh,configure.ac,debian/survex-aven.install,
debian/survex.install,lib/Makefile.am,lib/icons/,lib/images/,
src/aboutdlg.cc,src/aboutdlg.h,src/gla-gl.cc,survex.iss.in: Split
out the images which aren't icons into an "images" directory.

  • Property mode set to 100644
File size: 45.4 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    if (alpha == 1.0) {
960        glColor3ubv(&COLOURS[colour].r);
961    } else {
962        glColor4ub(COLOURS[colour].r,
963                   COLOURS[colour].g,
964                   COLOURS[colour].b,
965                   (unsigned char)(255 * alpha));
966    }
967}
968
969void GLACanvas::DrawText(glaCoord x, glaCoord y, glaCoord z, const wxString& str)
970{
971    // Draw a text string on the current buffer in the current font.
972    glRasterPos3d(x, y, z);
973    CHECK_GL_ERROR("DrawText", "glRasterPos3d");
974    m_Font.write_string(str.data(), str.size());
975}
976
977void GLACanvas::DrawIndicatorText(int x, int y, const wxString& str)
978{
979    glRasterPos2d(x, y);
980    CHECK_GL_ERROR("DrawIndicatorText", "glRasterPos2d");
981    m_Font.write_string(str.data(), str.size());
982}
983
984void GLACanvas::GetTextExtent(const wxString& str, int * x_ext, int * y_ext)
985{
986    m_Font.get_text_extent(str.data(), str.size(), x_ext, y_ext);
987}
988
989void GLACanvas::BeginQuadrilaterals()
990{
991    // Commence drawing of quadrilaterals.
992
993    glBegin(GL_QUADS);
994}
995
996void GLACanvas::EndQuadrilaterals()
997{
998    // Finish drawing of quadrilaterals.
999
1000    glEnd();
1001    CHECK_GL_ERROR("EndQuadrilaterals", "glEnd GL_QUADS");
1002}
1003
1004void GLACanvas::BeginLines()
1005{
1006    // Commence drawing of a set of lines.
1007
1008    glBegin(GL_LINES);
1009}
1010
1011void GLACanvas::EndLines()
1012{
1013    // Finish drawing of a set of lines.
1014
1015    glEnd();
1016    CHECK_GL_ERROR("EndLines", "glEnd GL_LINES");
1017}
1018
1019void GLACanvas::BeginTriangles()
1020{
1021    // Commence drawing of a set of triangles.
1022
1023    glBegin(GL_TRIANGLES);
1024}
1025
1026void GLACanvas::EndTriangles()
1027{
1028    // Finish drawing of a set of triangles.
1029
1030    glEnd();
1031    CHECK_GL_ERROR("EndTriangles", "glEnd GL_TRIANGLES");
1032}
1033
1034void GLACanvas::BeginTriangleStrip()
1035{
1036    // Commence drawing of a triangle strip.
1037
1038    glBegin(GL_TRIANGLE_STRIP);
1039}
1040
1041void GLACanvas::EndTriangleStrip()
1042{
1043    // Finish drawing of a triangle strip.
1044
1045    glEnd();
1046    CHECK_GL_ERROR("EndTriangleStrip", "glEnd GL_TRIANGLE_STRIP");
1047}
1048
1049void GLACanvas::BeginPolyline()
1050{
1051    // Commence drawing of a polyline.
1052
1053    glBegin(GL_LINE_STRIP);
1054}
1055
1056void GLACanvas::EndPolyline()
1057{
1058    // Finish drawing of a polyline.
1059
1060    glEnd();
1061    CHECK_GL_ERROR("EndPolyline", "glEnd GL_LINE_STRIP");
1062}
1063
1064void GLACanvas::BeginPolygon()
1065{
1066    // Commence drawing of a polygon.
1067
1068    glBegin(GL_POLYGON);
1069}
1070
1071void GLACanvas::EndPolygon()
1072{
1073    // Finish drawing of a polygon.
1074
1075    glEnd();
1076    CHECK_GL_ERROR("EndPolygon", "glEnd GL_POLYGON");
1077}
1078
1079void GLACanvas::PlaceVertex(glaCoord x, glaCoord y, glaCoord z)
1080{
1081    // Place a vertex for the current object being drawn.
1082
1083#ifdef GLA_DEBUG
1084    m_Vertices++;
1085#endif
1086    glVertex3d(x, y, z);
1087}
1088
1089void GLACanvas::PlaceVertex(glaCoord x, glaCoord y, glaCoord z,
1090                            glaTexCoord tex_x, glaTexCoord tex_y)
1091{
1092    // Place a vertex for the current object being drawn.
1093
1094#ifdef GLA_DEBUG
1095    m_Vertices++;
1096#endif
1097    glTexCoord2i(tex_x, tex_y);
1098    glVertex3d(x, y, z);
1099}
1100
1101void GLACanvas::PlaceIndicatorVertex(glaCoord x, glaCoord y)
1102{
1103    // Place a vertex for the current indicator object being drawn.
1104
1105    PlaceVertex(x, y, 0.0);
1106}
1107
1108void GLACanvas::BeginBlobs()
1109{
1110    // Commence drawing of a set of blobs.
1111    if (blob_method == POINT) {
1112        glPushAttrib(GL_ENABLE_BIT);
1113        CHECK_GL_ERROR("BeginBlobs", "glPushAttrib");
1114        glEnable(GL_ALPHA_TEST);
1115        CHECK_GL_ERROR("BeginBlobs", "glEnable GL_ALPHA_TEST");
1116        glEnable(GL_POINT_SMOOTH);
1117        CHECK_GL_ERROR("BeginBlobs", "glEnable GL_POINT_SMOOTH");
1118        glBegin(GL_POINTS);
1119    } else {
1120        glPushAttrib(GL_TRANSFORM_BIT|GL_VIEWPORT_BIT|GL_ENABLE_BIT);
1121        CHECK_GL_ERROR("BeginBlobs", "glPushAttrib");
1122        SetIndicatorTransform();
1123        glEnable(GL_DEPTH_TEST);
1124        CHECK_GL_ERROR("BeginBlobs", "glEnable GL_DEPTH_TEST");
1125        glBegin(GL_LINES);
1126    }
1127}
1128
1129void GLACanvas::EndBlobs()
1130{
1131    // Finish drawing of a set of blobs.
1132    glEnd();
1133    if (blob_method == POINT) {
1134        CHECK_GL_ERROR("EndBlobs", "glEnd GL_POINTS");
1135    } else {
1136        CHECK_GL_ERROR("EndBlobs", "glEnd GL_LINES");
1137    }
1138    glPopAttrib();
1139    CHECK_GL_ERROR("EndBlobs", "glPopAttrib");
1140}
1141
1142void GLACanvas::DrawBlob(glaCoord x, glaCoord y, glaCoord z)
1143{
1144    if (blob_method == POINT) {
1145        // Draw a marker.
1146        PlaceVertex(x, y, z);
1147    } else {
1148        double X, Y, Z;
1149        if (!Transform(Vector3(x, y, z), &X, &Y, &Z)) {
1150            printf("bad transform\n");
1151            return;
1152        }
1153        // Stuff behind us (in perspective view) will get clipped,
1154        // but we can save effort with a cheap check here.
1155        if (Z <= 0) return;
1156
1157        X -= BLOB_DIAMETER * 0.5;
1158        Y -= BLOB_DIAMETER * 0.5;
1159
1160        PlaceVertex(X, Y + 1, Z);
1161        PlaceVertex(X, Y + (BLOB_DIAMETER - 1), Z);
1162
1163        for (int i = 1; i < (BLOB_DIAMETER - 1); ++i) {
1164            PlaceVertex(X + i, Y, Z);
1165            PlaceVertex(X + i, Y + BLOB_DIAMETER, Z);
1166        }
1167
1168        PlaceVertex(X + (BLOB_DIAMETER - 1), Y + 1, Z);
1169        PlaceVertex(X + (BLOB_DIAMETER - 1), Y + (BLOB_DIAMETER - 1), Z);
1170    }
1171#ifdef GLA_DEBUG
1172    m_Vertices++;
1173#endif
1174}
1175
1176void GLACanvas::DrawBlob(glaCoord x, glaCoord y)
1177{
1178    if (blob_method == POINT) {
1179        // Draw a marker.
1180        PlaceVertex(x, y, 0);
1181    } else {
1182        x -= BLOB_DIAMETER * 0.5;
1183        y -= BLOB_DIAMETER * 0.5;
1184
1185        PlaceVertex(x, y + 1, 0);
1186        PlaceVertex(x, y + (BLOB_DIAMETER - 1), 0);
1187
1188        for (int i = 1; i < (BLOB_DIAMETER - 1); ++i) {
1189            PlaceVertex(x + i, y, 0);
1190            PlaceVertex(x + i, y + BLOB_DIAMETER, 0);
1191        }
1192
1193        PlaceVertex(x + (BLOB_DIAMETER - 1), y + 1, 0);
1194        PlaceVertex(x + (BLOB_DIAMETER - 1), y + (BLOB_DIAMETER - 1), 0);
1195    }
1196#ifdef GLA_DEBUG
1197    m_Vertices++;
1198#endif
1199}
1200
1201void GLACanvas::BeginCrosses()
1202{
1203    // Plot crosses.
1204    if (cross_method == SPRITE) {
1205        glPushAttrib(GL_ENABLE_BIT|GL_POINT_BIT);
1206        CHECK_GL_ERROR("BeginCrosses", "glPushAttrib");
1207        glBindTexture(GL_TEXTURE_2D, m_CrossTexture);
1208        CHECK_GL_ERROR("BeginCrosses", "glBindTexture");
1209        glEnable(GL_ALPHA_TEST);
1210        CHECK_GL_ERROR("BeginCrosses", "glEnable GL_ALPHA_TEST");
1211        glPointSize(8);
1212        CHECK_GL_ERROR("BeginCrosses", "glPointSize");
1213        glTexEnvi(GL_POINT_SPRITE, GL_COORD_REPLACE, GL_TRUE);
1214        CHECK_GL_ERROR("BeginCrosses", "glTexEnvi GL_POINT_SPRITE");
1215        glEnable(GL_TEXTURE_2D);
1216        CHECK_GL_ERROR("BeginCrosses", "glEnable GL_TEXTURE_2D");
1217        glEnable(GL_POINT_SPRITE);
1218        CHECK_GL_ERROR("BeginCrosses", "glEnable GL_POINT_SPRITE");
1219        glBegin(GL_POINTS);
1220    } else {
1221        // To get the crosses to appear at a constant size and orientation on
1222        // screen, we plot them in the Indicator transform coordinates (which
1223        // unfortunately means they can't be usefully put in an opengl display
1224        // list).
1225        glPushAttrib(GL_TRANSFORM_BIT|GL_VIEWPORT_BIT|GL_ENABLE_BIT);
1226        CHECK_GL_ERROR("BeginCrosses", "glPushAttrib 2");
1227        SetIndicatorTransform();
1228        glEnable(GL_DEPTH_TEST);
1229        CHECK_GL_ERROR("BeginCrosses", "glEnable GL_DEPTH_TEST");
1230        glBegin(GL_LINES);
1231    }
1232}
1233
1234void GLACanvas::EndCrosses()
1235{
1236    glEnd();
1237    if (cross_method == SPRITE) {
1238        CHECK_GL_ERROR("EndCrosses", "glEnd GL_POINTS");
1239    } else {
1240        CHECK_GL_ERROR("EndCrosses", "glEnd GL_LINES");
1241    }
1242    glPopAttrib();
1243    CHECK_GL_ERROR("EndCrosses", "glPopAttrib");
1244}
1245
1246void GLACanvas::DrawCross(glaCoord x, glaCoord y, glaCoord z)
1247{
1248    if (cross_method == SPRITE) {
1249        PlaceVertex(x, y, z);
1250    } else {
1251        double X, Y, Z;
1252        if (!Transform(Vector3(x, y, z), &X, &Y, &Z)) {
1253            printf("bad transform\n");
1254            return;
1255        }
1256        // Stuff behind us (in perspective view) will get clipped,
1257        // but we can save effort with a cheap check here.
1258        if (Z > 0) {
1259            // Round to integers before adding on the offsets for the
1260            // cross arms to avoid uneven crosses.
1261            X = rint(X);
1262            Y = rint(Y);
1263            PlaceVertex(X - 3, Y - 3, Z);
1264            PlaceVertex(X + 3, Y + 3, Z);
1265            PlaceVertex(X - 3, Y + 3, Z);
1266            PlaceVertex(X + 3, Y - 3, Z);
1267        }
1268    }
1269#ifdef GLA_DEBUG
1270    m_Vertices++;
1271#endif
1272}
1273
1274void GLACanvas::DrawRing(glaCoord x, glaCoord y)
1275{
1276    // Draw an unfilled circle
1277    const Double radius = 4;
1278    assert(m_Quadric);
1279    glMatrixMode(GL_MODELVIEW);
1280    CHECK_GL_ERROR("DrawRing", "glMatrixMode");
1281    glPushMatrix();
1282    CHECK_GL_ERROR("DrawRing", "glPushMatrix");
1283    glTranslated(x, y, 0.0);
1284    CHECK_GL_ERROR("DrawRing", "glTranslated");
1285    gluDisk(m_Quadric, radius - 1.0, radius, 12, 1);
1286    CHECK_GL_ERROR("DrawRing", "gluDisk");
1287    glPopMatrix();
1288    CHECK_GL_ERROR("DrawRing", "glPopMatrix");
1289}
1290
1291void GLACanvas::DrawRectangle(gla_colour edge, gla_colour fill,
1292                              glaCoord x0, glaCoord y0, glaCoord w, glaCoord h)
1293{
1294    // Draw a filled rectangle with an edge in the indicator plane.
1295    // (x0, y0) specify the bottom-left corner of the rectangle and (w, h) the
1296    // size.
1297
1298    SetColour(fill);
1299    BeginQuadrilaterals();
1300    PlaceIndicatorVertex(x0, y0);
1301    PlaceIndicatorVertex(x0 + w, y0);
1302    PlaceIndicatorVertex(x0 + w, y0 + h);
1303    PlaceIndicatorVertex(x0, y0 + h);
1304    EndQuadrilaterals();
1305
1306    if (edge != fill) {
1307        SetColour(edge);
1308        BeginLines();
1309        PlaceIndicatorVertex(x0, y0);
1310        PlaceIndicatorVertex(x0 + w, y0);
1311        PlaceIndicatorVertex(x0 + w, y0 + h);
1312        PlaceIndicatorVertex(x0, y0 + h);
1313        EndLines();
1314    }
1315}
1316
1317void
1318GLACanvas::DrawShadedRectangle(const GLAPen & fill_bot, const GLAPen & fill_top,
1319                               glaCoord x0, glaCoord y0,
1320                               glaCoord w, glaCoord h)
1321{
1322    // Draw a graduated filled rectangle in the indicator plane.
1323    // (x0, y0) specify the bottom-left corner of the rectangle and (w, h) the
1324    // size.
1325
1326    glShadeModel(GL_SMOOTH);
1327    CHECK_GL_ERROR("DrawShadedRectangle", "glShadeModel GL_SMOOTH");
1328    BeginQuadrilaterals();
1329    SetColour(fill_bot);
1330    PlaceIndicatorVertex(x0, y0);
1331    PlaceIndicatorVertex(x0 + w, y0);
1332    SetColour(fill_top);
1333    PlaceIndicatorVertex(x0 + w, y0 + h);
1334    PlaceIndicatorVertex(x0, y0 + h);
1335    EndQuadrilaterals();
1336    glShadeModel(GL_FLAT);
1337    CHECK_GL_ERROR("DrawShadedRectangle", "glShadeModel GL_FLAT");
1338}
1339
1340void GLACanvas::DrawCircle(gla_colour edge, gla_colour fill,
1341                           glaCoord cx, glaCoord cy, glaCoord radius)
1342{
1343    // Draw a filled circle with an edge.
1344    SetColour(fill);
1345    glMatrixMode(GL_MODELVIEW);
1346    CHECK_GL_ERROR("DrawCircle", "glMatrixMode");
1347    glPushMatrix();
1348    CHECK_GL_ERROR("DrawCircle", "glPushMatrix");
1349    glTranslated(cx, cy, 0.0);
1350    CHECK_GL_ERROR("DrawCircle", "glTranslated");
1351    assert(m_Quadric);
1352    gluDisk(m_Quadric, 0.0, radius, 36, 1);
1353    CHECK_GL_ERROR("DrawCircle", "gluDisk");
1354    SetColour(edge);
1355    gluDisk(m_Quadric, radius - 1.0, radius, 36, 1);
1356    CHECK_GL_ERROR("DrawCircle", "gluDisk (2)");
1357    glPopMatrix();
1358    CHECK_GL_ERROR("DrawCircle", "glPopMatrix");
1359}
1360
1361void GLACanvas::DrawSemicircle(gla_colour edge, gla_colour fill,
1362                               glaCoord cx, glaCoord cy,
1363                               glaCoord radius, glaCoord start)
1364{
1365    // Draw a filled semicircle with an edge.
1366    // The semicircle extends from "start" deg to "start"+180 deg (increasing
1367    // clockwise, 0 deg upwards).
1368    SetColour(fill);
1369    glMatrixMode(GL_MODELVIEW);
1370    CHECK_GL_ERROR("DrawSemicircle", "glMatrixMode");
1371    glPushMatrix();
1372    CHECK_GL_ERROR("DrawSemicircle", "glPushMatrix");
1373    glTranslated(cx, cy, 0.0);
1374    CHECK_GL_ERROR("DrawSemicircle", "glTranslated");
1375    assert(m_Quadric);
1376    gluPartialDisk(m_Quadric, 0.0, radius, 36, 1, start, 180.0);
1377    CHECK_GL_ERROR("DrawSemicircle", "gluPartialDisk");
1378    SetColour(edge);
1379    gluPartialDisk(m_Quadric, radius - 1.0, radius, 36, 1, start, 180.0);
1380    CHECK_GL_ERROR("DrawSemicircle", "gluPartialDisk (2)");
1381    glPopMatrix();
1382    CHECK_GL_ERROR("DrawSemicircle", "glPopMatrix");
1383}
1384
1385void
1386GLACanvas::DrawTriangle(gla_colour edge, gla_colour fill,
1387                        const Vector3 &p0, const Vector3 &p1, const Vector3 &p2)
1388{
1389    // Draw a filled triangle with an edge.
1390
1391    SetColour(fill);
1392    BeginTriangles();
1393    PlaceIndicatorVertex(p0.GetX(), p0.GetY());
1394    PlaceIndicatorVertex(p1.GetX(), p1.GetY());
1395    PlaceIndicatorVertex(p2.GetX(), p2.GetY());
1396    EndTriangles();
1397
1398    SetColour(edge);
1399    glBegin(GL_LINE_STRIP);
1400    PlaceIndicatorVertex(p0.GetX(), p0.GetY());
1401    PlaceIndicatorVertex(p1.GetX(), p1.GetY());
1402    PlaceIndicatorVertex(p2.GetX(), p2.GetY());
1403    glEnd();
1404    CHECK_GL_ERROR("DrawTriangle", "glEnd GL_LINE_STRIP");
1405}
1406
1407void GLACanvas::EnableDashedLines()
1408{
1409    // Enable dashed lines, and start drawing in them.
1410
1411    glLineStipple(1, 0x3333);
1412    CHECK_GL_ERROR("EnableDashedLines", "glLineStipple");
1413    glEnable(GL_LINE_STIPPLE);
1414    CHECK_GL_ERROR("EnableDashedLines", "glEnable GL_LINE_STIPPLE");
1415}
1416
1417void GLACanvas::DisableDashedLines()
1418{
1419    glDisable(GL_LINE_STIPPLE);
1420    CHECK_GL_ERROR("DisableDashedLines", "glDisable GL_LINE_STIPPLE");
1421}
1422
1423bool GLACanvas::Transform(const Vector3 & v,
1424                          double* x_out, double* y_out, double* z_out) const
1425{
1426    // Convert from data coordinates to screen coordinates.
1427
1428    // Perform the projection.
1429    return gluProject(v.GetX(), v.GetY(), v.GetZ(),
1430                      modelview_matrix, projection_matrix, viewport,
1431                      x_out, y_out, z_out);
1432}
1433
1434void GLACanvas::ReverseTransform(Double x, Double y,
1435                                 double* x_out, double* y_out, double* z_out) const
1436{
1437    // Convert from screen coordinates to data coordinates.
1438
1439    // Perform the projection.
1440    gluUnProject(x, y, 0.0, modelview_matrix, projection_matrix, viewport,
1441                 x_out, y_out, z_out);
1442    CHECK_GL_ERROR("ReverseTransform", "gluUnProject");
1443}
1444
1445Double GLACanvas::SurveyUnitsAcrossViewport() const
1446{
1447    // Measure the current viewport in survey units, taking into account the
1448    // current display scale.
1449
1450    assert(m_Scale != 0.0);
1451    list_flags |= INVALIDATE_ON_SCALE;
1452    return m_VolumeDiameter / m_Scale;
1453}
1454
1455void GLACanvas::ToggleSmoothShading()
1456{
1457    m_SmoothShading = !m_SmoothShading;
1458}
1459
1460void GLACanvas::ToggleTextured()
1461{
1462    m_Textured = !m_Textured;
1463    if (m_Textured && m_Texture == 0) {
1464        glGenTextures(1, &m_Texture);
1465        CHECK_GL_ERROR("ToggleTextured", "glGenTextures");
1466
1467        glBindTexture(GL_TEXTURE_2D, m_Texture);
1468        CHECK_GL_ERROR("ToggleTextured", "glBindTexture");
1469
1470        ::wxInitAllImageHandlers();
1471
1472        wxImage img;
1473        wxString texture(wmsg_cfgpth());
1474        texture += wxCONFIG_PATH_SEPARATOR;
1475        texture += wxT("images");
1476        texture += wxCONFIG_PATH_SEPARATOR;
1477        texture += wxT("texture.png");
1478        if (!img.LoadFile(texture, wxBITMAP_TYPE_PNG)) {
1479            // FIXME
1480            fprintf(stderr, "Couldn't load image.\n");
1481            exit(1);
1482        }
1483
1484        // Generate mipmaps.
1485        gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, // was GL_LUMINANCE
1486                          img.GetWidth(), img.GetHeight(),
1487                          GL_RGB, GL_UNSIGNED_BYTE, img.GetData());
1488        CHECK_GL_ERROR("ToggleTextured", "gluBuild2DMipmaps");
1489
1490        glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
1491        CHECK_GL_ERROR("ToggleTextured", "glTexEnvi");
1492    }
1493}
1494
1495bool GLACanvas::SaveScreenshot(const wxString & fnm, wxBitmapType type) const
1496{
1497    const int width = x_size;
1498    const int height = y_size;
1499    unsigned char *pixels = (unsigned char *)malloc(3 * width * (height + 1));
1500    if (!pixels) return false;
1501    glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, (GLvoid *)pixels);
1502    CHECK_GL_ERROR("SaveScreenshot", "glReadPixels");
1503    unsigned char * tmp_row = pixels + 3 * width * height;
1504    // We need to flip the image vertically - this approach should be more
1505    // efficient than using wxImage::Mirror(false) as that creates a new
1506    // wxImage object.
1507    for (int y = height / 2 - 1; y >= 0; --y) {
1508        unsigned char * upper = pixels + 3 * width * y;
1509        unsigned char * lower = pixels + 3 * width * (height - y - 1);
1510        memcpy(tmp_row, upper, 3 * width);
1511        memcpy(upper, lower, 3 * width);
1512        memcpy(lower, tmp_row, 3 * width);
1513    }
1514    // NB wxImage constructor calls free(pixels) for us.
1515    wxImage grab(width, height, pixels);
1516    return grab.SaveFile(fnm, type);
1517}
1518
1519bool GLACanvas::CheckVisualFidelity(const unsigned char * target) const
1520{
1521    unsigned char pixels[3 * 8 * 8];
1522    if (double_buffered) {
1523        glReadBuffer(GL_BACK);
1524        CHECK_GL_ERROR("FirstShow", "glReadBuffer");
1525    }
1526    glReadPixels(x_size / 2 - 3, y_size / 2 - 4, 8, 8,
1527                 GL_RGB, GL_UNSIGNED_BYTE, (GLvoid *)pixels);
1528    CHECK_GL_ERROR("CheckVisualFidelity", "glReadPixels");
1529    if (double_buffered) {
1530        glReadBuffer(GL_FRONT);
1531        CHECK_GL_ERROR("FirstShow", "glReadBuffer");
1532    }
1533#if 0
1534    // Show what got drawn and what was expected for debugging.
1535    for (int y = 0; y < 8; ++y) {
1536        for (int x = 0; x < 8; ++x) {
1537            int o = (y * 8 + x) * 3;
1538            printf("%c", pixels[o] ? 'X' : '.');
1539        }
1540        printf(" ");
1541        for (int x = 0; x < 8; ++x) {
1542            int o = (y * 8 + x) * 3;
1543            printf("%c", target[o] ? 'X' : '.');
1544        }
1545        printf("\n");
1546    }
1547#endif
1548    return (memcmp(pixels, target, sizeof(pixels)) == 0);
1549}
1550
1551void GLACanvas::ReadPixels(int width, int height, unsigned char * buf) const
1552{
1553    CHECK_GL_ERROR("ReadPixels", "glReadPixels");
1554    glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, (GLvoid *)buf);
1555}
1556
1557void GLACanvas::PolygonOffset(bool on) const
1558{
1559    if (on) {
1560        glPolygonOffset(1.0, 1.0);
1561        glEnable(GL_POLYGON_OFFSET_FILL);
1562    } else {
1563        glDisable(GL_POLYGON_OFFSET_FILL);
1564    }
1565}
Note: See TracBrowser for help on using the repository browser.