source: git/src/gla-gl.cc @ 967723f

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

src/fnt.h,src/gla-gl.cc: Pass the length of the string to
fntTexFont::puts() rather than recalculating it inside the
method.

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

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