source: git/src/gla-gl.cc @ 0e08ec1

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 0e08ec1 was d67450e, checked in by Olly Betts <olly@…>, 19 years ago

Add Point::Invalidate() and Point::IsValid?() rather than directly setting
the x coordinate to DBL_MAX and testing that.

Add the mechanics to the OpenGL layer to allow a "wireframe" view of the tunnel
walls (not yet hooked up to the UI).

Add the mechanics to allow smooth shading (but we need to set normals before
we can useful turn this on).

Pass/return Vector3 instead of x,y,z triples in many places.

Factor out GfxCore::DrawArrow?() from DrawCompass?()/DrawClino?().

Point class now uses Vector3 as a base class, so the vec() method is no longer
required.

Fix presentation playing to actually work (it wasn't storing the speed so it
never played...)

Inline GLACanvas::SetTranslation?() and AddTranslation?().

Vector3::set() renamed to Vector3::assign() for consistency with C++ STL.

Display altitude to 2 decimal places rather than the nearest metre/foot.

LabelInfo::GetText?() now returns const ref to wxString rather than wxString.

Factor apart WIN32 and non-WIN32 versions of AvenAllowOnTop? class as the
code is much clearer that way.

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

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