source: git/src/gla-gl.cc @ 08253d9

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 08253d9 was 08253d9, checked in by Olly Betts <olly@…>, 18 years ago

Just set the pan and tilt directly as OpenGL rotations rather than messing
around setting up and then decoding a quaternion. This fixes some glitches
when tilting while looking east, and eliminates 2 source files and 2 headers!

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

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