source: git/src/gla-gl.cc @ 4c46260

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 4c46260 was ff8de60, checked in by Olly Betts <olly@…>, 20 years ago

Fixed infinite loop.

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

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