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

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

src/gla-gl.cc,src/gla.h: Cache the best method for drawing blobs
and crosses, and recheck automatically if the graphics hardware
or drivers are changed or upgraded.

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

  • Property mode set to 100644
File size: 42.3 KB
Line 
1//
2//  gla-gl.cc
3//
4//  OpenGL implementation for the GLA abstraction layer.
5//
6//  Copyright (C) 2002-2003,2005 Mark R. Shinwell
7//  Copyright (C) 2003,2004,2005,2006,2007,2010 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    bool save_hints = false;
284    wxString vendor((const char *)glGetString(GL_VENDOR), wxConvUTF8);
285    wxString renderer((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            255,255,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,255,255,
429              0,  0,255,255,  0,  0,  0,  0,  0,  0,  0,  0,255,255,  0,  0,
430              0,  0,  0,  0,255,255,  0,  0,  0,  0,255,255,  0,  0,  0,  0,
431              0,  0,  0,  0,  0,  0,255,255,255,255,  0,  0,  0,  0,  0,  0,
432              0,  0,  0,  0,  0,  0,255,255,255,255,  0,  0,  0,  0,  0,  0,
433              0,  0,  0,  0,255,255,  0,  0,  0,  0,255,255,  0,  0,  0,  0,
434              0,  0,255,255,  0,  0,  0,  0,  0,  0,  0,  0,255,255,  0,  0,
435            255,255,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,255,255
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    if (save_hints) {
454        cfg->Write(wxT("opengl_vendor"), vendor);
455        cfg->Write(wxT("opengl_renderer"), renderer);
456        cout << "blob_method = " << (char)blob_method;
457        cfg->Write(wxT("blob_method"), blob_method);
458        cout << "cross_method = " << (char)cross_method;
459        cfg->Write(wxT("cross_method"), cross_method);
460        cfg->Flush();
461    }
462}
463
464void GLACanvas::Clear()
465{
466    // Clear the canvas.
467
468    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
469    CHECK_GL_ERROR("Clear", "glClear");
470}
471
472void GLACanvas::SetScale(Double scale)
473{
474    if (scale != m_Scale) {
475        vector<GLAList>::iterator i;
476        for (i = drawing_lists.begin(); i != drawing_lists.end(); ++i) {
477            if (*i && i->test_flag(INVALIDATE_ON_SCALE)) i->InvalidateList();
478        }
479
480        m_Scale = scale;
481    }
482}
483
484void GLACanvas::AddTranslationScreenCoordinates(int dx, int dy)
485{
486    // Translate the data by a given amount, specified in screen coordinates.
487
488    // Find out how far the translation takes us in data coordinates.
489    SetDataTransform();
490
491    double x0, y0, z0;
492    double x, y, z;
493    gluUnProject(0.0, 0.0, 0.0, modelview_matrix, projection_matrix, viewport,
494                 &x0, &y0, &z0);
495    CHECK_GL_ERROR("AddTranslationScreenCoordinates", "gluUnProject");
496    gluUnProject(dx, -dy, 0.0, modelview_matrix, projection_matrix, viewport,
497                 &x, &y, &z);
498    CHECK_GL_ERROR("AddTranslationScreenCoordinates", "gluUnProject (2)");
499
500    // Apply the translation.
501    AddTranslation(Vector3(x - x0, y - y0, z - z0));
502}
503
504void GLACanvas::SetVolumeDiameter(glaCoord diameter)
505{
506    // Set the size of the data drawing volume by giving the diameter of the
507    // smallest sphere containing it.
508
509    m_VolumeDiameter = max(glaCoord(1.0), diameter);
510}
511
512void GLACanvas::StartDrawing()
513{
514    // Prepare for a redraw operation.
515
516    SetCurrent();
517    glDepthMask(GL_TRUE);
518}
519
520void GLACanvas::EnableSmoothPolygons(bool filled)
521{
522    // Prepare for drawing smoothly-shaded polygons.
523    // Only use this when required (in particular lines in lists may not be
524    // coloured correctly when this is enabled).
525
526    glPushAttrib(GL_ENABLE_BIT|GL_LIGHTING_BIT|GL_POLYGON_BIT);
527    if (filled) {
528        glShadeModel(GL_SMOOTH);
529        glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
530    } else {
531        glDisable(GL_LINE_SMOOTH);
532        glDisable(GL_TEXTURE_2D);
533        glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
534    }
535    CHECK_GL_ERROR("EnableSmoothPolygons", "glPolygonMode");
536
537    if (filled && m_SmoothShading) {
538        static const GLfloat mat_specular[] = { 0.2, 0.2, 0.2, 1.0 };
539        static const GLfloat light_position[] = { -1.0, -1.0, -1.0, 0.0 };
540        static const GLfloat light_ambient[] = { 0.3, 0.3, 0.3, 1.0 };
541        static const GLfloat light_diffuse[] = { 0.7, 0.7, 0.7, 1.0 };
542        glEnable(GL_COLOR_MATERIAL);
543        glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mat_specular);
544        glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 10.0);
545        glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
546        glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
547        glLightfv(GL_LIGHT0, GL_POSITION, light_position);
548        glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
549        glEnable(GL_LIGHTING);
550        glEnable(GL_LIGHT0);
551    }
552}
553
554void GLACanvas::DisableSmoothPolygons()
555{
556    glPopAttrib();
557}
558
559void GLACanvas::PlaceNormal(const Vector3 &v)
560{
561    // Add a normal (for polygons etc.)
562
563    glNormal3d(v.GetX(), v.GetY(), v.GetZ());
564}
565
566void GLACanvas::SetDataTransform()
567{
568    // Set viewport.  The width and height go to zero when the panel is dragged
569    // right across so we clamp them to be at least 1 to avoid errors from the
570    // opengl calls below.
571    int window_width;
572    int window_height;
573    GetSize(&window_width, &window_height);
574    if (window_height < 1) window_height = 1;
575    if (window_width < 1) window_width = 1;
576    double aspect = double(window_height) / double(window_width);
577
578    glViewport(0, 0, window_width, window_height);
579    CHECK_GL_ERROR("SetDataTransform", "glViewport");
580
581    // Set projection.
582    glMatrixMode(GL_PROJECTION);
583    CHECK_GL_ERROR("SetDataTransform", "glMatrixMode");
584    glLoadIdentity();
585    CHECK_GL_ERROR("SetDataTransform", "glLoadIdentity");
586
587    Double near_plane = 1.0;
588    if (m_Perspective) {
589        Double lr = near_plane * tan(rad(25.0));
590        Double far_plane = m_VolumeDiameter * 5 + near_plane; // FIXME: work out properly
591        Double tb = lr * aspect;
592        glFrustum(-lr, lr, -tb, tb, near_plane, far_plane);
593        CHECK_GL_ERROR("SetViewportAndProjection", "glFrustum");
594    } else {
595        near_plane = 0.0;
596        assert(m_Scale != 0.0);
597        Double lr = m_VolumeDiameter / m_Scale * 0.5;
598        Double far_plane = m_VolumeDiameter + near_plane;
599        Double tb = lr * aspect;
600        glOrtho(-lr, lr, -tb, tb, near_plane, far_plane);
601        CHECK_GL_ERROR("SetViewportAndProjection", "glOrtho");
602    }
603
604    // Set the modelview transform for drawing data.
605    glMatrixMode(GL_MODELVIEW);
606    CHECK_GL_ERROR("SetDataTransform", "glMatrixMode");
607    glLoadIdentity();
608    CHECK_GL_ERROR("SetDataTransform", "glLoadIdentity");
609    if (m_Perspective) {
610        glTranslated(0.0, 0.0, -near_plane);
611    } else {
612        glTranslated(0.0, 0.0, -0.5 * m_VolumeDiameter);
613    }
614    CHECK_GL_ERROR("SetDataTransform", "glTranslated");
615    // Get axes the correct way around (z upwards, y into screen)
616    glRotated(-90.0, 1.0, 0.0, 0.0);
617    CHECK_GL_ERROR("SetDataTransform", "glRotated");
618    glRotated(m_Tilt, 1.0, 0.0, 0.0);
619    CHECK_GL_ERROR("SetDataTransform", "glRotated");
620    glRotated(m_Pan, 0.0, 0.0, 1.0);
621    CHECK_GL_ERROR("SetDataTransform", "CopyToOpenGL");
622    if (m_Perspective) {
623        glTranslated(m_Translation.GetX(),
624                     m_Translation.GetY(),
625                     m_Translation.GetZ());
626        CHECK_GL_ERROR("SetDataTransform", "glTranslated");
627    }
628
629    // Save projection matrix.
630    glGetDoublev(GL_PROJECTION_MATRIX, projection_matrix);
631    CHECK_GL_ERROR("SetDataTransform", "glGetDoublev");
632
633    // Save viewport coordinates.
634    glGetIntegerv(GL_VIEWPORT, viewport);
635    CHECK_GL_ERROR("SetDataTransform", "glGetIntegerv");
636
637    // Save modelview matrix.
638    glGetDoublev(GL_MODELVIEW_MATRIX, modelview_matrix);
639    CHECK_GL_ERROR("SetDataTransform", "glGetDoublev");
640
641    if (!m_Perspective) {
642        // Adjust the translation so we don't change the Z position of the model
643        double X, Y, Z;
644        gluProject(m_Translation.GetX(),
645                   m_Translation.GetY(),
646                   m_Translation.GetZ(),
647                   modelview_matrix, projection_matrix, viewport,
648                   &X, &Y, &Z);
649        double Tx, Ty, Tz;
650        gluUnProject(X, Y, 0.5, modelview_matrix, projection_matrix, viewport,
651                     &Tx, &Ty, &Tz);
652        glTranslated(Tx, Ty, Tz);
653        CHECK_GL_ERROR("SetDataTransform", "glTranslated");
654        glGetDoublev(GL_MODELVIEW_MATRIX, modelview_matrix);
655    }
656
657    glEnable(GL_DEPTH_TEST);
658    CHECK_GL_ERROR("SetDataTransform", "glEnable GL_DEPTH_TEST");
659
660    if (m_Textured) {
661        glBindTexture(GL_TEXTURE_2D, m_Texture);
662        glEnable(GL_TEXTURE_2D);
663        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
664        CHECK_GL_ERROR("ToggleTextured", "glTexParameteri GL_TEXTURE_WRAP_S");
665        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
666        CHECK_GL_ERROR("ToggleTextured", "glTexParameteri GL_TEXTURE_WRAP_T");
667        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
668        CHECK_GL_ERROR("ToggleTextured", "glTexParameteri GL_TEXTURE_MAG_FILTER");
669        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
670                        GL_LINEAR_MIPMAP_LINEAR);
671        CHECK_GL_ERROR("ToggleTextured", "glTexParameteri GL_TEXTURE_MIN_FILTER");
672        glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
673    } else {
674        glDisable(GL_TEXTURE_2D);
675    }
676    if (m_Fog) {
677        glFogf(GL_FOG_START, near_plane);
678        glFogf(GL_FOG_END, near_plane + m_VolumeDiameter);
679        glEnable(GL_FOG);
680    } else {
681        glDisable(GL_FOG);
682    }
683
684    if (m_AntiAlias) {
685        glEnable(GL_LINE_SMOOTH);
686        glEnable(GL_BLEND);
687        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
688    } else {
689        glDisable(GL_LINE_SMOOTH);
690    }
691}
692
693void GLACanvas::SetIndicatorTransform()
694{
695    list_flags |= NEVER_CACHE;
696
697    // Set the modelview transform and projection for drawing indicators.
698    wxSize size = GetSize();
699    int window_width = max(size.GetWidth(), 1);
700    int window_height = max(size.GetHeight(), 1);
701
702    glDisable(GL_DEPTH_TEST);
703    CHECK_GL_ERROR("SetIndicatorTransform", "glDisable GL_DEPTH_TEST");
704    glDisable(GL_FOG);
705    CHECK_GL_ERROR("SetIndicatorTransform", "glDisable GL_FOG");
706
707    // Just a simple 2D projection.
708    glMatrixMode(GL_PROJECTION);
709    CHECK_GL_ERROR("SetIndicatorTransform", "glMatrixMode");
710    glLoadIdentity();
711    CHECK_GL_ERROR("SetIndicatorTransform", "glLoadIdentity (2)");
712    gluOrtho2D(0, window_width, 0, window_height);
713    CHECK_GL_ERROR("SetIndicatorTransform", "gluOrtho2D");
714
715    // No modelview transform.
716    glMatrixMode(GL_MODELVIEW);
717    CHECK_GL_ERROR("SetIndicatorTransform", "glMatrixMode");
718    glLoadIdentity();
719    CHECK_GL_ERROR("SetIndicatorTransform", "glLoadIdentity");
720
721    glDisable(GL_TEXTURE_2D);
722    CHECK_GL_ERROR("SetIndicatorTransform", "glDisable GL_TEXTURE_2D");
723    glDisable(GL_BLEND);
724    CHECK_GL_ERROR("SetIndicatorTransform", "glDisable GL_BLEND");
725    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
726    CHECK_GL_ERROR("SetIndicatorTransform", "glTexParameteri GL_TEXTURE_WRAP_S");
727    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
728    CHECK_GL_ERROR("SetIndicatorTransform", "glTexParameteri GL_TEXTURE_WRAP_T");
729    glAlphaFunc(GL_GREATER, 0.5f);
730    CHECK_GL_ERROR("SetIndicatorTransform", "glAlphaFunc");
731    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
732    CHECK_GL_ERROR("SetIndicatorTransform", "glTexParameteri GL_TEXTURE_MAG_FILTER");
733    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
734    CHECK_GL_ERROR("SetIndicatorTransform", "glTexParameteri GL_TEXTURE_MIN_FILTER");
735    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
736    CHECK_GL_ERROR("SetIndicatorTransform", "glHint");
737}
738
739void GLACanvas::FinishDrawing()
740{
741    // Complete a redraw operation.
742
743//    glFlush();
744//    CHECK_GL_ERROR("FinishDrawing", "glFlush");
745    SwapBuffers();
746}
747
748void GLACanvas::DrawList(unsigned int l)
749{
750    // FIXME: uncomment to disable use of lists for debugging:
751    // GenerateList(l); return;
752    if (l >= drawing_lists.size()) drawing_lists.resize(l + 1);
753
754    // We generate the OpenGL lists lazily to minimise delays on startup.
755    // So check if we need to generate the OpenGL list now.
756    if (!drawing_lists[l] && !drawing_lists[l].test_flag(NEVER_CACHE)) {
757        // Create a new OpenGL list to hold this sequence of drawing
758        // operations.
759        GLuint list = glGenLists(1);
760#ifdef GLA_DEBUG
761        printf("new list #%d: %d... ", l, list);
762        m_Vertices = 0;
763#endif
764        CHECK_GL_ERROR("DrawList", "glGenLists");
765        if (list == 0) {
766            // If we can't create a list, fall back to just drawing directly.
767            GenerateList(l);
768            return;
769        }
770
771        // We should have 256 lists for font drawing and a dozen or so for 2D
772        // and 3D lists.  So something is amiss if we've generated 1000 lists,
773        // probably a infinite loop in the lazy list mechanism.
774        assert(list < 1000);
775
776        // http://www.opengl.org/resources/faq/technical/displaylist.htm
777        // advises:
778        // "Stay away from GL_COMPILE_AND_EXECUTE mode. Instead, create the
779        // list using GL_COMPILE mode, then execute it with glCallList()."
780        glNewList(list, GL_COMPILE);
781        CHECK_GL_ERROR("DrawList", "glNewList");
782        // Clear list_flags so that we can note what conditions to invalidate
783        // the cached OpenGL list on.
784        list_flags = 0;
785        GenerateList(l);
786        glEndList();
787        CHECK_GL_ERROR("DrawList", "glEndList");
788#ifdef GLA_DEBUG
789        printf("done (%d vertices)\n", m_Vertices);
790#endif
791        drawing_lists[l] = GLAList(list, list_flags);
792    }
793
794    if (drawing_lists[l].test_flag(NEVER_CACHE)) {
795        // That list can't be cached.
796        GenerateList(l);
797    } else {
798        // Perform the operations specified by the OpenGL display list.
799        drawing_lists[l].DrawList();
800    }
801}
802
803void GLACanvas::DrawList2D(unsigned int l, glaCoord x, glaCoord y, Double rotation)
804{
805    glMatrixMode(GL_PROJECTION);
806    CHECK_GL_ERROR("DrawList2D", "glMatrixMode");
807    glPushMatrix();
808    CHECK_GL_ERROR("DrawList2D", "glPushMatrix");
809    glTranslated(x, y, 0);
810    CHECK_GL_ERROR("DrawList2D", "glTranslated");
811    if (rotation != 0.0) {
812        glRotated(rotation, 0, 0, -1);
813        CHECK_GL_ERROR("DrawList2D", "glRotated");
814    }
815    DrawList(l);
816    glMatrixMode(GL_PROJECTION);
817    CHECK_GL_ERROR("DrawList2D", "glMatrixMode 2");
818    glPopMatrix();
819    CHECK_GL_ERROR("DrawList2D", "glPopMatrix");
820}
821
822void GLACanvas::InvalidateList(unsigned int l)
823{
824    if (l < drawing_lists.size() && drawing_lists[l]) {
825        // Delete any existing OpenGL list.
826        drawing_lists[l].InvalidateList();
827    }
828}
829
830void GLACanvas::SetBackgroundColour(float red, float green, float blue)
831{
832    // Set the background colour of the canvas.
833
834    glClearColor(red, green, blue, 1.0);
835    CHECK_GL_ERROR("SetBackgroundColour", "glClearColor");
836}
837
838void GLACanvas::SetColour(const GLAPen& pen, double rgb_scale)
839{
840    // Set the colour for subsequent operations.
841    glColor3f(pen.GetRed() * rgb_scale, pen.GetGreen() * rgb_scale,
842              pen.GetBlue() * rgb_scale);
843}
844
845void GLACanvas::SetColour(const GLAPen& pen)
846{
847    // Set the colour for subsequent operations.
848    glColor3dv(pen.components);
849}
850
851void GLACanvas::SetColour(gla_colour colour)
852{
853    // Set the colour for subsequent operations.
854    glColor3ubv(&COLOURS[colour].r);
855}
856
857void GLACanvas::DrawText(glaCoord x, glaCoord y, glaCoord z, const wxString& str)
858{
859    // Draw a text string on the current buffer in the current font.
860#ifdef USE_FNT
861    GLdouble X, Y, Z;
862    if (!gluProject(x, y, z, modelview_matrix, projection_matrix, viewport,
863                    &X, &Y, &Z)) return;
864    // Only draw text which is in front of the viewer.
865    if (Z > 0) DrawIndicatorText((int)X, (int)Y, str);
866#else
867    glRasterPos3d(x, y, z);
868    CHECK_GL_ERROR("DrawText", "glRasterPos3d");
869
870// glutBitmapString() useless for Unicode strings.
871//#ifdef FREEGLUT
872#if 0
873    glutBitmapString(m_Font, (const unsigned char *)str.c_str());
874    CHECK_GL_ERROR("DrawText", "glutBitmapString");
875#else
876    for (size_t pos = 0; pos < str.length(); pos++) {
877        glutBitmapCharacter(m_Font, int((unsigned char)str[pos]));
878        CHECK_GL_ERROR("DrawText", "glutBitmapCharacter");
879    }
880#endif
881#endif
882}
883
884void GLACanvas::DrawIndicatorText(int x, int y, const wxString& str)
885{
886#ifdef USE_FNT
887    glPushAttrib(GL_ENABLE_BIT);
888    glDisable(GL_ALPHA_TEST);
889    glEnable(GL_BLEND);
890    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
891    glEnable(GL_TEXTURE_2D);
892    glDisable(GL_DEPTH_TEST);
893    m_Font.puts(x, y, str.data(), str.size());
894    glPopAttrib();
895#else
896    DrawText(x, y, 0.0, str);
897#endif
898}
899
900void GLACanvas::GetTextExtent(const wxString& str, int * x_ext, int * y_ext)
901{
902#ifdef USE_FNT
903    m_Font.getTextExtent(str.c_str(), x_ext, y_ext);
904#else
905    if (x_ext) {
906// glutBitmapLength() useless for Unicode strings.
907#if 0
908        *x_ext = glutBitmapLength(m_Font, (const unsigned char *)str.c_str());
909        CHECK_GL_ERROR("GetTextExtent", "glutBitmapLength");
910#else
911        *x_ext = 0;
912        for (size_t pos = 0; pos < str.length(); pos++) {
913            x_ext += glutBitmapWidth(m_Font, int((unsigned char)str[pos]));
914            CHECK_GL_ERROR("GetTextExtent", "glutBitmapWidth");
915        }
916#endif
917    }
918    if (y_ext) *y_ext = m_FontSize + 2;
919#endif
920}
921
922void GLACanvas::BeginQuadrilaterals()
923{
924    // Commence drawing of quadrilaterals.
925
926    glBegin(GL_QUADS);
927}
928
929void GLACanvas::EndQuadrilaterals()
930{
931    // Finish drawing of quadrilaterals.
932
933    glEnd();
934    CHECK_GL_ERROR("EndQuadrilaterals", "glEnd GL_QUADS");
935}
936
937void GLACanvas::BeginLines()
938{
939    // Commence drawing of a set of lines.
940
941    glBegin(GL_LINES);
942}
943
944void GLACanvas::EndLines()
945{
946    // Finish drawing of a set of lines.
947
948    glEnd();
949    CHECK_GL_ERROR("EndLines", "glEnd GL_LINES");
950}
951
952void GLACanvas::BeginTriangles()
953{
954    // Commence drawing of a set of triangles.
955
956    glBegin(GL_TRIANGLES);
957}
958
959void GLACanvas::EndTriangles()
960{
961    // Finish drawing of a set of triangles.
962
963    glEnd();
964    CHECK_GL_ERROR("EndTriangles", "glEnd GL_TRIANGLES");
965}
966
967void GLACanvas::BeginTriangleStrip()
968{
969    // Commence drawing of a triangle strip.
970
971    glBegin(GL_TRIANGLE_STRIP);
972}
973
974void GLACanvas::EndTriangleStrip()
975{
976    // Finish drawing of a triangle strip.
977
978    glEnd();
979    CHECK_GL_ERROR("EndTriangleStrip", "glEnd GL_TRIANGLE_STRIP");
980}
981
982void GLACanvas::BeginPolyline()
983{
984    // Commence drawing of a polyline.
985
986    glBegin(GL_LINE_STRIP);
987}
988
989void GLACanvas::EndPolyline()
990{
991    // Finish drawing of a polyline.
992
993    glEnd();
994    CHECK_GL_ERROR("EndPolyline", "glEnd GL_LINE_STRIP");
995}
996
997void GLACanvas::BeginPolygon()
998{
999    // Commence drawing of a polygon.
1000
1001    glBegin(GL_POLYGON);
1002}
1003
1004void GLACanvas::EndPolygon()
1005{
1006    // Finish drawing of a polygon.
1007
1008    glEnd();
1009    CHECK_GL_ERROR("EndPolygon", "glEnd GL_POLYGON");
1010}
1011
1012void GLACanvas::PlaceVertex(glaCoord x, glaCoord y, glaCoord z)
1013{
1014    // Place a vertex for the current object being drawn.
1015
1016#ifdef GLA_DEBUG
1017    m_Vertices++;
1018#endif
1019    glVertex3d(x, y, z);
1020}
1021
1022void GLACanvas::PlaceIndicatorVertex(glaCoord x, glaCoord y)
1023{
1024    // Place a vertex for the current indicator object being drawn.
1025
1026    PlaceVertex(x, y, 0.0);
1027}
1028
1029void GLACanvas::BeginBlobs()
1030{
1031    // Commence drawing of a set of blobs.
1032    if (blob_method == POINT) {
1033        glPushAttrib(GL_ENABLE_BIT);
1034        CHECK_GL_ERROR("BeginBlobs", "glPushAttrib");
1035        glEnable(GL_ALPHA_TEST);
1036        CHECK_GL_ERROR("BeginBlobs", "glEnable GL_ALPHA_TEST");
1037        glEnable(GL_POINT_SMOOTH);
1038        CHECK_GL_ERROR("BeginBlobs", "glEnable GL_POINT_SMOOTH");
1039        glBegin(GL_POINTS);
1040    } else {
1041        glPushAttrib(GL_TRANSFORM_BIT|GL_VIEWPORT_BIT|GL_ENABLE_BIT);
1042        CHECK_GL_ERROR("BeginBlobs", "glPushAttrib");
1043        SetIndicatorTransform();
1044        glEnable(GL_DEPTH_TEST);
1045        CHECK_GL_ERROR("BeginBlobs", "glEnable GL_DEPTH_TEST");
1046        glBegin(GL_LINES);
1047    }
1048}
1049
1050void GLACanvas::EndBlobs()
1051{
1052    // Finish drawing of a set of blobs.
1053    glEnd();
1054    if (blob_method == POINT) {
1055        CHECK_GL_ERROR("EndBlobs", "glEnd GL_POINTS");
1056    } else {
1057        CHECK_GL_ERROR("EndBlobs", "glEnd GL_LINES");
1058    }
1059    glPopAttrib();
1060    CHECK_GL_ERROR("EndBlobs", "glPopAttrib");
1061}
1062
1063void GLACanvas::DrawBlob(glaCoord x, glaCoord y, glaCoord z)
1064{
1065    if (blob_method == POINT) {
1066        // Draw a marker.
1067        PlaceVertex(x, y, z);
1068    } else {
1069        double X, Y, Z;
1070        if (!Transform(Vector3(x, y, z), &X, &Y, &Z)) {
1071            printf("bad transform\n");
1072            return;
1073        }
1074        // Stuff behind us (in perspective view) will get clipped,
1075        // but we can save effort with a cheap check here.
1076        if (Z <= 0) return;
1077
1078        X -= BLOB_DIAMETER * 0.5;
1079        Y -= BLOB_DIAMETER * 0.5;
1080
1081        PlaceVertex(X, Y + 1, Z);
1082        PlaceVertex(X, Y + (BLOB_DIAMETER - 1), Z);
1083
1084        for (int i = 1; i < (BLOB_DIAMETER - 1); ++i) {
1085            PlaceVertex(X + i, Y, Z);
1086            PlaceVertex(X + i, Y + BLOB_DIAMETER, Z);
1087        }
1088
1089        PlaceVertex(X + (BLOB_DIAMETER - 1), Y + 1, Z);
1090        PlaceVertex(X + (BLOB_DIAMETER - 1), Y + (BLOB_DIAMETER - 1), Z);
1091    }
1092#ifdef GLA_DEBUG
1093    m_Vertices++;
1094#endif
1095}
1096
1097void GLACanvas::DrawBlob(glaCoord x, glaCoord y)
1098{
1099    if (blob_method == POINT) {
1100        // Draw a marker.
1101        PlaceVertex(x, y, 0);
1102    } else {
1103        x -= BLOB_DIAMETER * 0.5;
1104        y -= BLOB_DIAMETER * 0.5;
1105
1106        PlaceVertex(x, y + 1, 0);
1107        PlaceVertex(x, y + (BLOB_DIAMETER - 1), 0);
1108
1109        for (int i = 1; i < (BLOB_DIAMETER - 1); ++i) {
1110            PlaceVertex(x + i, y, 0);
1111            PlaceVertex(x + i, y + BLOB_DIAMETER, 0);
1112        }
1113
1114        PlaceVertex(x + (BLOB_DIAMETER - 1), y + 1, 0);
1115        PlaceVertex(x + (BLOB_DIAMETER - 1), y + (BLOB_DIAMETER - 1), 0);
1116    }
1117#ifdef GLA_DEBUG
1118    m_Vertices++;
1119#endif
1120}
1121
1122void GLACanvas::BeginCrosses()
1123{
1124    // Plot crosses.
1125    if (cross_method == SPRITE) {
1126        glPushAttrib(GL_ENABLE_BIT|GL_POINT_BIT);
1127        CHECK_GL_ERROR("BeginCrosses", "glPushAttrib");
1128        glBindTexture(GL_TEXTURE_2D, m_CrossTexture);
1129        CHECK_GL_ERROR("BeginCrosses", "glBindTexture");
1130        glEnable(GL_ALPHA_TEST);
1131        CHECK_GL_ERROR("BeginCrosses", "glEnable GL_ALPHA_TEST");
1132        glPointSize(8);
1133        CHECK_GL_ERROR("BeginCrosses", "glPointSize");
1134        glTexEnvi(GL_POINT_SPRITE, GL_COORD_REPLACE, GL_TRUE);
1135        CHECK_GL_ERROR("BeginCrosses", "glTexEnvi GL_POINT_SPRITE");
1136        glEnable(GL_TEXTURE_2D);
1137        CHECK_GL_ERROR("BeginCrosses", "glEnable GL_TEXTURE_2D");
1138        glEnable(GL_POINT_SPRITE);
1139        CHECK_GL_ERROR("BeginCrosses", "glEnable GL_POINT_SPRITE");
1140        glBegin(GL_POINTS);
1141    } else {
1142        // To get the crosses to appear at a constant size and orientation on
1143        // screen, we plot them in the Indicator transform coordinates (which
1144        // unfortunately means they can't be usefully put in an opengl display
1145        // list).
1146        glPushAttrib(GL_TRANSFORM_BIT|GL_VIEWPORT_BIT|GL_ENABLE_BIT);
1147        CHECK_GL_ERROR("BeginCrosses", "glPushAttrib 2");
1148        SetIndicatorTransform();
1149        glEnable(GL_DEPTH_TEST);
1150        CHECK_GL_ERROR("BeginCrosses", "glEnable GL_DEPTH_TEST");
1151        glBegin(GL_LINES);
1152    }
1153}
1154
1155void GLACanvas::EndCrosses()
1156{
1157    glEnd();
1158    if (cross_method == SPRITE) {
1159        CHECK_GL_ERROR("EndCrosses", "glEnd GL_POINTS");
1160    } else {
1161        CHECK_GL_ERROR("EndCrosses", "glEnd GL_LINES");
1162    }
1163    glPopAttrib();
1164    CHECK_GL_ERROR("EndCrosses", "glPopAttrib");
1165}
1166
1167void GLACanvas::DrawCross(glaCoord x, glaCoord y, glaCoord z)
1168{
1169    if (cross_method == SPRITE) {
1170        PlaceVertex(x, y, z);
1171    } else {
1172        double X, Y, Z;
1173        if (!Transform(Vector3(x, y, z), &X, &Y, &Z)) {
1174            printf("bad transform\n");
1175            return;
1176        }
1177        // Stuff behind us (in perspective view) will get clipped,
1178        // but we can save effort with a cheap check here.
1179        if (Z > 0) {
1180            // Round to integers before adding on the offsets for the
1181            // cross arms to avoid uneven crosses.
1182            X = rint(X);
1183            Y = rint(Y);
1184            PlaceVertex(X - 3, Y - 3, Z);
1185            PlaceVertex(X + 3, Y + 3, Z);
1186            PlaceVertex(X - 3, Y + 3, Z);
1187            PlaceVertex(X + 3, Y - 3, Z);
1188        }
1189    }
1190#ifdef GLA_DEBUG
1191    m_Vertices++;
1192#endif
1193}
1194
1195void GLACanvas::DrawRing(glaCoord x, glaCoord y)
1196{
1197    // Draw an unfilled circle
1198    const Double radius = 4;
1199    assert(m_Quadric);
1200    glMatrixMode(GL_MODELVIEW);
1201    CHECK_GL_ERROR("DrawRing", "glMatrixMode");
1202    glPushMatrix();
1203    CHECK_GL_ERROR("DrawRing", "glPushMatrix");
1204    glTranslated(x, y, 0.0);
1205    CHECK_GL_ERROR("DrawRing", "glTranslated");
1206    gluDisk(m_Quadric, radius - 1.0, radius, 12, 1);
1207    CHECK_GL_ERROR("DrawRing", "gluDisk");
1208    glPopMatrix();
1209    CHECK_GL_ERROR("DrawRing", "glPopMatrix");
1210}
1211
1212void GLACanvas::DrawRectangle(gla_colour edge, gla_colour fill,
1213                              glaCoord x0, glaCoord y0, glaCoord w, glaCoord h)
1214{
1215    // Draw a filled rectangle with an edge in the indicator plane.
1216    // (x0, y0) specify the bottom-left corner of the rectangle and (w, h) the
1217    // size.
1218
1219    SetColour(fill);
1220    BeginQuadrilaterals();
1221    PlaceIndicatorVertex(x0, y0);
1222    PlaceIndicatorVertex(x0 + w, y0);
1223    PlaceIndicatorVertex(x0 + w, y0 + h);
1224    PlaceIndicatorVertex(x0, y0 + h);
1225    EndQuadrilaterals();
1226
1227    if (edge != fill) {
1228        SetColour(edge);
1229        BeginLines();
1230        PlaceIndicatorVertex(x0, y0);
1231        PlaceIndicatorVertex(x0 + w, y0);
1232        PlaceIndicatorVertex(x0 + w, y0 + h);
1233        PlaceIndicatorVertex(x0, y0 + h);
1234        EndLines();
1235    }
1236}
1237
1238void
1239GLACanvas::DrawShadedRectangle(const GLAPen & fill_bot, const GLAPen & fill_top,
1240                               glaCoord x0, glaCoord y0,
1241                               glaCoord w, glaCoord h)
1242{
1243    // Draw a graduated filled rectangle in the indicator plane.
1244    // (x0, y0) specify the bottom-left corner of the rectangle and (w, h) the
1245    // size.
1246
1247    glShadeModel(GL_SMOOTH);
1248    CHECK_GL_ERROR("DrawShadedRectangle", "glShadeModel GL_SMOOTH");
1249    BeginQuadrilaterals();
1250    SetColour(fill_bot);
1251    PlaceIndicatorVertex(x0, y0);
1252    PlaceIndicatorVertex(x0 + w, y0);
1253    SetColour(fill_top);
1254    PlaceIndicatorVertex(x0 + w, y0 + h);
1255    PlaceIndicatorVertex(x0, y0 + h);
1256    EndQuadrilaterals();
1257    glShadeModel(GL_FLAT);
1258    CHECK_GL_ERROR("DrawShadedRectangle", "glShadeModel GL_FLAT");
1259}
1260
1261void GLACanvas::DrawCircle(gla_colour edge, gla_colour fill,
1262                           glaCoord cx, glaCoord cy, glaCoord radius)
1263{
1264    // Draw a filled circle with an edge.
1265    SetColour(fill);
1266    glMatrixMode(GL_MODELVIEW);
1267    CHECK_GL_ERROR("DrawCircle", "glMatrixMode");
1268    glPushMatrix();
1269    CHECK_GL_ERROR("DrawCircle", "glPushMatrix");
1270    glTranslated(cx, cy, 0.0);
1271    CHECK_GL_ERROR("DrawCircle", "glTranslated");
1272    assert(m_Quadric);
1273    gluDisk(m_Quadric, 0.0, radius, 36, 1);
1274    CHECK_GL_ERROR("DrawCircle", "gluDisk");
1275    SetColour(edge);
1276    gluDisk(m_Quadric, radius - 1.0, radius, 36, 1);
1277    CHECK_GL_ERROR("DrawCircle", "gluDisk (2)");
1278    glPopMatrix();
1279    CHECK_GL_ERROR("DrawCircle", "glPopMatrix");
1280}
1281
1282void GLACanvas::DrawSemicircle(gla_colour edge, gla_colour fill,
1283                               glaCoord cx, glaCoord cy,
1284                               glaCoord radius, glaCoord start)
1285{
1286    // Draw a filled semicircle with an edge.
1287    // The semicircle extends from "start" deg to "start"+180 deg (increasing
1288    // clockwise, 0 deg upwards).
1289    SetColour(fill);
1290    glMatrixMode(GL_MODELVIEW);
1291    CHECK_GL_ERROR("DrawSemicircle", "glMatrixMode");
1292    glPushMatrix();
1293    CHECK_GL_ERROR("DrawSemicircle", "glPushMatrix");
1294    glTranslated(cx, cy, 0.0);
1295    CHECK_GL_ERROR("DrawSemicircle", "glTranslated");
1296    assert(m_Quadric);
1297    gluPartialDisk(m_Quadric, 0.0, radius, 36, 1, start, 180.0);
1298    CHECK_GL_ERROR("DrawSemicircle", "gluPartialDisk");
1299    SetColour(edge);
1300    gluPartialDisk(m_Quadric, radius - 1.0, radius, 36, 1, start, 180.0);
1301    CHECK_GL_ERROR("DrawSemicircle", "gluPartialDisk (2)");
1302    glPopMatrix();
1303    CHECK_GL_ERROR("DrawSemicircle", "glPopMatrix");
1304}
1305
1306void
1307GLACanvas::DrawTriangle(gla_colour edge, gla_colour fill,
1308                        const Vector3 &p0, const Vector3 &p1, const Vector3 &p2)
1309{
1310    // Draw a filled triangle with an edge.
1311
1312    SetColour(fill);
1313    BeginTriangles();
1314    PlaceIndicatorVertex(p0.GetX(), p0.GetY());
1315    PlaceIndicatorVertex(p1.GetX(), p1.GetY());
1316    PlaceIndicatorVertex(p2.GetX(), p2.GetY());
1317    EndTriangles();
1318
1319    SetColour(edge);
1320    glBegin(GL_LINE_STRIP);
1321    PlaceIndicatorVertex(p0.GetX(), p0.GetY());
1322    PlaceIndicatorVertex(p1.GetX(), p1.GetY());
1323    PlaceIndicatorVertex(p2.GetX(), p2.GetY());
1324    glEnd();
1325    CHECK_GL_ERROR("DrawTriangle", "glEnd GL_LINE_STRIP");
1326}
1327
1328void GLACanvas::EnableDashedLines()
1329{
1330    // Enable dashed lines, and start drawing in them.
1331
1332    glLineStipple(1, 0x3333);
1333    CHECK_GL_ERROR("EnableDashedLines", "glLineStipple");
1334    glEnable(GL_LINE_STIPPLE);
1335    CHECK_GL_ERROR("EnableDashedLines", "glEnable GL_LINE_STIPPLE");
1336}
1337
1338void GLACanvas::DisableDashedLines()
1339{
1340    glDisable(GL_LINE_STIPPLE);
1341    CHECK_GL_ERROR("DisableDashedLines", "glDisable GL_LINE_STIPPLE");
1342}
1343
1344bool GLACanvas::Transform(const Vector3 & v,
1345                          double* x_out, double* y_out, double* z_out) const
1346{
1347    // Convert from data coordinates to screen coordinates.
1348
1349    // Perform the projection.
1350    return gluProject(v.GetX(), v.GetY(), v.GetZ(),
1351                      modelview_matrix, projection_matrix, viewport,
1352                      x_out, y_out, z_out);
1353}
1354
1355void GLACanvas::ReverseTransform(Double x, Double y,
1356                                 double* x_out, double* y_out, double* z_out) const
1357{
1358    // Convert from screen coordinates to data coordinates.
1359
1360    // Perform the projection.
1361    gluUnProject(x, y, 0.0, modelview_matrix, projection_matrix, viewport,
1362                 x_out, y_out, z_out);
1363    CHECK_GL_ERROR("ReverseTransform", "gluUnProject");
1364}
1365
1366Double GLACanvas::SurveyUnitsAcrossViewport() const
1367{
1368    // Measure the current viewport in survey units, taking into account the
1369    // current display scale.
1370
1371    assert(m_Scale != 0.0);
1372    list_flags |= INVALIDATE_ON_SCALE;
1373    return m_VolumeDiameter / m_Scale;
1374}
1375
1376void GLACanvas::ToggleSmoothShading()
1377{
1378    m_SmoothShading = !m_SmoothShading;
1379}
1380
1381void GLACanvas::ToggleTextured()
1382{
1383    m_Textured = !m_Textured;
1384    if (m_Textured && m_Texture == 0) {
1385        glGenTextures(1, &m_Texture);
1386        CHECK_GL_ERROR("ToggleTextured", "glGenTextures");
1387
1388        glBindTexture(GL_TEXTURE_2D, m_Texture);
1389        CHECK_GL_ERROR("ToggleTextured", "glBindTexture");
1390
1391        ::wxInitAllImageHandlers();
1392
1393        wxImage img;
1394        wxString texture(wmsg_cfgpth());
1395        texture += wxCONFIG_PATH_SEPARATOR;
1396        texture += wxT("icons");
1397        texture += wxCONFIG_PATH_SEPARATOR;
1398        texture += wxT("texture.png");
1399        if (!img.LoadFile(texture, wxBITMAP_TYPE_PNG)) {
1400            // FIXME
1401            fprintf(stderr, "Couldn't load image.\n");
1402            exit(1);
1403        }
1404
1405        // Generate mipmaps.
1406        gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, // was GL_LUMINANCE
1407                          img.GetWidth(), img.GetHeight(),
1408                          GL_RGB, GL_UNSIGNED_BYTE, img.GetData());
1409        CHECK_GL_ERROR("ToggleTextured", "gluBuild2DMipmaps");
1410
1411        glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
1412        CHECK_GL_ERROR("ToggleTextured", "glTexEnvi");
1413    }
1414}
1415
1416bool GLACanvas::SaveScreenshot(const wxString & fnm, int type) const
1417{
1418    int width;
1419    int height;
1420    GetSize(&width, &height);
1421    unsigned char *pixels = (unsigned char *)malloc(3 * width * (height + 1));
1422    if (!pixels) return false;
1423    glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, (GLvoid *)pixels);
1424    unsigned char * tmp_row = pixels + 3 * width * height;
1425    // We need to flip the image vertically - this approach should be more
1426    // efficient than using wxImage::Mirror(false) as that creates a new
1427    // wxImage object.
1428    for (int y = height / 2 - 1; y >= 0; --y) {
1429        unsigned char * upper = pixels + 3 * width * y;
1430        unsigned char * lower = pixels + 3 * width * (height - y - 1);
1431        memcpy(tmp_row, upper, 3 * width);
1432        memcpy(upper, lower, 3 * width);
1433        memcpy(lower, tmp_row, 3 * width);
1434    }
1435    // NB wxImage constructor calls free(pixels) for us.
1436    wxImage grab(width, height, pixels);
1437    return grab.SaveFile(fnm, type);
1438}
Note: See TracBrowser for help on using the repository browser.