source: git/src/gla-gl.cc @ 99d8798

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 99d8798 was b72f4b5, checked in by Olly Betts <olly@…>, 19 years ago

Apply Mark's OS X changes.

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

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