source: git/src/gla-gl.cc @ 29d5b30

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

(MS Windows version): aven: Fix cursor keys to pan survey (the answer which
had been eluding me for so long was to add the window flag wxWANTS_CHARS).

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

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