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

RELEASE/1.2debug-cidebug-ci-sanitisersstereowalls-datawalls-data-hanging-as-warning
Last change on this file since ad661cc was ee63994, checked in by Olly Betts <olly@…>, 11 years ago

configure.ac,src/gla-gl.cc,src/glbitmapfont.h: It seems we need to
include GL/gl.h before we can safely include GL/glext.h (at least on
Debian wheezy).

  • Property mode set to 100644
File size: 44.8 KB
Line 
1//
2//  gla-gl.cc
3//
4//  OpenGL implementation for the GLA abstraction layer.
5//
6//  Copyright (C) 2002-2003,2005 Mark R. Shinwell
7//  Copyright (C) 2003,2004,2005,2006,2007,2010,2011,2012,2013 Olly Betts
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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
22//
23
24#ifdef HAVE_CONFIG_H
25#include <config.h>
26#endif
27
28#include <wx/confbase.h>
29#include <wx/image.h>
30
31#include <algorithm>
32
33#include "aven.h"
34#include "gla.h"
35#include "message.h"
36#include "useful.h"
37
38#ifdef HAVE_GL_GL_H
39# include <GL/gl.h>
40#elif defined HAVE_OPENGL_GL_H
41# include <OpenGL/gl.h>
42#endif
43
44#ifdef HAVE_GL_GLEXT_H
45# include <GL/glext.h>
46#elif defined HAVE_OPENGL_GLEXT_H
47# include <OpenGL/glext.h>
48#endif
49
50#ifndef GL_POINT_SIZE_MAX
51#define GL_POINT_SIZE_MAX 0x8127
52#endif
53#ifndef GL_POINT_SPRITE
54#define GL_POINT_SPRITE 0x8861
55#endif
56#ifndef GL_COORD_REPLACE
57#define GL_COORD_REPLACE 0x8862
58#endif
59// GL_POINT_SIZE_RANGE is deprecated in OpenGL 1.2 and later, and replaced by
60// GL_SMOOTH_POINT_SIZE_RANGE.
61#ifndef GL_SMOOTH_POINT_SIZE_RANGE
62#define GL_SMOOTH_POINT_SIZE_RANGE GL_POINT_SIZE_RANGE
63#endif
64// GL_POINT_SIZE_GRANULARITY is deprecated in OpenGL 1.2 and later, and
65// replaced by GL_SMOOTH_POINT_SIZE_GRANULARITY.
66#ifndef GL_SMOOTH_POINT_SIZE_GRANULARITY
67#define GL_SMOOTH_POINT_SIZE_GRANULARITY GL_POINT_SIZE_GRANULARITY
68#endif
69// GL_ALIASED_POINT_SIZE_RANGE was added in OpenGL 1.2.
70#ifndef GL_ALIASED_POINT_SIZE_RANGE
71#define GL_ALIASED_POINT_SIZE_RANGE 0x846D
72#endif
73
74using namespace std;
75
76const int BLOB_DIAMETER = 5;
77
78static bool opengl_initialised = false;
79
80string GetGLSystemDescription()
81{
82    // If OpenGL isn't initialised we may get a SEGV from glGetString.
83    if (!opengl_initialised)
84        return "No OpenGL information available yet - try opening a file.";
85    const char *p = (const char*)glGetString(GL_VERSION);
86    if (!p)
87        return "Couldn't read OpenGL version!";
88
89    string info;
90    info += "OpenGL ";
91    info += p;
92    info += '\n';
93    info += (const char*)glGetString(GL_VENDOR);
94    info += '\n';
95    info += (const char*)glGetString(GL_RENDERER);
96#if defined __WXGTK__ || defined __WXX11__ || defined __WXMOTIF__
97    info += string_format("\nGLX %0.1f\n", wxGLCanvas::GetGLXVersion() * 0.1);
98#else
99    info += '\n';
100#endif
101
102    GLint red, green, blue;
103    glGetIntegerv(GL_RED_BITS, &red);
104    glGetIntegerv(GL_GREEN_BITS, &green);
105    glGetIntegerv(GL_BLUE_BITS, &blue);
106    GLint max_texture_size;
107    glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size);
108    GLint max_viewport[2];
109    glGetIntegerv(GL_MAX_VIEWPORT_DIMS, max_viewport);
110    GLdouble point_size_range[2];
111    glGetDoublev(GL_SMOOTH_POINT_SIZE_RANGE, point_size_range);
112    GLdouble point_size_granularity;
113    glGetDoublev(GL_SMOOTH_POINT_SIZE_GRANULARITY, &point_size_granularity);
114    info += string_format("R%dG%dB%d\n"
115             "Max Texture size: %dx%d\n"
116             "Max Viewport size: %dx%d\n"
117             "Smooth Point Size %.3f-%.3f (granularity %.3f)",
118             (int)red, (int)green, (int)blue,
119             (int)max_texture_size, (int)max_texture_size,
120             (int)max_viewport[0], (int)max_viewport[1],
121             point_size_range[0], point_size_range[1],
122             point_size_granularity);
123    glGetDoublev(GL_ALIASED_POINT_SIZE_RANGE, point_size_range);
124    if (glGetError() != GL_INVALID_ENUM) {
125        info += string_format("\nAliased point size %.3f-%.3f",
126                              point_size_range[0], point_size_range[1]);
127    }
128
129    info += "\nDouble buffered: ";
130    if (double_buffered)
131        info += "true";
132    else
133        info += "false";
134
135    const GLubyte* gl_extensions = glGetString(GL_EXTENSIONS);
136    if (*gl_extensions) {
137        info += '\n';
138        info += (const char*)gl_extensions;
139    }
140    return info;
141}
142
143// Important: CHECK_GL_ERROR must not be called within a glBegin()/glEnd() pair
144//            (thus it must not be called from BeginLines(), etc., or within a
145//             BeginLines()/EndLines() block etc.)
146#define CHECK_GL_ERROR(M, F) do { \
147    if (!opengl_initialised) { \
148        wxLogError(wxT(__FILE__":"STRING(__LINE__)": OpenGL not initialised before (call "F" in method "M")")); \
149    } \
150    GLenum error_code_ = glGetError(); \
151    if (error_code_ != GL_NO_ERROR) { \
152        wxLogError(wxT(__FILE__":"STRING(__LINE__)": OpenGL error: %s " \
153                   "(call "F" in method "M")"), \
154                   wxString((const char *)gluErrorString(error_code_), \
155                            wxConvUTF8).c_str()); \
156    } \
157} while (0)
158
159//
160//  GLAPen
161//
162
163GLAPen::GLAPen()
164{
165    components[0] = components[1] = components[2] = 0.0;
166}
167
168void GLAPen::SetColour(double red, double green, double blue)
169{
170    components[0] = red;
171    components[1] = green;
172    components[2] = blue;
173}
174
175double GLAPen::GetRed() const
176{
177    return components[0];
178}
179
180double GLAPen::GetGreen() const
181{
182    return components[1];
183}
184
185double GLAPen::GetBlue() const
186{
187    return components[2];
188}
189
190void GLAPen::Interpolate(const GLAPen& pen, double how_far)
191{
192    components[0] += how_far * (pen.GetRed() - components[0]);
193    components[1] += how_far * (pen.GetGreen() - components[1]);
194    components[2] += how_far * (pen.GetBlue() - components[2]);
195}
196
197struct ColourTriple {
198    // RGB triple: values are from 0-255 inclusive for each component.
199    unsigned char r, g, b;
200};
201
202// These must be in the same order as the entries in COLOURS[] below.
203const ColourTriple COLOURS[] = {
204    { 0, 0, 0 },       // black
205    { 100, 100, 100 }, // grey
206    { 180, 180, 180 }, // light grey
207    { 140, 140, 140 }, // light grey 2
208    { 90, 90, 90 },    // dark grey
209    { 255, 255, 255 }, // white
210    { 0, 100, 255},    // turquoise
211    { 0, 255, 40 },    // green
212    { 150, 205, 224 }, // indicator 1
213    { 114, 149, 160 }, // indicator 2
214    { 255, 255, 0 },   // yellow
215    { 255, 0, 0 },     // red
216    { 40, 40, 255 },   // blue
217};
218
219bool GLAList::need_to_generate() {
220    // Bail out if the list is already cached, or can't usefully be cached.
221    if (flags & (GLACanvas::CACHED|GLACanvas::NEVER_CACHE))
222        return false;
223
224    // Create a new OpenGL list to hold this sequence of drawing
225    // operations.
226    if (gl_list == 0) {
227        gl_list = glGenLists(1);
228        CHECK_GL_ERROR("GLAList::need_to_generate", "glGenLists");
229#ifdef GLA_DEBUG
230        printf("glGenLists(1) returned %u\n", (unsigned)gl_list);
231#endif
232        if (gl_list == 0) {
233            // If we can't create a list for any reason, fall back to just
234            // drawing directly, and flag the list as NEVER_CACHE as there's
235            // unlikely to be much point calling glGenLists() again.
236            flags = GLACanvas::NEVER_CACHE;
237            return false;
238        }
239
240        // We should have 256 lists for font drawing and a dozen or so for 2D
241        // and 3D lists.  So something is amiss if we've generated 1000 lists,
242        // probably a infinite loop in the lazy list mechanism.
243        assert(gl_list < 1000);
244    }
245    // http://www.opengl.org/resources/faq/technical/displaylist.htm advises:
246    //
247    // "Stay away from GL_COMPILE_AND_EXECUTE mode. Instead, create the
248    // list using GL_COMPILE mode, then execute it with glCallList()."
249    glNewList(gl_list, GL_COMPILE);
250    CHECK_GL_ERROR("GLAList::need_to_generate", "glNewList");
251    return true;
252}
253
254void GLAList::finalise(unsigned int list_flags)
255{
256    glEndList();
257    CHECK_GL_ERROR("GLAList::finalise", "glEndList");
258    if (list_flags & GLACanvas::NEVER_CACHE) {
259        glDeleteLists(gl_list, 1);
260        CHECK_GL_ERROR("GLAList::finalise", "glDeleteLists");
261        gl_list = 0;
262        flags = GLACanvas::NEVER_CACHE;
263    } else {
264        flags = list_flags | GLACanvas::CACHED;
265    }
266}
267
268bool GLAList::DrawList() const {
269    if ((flags & GLACanvas::CACHED) == 0)
270        return false;
271    glCallList(gl_list);
272    CHECK_GL_ERROR("GLAList::DrawList", "glCallList");
273    return true;
274}
275
276//
277//  GLACanvas
278//
279
280BEGIN_EVENT_TABLE(GLACanvas, wxGLCanvas)
281    EVT_SIZE(GLACanvas::OnSize)
282END_EVENT_TABLE()
283
284// Pass wxWANTS_CHARS so that the window gets cursor keys on MS Windows.
285GLACanvas::GLACanvas(wxWindow* parent, int id)
286    : wxGLCanvas(parent, id, NULL, wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS),
287      ctx(this), m_Translation(), blob_method(UNKNOWN), cross_method(UNKNOWN),
288      x_size(0), y_size(0)
289{
290    // Constructor.
291
292    m_Quadric = NULL;
293    m_Pan = 0.0;
294    m_Tilt = 0.0;
295    m_Scale = 0.0;
296    m_VolumeDiameter = 1.0;
297    m_SmoothShading = false;
298    m_Texture = 0;
299    m_Textured = false;
300    m_Perspective = false;
301    m_Fog = false;
302    m_AntiAlias = false;
303    list_flags = 0;
304}
305
306GLACanvas::~GLACanvas()
307{
308    // Destructor.
309
310    if (m_Quadric) {
311        gluDeleteQuadric(m_Quadric);
312        CHECK_GL_ERROR("~GLACanvas", "gluDeleteQuadric");
313    }
314}
315
316void GLACanvas::FirstShow()
317{
318    // Update our record of the client area size and centre.
319    GetClientSize(&x_size, &y_size);
320    if (x_size < 1) x_size = 1;
321    if (y_size < 1) y_size = 1;
322
323    ctx.SetCurrent(*this);
324    opengl_initialised = true;
325
326    // Set the background colour of the canvas to black.
327    glClearColor(0.0, 0.0, 0.0, 1.0);
328    CHECK_GL_ERROR("FirstShow", "glClearColor");
329
330    // Set viewport.
331    glViewport(0, 0, x_size, y_size);
332    CHECK_GL_ERROR("FirstShow", "glViewport");
333
334    save_hints = false;
335    vendor = wxString((const char *)glGetString(GL_VENDOR), wxConvUTF8);
336    renderer = wxString((const char *)glGetString(GL_RENDERER), wxConvUTF8);
337    wxConfigBase * cfg = wxConfigBase::Get();
338    {
339        wxString s;
340        if (cfg->Read(wxT("opengl_vendor"), &s, wxString()) && s == vendor &&
341            cfg->Read(wxT("opengl_renderer"), &s, wxString()) && s == renderer) {
342            // The vendor and renderer are the same as the values we have cached,
343            // so use the hints we have cached.
344            int v;
345            if (cfg->Read(wxT("blob_method"), &v, 0) &&
346                (v == POINT || v == LINES)) {
347                // How to draw blobs.
348                blob_method = v;
349            }
350            if (cfg->Read(wxT("cross_method"), &v, 0) &&
351                (v == SPRITE || v == LINES)) {
352                // How to draw crosses.
353                cross_method = v;
354            }
355        }
356    }
357
358    if (m_Quadric) return;
359    // One time initialisation follows.
360
361    m_Quadric = gluNewQuadric();
362    CHECK_GL_ERROR("FirstShow", "gluNewQuadric");
363    if (!m_Quadric) {
364        abort(); // FIXME need to cope somehow
365    }
366
367    glShadeModel(GL_FLAT);
368    CHECK_GL_ERROR("FirstShow", "glShadeModel");
369    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); // So text works.
370    CHECK_GL_ERROR("FirstShow", "glPolygonMode");
371    //glAlphaFunc(GL_GREATER, 0.5f);
372    //CHECK_GL_ERROR("FirstShow", "glAlphaFunc");
373
374    // We want glReadPixels() to read from the front buffer (which is the
375    // default for single-buffered displays).
376    if (double_buffered) {
377        glReadBuffer(GL_FRONT);
378        CHECK_GL_ERROR("FirstShow", "glReadBuffer");
379    }
380
381    // Grey fog effect.
382    GLfloat fogcolour[4] = { 0.5, 0.5, 0.5, 1.0 };
383    glFogfv(GL_FOG_COLOR, fogcolour);
384    CHECK_GL_ERROR("FirstShow", "glFogfv");
385
386    // Linear fogging.
387    glFogi(GL_FOG_MODE, GL_LINEAR);
388    CHECK_GL_ERROR("FirstShow", "glFogi");
389
390    // Optimise for speed (compute fog per vertex).
391    glHint(GL_FOG_HINT, GL_FASTEST);
392    CHECK_GL_ERROR("FirstShow", "glHint");
393
394    // No padding on pixel packing and unpacking (default is to pad each
395    // line to a multiple of 4 bytes).
396    glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // For setting texture maps.
397    CHECK_GL_ERROR("FirstShow", "glPixelStorei GL_UNPACK_ALIGNMENT");
398    glPixelStorei(GL_PACK_ALIGNMENT, 1); // For screengrabs and movies.
399    CHECK_GL_ERROR("FirstShow", "glPixelStorei GL_PACK_ALIGNMENT");
400
401    // Load font
402    wxString path = wmsg_cfgpth();
403    path += wxCONFIG_PATH_SEPARATOR;
404    path += wxT("unifont.pixelfont");
405    if (!m_Font.load(path)) {
406        // FIXME: do something better.
407        // We have this message available: Error in format of font file “%s”
408        fprintf(stderr, "Couldn't load font.\n");
409        exit(1);
410    }
411
412    if (blob_method == UNKNOWN) {
413        // Check if we can use GL_POINTS to plot blobs at stations.
414        GLdouble point_size_range[2];
415        glGetDoublev(GL_SMOOTH_POINT_SIZE_RANGE, point_size_range);
416        CHECK_GL_ERROR("FirstShow", "glGetDoublev GL_SMOOTH_POINT_SIZE_RANGE");
417        if (point_size_range[0] <= BLOB_DIAMETER &&
418            point_size_range[1] >= BLOB_DIAMETER) {
419            blob_method = POINT;
420        } else {
421            blob_method = LINES;
422        }
423        save_hints = true;
424    }
425
426    if (blob_method == POINT) {
427        glPointSize(BLOB_DIAMETER);
428        CHECK_GL_ERROR("FirstShow", "glPointSize");
429    }
430
431    // Point sprites provide an easy, fast way for us to draw crosses by
432    // texture mapping GL points.
433    //
434    // If we have OpenGL >= 2.0 then we definitely have GL_POINT_SPRITE.
435    // Otherwise see if we have the GL_ARB_point_sprite or GL_NV_point_sprite
436    // extensions.
437    //
438    // The symbolic constants GL_POINT_SPRITE, GL_POINT_SPRITE_ARB, and
439    // GL_POINT_SPRITE_NV all give the same number so it doesn't matter
440    // which we use.
441    if (cross_method == UNKNOWN) {
442        bool glpoint_sprite = false;
443        float maxSize = 0.0f;
444        glGetFloatv(GL_POINT_SIZE_MAX, &maxSize);
445        if (maxSize >= 8) {
446            glpoint_sprite = (atoi((const char *)glGetString(GL_VERSION)) >= 2);
447            if (!glpoint_sprite) {
448                const char * p = (const char *)glGetString(GL_EXTENSIONS);
449                while (true) {
450                    size_t l = 0;
451                    if (memcmp(p, "GL_ARB_point_sprite", 19) == 0) {
452                        l = 19;
453                    } else if (memcmp(p, "GL_NV_point_sprite", 18) == 0) {
454                        l = 18;
455                    }
456                    if (l) {
457                        p += l;
458                        if (*p == '\0' || *p == ' ') {
459                            glpoint_sprite = true;
460                            break;
461                        }
462                    }
463                    p = strchr(p + 1, ' ');
464                    if (!p) break;
465                    ++p;
466                }
467            }
468        }
469        cross_method = glpoint_sprite ? SPRITE : LINES;
470        save_hints = true;
471    }
472
473    if (cross_method == SPRITE) {
474        glGenTextures(1, &m_CrossTexture);
475        CHECK_GL_ERROR("FirstShow", "glGenTextures");
476        glBindTexture(GL_TEXTURE_2D, m_CrossTexture);
477        CHECK_GL_ERROR("FirstShow", "glBindTexture");
478        // Cross image for drawing crosses using texture mapped point sprites.
479        const unsigned char crossteximage[128] = {
480              0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
481            255,255,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,255,255,  0,  0,
482              0,  0,255,255,  0,  0,  0,  0,  0,  0,255,255,  0,  0,  0,  0,
483              0,  0,  0,  0,255,255,  0,  0,255,255,  0,  0,  0,  0,  0,  0,
484              0,  0,  0,  0,  0,  0,255,255,  0,  0,  0,  0,  0,  0,  0,  0,
485              0,  0,  0,  0,255,255,  0,  0,255,255,  0,  0,  0,  0,  0,  0,
486              0,  0,255,255,  0,  0,  0,  0,  0,  0,255,255,  0,  0,  0,  0,
487            255,255,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,255,255,  0,  0
488        };
489        glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
490        CHECK_GL_ERROR("FirstShow", "glPixelStorei");
491        glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
492        CHECK_GL_ERROR("FirstShow", "glTexEnvi");
493        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
494        CHECK_GL_ERROR("FirstShow", "glTexParameteri GL_TEXTURE_WRAP_S");
495        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
496        CHECK_GL_ERROR("FirstShow", "glTexParameteri GL_TEXTURE_WRAP_T");
497        glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, 8, 8, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, (GLvoid *)crossteximage);
498        CHECK_GL_ERROR("FirstShow", "glTexImage2D");
499        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
500        CHECK_GL_ERROR("FirstShow", "glTexParameteri GL_TEXTURE_MAG_FILTER");
501        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
502        CHECK_GL_ERROR("FirstShow", "glTexParameteri GL_TEXTURE_MIN_FILTER");
503    }
504}
505
506void GLACanvas::Clear()
507{
508    // Clear the canvas.
509
510    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
511    CHECK_GL_ERROR("Clear", "glClear");
512}
513
514void GLACanvas::SetScale(Double scale)
515{
516    if (scale != m_Scale) {
517        vector<GLAList>::iterator i;
518        for (i = drawing_lists.begin(); i != drawing_lists.end(); ++i) {
519            i->invalidate_if(INVALIDATE_ON_SCALE);
520        }
521
522        m_Scale = scale;
523    }
524}
525
526void GLACanvas::OnSize(wxSizeEvent & event)
527{
528    wxSize size = event.GetSize();
529
530    unsigned int mask = 0;
531    if (size.GetWidth() != x_size) mask |= INVALIDATE_ON_X_RESIZE;
532    if (size.GetHeight() != y_size) mask |= INVALIDATE_ON_Y_RESIZE;
533    if (mask) {
534        vector<GLAList>::iterator i;
535        for (i = drawing_lists.begin(); i != drawing_lists.end(); ++i) {
536            i->invalidate_if(mask);
537        }
538
539        // The width and height go to zero when the panel is dragged right
540        // across so we clamp them to be at least 1 to avoid problems.
541        x_size = size.GetWidth();
542        y_size = size.GetHeight();
543        if (x_size < 1) x_size = 1;
544        if (y_size < 1) y_size = 1;
545    }
546
547    event.Skip();
548
549    if (!opengl_initialised) return;
550
551    // Set viewport.
552    glViewport(0, 0, x_size, y_size);
553    CHECK_GL_ERROR("OnSize", "glViewport");
554}
555
556void GLACanvas::AddTranslationScreenCoordinates(int dx, int dy)
557{
558    // Translate the data by a given amount, specified in screen coordinates.
559
560    // Find out how far the translation takes us in data coordinates.
561    SetDataTransform();
562
563    double x0, y0, z0;
564    double x, y, z;
565    gluUnProject(0.0, 0.0, 0.0, modelview_matrix, projection_matrix, viewport,
566                 &x0, &y0, &z0);
567    CHECK_GL_ERROR("AddTranslationScreenCoordinates", "gluUnProject");
568    gluUnProject(dx, -dy, 0.0, modelview_matrix, projection_matrix, viewport,
569                 &x, &y, &z);
570    CHECK_GL_ERROR("AddTranslationScreenCoordinates", "gluUnProject (2)");
571
572    // Apply the translation.
573    AddTranslation(Vector3(x - x0, y - y0, z - z0));
574}
575
576void GLACanvas::SetVolumeDiameter(glaCoord diameter)
577{
578    // Set the size of the data drawing volume by giving the diameter of the
579    // smallest sphere containing it.
580
581    m_VolumeDiameter = max(glaCoord(1.0), diameter);
582}
583
584void GLACanvas::StartDrawing()
585{
586    // Prepare for a redraw operation.
587
588    ctx.SetCurrent(*this);
589    glDepthMask(GL_TRUE);
590
591    if (!save_hints) return;
592
593    // We want to check on the second redraw.
594    static int draw_count = 2;
595    if (--draw_count != 0) return;
596
597    if (cross_method != LINES) {
598        SetColour(col_WHITE);
599        Clear();
600        SetDataTransform();
601        BeginCrosses();
602        DrawCross(-m_Translation.GetX(), -m_Translation.GetY(), -m_Translation.GetZ());
603        EndCrosses();
604        static const unsigned char expected_cross[64 * 3] = {
605#define o 0,0,0
606#define I 255,255,255
607            o, o, o, o, o, o, o, o,
608            I, o, o, o, o, o, I, o,
609            o, I, o, o, o, I, o, o,
610            o, o, I, o, I, o, o, o,
611            o, o, o, I, o, o, o, o,
612            o, o, I, o, I, o, o, o,
613            o, I, o, o, o, I, o, o,
614            I, o, o, o, o, o, I, o
615#undef o
616#undef I
617        };
618        if (!CheckVisualFidelity(expected_cross)) {
619            cross_method = LINES;
620            save_hints = true;
621        }
622    }
623
624    if (blob_method != LINES) {
625        SetColour(col_WHITE);
626        Clear();
627        SetDataTransform();
628        BeginBlobs();
629        DrawBlob(-m_Translation.GetX(), -m_Translation.GetY(), -m_Translation.GetZ());
630        EndBlobs();
631        static const unsigned char expected_blob[64 * 3] = {
632#define o 0,0,0
633#define I 255,255,255
634            o, o, o, o, o, o, o, o,
635            o, o, o, o, o, o, o, o,
636            o, o, I, I, I, o, o, o,
637            o, I, I, I, I, I, o, o,
638            o, I, I, I, I, I, o, o,
639            o, I, I, I, I, I, o, o,
640            o, o, I, I, I, o, o, o,
641            o, o, o, o, o, o, o, o
642#undef o
643#undef I
644        };
645        if (!CheckVisualFidelity(expected_blob)) {
646            blob_method = LINES;
647            save_hints = true;
648        }
649    }
650
651    wxConfigBase * cfg = wxConfigBase::Get();
652    cfg->Write(wxT("opengl_vendor"), vendor);
653    cfg->Write(wxT("opengl_renderer"), renderer);
654    cfg->Write(wxT("blob_method"), blob_method);
655    cfg->Write(wxT("cross_method"), cross_method);
656    cfg->Flush();
657    save_hints = false;
658}
659
660void GLACanvas::EnableSmoothPolygons(bool filled)
661{
662    // Prepare for drawing smoothly-shaded polygons.
663    // Only use this when required (in particular lines in lists may not be
664    // coloured correctly when this is enabled).
665
666    glPushAttrib(GL_ENABLE_BIT|GL_LIGHTING_BIT|GL_POLYGON_BIT);
667    if (filled) {
668        glShadeModel(GL_SMOOTH);
669        glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
670    } else {
671        glDisable(GL_LINE_SMOOTH);
672        glDisable(GL_TEXTURE_2D);
673        glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
674    }
675    CHECK_GL_ERROR("EnableSmoothPolygons", "glPolygonMode");
676
677    if (filled && m_SmoothShading) {
678        static const GLfloat mat_specular[] = { 0.2, 0.2, 0.2, 1.0 };
679        static const GLfloat light_position[] = { -1.0, -1.0, -1.0, 0.0 };
680        static const GLfloat light_ambient[] = { 0.3, 0.3, 0.3, 1.0 };
681        static const GLfloat light_diffuse[] = { 0.7, 0.7, 0.7, 1.0 };
682        glEnable(GL_COLOR_MATERIAL);
683        glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mat_specular);
684        glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 10.0);
685        glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
686        glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
687        glLightfv(GL_LIGHT0, GL_POSITION, light_position);
688        glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
689        glEnable(GL_LIGHTING);
690        glEnable(GL_LIGHT0);
691    }
692}
693
694void GLACanvas::DisableSmoothPolygons()
695{
696    glPopAttrib();
697}
698
699void GLACanvas::PlaceNormal(const Vector3 &v)
700{
701    // Add a normal (for polygons etc.)
702
703    glNormal3d(v.GetX(), v.GetY(), v.GetZ());
704}
705
706void GLACanvas::SetDataTransform()
707{
708    // Set projection.
709    glMatrixMode(GL_PROJECTION);
710    CHECK_GL_ERROR("SetDataTransform", "glMatrixMode");
711    glLoadIdentity();
712    CHECK_GL_ERROR("SetDataTransform", "glLoadIdentity");
713
714    double aspect = double(y_size) / double(x_size);
715
716    Double near_plane = 1.0;
717    if (m_Perspective) {
718        Double lr = near_plane * tan(rad(25.0));
719        Double far_plane = m_VolumeDiameter * 5 + near_plane; // FIXME: work out properly
720        Double tb = lr * aspect;
721        glFrustum(-lr, lr, -tb, tb, near_plane, far_plane);
722        CHECK_GL_ERROR("SetViewportAndProjection", "glFrustum");
723    } else {
724        near_plane = 0.0;
725        assert(m_Scale != 0.0);
726        Double lr = m_VolumeDiameter / m_Scale * 0.5;
727        Double far_plane = m_VolumeDiameter + near_plane;
728        Double tb = lr * aspect;
729        glOrtho(-lr, lr, -tb, tb, near_plane, far_plane);
730        CHECK_GL_ERROR("SetViewportAndProjection", "glOrtho");
731    }
732
733    // Set the modelview transform for drawing data.
734    glMatrixMode(GL_MODELVIEW);
735    CHECK_GL_ERROR("SetDataTransform", "glMatrixMode");
736    glLoadIdentity();
737    CHECK_GL_ERROR("SetDataTransform", "glLoadIdentity");
738    if (m_Perspective) {
739        glTranslated(0.0, 0.0, -near_plane);
740    } else {
741        glTranslated(0.0, 0.0, -0.5 * m_VolumeDiameter);
742    }
743    CHECK_GL_ERROR("SetDataTransform", "glTranslated");
744    // Get axes the correct way around (z upwards, y into screen)
745    glRotated(-90.0, 1.0, 0.0, 0.0);
746    CHECK_GL_ERROR("SetDataTransform", "glRotated");
747    glRotated(-m_Tilt, 1.0, 0.0, 0.0);
748    CHECK_GL_ERROR("SetDataTransform", "glRotated");
749    glRotated(m_Pan, 0.0, 0.0, 1.0);
750    CHECK_GL_ERROR("SetDataTransform", "CopyToOpenGL");
751    if (m_Perspective) {
752        glTranslated(m_Translation.GetX(),
753                     m_Translation.GetY(),
754                     m_Translation.GetZ());
755        CHECK_GL_ERROR("SetDataTransform", "glTranslated");
756    }
757
758    // Save projection matrix.
759    glGetDoublev(GL_PROJECTION_MATRIX, projection_matrix);
760    CHECK_GL_ERROR("SetDataTransform", "glGetDoublev");
761
762    // Save viewport coordinates.
763    glGetIntegerv(GL_VIEWPORT, viewport);
764    CHECK_GL_ERROR("SetDataTransform", "glGetIntegerv");
765
766    // Save modelview matrix.
767    glGetDoublev(GL_MODELVIEW_MATRIX, modelview_matrix);
768    CHECK_GL_ERROR("SetDataTransform", "glGetDoublev");
769
770    if (!m_Perspective) {
771        // Adjust the translation so we don't change the Z position of the model
772        double X, Y, Z;
773        gluProject(m_Translation.GetX(),
774                   m_Translation.GetY(),
775                   m_Translation.GetZ(),
776                   modelview_matrix, projection_matrix, viewport,
777                   &X, &Y, &Z);
778        double Tx, Ty, Tz;
779        gluUnProject(X, Y, 0.5, modelview_matrix, projection_matrix, viewport,
780                     &Tx, &Ty, &Tz);
781        glTranslated(Tx, Ty, Tz);
782        CHECK_GL_ERROR("SetDataTransform", "glTranslated");
783        glGetDoublev(GL_MODELVIEW_MATRIX, modelview_matrix);
784    }
785
786    glEnable(GL_DEPTH_TEST);
787    CHECK_GL_ERROR("SetDataTransform", "glEnable GL_DEPTH_TEST");
788
789    if (m_Textured) {
790        glBindTexture(GL_TEXTURE_2D, m_Texture);
791        glEnable(GL_TEXTURE_2D);
792        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
793        CHECK_GL_ERROR("ToggleTextured", "glTexParameteri GL_TEXTURE_WRAP_S");
794        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
795        CHECK_GL_ERROR("ToggleTextured", "glTexParameteri GL_TEXTURE_WRAP_T");
796        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
797        CHECK_GL_ERROR("ToggleTextured", "glTexParameteri GL_TEXTURE_MAG_FILTER");
798        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
799                        GL_LINEAR_MIPMAP_LINEAR);
800        CHECK_GL_ERROR("ToggleTextured", "glTexParameteri GL_TEXTURE_MIN_FILTER");
801        glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
802    } else {
803        glDisable(GL_TEXTURE_2D);
804    }
805    if (m_Fog) {
806        glFogf(GL_FOG_START, near_plane);
807        glFogf(GL_FOG_END, near_plane + m_VolumeDiameter);
808        glEnable(GL_FOG);
809    } else {
810        glDisable(GL_FOG);
811    }
812
813    if (m_AntiAlias) {
814        glEnable(GL_LINE_SMOOTH);
815        glEnable(GL_BLEND);
816        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
817    } else {
818        glDisable(GL_LINE_SMOOTH);
819    }
820}
821
822void GLACanvas::SetIndicatorTransform()
823{
824    list_flags |= NEVER_CACHE;
825
826    // Set the modelview transform and projection for drawing indicators.
827
828    glDisable(GL_DEPTH_TEST);
829    CHECK_GL_ERROR("SetIndicatorTransform", "glDisable GL_DEPTH_TEST");
830    glDisable(GL_FOG);
831    CHECK_GL_ERROR("SetIndicatorTransform", "glDisable GL_FOG");
832
833    // Just a simple 2D projection.
834    glMatrixMode(GL_PROJECTION);
835    CHECK_GL_ERROR("SetIndicatorTransform", "glMatrixMode");
836    glLoadIdentity();
837    CHECK_GL_ERROR("SetIndicatorTransform", "glLoadIdentity (2)");
838    gluOrtho2D(0, x_size, 0, y_size);
839    CHECK_GL_ERROR("SetIndicatorTransform", "gluOrtho2D");
840
841    // No modelview transform.
842    glMatrixMode(GL_MODELVIEW);
843    CHECK_GL_ERROR("SetIndicatorTransform", "glMatrixMode");
844    glLoadIdentity();
845    CHECK_GL_ERROR("SetIndicatorTransform", "glLoadIdentity");
846
847    glDisable(GL_TEXTURE_2D);
848    CHECK_GL_ERROR("SetIndicatorTransform", "glDisable GL_TEXTURE_2D");
849    glDisable(GL_BLEND);
850    CHECK_GL_ERROR("SetIndicatorTransform", "glDisable GL_BLEND");
851    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
852    CHECK_GL_ERROR("SetIndicatorTransform", "glTexParameteri GL_TEXTURE_WRAP_S");
853    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
854    CHECK_GL_ERROR("SetIndicatorTransform", "glTexParameteri GL_TEXTURE_WRAP_T");
855    glAlphaFunc(GL_GREATER, 0.5f);
856    CHECK_GL_ERROR("SetIndicatorTransform", "glAlphaFunc");
857    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
858    CHECK_GL_ERROR("SetIndicatorTransform", "glTexParameteri GL_TEXTURE_MAG_FILTER");
859    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
860    CHECK_GL_ERROR("SetIndicatorTransform", "glTexParameteri GL_TEXTURE_MIN_FILTER");
861    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
862    CHECK_GL_ERROR("SetIndicatorTransform", "glHint");
863}
864
865void GLACanvas::FinishDrawing()
866{
867    // Complete a redraw operation.
868
869    if (double_buffered) {
870        SwapBuffers();
871    } else {
872        glFlush();
873        CHECK_GL_ERROR("FinishDrawing", "glFlush");
874    }
875}
876
877void GLACanvas::DrawList(unsigned int l)
878{
879    // FIXME: uncomment to disable use of lists for debugging:
880    // GenerateList(l); return;
881    if (l >= drawing_lists.size()) drawing_lists.resize(l + 1);
882
883    // We generate the OpenGL lists lazily to minimise delays on startup.
884    // So check if we need to generate the OpenGL list now.
885    if (drawing_lists[l].need_to_generate()) {
886        // Clear list_flags so that we can note what conditions to invalidate
887        // the cached OpenGL list on.
888        list_flags = 0;
889
890#ifdef GLA_DEBUG
891        printf("generating list #%u... ", l);
892        m_Vertices = 0;
893#endif
894        GenerateList(l);
895#ifdef GLA_DEBUG
896        printf("done (%d vertices)\n", m_Vertices);
897#endif
898        drawing_lists[l].finalise(list_flags);
899    }
900
901    if (!drawing_lists[l].DrawList()) {
902        // That list isn't cached (which means it probably can't usefully be
903        // cached).
904        GenerateList(l);
905    }
906}
907
908void GLACanvas::DrawList2D(unsigned int l, glaCoord x, glaCoord y, Double rotation)
909{
910    glMatrixMode(GL_PROJECTION);
911    CHECK_GL_ERROR("DrawList2D", "glMatrixMode");
912    glPushMatrix();
913    CHECK_GL_ERROR("DrawList2D", "glPushMatrix");
914    glTranslated(x, y, 0);
915    CHECK_GL_ERROR("DrawList2D", "glTranslated");
916    if (rotation != 0.0) {
917        glRotated(rotation, 0, 0, -1);
918        CHECK_GL_ERROR("DrawList2D", "glRotated");
919    }
920    DrawList(l);
921    glMatrixMode(GL_PROJECTION);
922    CHECK_GL_ERROR("DrawList2D", "glMatrixMode 2");
923    glPopMatrix();
924    CHECK_GL_ERROR("DrawList2D", "glPopMatrix");
925}
926
927void GLACanvas::SetColour(const GLAPen& pen, double rgb_scale)
928{
929    // Set the colour for subsequent operations.
930    glColor3f(pen.GetRed() * rgb_scale, pen.GetGreen() * rgb_scale,
931              pen.GetBlue() * rgb_scale);
932}
933
934void GLACanvas::SetColour(const GLAPen& pen)
935{
936    // Set the colour for subsequent operations.
937    glColor3dv(pen.components);
938}
939
940void GLACanvas::SetColour(gla_colour colour)
941{
942    // Set the colour for subsequent operations.
943    glColor3ubv(&COLOURS[colour].r);
944}
945
946void GLACanvas::DrawText(glaCoord x, glaCoord y, glaCoord z, const wxString& str)
947{
948    // Draw a text string on the current buffer in the current font.
949    glRasterPos3d(x, y, z);
950    CHECK_GL_ERROR("DrawText", "glRasterPos3d");
951    m_Font.write_string(str.data(), str.size());
952}
953
954void GLACanvas::DrawIndicatorText(int x, int y, const wxString& str)
955{
956    glRasterPos2d(x, y);
957    CHECK_GL_ERROR("DrawIndicatorText", "glRasterPos2d");
958    m_Font.write_string(str.data(), str.size());
959}
960
961void GLACanvas::GetTextExtent(const wxString& str, int * x_ext, int * y_ext)
962{
963    m_Font.get_text_extent(str.data(), str.size(), x_ext, y_ext);
964}
965
966void GLACanvas::BeginQuadrilaterals()
967{
968    // Commence drawing of quadrilaterals.
969
970    glBegin(GL_QUADS);
971}
972
973void GLACanvas::EndQuadrilaterals()
974{
975    // Finish drawing of quadrilaterals.
976
977    glEnd();
978    CHECK_GL_ERROR("EndQuadrilaterals", "glEnd GL_QUADS");
979}
980
981void GLACanvas::BeginLines()
982{
983    // Commence drawing of a set of lines.
984
985    glBegin(GL_LINES);
986}
987
988void GLACanvas::EndLines()
989{
990    // Finish drawing of a set of lines.
991
992    glEnd();
993    CHECK_GL_ERROR("EndLines", "glEnd GL_LINES");
994}
995
996void GLACanvas::BeginTriangles()
997{
998    // Commence drawing of a set of triangles.
999
1000    glBegin(GL_TRIANGLES);
1001}
1002
1003void GLACanvas::EndTriangles()
1004{
1005    // Finish drawing of a set of triangles.
1006
1007    glEnd();
1008    CHECK_GL_ERROR("EndTriangles", "glEnd GL_TRIANGLES");
1009}
1010
1011void GLACanvas::BeginTriangleStrip()
1012{
1013    // Commence drawing of a triangle strip.
1014
1015    glBegin(GL_TRIANGLE_STRIP);
1016}
1017
1018void GLACanvas::EndTriangleStrip()
1019{
1020    // Finish drawing of a triangle strip.
1021
1022    glEnd();
1023    CHECK_GL_ERROR("EndTriangleStrip", "glEnd GL_TRIANGLE_STRIP");
1024}
1025
1026void GLACanvas::BeginPolyline()
1027{
1028    // Commence drawing of a polyline.
1029
1030    glBegin(GL_LINE_STRIP);
1031}
1032
1033void GLACanvas::EndPolyline()
1034{
1035    // Finish drawing of a polyline.
1036
1037    glEnd();
1038    CHECK_GL_ERROR("EndPolyline", "glEnd GL_LINE_STRIP");
1039}
1040
1041void GLACanvas::BeginPolygon()
1042{
1043    // Commence drawing of a polygon.
1044
1045    glBegin(GL_POLYGON);
1046}
1047
1048void GLACanvas::EndPolygon()
1049{
1050    // Finish drawing of a polygon.
1051
1052    glEnd();
1053    CHECK_GL_ERROR("EndPolygon", "glEnd GL_POLYGON");
1054}
1055
1056void GLACanvas::PlaceVertex(glaCoord x, glaCoord y, glaCoord z)
1057{
1058    // Place a vertex for the current object being drawn.
1059
1060#ifdef GLA_DEBUG
1061    m_Vertices++;
1062#endif
1063    glVertex3d(x, y, z);
1064}
1065
1066void GLACanvas::PlaceVertex(glaCoord x, glaCoord y, glaCoord z,
1067                            glaTexCoord tex_x, glaTexCoord tex_y)
1068{
1069    // Place a vertex for the current object being drawn.
1070
1071#ifdef GLA_DEBUG
1072    m_Vertices++;
1073#endif
1074    glTexCoord2i(tex_x, tex_y);
1075    glVertex3d(x, y, z);
1076}
1077
1078void GLACanvas::PlaceIndicatorVertex(glaCoord x, glaCoord y)
1079{
1080    // Place a vertex for the current indicator object being drawn.
1081
1082    PlaceVertex(x, y, 0.0);
1083}
1084
1085void GLACanvas::BeginBlobs()
1086{
1087    // Commence drawing of a set of blobs.
1088    if (blob_method == POINT) {
1089        glPushAttrib(GL_ENABLE_BIT);
1090        CHECK_GL_ERROR("BeginBlobs", "glPushAttrib");
1091        glEnable(GL_ALPHA_TEST);
1092        CHECK_GL_ERROR("BeginBlobs", "glEnable GL_ALPHA_TEST");
1093        glEnable(GL_POINT_SMOOTH);
1094        CHECK_GL_ERROR("BeginBlobs", "glEnable GL_POINT_SMOOTH");
1095        glBegin(GL_POINTS);
1096    } else {
1097        glPushAttrib(GL_TRANSFORM_BIT|GL_VIEWPORT_BIT|GL_ENABLE_BIT);
1098        CHECK_GL_ERROR("BeginBlobs", "glPushAttrib");
1099        SetIndicatorTransform();
1100        glEnable(GL_DEPTH_TEST);
1101        CHECK_GL_ERROR("BeginBlobs", "glEnable GL_DEPTH_TEST");
1102        glBegin(GL_LINES);
1103    }
1104}
1105
1106void GLACanvas::EndBlobs()
1107{
1108    // Finish drawing of a set of blobs.
1109    glEnd();
1110    if (blob_method == POINT) {
1111        CHECK_GL_ERROR("EndBlobs", "glEnd GL_POINTS");
1112    } else {
1113        CHECK_GL_ERROR("EndBlobs", "glEnd GL_LINES");
1114    }
1115    glPopAttrib();
1116    CHECK_GL_ERROR("EndBlobs", "glPopAttrib");
1117}
1118
1119void GLACanvas::DrawBlob(glaCoord x, glaCoord y, glaCoord z)
1120{
1121    if (blob_method == POINT) {
1122        // Draw a marker.
1123        PlaceVertex(x, y, z);
1124    } else {
1125        double X, Y, Z;
1126        if (!Transform(Vector3(x, y, z), &X, &Y, &Z)) {
1127            printf("bad transform\n");
1128            return;
1129        }
1130        // Stuff behind us (in perspective view) will get clipped,
1131        // but we can save effort with a cheap check here.
1132        if (Z <= 0) return;
1133
1134        X -= BLOB_DIAMETER * 0.5;
1135        Y -= BLOB_DIAMETER * 0.5;
1136
1137        PlaceVertex(X, Y + 1, Z);
1138        PlaceVertex(X, Y + (BLOB_DIAMETER - 1), Z);
1139
1140        for (int i = 1; i < (BLOB_DIAMETER - 1); ++i) {
1141            PlaceVertex(X + i, Y, Z);
1142            PlaceVertex(X + i, Y + BLOB_DIAMETER, Z);
1143        }
1144
1145        PlaceVertex(X + (BLOB_DIAMETER - 1), Y + 1, Z);
1146        PlaceVertex(X + (BLOB_DIAMETER - 1), Y + (BLOB_DIAMETER - 1), Z);
1147    }
1148#ifdef GLA_DEBUG
1149    m_Vertices++;
1150#endif
1151}
1152
1153void GLACanvas::DrawBlob(glaCoord x, glaCoord y)
1154{
1155    if (blob_method == POINT) {
1156        // Draw a marker.
1157        PlaceVertex(x, y, 0);
1158    } else {
1159        x -= BLOB_DIAMETER * 0.5;
1160        y -= BLOB_DIAMETER * 0.5;
1161
1162        PlaceVertex(x, y + 1, 0);
1163        PlaceVertex(x, y + (BLOB_DIAMETER - 1), 0);
1164
1165        for (int i = 1; i < (BLOB_DIAMETER - 1); ++i) {
1166            PlaceVertex(x + i, y, 0);
1167            PlaceVertex(x + i, y + BLOB_DIAMETER, 0);
1168        }
1169
1170        PlaceVertex(x + (BLOB_DIAMETER - 1), y + 1, 0);
1171        PlaceVertex(x + (BLOB_DIAMETER - 1), y + (BLOB_DIAMETER - 1), 0);
1172    }
1173#ifdef GLA_DEBUG
1174    m_Vertices++;
1175#endif
1176}
1177
1178void GLACanvas::BeginCrosses()
1179{
1180    // Plot crosses.
1181    if (cross_method == SPRITE) {
1182        glPushAttrib(GL_ENABLE_BIT|GL_POINT_BIT);
1183        CHECK_GL_ERROR("BeginCrosses", "glPushAttrib");
1184        glBindTexture(GL_TEXTURE_2D, m_CrossTexture);
1185        CHECK_GL_ERROR("BeginCrosses", "glBindTexture");
1186        glEnable(GL_ALPHA_TEST);
1187        CHECK_GL_ERROR("BeginCrosses", "glEnable GL_ALPHA_TEST");
1188        glPointSize(8);
1189        CHECK_GL_ERROR("BeginCrosses", "glPointSize");
1190        glTexEnvi(GL_POINT_SPRITE, GL_COORD_REPLACE, GL_TRUE);
1191        CHECK_GL_ERROR("BeginCrosses", "glTexEnvi GL_POINT_SPRITE");
1192        glEnable(GL_TEXTURE_2D);
1193        CHECK_GL_ERROR("BeginCrosses", "glEnable GL_TEXTURE_2D");
1194        glEnable(GL_POINT_SPRITE);
1195        CHECK_GL_ERROR("BeginCrosses", "glEnable GL_POINT_SPRITE");
1196        glBegin(GL_POINTS);
1197    } else {
1198        // To get the crosses to appear at a constant size and orientation on
1199        // screen, we plot them in the Indicator transform coordinates (which
1200        // unfortunately means they can't be usefully put in an opengl display
1201        // list).
1202        glPushAttrib(GL_TRANSFORM_BIT|GL_VIEWPORT_BIT|GL_ENABLE_BIT);
1203        CHECK_GL_ERROR("BeginCrosses", "glPushAttrib 2");
1204        SetIndicatorTransform();
1205        glEnable(GL_DEPTH_TEST);
1206        CHECK_GL_ERROR("BeginCrosses", "glEnable GL_DEPTH_TEST");
1207        glBegin(GL_LINES);
1208    }
1209}
1210
1211void GLACanvas::EndCrosses()
1212{
1213    glEnd();
1214    if (cross_method == SPRITE) {
1215        CHECK_GL_ERROR("EndCrosses", "glEnd GL_POINTS");
1216    } else {
1217        CHECK_GL_ERROR("EndCrosses", "glEnd GL_LINES");
1218    }
1219    glPopAttrib();
1220    CHECK_GL_ERROR("EndCrosses", "glPopAttrib");
1221}
1222
1223void GLACanvas::DrawCross(glaCoord x, glaCoord y, glaCoord z)
1224{
1225    if (cross_method == SPRITE) {
1226        PlaceVertex(x, y, z);
1227    } else {
1228        double X, Y, Z;
1229        if (!Transform(Vector3(x, y, z), &X, &Y, &Z)) {
1230            printf("bad transform\n");
1231            return;
1232        }
1233        // Stuff behind us (in perspective view) will get clipped,
1234        // but we can save effort with a cheap check here.
1235        if (Z > 0) {
1236            // Round to integers before adding on the offsets for the
1237            // cross arms to avoid uneven crosses.
1238            X = rint(X);
1239            Y = rint(Y);
1240            PlaceVertex(X - 3, Y - 3, Z);
1241            PlaceVertex(X + 3, Y + 3, Z);
1242            PlaceVertex(X - 3, Y + 3, Z);
1243            PlaceVertex(X + 3, Y - 3, Z);
1244        }
1245    }
1246#ifdef GLA_DEBUG
1247    m_Vertices++;
1248#endif
1249}
1250
1251void GLACanvas::DrawRing(glaCoord x, glaCoord y)
1252{
1253    // Draw an unfilled circle
1254    const Double radius = 4;
1255    assert(m_Quadric);
1256    glMatrixMode(GL_MODELVIEW);
1257    CHECK_GL_ERROR("DrawRing", "glMatrixMode");
1258    glPushMatrix();
1259    CHECK_GL_ERROR("DrawRing", "glPushMatrix");
1260    glTranslated(x, y, 0.0);
1261    CHECK_GL_ERROR("DrawRing", "glTranslated");
1262    gluDisk(m_Quadric, radius - 1.0, radius, 12, 1);
1263    CHECK_GL_ERROR("DrawRing", "gluDisk");
1264    glPopMatrix();
1265    CHECK_GL_ERROR("DrawRing", "glPopMatrix");
1266}
1267
1268void GLACanvas::DrawRectangle(gla_colour edge, gla_colour fill,
1269                              glaCoord x0, glaCoord y0, glaCoord w, glaCoord h)
1270{
1271    // Draw a filled rectangle with an edge in the indicator plane.
1272    // (x0, y0) specify the bottom-left corner of the rectangle and (w, h) the
1273    // size.
1274
1275    SetColour(fill);
1276    BeginQuadrilaterals();
1277    PlaceIndicatorVertex(x0, y0);
1278    PlaceIndicatorVertex(x0 + w, y0);
1279    PlaceIndicatorVertex(x0 + w, y0 + h);
1280    PlaceIndicatorVertex(x0, y0 + h);
1281    EndQuadrilaterals();
1282
1283    if (edge != fill) {
1284        SetColour(edge);
1285        BeginLines();
1286        PlaceIndicatorVertex(x0, y0);
1287        PlaceIndicatorVertex(x0 + w, y0);
1288        PlaceIndicatorVertex(x0 + w, y0 + h);
1289        PlaceIndicatorVertex(x0, y0 + h);
1290        EndLines();
1291    }
1292}
1293
1294void
1295GLACanvas::DrawShadedRectangle(const GLAPen & fill_bot, const GLAPen & fill_top,
1296                               glaCoord x0, glaCoord y0,
1297                               glaCoord w, glaCoord h)
1298{
1299    // Draw a graduated filled rectangle in the indicator plane.
1300    // (x0, y0) specify the bottom-left corner of the rectangle and (w, h) the
1301    // size.
1302
1303    glShadeModel(GL_SMOOTH);
1304    CHECK_GL_ERROR("DrawShadedRectangle", "glShadeModel GL_SMOOTH");
1305    BeginQuadrilaterals();
1306    SetColour(fill_bot);
1307    PlaceIndicatorVertex(x0, y0);
1308    PlaceIndicatorVertex(x0 + w, y0);
1309    SetColour(fill_top);
1310    PlaceIndicatorVertex(x0 + w, y0 + h);
1311    PlaceIndicatorVertex(x0, y0 + h);
1312    EndQuadrilaterals();
1313    glShadeModel(GL_FLAT);
1314    CHECK_GL_ERROR("DrawShadedRectangle", "glShadeModel GL_FLAT");
1315}
1316
1317void GLACanvas::DrawCircle(gla_colour edge, gla_colour fill,
1318                           glaCoord cx, glaCoord cy, glaCoord radius)
1319{
1320    // Draw a filled circle with an edge.
1321    SetColour(fill);
1322    glMatrixMode(GL_MODELVIEW);
1323    CHECK_GL_ERROR("DrawCircle", "glMatrixMode");
1324    glPushMatrix();
1325    CHECK_GL_ERROR("DrawCircle", "glPushMatrix");
1326    glTranslated(cx, cy, 0.0);
1327    CHECK_GL_ERROR("DrawCircle", "glTranslated");
1328    assert(m_Quadric);
1329    gluDisk(m_Quadric, 0.0, radius, 36, 1);
1330    CHECK_GL_ERROR("DrawCircle", "gluDisk");
1331    SetColour(edge);
1332    gluDisk(m_Quadric, radius - 1.0, radius, 36, 1);
1333    CHECK_GL_ERROR("DrawCircle", "gluDisk (2)");
1334    glPopMatrix();
1335    CHECK_GL_ERROR("DrawCircle", "glPopMatrix");
1336}
1337
1338void GLACanvas::DrawSemicircle(gla_colour edge, gla_colour fill,
1339                               glaCoord cx, glaCoord cy,
1340                               glaCoord radius, glaCoord start)
1341{
1342    // Draw a filled semicircle with an edge.
1343    // The semicircle extends from "start" deg to "start"+180 deg (increasing
1344    // clockwise, 0 deg upwards).
1345    SetColour(fill);
1346    glMatrixMode(GL_MODELVIEW);
1347    CHECK_GL_ERROR("DrawSemicircle", "glMatrixMode");
1348    glPushMatrix();
1349    CHECK_GL_ERROR("DrawSemicircle", "glPushMatrix");
1350    glTranslated(cx, cy, 0.0);
1351    CHECK_GL_ERROR("DrawSemicircle", "glTranslated");
1352    assert(m_Quadric);
1353    gluPartialDisk(m_Quadric, 0.0, radius, 36, 1, start, 180.0);
1354    CHECK_GL_ERROR("DrawSemicircle", "gluPartialDisk");
1355    SetColour(edge);
1356    gluPartialDisk(m_Quadric, radius - 1.0, radius, 36, 1, start, 180.0);
1357    CHECK_GL_ERROR("DrawSemicircle", "gluPartialDisk (2)");
1358    glPopMatrix();
1359    CHECK_GL_ERROR("DrawSemicircle", "glPopMatrix");
1360}
1361
1362void
1363GLACanvas::DrawTriangle(gla_colour edge, gla_colour fill,
1364                        const Vector3 &p0, const Vector3 &p1, const Vector3 &p2)
1365{
1366    // Draw a filled triangle with an edge.
1367
1368    SetColour(fill);
1369    BeginTriangles();
1370    PlaceIndicatorVertex(p0.GetX(), p0.GetY());
1371    PlaceIndicatorVertex(p1.GetX(), p1.GetY());
1372    PlaceIndicatorVertex(p2.GetX(), p2.GetY());
1373    EndTriangles();
1374
1375    SetColour(edge);
1376    glBegin(GL_LINE_STRIP);
1377    PlaceIndicatorVertex(p0.GetX(), p0.GetY());
1378    PlaceIndicatorVertex(p1.GetX(), p1.GetY());
1379    PlaceIndicatorVertex(p2.GetX(), p2.GetY());
1380    glEnd();
1381    CHECK_GL_ERROR("DrawTriangle", "glEnd GL_LINE_STRIP");
1382}
1383
1384void GLACanvas::EnableDashedLines()
1385{
1386    // Enable dashed lines, and start drawing in them.
1387
1388    glLineStipple(1, 0x3333);
1389    CHECK_GL_ERROR("EnableDashedLines", "glLineStipple");
1390    glEnable(GL_LINE_STIPPLE);
1391    CHECK_GL_ERROR("EnableDashedLines", "glEnable GL_LINE_STIPPLE");
1392}
1393
1394void GLACanvas::DisableDashedLines()
1395{
1396    glDisable(GL_LINE_STIPPLE);
1397    CHECK_GL_ERROR("DisableDashedLines", "glDisable GL_LINE_STIPPLE");
1398}
1399
1400bool GLACanvas::Transform(const Vector3 & v,
1401                          double* x_out, double* y_out, double* z_out) const
1402{
1403    // Convert from data coordinates to screen coordinates.
1404
1405    // Perform the projection.
1406    return gluProject(v.GetX(), v.GetY(), v.GetZ(),
1407                      modelview_matrix, projection_matrix, viewport,
1408                      x_out, y_out, z_out);
1409}
1410
1411void GLACanvas::ReverseTransform(Double x, Double y,
1412                                 double* x_out, double* y_out, double* z_out) const
1413{
1414    // Convert from screen coordinates to data coordinates.
1415
1416    // Perform the projection.
1417    gluUnProject(x, y, 0.0, modelview_matrix, projection_matrix, viewport,
1418                 x_out, y_out, z_out);
1419    CHECK_GL_ERROR("ReverseTransform", "gluUnProject");
1420}
1421
1422Double GLACanvas::SurveyUnitsAcrossViewport() const
1423{
1424    // Measure the current viewport in survey units, taking into account the
1425    // current display scale.
1426
1427    assert(m_Scale != 0.0);
1428    list_flags |= INVALIDATE_ON_SCALE;
1429    return m_VolumeDiameter / m_Scale;
1430}
1431
1432void GLACanvas::ToggleSmoothShading()
1433{
1434    m_SmoothShading = !m_SmoothShading;
1435}
1436
1437void GLACanvas::ToggleTextured()
1438{
1439    m_Textured = !m_Textured;
1440    if (m_Textured && m_Texture == 0) {
1441        glGenTextures(1, &m_Texture);
1442        CHECK_GL_ERROR("ToggleTextured", "glGenTextures");
1443
1444        glBindTexture(GL_TEXTURE_2D, m_Texture);
1445        CHECK_GL_ERROR("ToggleTextured", "glBindTexture");
1446
1447        ::wxInitAllImageHandlers();
1448
1449        wxImage img;
1450        wxString texture(wmsg_cfgpth());
1451        texture += wxCONFIG_PATH_SEPARATOR;
1452        texture += wxT("icons");
1453        texture += wxCONFIG_PATH_SEPARATOR;
1454        texture += wxT("texture.png");
1455        if (!img.LoadFile(texture, wxBITMAP_TYPE_PNG)) {
1456            // FIXME
1457            fprintf(stderr, "Couldn't load image.\n");
1458            exit(1);
1459        }
1460
1461        // Generate mipmaps.
1462        gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, // was GL_LUMINANCE
1463                          img.GetWidth(), img.GetHeight(),
1464                          GL_RGB, GL_UNSIGNED_BYTE, img.GetData());
1465        CHECK_GL_ERROR("ToggleTextured", "gluBuild2DMipmaps");
1466
1467        glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
1468        CHECK_GL_ERROR("ToggleTextured", "glTexEnvi");
1469    }
1470}
1471
1472bool GLACanvas::SaveScreenshot(const wxString & fnm, wxBitmapType type) const
1473{
1474    const int width = x_size;
1475    const int height = y_size;
1476    unsigned char *pixels = (unsigned char *)malloc(3 * width * (height + 1));
1477    if (!pixels) return false;
1478    glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, (GLvoid *)pixels);
1479    CHECK_GL_ERROR("SaveScreenshot", "glReadPixels");
1480    unsigned char * tmp_row = pixels + 3 * width * height;
1481    // We need to flip the image vertically - this approach should be more
1482    // efficient than using wxImage::Mirror(false) as that creates a new
1483    // wxImage object.
1484    for (int y = height / 2 - 1; y >= 0; --y) {
1485        unsigned char * upper = pixels + 3 * width * y;
1486        unsigned char * lower = pixels + 3 * width * (height - y - 1);
1487        memcpy(tmp_row, upper, 3 * width);
1488        memcpy(upper, lower, 3 * width);
1489        memcpy(lower, tmp_row, 3 * width);
1490    }
1491    // NB wxImage constructor calls free(pixels) for us.
1492    wxImage grab(width, height, pixels);
1493    return grab.SaveFile(fnm, type);
1494}
1495
1496bool GLACanvas::CheckVisualFidelity(const unsigned char * target) const
1497{
1498    unsigned char pixels[3 * 8 * 8];
1499    if (double_buffered) {
1500        glReadBuffer(GL_BACK);
1501        CHECK_GL_ERROR("FirstShow", "glReadBuffer");
1502    }
1503    glReadPixels(x_size / 2 - 3, y_size / 2 - 4, 8, 8,
1504                 GL_RGB, GL_UNSIGNED_BYTE, (GLvoid *)pixels);
1505    CHECK_GL_ERROR("CheckVisualFidelity", "glReadPixels");
1506    if (double_buffered) {
1507        glReadBuffer(GL_FRONT);
1508        CHECK_GL_ERROR("FirstShow", "glReadBuffer");
1509    }
1510#if 0
1511    // Show what got drawn and what was expected for debugging.
1512    for (int y = 0; y < 8; ++y) {
1513        for (int x = 0; x < 8; ++x) {
1514            int o = (y * 8 + x) * 3;
1515            printf("%c", pixels[o] ? 'X' : '.');
1516        }
1517        printf(" ");
1518        for (int x = 0; x < 8; ++x) {
1519            int o = (y * 8 + x) * 3;
1520            printf("%c", target[o] ? 'X' : '.');
1521        }
1522        printf("\n");
1523    }
1524#endif
1525    return (memcmp(pixels, target, sizeof(pixels)) == 0);
1526}
1527
1528void GLACanvas::ReadPixels(int width, int height, unsigned char * buf) const
1529{
1530    CHECK_GL_ERROR("ReadPixels", "glReadPixels");
1531    glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, (GLvoid *)buf);
1532}
1533
1534void GLACanvas::PolygonOffset(bool on) const
1535{
1536    if (on) {
1537        glPolygonOffset(1.0, 1.0);
1538        glEnable(GL_POLYGON_OFFSET_FILL);
1539    } else {
1540        glDisable(GL_POLYGON_OFFSET_FILL);
1541    }
1542}
Note: See TracBrowser for help on using the repository browser.