Permalink
Switch branches/tags
Nothing to show
Find file
Fetching contributors…
Cannot retrieve contributors at this time
498 lines (384 sloc) 13.6 KB
/*
Copyright (c) 2008 Andrew Caudwell (acaudwell@gmail.com)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "display.h"
#include "sdlapp.h"
#include <iostream>
SDLAppDisplay display;
SDLAppDisplay::SDLAppDisplay() {
clearColour = vec4(0.0f,0.0f,0.0f,1.0f);
enable_shaders=false;
enable_alpha=false;
vsync=false;
resizable=false;
frameless=false;
multi_sample = 0;
width = 0;
height = 0;
desktop_width = 0;
desktop_height = 0;
windowed_width = 0;
windowed_height = 0;
#if SDL_VERSION_ATLEAST(1,3,0)
sdl_window = 0;
gl_context = 0;
#else
surface = 0;
#endif
screensaver=false;
#ifdef SDLAPP_XWINDOWS
xwindow = 0;
#endif
}
SDLAppDisplay::~SDLAppDisplay() {
}
void SDLAppDisplay::setClearColour(vec3 colour) {
setClearColour(vec4(colour, enable_alpha ? 0.0f : 1.0f));
}
void SDLAppDisplay::setClearColour(vec4 colour) {
clearColour = colour;
}
int SDLAppDisplay::SDLFlags(bool fullscreen) {
int flags = SDL_OPENGL | SDL_HWSURFACE | SDL_ANYFORMAT | SDL_DOUBLEBUF;
if (frameless) flags |= SDL_NOFRAME;
if (resizable && !fullscreen) flags |= SDL_RESIZABLE;
if (fullscreen) flags |= SDL_FULLSCREEN;
//SDL 1.3 vsync
#ifdef SDL_RENDERER_PRESENTVSYNC
if (!vsync) flags |= SDL_RENDERER_PRESENTVSYNC;
#endif
return flags;
}
void SDLAppDisplay::enableVsync(bool vsync) {
this->vsync = vsync;
}
void SDLAppDisplay::enableShaders(bool enable) {
enable_shaders = enable;
}
void SDLAppDisplay::enableResize(bool resizable) {
this->resizable = resizable;
}
void SDLAppDisplay::enableAlpha(bool enable) {
enable_alpha = enable;
}
void SDLAppDisplay::multiSample(int samples) {
multi_sample = samples;
}
void SDLAppDisplay::setupExtensions() {
GLenum err = glewInit();
if (GLEW_OK != err) {
/* Problem: glewInit failed, something is seriously wrong. */
char glewerr[1024];
snprintf(glewerr, 1024, "GLEW Error: %s", glewGetErrorString(err));
throw SDLInitException(std::string(glewerr));
}
}
bool SDLAppDisplay::multiSamplingEnabled() {
int value;
SDL_GL_GetAttribute( SDL_GL_MULTISAMPLEBUFFERS, &value );
return value==1;
}
#ifdef SDLAPP_XWINDOWS
void SDLAppDisplay::setXWindow(const Window& xwindow) {
this->xwindow = xwindow;
}
#endif
void SDLAppDisplay::setVideoMode(int width, int height, bool fullscreen) {
int gl_depth = 24;
int colour_depth = 32;
#ifndef _WIN32
if(!enable_shaders) {
gl_depth = 16;
colour_depth = 24;
}
#endif
bool no_cursor = false;
#ifdef SDLAPP_XWINDOWS
/* check for window_id in ENV */
if(xwindow == 0) {
char* xscreensaver_window_env = getenv("XSCREENSAVER_WINDOW");
if(xscreensaver_window_env != 0) {
//parse xscreensaver window id
sscanf(xscreensaver_window_env, "0x%lx", &xwindow);
if (!xwindow) sscanf(xscreensaver_window_env, "%lu", &xwindow);
if (!xwindow) throw SDLInitException("Invalid window");
}
}
if(xwindow != 0) {
char sdl_window_env[100];
snprintf(sdl_window_env, 100, "SDL_WINDOWID=%d", xwindow);
putenv(sdl_window_env);
Display* dpy = XOpenDisplay(0);
if(!dpy) {
throw SDLInitException("Cannot get display");
}
XWindowAttributes win_attributes;
if (!XGetWindowAttributes(dpy, xwindow, &win_attributes)) {
throw SDLInitException("Cannot get window attributes");
}
width = win_attributes.width;
height = win_attributes.height;
colour_depth = win_attributes.depth;
screensaver = true;
fullscreen = false;
// assume window opened at y=0 by xscreensaver is actually running as a screensaver and not a preview
if(win_attributes.y == 0) no_cursor = true;
}
#endif
#if SDL_VERSION_ATLEAST(1,3,0)
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
Uint32 flags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN;
if(resizable && !fullscreen) flags |= SDL_WINDOW_RESIZABLE;
if(fullscreen) flags |= SDL_WINDOW_FULLSCREEN;
if(gl_context != 0) SDL_GL_DeleteContext(gl_context);
if(sdl_window != 0) SDL_DestroyWindow(sdl_window);
sdl_window = SDL_CreateWindow(
gSDLAppTitle.c_str(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
width, height, flags);
if (!sdl_window) {
std::string sdlerr(SDL_GetError());
throw SDLInitException(sdlerr);
}
gl_context = SDL_GL_CreateContext(sdl_window);
if(vsync) SDL_GL_SetSwapInterval(1);
#else
int depth = 32;
int flags = SDLFlags(fullscreen);
if(no_cursor) SDL_ShowCursor(false);
if(vsync) SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1);
else SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 0);
if(multi_sample > 0) {
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, (GLuint) multi_sample);
}
if(enable_alpha) {
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
}
#ifdef _WIN32
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
surface = SDL_SetVideoMode(width, height, depth, flags);
#else
if(enable_shaders) {
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
surface = SDL_SetVideoMode(width, height, depth, flags);
} else {
depth = 24;
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
surface = SDL_SetVideoMode(width, height, depth, flags);
}
#endif
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, gl_depth);
surface = SDL_SetVideoMode(width, height, colour_depth, flags);
if (!surface) {
if (multi_sample > 0) {
#ifndef _WIN32
// Retry without multi-sampling before failing
std::cerr << "Failed to set video mode: " << SDL_GetError() << std::endl
<< "Trying again without multi-sampling" << std::endl;
#endif
multi_sample = 0;
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0);
surface = SDL_SetVideoMode(width, height, depth, flags);
}
if (!surface) {
std::string sdlerr(SDL_GetError());
throw SDLInitException(sdlerr);
}
}
#endif
setupExtensions();
}
void SDLAppDisplay::toggleFullscreen() {
int width = this->width;
int height = this->height;
if(!fullscreen) {
//save windowed width and height
windowed_width = width;
windowed_height = height;
int fullscreen_width = desktop_width;
int fullscreen_height = desktop_height;
float aspect_ratio = fullscreen_width / (float) fullscreen_height;
// if the aspect ratio suggests the person is using multiple monitors
// find a supported resolution with a lower aspect ratio with the same
// fullscreen height
#if SDL_VERSION_ATLEAST(1,3,0)
// TODO: do something with the 1.3 API here
#else
if(aspect_ratio >= 2.5) {
SDL_Rect** modes = SDL_ListModes(0, SDLFlags(true));
if(modes != (SDL_Rect**)0 && modes != (SDL_Rect**)-1) {
for (int i=0; modes[i]; i++) {
if(modes[i]->h == fullscreen_height && (modes[i]->w/(float)modes[i]->h) < 2.5) {
fullscreen_width = modes[i]->w;
break;
}
}
}
}
#endif
width = fullscreen_width;
height = fullscreen_height;
} else {
//switch back to window dimensions, if known
if(windowed_width != 0) {
width = windowed_width;
height = windowed_height;
}
}
fullscreen = !fullscreen;
setVideoMode(width, height, fullscreen);
int resized_width, resized_height;
#if SDL_VERSION_ATLEAST(1,3,0)
SDL_GetWindowSize(sdl_window, &resized_width, &resized_height);
#else
const SDL_VideoInfo* display_info = SDL_GetVideoInfo();
resized_width = display_info->current_w;
resized_height = display_info->current_h;
#endif
//set viewport to match what we ended up on
glViewport(0, 0, resized_width, resized_height);
this->width = resized_width;
this->height = resized_height;
}
void SDLAppDisplay::resize(int width, int height) {
int resized_width, resized_height;
#if SDL_VERSION_ATLEAST(1,3,0)
SDL_GetWindowSize(sdl_window, &resized_width, &resized_height);
#else
setVideoMode(width, height, fullscreen);
const SDL_VideoInfo* display_info = SDL_GetVideoInfo();
resized_width = display_info->current_w;
resized_height = display_info->current_h;
#endif
//set viewport to match what we ended up on
glViewport(0, 0, resized_width, resized_height);
this->width = resized_width;
this->height = resized_height;
}
void SDLAppDisplay::init(std::string window_title, int width, int height, bool fullscreen) {
if(SDL_Init(SDL_INIT_TIMER | SDL_INIT_VIDEO) != 0) {
throw SDLInitException(SDL_GetError());
}
const SDL_VideoInfo* display_info = SDL_GetVideoInfo();
//save the desktop resolution
desktop_width = display_info->current_w;
desktop_height = display_info->current_h;
atexit(SDL_Quit);
SDL_EnableUNICODE(1);
#if SDL_VERSION_ATLEAST(1,3,0)
#else
SDL_WM_SetCaption(window_title.c_str(),0);
#endif
setVideoMode(width, height, fullscreen);
//get actual opengl viewport
GLint viewport[4];
glGetIntegerv( GL_VIEWPORT, viewport );
this->width = viewport[2];
this->height = viewport[3];
this->fullscreen = fullscreen;
}
void SDLAppDisplay::quit() {
#if SDL_VERSION_ATLEAST(1,3,0)
if(gl_context != 0) SDL_GL_DeleteContext(gl_context);
if(sdl_window != 0) SDL_DestroyWindow(sdl_window);
#endif
texturemanager.purge();
shadermanager.purge();
fontmanager.purge();
fontmanager.destroy();
}
void SDLAppDisplay::update() {
#if SDL_VERSION_ATLEAST(1,3,0)
SDL_GL_SwapWindow(sdl_window);
#else
SDL_GL_SwapBuffers();
#endif
}
void SDLAppDisplay::clear() {
glClearColor(clearColour.x, clearColour.y, clearColour.z, clearColour.w);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
void SDLAppDisplay::mode3D(float fov, float znear, float zfar) {
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(fov, (GLfloat)width/(GLfloat)height, znear, zfar);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void SDLAppDisplay::mode2D() {
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, width, height, 0, -1.0, 1.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void SDLAppDisplay::push2D() {
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glOrtho(0, display.width, display.height, 0, -1.0, 1.0);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
}
void SDLAppDisplay::pop2D() {
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
}
vec4 SDLAppDisplay::currentColour() {
vec4 colour;
glGetFloatv(GL_CURRENT_COLOR, glm::value_ptr(colour));
return colour;
}
vec3 SDLAppDisplay::project(vec3 pos) {
GLint viewport[4];
GLdouble modelview[16];
GLdouble projection[16];
GLdouble winX, winY, winZ;
glGetDoublev( GL_MODELVIEW_MATRIX, modelview );
glGetDoublev( GL_PROJECTION_MATRIX, projection );
glGetIntegerv( GL_VIEWPORT, viewport );
gluProject( pos.x, pos.y, pos.z, modelview, projection, viewport, &winX, &winY, &winZ);
winY = (float)viewport[3] - winY;
return vec3((float) winX, (float) winY, (float) winZ);
}
vec3 SDLAppDisplay::unproject(vec2 pos) {
GLint viewport[4];
GLdouble modelview[16];
GLdouble projection[16];
GLfloat winX, winY, winZ;
GLdouble posX, posY, posZ;
glGetDoublev( GL_MODELVIEW_MATRIX, modelview );
glGetDoublev( GL_PROJECTION_MATRIX, projection );
glGetIntegerv( GL_VIEWPORT, viewport );
winX = pos.x;
winY = (float)viewport[3] - pos.y;
glReadPixels( int(winX), int(winY), 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ );
gluUnProject( winX, winY, winZ, modelview, projection, viewport, &posX, &posY, &posZ);
return vec3((float) posX, (float) posY, (float) posZ);
}