source: git/src/gla-gl.cc @ 90688f5

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

src/gla-gl.cc: Only try to read the GLX version on platforms which
use GLX.

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

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