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

RELEASE/1.2 debug-ci debug-ci-sanitisers faster-cavernlog log-select main stereo stereo-2025 walls-data walls-data-hanging-as-warning warn-only-for-hanging-survey
Last change on this file since a72ed95 was 09dfd18, checked in by Olly Betts <olly@…>, 11 years ago

buildmacosx.sh,configure.ac,debian/survex-aven.install,
debian/survex.install,lib/Makefile.am,lib/icons/,lib/images/,
src/aboutdlg.cc,src/aboutdlg.h,src/gla-gl.cc,survex.iss.in: Split
out the images which aren't icons into an "images" directory.

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