source: git/src/gla-gl.cc @ 807f9dd

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

src/gla-gl.cc,src/gla.h: Check whether blobs and crosses actually
render correctly as points/point sprites, and if they don't, fall
back to drawing them with lines.

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

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