source: git/src/gla-gl.cc @ 5f97258

RELEASE/1.1RELEASE/1.2debug-cidebug-ci-sanitisersfaster-cavernlogstereowalls-datawalls-data-hanging-as-warning
Last change on this file since 5f97258 was 5627cbb, checked in by Olly Betts <olly@…>, 14 years ago
  • Fix to build with a "unicode" build of wx.
  • Add "Copy" button to the About dialog to copy the system info to the clipboard.
  • List OpenGL extensions last, since there are usually lots of them with a modern gfx card.
  • When processing survey data, auto-scroll the log window until we've reported a warning or error.
  • Put the survey data log window in a splitter in the standard frame rather than having a separate frame for it.

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

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