source: git/src/gla-gl.cc @ 226b599e

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

src/gla-gl.cc: Add \n to commented out debug message.

git-svn-id: file:///home/survex-svn/survex/trunk@3530 4b37db11-9a0c-4f06-9ece-9ab7cdaee568

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