1 
2 /*
3     Copyright (c) 2008 Andrew Caudwell (acaudwell@gmail.com)
4     All rights reserved.
5 
6     Redistribution and use in source and binary forms, with or without
7     modification, are permitted provided that the following conditions
8     are met:
9     1. Redistributions of source code must retain the above copyright
10        notice, this list of conditions and the following disclaimer.
11     2. Redistributions in binary form must reproduce the above copyright
12        notice, this list of conditions and the following disclaimer in the
13        documentation and/or other materials provided with the distribution.
14     3. The name of the author may not be used to endorse or promote products
15        derived from this software without specific prior written permission.
16 
17     THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18     IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19     OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20     IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21     INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22     NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23     DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24     THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25     (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26     THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28 
29 #include "display.h"
30 #include "sdlapp.h"
31 #include <iostream>
32 
33 #if SDL_VERSION_ATLEAST(2,0,0)
34 #include "SDL_syswm.h"
35 #endif
36 
37 SDLAppDisplay display;
38 
SDLAppDisplay()39 SDLAppDisplay::SDLAppDisplay() {
40     clear_colour    = vec4(0.0f,0.0f,0.0f,1.0f);
41     zbuffer_depth   = 16;
42     enable_alpha    = false;
43     vsync           = false;
44     resizable       = false;
45     frameless       = false;
46     multi_sample    = 0;
47     width           = 0;
48     height          = 0;
49     desktop_width   = 0;
50     desktop_height  = 0;
51     windowed_width  = 0;
52     windowed_height = 0;
53 #if SDL_VERSION_ATLEAST(2,0,0)
54     sdl_window = 0;
55     gl_context = 0;
56 
57     framed_width  = 0;
58     framed_height = 0;
59     framed_x      = 0;
60     framed_y      = 0;
61 #else
62     surface = 0;
63 #endif
64 
65 }
66 
~SDLAppDisplay()67 SDLAppDisplay::~SDLAppDisplay() {
68 }
69 
setClearColour(vec3 colour)70 void SDLAppDisplay::setClearColour(vec3 colour) {
71     setClearColour(vec4(colour, enable_alpha ? 0.0f : 1.0f));
72 }
73 
setClearColour(vec4 colour)74 void SDLAppDisplay::setClearColour(vec4 colour) {
75     clear_colour = colour;
76 }
77 
SDLWindowFlags(bool fullscreen)78 Uint32 SDLAppDisplay::SDLWindowFlags(bool fullscreen) {
79 #if SDL_VERSION_ATLEAST(2,0,0)
80     Uint32 flags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN;
81 
82     if (frameless) flags |= SDL_WINDOW_BORDERLESS;
83     if (resizable && !frameless) flags |= SDL_WINDOW_RESIZABLE;
84     if (fullscreen) flags |= SDL_WINDOW_FULLSCREEN;
85 #else
86     Uint32 flags = SDL_OPENGL | SDL_HWSURFACE | SDL_ANYFORMAT | SDL_DOUBLEBUF;
87 
88     if (frameless) flags |= SDL_NOFRAME;
89     if (resizable && !fullscreen) flags |= SDL_RESIZABLE;
90     if (fullscreen) flags |= SDL_FULLSCREEN;
91 #endif
92     return flags;
93 }
94 
enableVsync(bool vsync)95 void SDLAppDisplay::enableVsync(bool vsync) {
96     this->vsync = vsync;
97 }
98 
setZBufferDepth(int zbuffer_depth)99 void SDLAppDisplay::setZBufferDepth(int zbuffer_depth) {
100     this->zbuffer_depth = zbuffer_depth;
101 }
102 
enableResize(bool resizable)103 void SDLAppDisplay::enableResize(bool resizable) {
104     this->resizable = resizable;
105 }
106 
enableFrameless(bool frameless)107 void SDLAppDisplay::enableFrameless(bool frameless) {
108     this->frameless = frameless;
109 }
110 
enableAlpha(bool enable)111 void SDLAppDisplay::enableAlpha(bool enable) {
112     enable_alpha = enable;
113 }
114 
multiSample(int samples)115 void SDLAppDisplay::multiSample(int samples) {
116     multi_sample = samples;
117 }
118 
setupExtensions()119 void SDLAppDisplay::setupExtensions() {
120 
121     GLenum err = glewInit();
122 
123     if (GLEW_OK != err) {
124         /* Problem: glewInit failed, something is seriously wrong. */
125         char glewerr[1024];
126         snprintf(glewerr, 1024, "GLEW Error: %s", glewGetErrorString(err));
127 
128         throw SDLInitException(std::string(glewerr));
129     }
130 }
131 
multiSamplingEnabled()132 bool SDLAppDisplay::multiSamplingEnabled() {
133     int value;
134     SDL_GL_GetAttribute( SDL_GL_MULTISAMPLEBUFFERS, &value );
135     return value==1;
136 }
137 
138 #if SDL_VERSION_ATLEAST(2,0,0) && defined(_WIN32)
139 WNDPROC window_proc = 0;
140 
window_filter_proc(HWND wnd,UINT msg,WPARAM wparam,LPARAM lparam)141 LRESULT CALLBACK window_filter_proc(HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam) {
142 
143    if (msg == WM_SYSCOMMAND && (wparam & 0xfff0) == SC_KEYMENU) {
144         return 0;
145    }
146 
147    return CallWindowProc(window_proc, wnd, msg, wparam, lparam);
148 }
149 #endif
150 
setVideoMode(int width,int height,bool fullscreen,int screen)151 void SDLAppDisplay::setVideoMode(int width, int height, bool fullscreen, int screen) {
152 #if SDL_VERSION_ATLEAST(2,0,0)
153 
154     SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
155     SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE,   zbuffer_depth);
156 
157     if(multi_sample > 0) {
158         SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
159         SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, (GLuint) multi_sample);
160     }
161 
162     if(enable_alpha) {
163         SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
164     }
165 
166     Uint32 flags = SDLWindowFlags(fullscreen);
167 
168     if(gl_context != 0) SDL_GL_DeleteContext(gl_context);
169 
170 
171     int position_x = -1;
172     int position_y = -1;
173 
174     int display_index = -1;
175 
176     if(sdl_window != 0) {
177         display_index = SDL_GetWindowDisplayIndex(sdl_window);
178         SDL_GetWindowPosition(sdl_window, &position_x, &position_y);
179         SDL_DestroyWindow(sdl_window);
180 
181     } else if(screen > 0 && screen <= SDL_GetNumVideoDisplays()) {
182         display_index = screen-1;
183     }
184 
185     if(display_index != -1) {
186         sdl_window = SDL_CreateWindow(gSDLAppTitle.c_str(), SDL_WINDOWPOS_UNDEFINED_DISPLAY(display_index), SDL_WINDOWPOS_UNDEFINED_DISPLAY(display_index), width, height, flags);
187 
188         if(sdl_window && position_x >= 0 && position_y >= 0) {
189             SDL_SetWindowPosition(sdl_window, position_x, position_y);
190         }
191 
192     } else {
193         sdl_window = SDL_CreateWindow(gSDLAppTitle.c_str(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, flags);
194     }
195 
196     if (!sdl_window) {
197 
198         // retry without multi-sampling enabled
199         if(multi_sample > 0) {
200             multi_sample = 0;
201             SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0);
202             SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0);
203 
204             if(display_index != -1) {
205                 sdl_window = SDL_CreateWindow(gSDLAppTitle.c_str(), SDL_WINDOWPOS_UNDEFINED_DISPLAY(display_index), SDL_WINDOWPOS_UNDEFINED_DISPLAY(display_index), width, height, flags);
206 
207                 if(sdl_window && position_x >= 0 && position_y >= 0) {
208                     SDL_SetWindowPosition(sdl_window, position_x, position_y);
209                 }
210             } else {
211                 sdl_window = SDL_CreateWindow(gSDLAppTitle.c_str(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, flags);
212             }
213         }
214 
215         if(!sdl_window) {
216             std::string sdlerr(SDL_GetError());
217             throw SDLInitException(sdlerr);
218         }
219     }
220 
221     gl_context = SDL_GL_CreateContext(sdl_window);
222 
223     if(!gl_context) {
224         std::string sdlerr(SDL_GetError());
225         throw SDLInitException(sdlerr);
226     }
227 
228     if(vsync) SDL_GL_SetSwapInterval(1);
229     else SDL_GL_SetSwapInterval(0);
230 
231 #else
232     int bpp = 32;
233 
234     int flags = SDLWindowFlags(fullscreen);
235 
236     if(vsync) SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1);
237     else SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 0);
238 
239     if(multi_sample > 0) {
240         SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
241         SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, (GLuint) multi_sample);
242     }
243 
244     if(enable_alpha) {
245         SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
246     }
247 
248     SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, zbuffer_depth);
249     surface = SDL_SetVideoMode(width, height, bpp, flags);
250 
251     if (!surface) {
252         if (multi_sample > 0) {
253 #ifndef _WIN32
254             // Retry without multi-sampling before failing
255             std::cerr << "Failed to set video mode: " << SDL_GetError() << std::endl
256                       << "Trying again without multi-sampling" << std::endl;
257 #endif
258             multi_sample = 0;
259             SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0);
260             SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0);
261             surface = SDL_SetVideoMode(width, height, bpp, flags);
262         }
263 
264         if (!surface) {
265             std::string sdlerr(SDL_GetError());
266             throw SDLInitException(sdlerr);
267         }
268     }
269 #endif
270 
271     setupExtensions();
272 
273 #if SDL_VERSION_ATLEAST(2,0,0) && defined(_WIN32)
274     // suppress 'ding' noise when doing alt+key combinations
275     // solution from: http://forums.libsdl.org/viewtopic.php?t=6075
276 
277     SDL_SysWMinfo sys_window_info;
278 
279     SDL_VERSION(&sys_window_info.version);
280 
281     if(SDL_GetWindowWMInfo(sdl_window, &sys_window_info)) {
282         HWND wnd = sys_window_info.info.win.window;
283         window_proc = (WNDPROC) GetWindowLongPtr(wnd, GWLP_WNDPROC);
284         SetWindowLongPtr(wnd, GWLP_WNDPROC, (LONG_PTR) &window_filter_proc);
285     }
286 #endif
287 }
288 
getFullscreenResolution(int & width,int & height)289 void SDLAppDisplay::getFullscreenResolution(int& width, int& height) {
290 
291     int fullscreen_width  = desktop_width;
292     int fullscreen_height = desktop_height;
293 
294 #if SDL_VERSION_ATLEAST(2,0,0)
295     // TODO: SDL2 api will have a nice way to do this ...
296 #else
297     float aspect_ratio = fullscreen_width / (float) fullscreen_height;
298 
299     if(aspect_ratio > 2.5) {
300 
301         SDL_Rect** modes = SDL_ListModes(0, SDLWindowFlags(true));
302 
303         if(modes != (SDL_Rect**)0 && modes != (SDL_Rect**)-1) {
304 
305             for (int i=0; modes[i]; i++) {
306                 if(modes[i]->h == fullscreen_height && (modes[i]->w/(float)modes[i]->h) < 2.5) {
307                     fullscreen_width = modes[i]->w;
308                     break;
309                 }
310             }
311         }
312     }
313 #endif
314     width  = fullscreen_width;
315     height = fullscreen_height;
316 }
317 
toggleFullscreen()318 void SDLAppDisplay::toggleFullscreen() {
319 
320     int width  = this->width;
321     int height = this->height;
322 
323     if(!fullscreen) {
324 
325         //save windowed width and height
326         windowed_width  = width;
327         windowed_height = height;
328 
329         getFullscreenResolution(width, height);
330 
331     } else {
332         //switch back to window dimensions, if known
333         if(windowed_width != 0) {
334             width  = windowed_width;
335             height = windowed_height;
336         }
337     }
338 
339     fullscreen = !fullscreen;
340 
341     int resized_width, resized_height;
342 
343 #if SDL_VERSION_ATLEAST(2,0,0)
344     SDL_SetWindowFullscreen(sdl_window, fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
345     SDL_GetWindowSize(sdl_window, &resized_width, &resized_height);
346 #else
347     setVideoMode(width, height, fullscreen);
348 
349     const SDL_VideoInfo* display_info = SDL_GetVideoInfo();
350 
351     resized_width  = display_info->current_w;
352     resized_height = display_info->current_h;
353 #endif
354 
355     //set viewport to match what we ended up on
356     glViewport(0, 0, resized_width, resized_height);
357 
358     this->width  = resized_width;
359     this->height = resized_height;
360 }
361 
toggleFrameless()362 void SDLAppDisplay::toggleFrameless() {
363 #if SDL_VERSION_ATLEAST(2,0,0)
364     if(fullscreen) return;
365 
366     frameless = !frameless;
367 
368     if(frameless) {
369 
370         int position_x, position_y;
371         SDL_GetWindowPosition(sdl_window, &position_x, &position_y);
372 
373         framed_width  = width;
374         framed_height = height;
375         framed_x      = position_x;
376         framed_y      = position_y;
377 
378 #ifdef _WIN32
379         SDL_SysWMinfo sys_window_info;
380         SDL_VERSION(&sys_window_info.version);
381 
382         if(SDL_GetWindowWMInfo(sdl_window, &sys_window_info)) {
383 
384             //make the new window equal the size of the old window including frame
385 
386             HWND wnd = sys_window_info.info.win.window;
387 
388             RECT rect;
389             GetWindowRect(wnd, &rect);
390 
391             position_x = rect.left;
392             position_y = rect.top;
393 
394             width  = rect.right - rect.left;
395             height = rect.bottom - rect.top;
396         }
397 #endif
398 
399         //work around window position changing when when frame is toggled
400         //related bug: https://bugzilla.libsdl.org/show_bug.cgi?id=2791
401 
402         SDL_SetWindowBordered(sdl_window, SDL_FALSE);
403         SDL_SetWindowSize(sdl_window, width, height);
404         SDL_SetWindowPosition(sdl_window, position_x, position_y);
405 
406         //window needs to be recreated to remove SDL_WINDOW_RESIZABLE flag
407         //otherwise there is still a weird border
408 
409         setVideoMode(width, height, fullscreen);
410 
411     } else {
412 
413 #ifdef _WIN32
414         // handle computing framed window position
415         // if launched in frameless mode initially
416         if(framed_width == 0) {
417             SDL_SysWMinfo sys_window_info;
418             SDL_VERSION(&sys_window_info.version);
419 
420             if(SDL_GetWindowWMInfo(sdl_window, &sys_window_info)) {
421 
422                 HWND wnd = sys_window_info.info.win.window;
423 
424                 RECT old_rect;
425                 GetWindowRect(wnd, &old_rect);
426 
427                 SDL_SetWindowBordered(sdl_window, SDL_TRUE);
428 
429                 RECT new_rect;
430                 GetWindowRect(wnd, &new_rect);
431 
432                 SDL_GetWindowSize(sdl_window, &framed_width, &framed_height);
433                 SDL_GetWindowPosition(sdl_window, &framed_x, &framed_y);
434 
435                 int width_delta  = (new_rect.right - new_rect.left) - (old_rect.right - old_rect.left);
436                 int height_delta = (new_rect.bottom - new_rect.top) - (old_rect.bottom - old_rect.top);
437 
438                 framed_width  = width - width_delta;
439                 framed_height = height - height_delta;
440 
441                 framed_x += width_delta;
442                 framed_y += height_delta;
443 
444                 // HACK: account for the resizable windows border being 2 pixels wider
445 
446                 if(resizable) {
447                     framed_x += 2;
448                     framed_y += 2;
449                 }
450             }
451         }
452 #endif
453         SDL_SetWindowBordered(sdl_window, SDL_TRUE);
454 
455         if(framed_width > 0) {
456             width  = framed_width;
457             height = framed_height;
458         }
459 
460         SDL_SetWindowSize(sdl_window, width, height);
461 
462         if(framed_width > 0) {
463             SDL_SetWindowPosition(sdl_window, framed_x, framed_y);
464         }
465 
466         setVideoMode(width, height, fullscreen);
467     }
468 #endif
469 }
470 
isFullscreen() const471 bool SDLAppDisplay::isFullscreen() const {
472     return fullscreen;
473 }
474 
isFrameless() const475 bool SDLAppDisplay::isFrameless() const {
476     return frameless;
477 }
478 
resize(int width,int height)479 void SDLAppDisplay::resize(int width, int height) {
480 
481     int resized_width, resized_height;
482 
483 #if SDL_VERSION_ATLEAST(2,0,0)
484     SDL_GetWindowSize(sdl_window, &resized_width, &resized_height);
485 #else
486     setVideoMode(width, height, fullscreen);
487 
488     const SDL_VideoInfo* display_info = SDL_GetVideoInfo();
489 
490     resized_width  = display_info->current_w;
491     resized_height = display_info->current_h;
492 #endif
493 
494     //set viewport to match what we ended up on
495     glViewport(0, 0, resized_width, resized_height);
496 
497     this->width  = resized_width;
498     this->height = resized_height;
499 }
500 
init(std::string window_title,int width,int height,bool fullscreen,int screen)501 void SDLAppDisplay::init(std::string window_title, int width, int height, bool fullscreen, int screen) {
502 
503     if(SDL_Init(SDL_INIT_TIMER | SDL_INIT_VIDEO) != 0) {
504         throw SDLInitException(SDL_GetError());
505     }
506 
507 
508 #if SDL_VERSION_ATLEAST(2,0,0)
509 
510     // check screen is valid
511     if(screen <= 0 || screen > SDL_GetNumVideoDisplays()) {
512         screen = -1;
513     }
514 
515     SDL_Rect display_rect;
516     SDL_GetDisplayBounds(screen > 0 ? screen-1 : 0, &display_rect);
517 
518     desktop_width  = display_rect.w;
519     desktop_height = display_rect.h;
520 
521 #else
522     const SDL_VideoInfo* display_info = SDL_GetVideoInfo();
523 
524     //save the desktop resolution
525     desktop_width  = display_info->current_w;
526     desktop_height = display_info->current_h;
527 #endif
528 
529     //initialize width and height to desktop resolution if un-specified
530     if(!width || !height) {
531         if(fullscreen) {
532             getFullscreenResolution(width, height);
533         } else {
534             if(!width) width   = desktop_width;
535             if(!height) height = desktop_height;
536         }
537     }
538 
539     atexit(SDL_Quit);
540 
541 #if SDL_VERSION_ATLEAST(2,0,0)
542 
543 #else
544     SDL_EnableUNICODE(1);
545     SDL_WM_SetCaption(window_title.c_str(),0);
546 #endif
547 
548     setVideoMode(width, height, fullscreen, screen);
549 
550     //get actual opengl viewport
551     GLint viewport[4];
552     glGetIntegerv( GL_VIEWPORT, viewport );
553 
554     this->width      = viewport[2];
555     this->height     = viewport[3];
556     this->fullscreen = fullscreen;
557 
558     glViewport(0, 0, this->width, this->height);
559 }
560 
quit()561 void SDLAppDisplay::quit() {
562 
563 #if SDL_VERSION_ATLEAST(2,0,0)
564     if(gl_context != 0) SDL_GL_DeleteContext(gl_context);
565     if(sdl_window != 0) SDL_DestroyWindow(sdl_window);
566 #endif
567 
568     texturemanager.purge();
569     shadermanager.purge();
570     fontmanager.purge();
571     fontmanager.destroy();
572 }
573 
update()574 void SDLAppDisplay::update() {
575 #if SDL_VERSION_ATLEAST(2,0,0)
576     SDL_GL_SwapWindow(sdl_window);
577 #else
578     SDL_GL_SwapBuffers();
579 #endif
580 }
581 
clear()582 void SDLAppDisplay::clear() {
583     glClearColor(clear_colour.x, clear_colour.y, clear_colour.z, clear_colour.w);
584     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
585 }
586 
mode3D(float fov,float znear,float zfar)587 void SDLAppDisplay::mode3D(float fov, float znear, float zfar) {
588     glMatrixMode(GL_PROJECTION);
589     glLoadIdentity();
590     gluPerspective(fov, (GLfloat)width/(GLfloat)height, znear, zfar);
591     glMatrixMode(GL_MODELVIEW);
592     glLoadIdentity();
593 }
594 
mode2D()595 void SDLAppDisplay::mode2D() {
596     glMatrixMode(GL_PROJECTION);
597     glLoadIdentity();
598     glOrtho(0, width, height, 0, -1.0, 1.0);
599     glMatrixMode(GL_MODELVIEW);
600     glLoadIdentity();
601 }
602 
push2D()603 void SDLAppDisplay::push2D() {
604     glMatrixMode(GL_PROJECTION);
605         glPushMatrix();
606         glLoadIdentity();
607             glOrtho(0, display.width, display.height, 0, -1.0, 1.0);
608 
609     glMatrixMode(GL_MODELVIEW);
610         glPushMatrix();
611         glLoadIdentity();
612 }
613 
pop2D()614 void SDLAppDisplay::pop2D() {
615         glMatrixMode(GL_PROJECTION);
616         glPopMatrix();
617         glMatrixMode(GL_MODELVIEW);
618         glPopMatrix();
619 }
620 
currentColour()621 vec4 SDLAppDisplay::currentColour() {
622     vec4 colour;
623     glGetFloatv(GL_CURRENT_COLOR, glm::value_ptr(colour));
624     return colour;
625 }
626 
project(vec3 pos)627 vec3 SDLAppDisplay::project(vec3 pos) {
628     GLint viewport[4];
629     GLdouble modelview[16];
630     GLdouble projection[16];
631     GLdouble winX, winY, winZ;
632 
633     glGetDoublev( GL_MODELVIEW_MATRIX, modelview );
634     glGetDoublev( GL_PROJECTION_MATRIX, projection );
635     glGetIntegerv( GL_VIEWPORT, viewport );
636 
637     gluProject( pos.x, pos.y, pos.z, modelview, projection, viewport, &winX, &winY, &winZ);
638 
639     winY = (float)viewport[3] - winY;
640 
641     return vec3((float) winX, (float) winY, (float) winZ);
642 }
643 
unproject(vec2 pos)644 vec3 SDLAppDisplay::unproject(vec2 pos) {
645     GLint viewport[4];
646     GLdouble modelview[16];
647     GLdouble projection[16];
648     GLfloat winX, winY, winZ;
649     GLdouble posX, posY, posZ;
650 
651     glGetDoublev( GL_MODELVIEW_MATRIX, modelview );
652     glGetDoublev( GL_PROJECTION_MATRIX, projection );
653     glGetIntegerv( GL_VIEWPORT, viewport );
654 
655     winX = pos.x;
656     winY = (float)viewport[3] - pos.y;
657     glReadPixels( int(winX), int(winY), 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ );
658     gluUnProject( winX, winY, winZ, modelview, projection, viewport, &posX, &posY, &posZ);
659 
660     return vec3((float) posX, (float) posY, (float) posZ);
661 }
662