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

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 efa5aea was 4f3e5d1, checked in by Olly Betts <olly@…>, 19 years ago

Avoid SEGV if we can't call glGetString(GL_VERSION) before initialising OpenGL.

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

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