source: git/src/gla-gl.cc@ 226b599e

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 226b599e was 226b599e, checked in by Olly Betts <olly@…>, 16 years ago

src/gla-gl.cc: Add \n to commented out debug message.

git-svn-id: file:///home/survex-svn/survex/trunk@3530 4b37db11-9a0c-4f06-9ece-9ab7cdaee568

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