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

RELEASE/1.1RELEASE/1.2debug-cidebug-ci-sanitisersfaster-cavernloglog-selectstereowalls-datawalls-data-hanging-as-warning
Last change on this file since ac7e4da was 1bd4841, checked in by Olly Betts <olly@…>, 15 years ago

When saving a screenshot, perform the vertical flip of the image data in-place
rather than using wxImage::Mirror() which creates a whole new wxImage object.

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

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