source: git/src/gla-gl.cc @ 5627cbb

RELEASE/1.1RELEASE/1.2debug-cidebug-ci-sanitisersstereowalls-data
Last change on this file since 5627cbb was 5627cbb, checked in by Olly Betts <olly@…>, 14 years ago
  • Fix to build with a "unicode" build of wx.
  • Add "Copy" button to the About dialog to copy the system info to the clipboard.
  • List OpenGL extensions last, since there are usually lots of them with a modern gfx card.
  • When processing survey data, auto-scroll the log window until we've reported a warning or error.
  • Put the survey data log window in a splitter in the standard frame rather than having a separate frame for it.

git-svn-id: file:///home/survex-svn/survex/branches/survex-1_1@3356 4b37db11-9a0c-4f06-9ece-9ab7cdaee568

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