source: git/src/gla-gl.cc @ 9f6ea6c

RELEASE/1.1RELEASE/1.2debug-cidebug-ci-sanitisersfaster-cavernloglog-selectstereostereo-2025walls-datawalls-data-hanging-as-warningwarn-only-for-hanging-survey
Last change on this file since 9f6ea6c was 81aea4e, checked in by Olly Betts <olly@…>, 19 years ago

Add 2D blob printing call so that drawing the measuring line blob works
correctly if we have to do it by drawing a small disk.

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

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