// SuperTux
// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
+// Updated by GiBy 2013 for SDL2 <giby_the_kid@yahoo.fr>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
#include <iomanip>
#include <iostream>
-#include <math.h>
#include <physfs.h>
+#include "SDL.h"
#include "supertux/gameconfig.hpp"
#include "supertux/globals.hpp"
#include "video/drawing_request.hpp"
#include "video/gl/gl_surface_data.hpp"
#include "video/gl/gl_texture.hpp"
+#include "video/util.hpp"
+
#define LIGHTMAP_DIV 5
#ifdef GL_VERSION_ES_CM_1_0
# define glOrtho glOrthof
#endif
-namespace {
-
-inline void intern_draw(float left, float top, float right, float bottom,
- float uv_left, float uv_top,
- float uv_right, float uv_bottom,
- float angle, float alpha,
- const Color& color,
- const Blend& blend,
- DrawingEffect effect)
-{
- if(effect & HORIZONTAL_FLIP)
- std::swap(uv_left, uv_right);
-
- if(effect & VERTICAL_FLIP)
- std::swap(uv_top, uv_bottom);
-
- // unrotated blit
- glBlendFunc(blend.sfactor, blend.dfactor);
- glColor4f(color.red, color.green, color.blue, color.alpha * alpha);
-
- if (angle == 0.0f) {
- float vertices[] = {
- left, top,
- right, top,
- right, bottom,
- left, bottom,
- };
- glVertexPointer(2, GL_FLOAT, 0, vertices);
-
- float uvs[] = {
- uv_left, uv_top,
- uv_right, uv_top,
- uv_right, uv_bottom,
- uv_left, uv_bottom,
- };
- glTexCoordPointer(2, GL_FLOAT, 0, uvs);
-
- glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
- } else {
- // rotated blit
- float center_x = (left + right) / 2;
- float center_y = (top + bottom) / 2;
-
- float sa = sinf(angle/180.0f*M_PI);
- float ca = cosf(angle/180.0f*M_PI);
-
- left -= center_x;
- right -= center_x;
-
- top -= center_y;
- bottom -= center_y;
-
- float vertices[] = {
- left*ca - top*sa + center_x, left*sa + top*ca + center_y,
- right*ca - top*sa + center_x, right*sa + top*ca + center_y,
- right*ca - bottom*sa + center_x, right*sa + bottom*ca + center_y,
- left*ca - bottom*sa + center_x, left*sa + bottom*ca + center_y
- };
- glVertexPointer(2, GL_FLOAT, 0, vertices);
-
- float uvs[] = {
- uv_left, uv_top,
- uv_right, uv_top,
- uv_right, uv_bottom,
- uv_left, uv_bottom,
- };
- glTexCoordPointer(2, GL_FLOAT, 0, uvs);
-
- glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
- }
-
- // FIXME: find a better way to restore the blend mode
- glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-}
-
-} // namespace
-
GLRenderer::GLRenderer() :
- desktop_size(-1, -1),
- screen_size(-1, -1),
- fullscreen_active(false)
+ window(),
+ glcontext(),
+ viewport(),
+ desktop_size(0, 0),
+ fullscreen_active(false),
+ last_texture(static_cast<GLuint> (-1))
{
Renderer::instance_ = this;
-#if SDL_MAJOR_VERSION > 1 || SDL_MINOR_VERSION > 2 || (SDL_MINOR_VERSION == 2 && SDL_PATCHLEVEL >= 10)
- // unfortunately only newer SDLs have these infos.
- // This must be called before SDL_SetVideoMode() or it will return
- // the window size instead of the desktop size.
- const SDL_VideoInfo *info = SDL_GetVideoInfo();
- if (info)
- {
- desktop_size = Size(info->current_w, info->current_h);
- }
-#endif
+ SDL_DisplayMode mode;
+ SDL_GetCurrentDisplayMode(0, &mode);
+ desktop_size = Size(mode.w, mode.h);
if(texture_manager != 0)
texture_manager->save_textures();
-#ifdef SDL_GL_SWAP_CONTROL
- if(config->try_vsync) {
+ if(g_config->try_vsync) {
/* we want vsync for smooth scrolling */
- SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1);
+ if (SDL_GL_SetSwapInterval(-1) != 0)
+ {
+ log_info << "no support for late swap tearing vsync: " << SDL_GetError() << std::endl;
+ if (SDL_GL_SetSwapInterval(1))
+ {
+ log_info << "no support for vsync: " << SDL_GetError() << std::endl;
+ }
+ }
}
-#endif
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
- // FIXME: Hu? 16bit rendering?
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
- if(g_config->use_fullscreen)
- {
- apply_video_mode(g_config->fullscreen_size, true);
- }
- else
- {
- apply_video_mode(g_config->window_size, false);
- }
+ apply_video_mode();
// setup opengl state and transform
glDisable(GL_DEPTH_TEST);
// Init the projection matrix, viewport and stuff
apply_config();
-
+
if(texture_manager == 0)
texture_manager = new TextureManager();
else
texture_manager->reload_textures();
-
+
#ifndef GL_VERSION_ES_CM_1_0
GLenum err = glewInit();
if (GLEW_OK != err)
throw std::runtime_error(out.str());
}
log_info << "Using GLEW " << glewGetString(GLEW_VERSION) << std::endl;
- log_info << "GLEW_ARB_texture_non_power_of_two: " << GLEW_ARB_texture_non_power_of_two << std::endl;
+ log_info << "GLEW_ARB_texture_non_power_of_two: " << static_cast<int>(GLEW_ARB_texture_non_power_of_two) << std::endl;
#endif
}
GLRenderer::~GLRenderer()
{
+ SDL_GL_DeleteContext(glcontext);
+ SDL_DestroyWindow(window);
}
void
GLRenderer::draw_surface(const DrawingRequest& request)
{
- const Surface* surface = (const Surface*) request.request_data;
- GLTexture *gltexture = dynamic_cast<GLTexture *>(surface->get_texture());
- GLSurfaceData *surface_data = reinterpret_cast<GLSurfaceData *>(surface->get_surface_data());
+ const Surface* surface = static_cast<const SurfaceRequest*>(request.request_data)->surface;
+ if(surface == NULL)
+ {
+ return;
+ }
+ GLTexture* gltexture = static_cast<GLTexture*>(surface->get_texture().get());
+ if(gltexture == NULL)
+ {
+ return;
+ }
+ GLSurfaceData *surface_data = static_cast<GLSurfaceData*>(surface->get_surface_data());
+ if(surface_data == NULL)
+ {
+ return;
+ }
- glBindTexture(GL_TEXTURE_2D, gltexture->get_handle());
+ GLuint th = gltexture->get_handle();
+ if (th != last_texture) {
+ last_texture = th;
+ glBindTexture(GL_TEXTURE_2D, th);
+ }
intern_draw(request.pos.x, request.pos.y,
request.pos.x + surface->get_width(),
request.pos.y + surface->get_height(),
{
const SurfacePartRequest* surfacepartrequest
= (SurfacePartRequest*) request.request_data;
- const Surface *surface = surfacepartrequest->surface;
- GLTexture *gltexture = dynamic_cast<GLTexture *>(surface->get_texture());
+ const Surface* surface = surfacepartrequest->surface;
+ boost::shared_ptr<GLTexture> gltexture = boost::dynamic_pointer_cast<GLTexture>(surface->get_texture());
GLSurfaceData *surface_data = reinterpret_cast<GLSurfaceData *>(surface->get_surface_data());
float uv_width = surface_data->get_uv_right() - surface_data->get_uv_left();
float uv_right = surface_data->get_uv_left() + (uv_width * (surfacepartrequest->source.x + surfacepartrequest->size.x)) / surface->get_width();
float uv_bottom = surface_data->get_uv_top() + (uv_height * (surfacepartrequest->source.y + surfacepartrequest->size.y)) / surface->get_height();
- glBindTexture(GL_TEXTURE_2D, gltexture->get_handle());
+ GLuint th = gltexture->get_handle();
+ if (th != last_texture) {
+ last_texture = th;
+ glBindTexture(GL_TEXTURE_2D, th);
+ }
intern_draw(request.pos.x, request.pos.y,
request.pos.x + surfacepartrequest->size.x,
request.pos.y + surfacepartrequest->size.y,
void
GLRenderer::draw_gradient(const DrawingRequest& request)
{
- const GradientRequest* gradientrequest
+ const GradientRequest* gradientrequest
= (GradientRequest*) request.request_data;
const Color& top = gradientrequest->top;
const Color& bottom = gradientrequest->bottom;
float vertices[] = {
0, 0,
- SCREEN_WIDTH, 0,
- SCREEN_WIDTH, SCREEN_HEIGHT,
- 0, SCREEN_HEIGHT
+ float(SCREEN_WIDTH), 0,
+ float(SCREEN_WIDTH), float(SCREEN_HEIGHT),
+ 0, float(SCREEN_HEIGHT)
};
glVertexPointer(2, GL_FLOAT, 0, vertices);
glColor4f(fillrectrequest->color.red, fillrectrequest->color.green,
fillrectrequest->color.blue, fillrectrequest->color.alpha);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
-
+
if (fillrectrequest->radius != 0.0f)
{
// draw round rect
// inner rectangle
Rectf irect(request.pos.x + radius,
- request.pos.y + radius,
- request.pos.x + fillrectrequest->size.x - radius,
- request.pos.y + fillrectrequest->size.y - radius);
+ request.pos.y + radius,
+ request.pos.x + fillrectrequest->size.x - radius,
+ request.pos.y + fillrectrequest->size.y - radius);
int n = 8;
int p = 0;
glDisable(GL_TEXTURE_2D);
glColor4f(ellipse->color.red, ellipse->color.green,
ellipse->color.blue, ellipse->color.alpha);
-
+
float x = request.pos.x;
float y = request.pos.y;
float w = ellipse->size.x/2.0f;
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnable(GL_TEXTURE_2D);
- glColor4f(1, 1, 1, 1);
+ glColor4f(1, 1, 1, 1);
}
-void
+void
GLRenderer::do_take_screenshot()
{
// [Christoph] TODO: Yes, this method also takes care of the actual disk I/O. Split it?
SDL_Surface *shot_surf;
// create surface to hold screenshot
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
- shot_surf = SDL_CreateRGBSurface(SDL_SWSURFACE, SCREEN_WIDTH, SCREEN_HEIGHT, 24, 0x00FF0000, 0x0000FF00, 0x000000FF, 0);
+ shot_surf = SDL_CreateRGBSurface(0, SCREEN_WIDTH, SCREEN_HEIGHT, 24, 0x00FF0000, 0x0000FF00, 0x000000FF, 0);
#else
- shot_surf = SDL_CreateRGBSurface(SDL_SWSURFACE, SCREEN_WIDTH, SCREEN_HEIGHT, 24, 0x000000FF, 0x0000FF00, 0x00FF0000, 0);
+ shot_surf = SDL_CreateRGBSurface(0, SCREEN_WIDTH, SCREEN_HEIGHT, 24, 0x000000FF, 0x0000FF00, 0x00FF0000, 0);
#endif
if (!shot_surf) {
log_warning << "Could not create RGB Surface to contain screenshot" << std::endl;
GLRenderer::flip()
{
assert_gl("drawing");
- SDL_GL_SwapBuffers();
+ SDL_GL_SwapWindow(window);
}
void
GLRenderer::resize(int w, int h)
{
- // This causes the screen to go black, which is annoying, but seems
- // unavoidable with SDL at the moment
- SDL_SetVideoMode(w, h, 0, SDL_OPENGL /*| SDL_RESIZABLE*/);
-
g_config->window_size = Size(w, h);
apply_config();
void
GLRenderer::apply_config()
-{
- if (false)
- {
- log_info << "Applying Config:"
- << "\n Desktop: " << desktop_size.width << "x" << desktop_size.height
- << "\n Window: " << g_config->window_size
- << "\n FullRes: " << g_config->fullscreen_size
- << "\n Aspect: " << g_config->aspect_size
- << "\n Magnif: " << g_config->magnification
- << std::endl;
- }
-
- float target_aspect = static_cast<float>(desktop_size.width) / static_cast<float>(desktop_size.height);
- if (g_config->aspect_size != Size(0, 0))
- {
- target_aspect = float(g_config->aspect_size.width) / float(g_config->aspect_size.height);
- }
-
- float desktop_aspect = 4.0f / 3.0f; // random default fallback guess
- if (desktop_size.width != -1 && desktop_size.height != -1)
- {
- desktop_aspect = float(desktop_size.width) / float(desktop_size.height);
- }
+{
+ apply_video_mode();
- Size screen_size;
+ Size target_size = g_config->use_fullscreen ?
+ ((g_config->fullscreen_size == Size(0, 0)) ? desktop_size : g_config->fullscreen_size) :
+ g_config->window_size;
- // Get the screen width
- if (g_config->use_fullscreen)
+ float pixel_aspect_ratio = 1.0f;
+ if (g_config->aspect_size != Size(0, 0))
{
- screen_size = g_config->fullscreen_size;
- desktop_aspect = float(screen_size.width) / float(screen_size.height);
+ pixel_aspect_ratio = calculate_pixel_aspect_ratio(desktop_size,
+ g_config->aspect_size);
}
- else
+ else if (g_config->use_fullscreen)
{
- screen_size = g_config->window_size;
+ pixel_aspect_ratio = calculate_pixel_aspect_ratio(desktop_size,
+ target_size);
}
- apply_video_mode(screen_size, g_config->use_fullscreen);
+ Size max_size(1280, 800);
+ Size min_size(640, 480);
- if (target_aspect > 1.0f)
- {
- SCREEN_WIDTH = static_cast<int>(screen_size.width * (target_aspect / desktop_aspect));
- SCREEN_HEIGHT = static_cast<int>(screen_size.height);
- }
- else
- {
- SCREEN_WIDTH = static_cast<int>(screen_size.width);
- SCREEN_HEIGHT = static_cast<int>(screen_size.height * (target_aspect / desktop_aspect));
- }
-
- Size max_size(1600, 1200); // FIXME: Maybe 1920 is ok too
+ Vector scale;
+ Size logical_size;
+ calculate_viewport(min_size, max_size, target_size,
+ pixel_aspect_ratio,
+ g_config->magnification,
+ scale,
+ logical_size,
+ viewport);
- if (g_config->magnification == 0.0f) // Magic value that means 'minfill'
- {
- // This scales SCREEN_WIDTH/SCREEN_HEIGHT so that they never excede
- // max_size.width/max_size.height
- if (SCREEN_WIDTH > max_size.width || SCREEN_HEIGHT > max_size.height)
- {
- float scale1 = float(max_size.width)/SCREEN_WIDTH;
- float scale2 = float(max_size.height)/SCREEN_HEIGHT;
- float scale = (scale1 < scale2) ? scale1 : scale2;
- SCREEN_WIDTH = static_cast<int>(SCREEN_WIDTH * scale);
- SCREEN_HEIGHT = static_cast<int>(SCREEN_HEIGHT * scale);
- }
+ SCREEN_WIDTH = logical_size.width;
+ SCREEN_HEIGHT = logical_size.height;
- glViewport(0, 0, screen_size.width, screen_size.height);
- }
- else
+ if (viewport.x != 0 || viewport.y != 0)
{
- SCREEN_WIDTH = static_cast<int>(SCREEN_WIDTH / g_config->magnification);
- SCREEN_HEIGHT = static_cast<int>(SCREEN_HEIGHT / g_config->magnification);
-
- // This works by adding black borders around the screen to limit
- // SCREEN_WIDTH/SCREEN_HEIGHT to max_size.width/max_size.height
- Size new_size = screen_size;
-
- if (SCREEN_WIDTH > max_size.width)
- {
- new_size.width = static_cast<int>((float) new_size.width * float(max_size.width)/SCREEN_WIDTH);
- SCREEN_WIDTH = static_cast<int>(max_size.width);
- }
-
- if (SCREEN_HEIGHT > max_size.height)
- {
- new_size.height = static_cast<int>((float) new_size.height * float(max_size.height)/SCREEN_HEIGHT);
- SCREEN_HEIGHT = static_cast<int>(max_size.height);
- }
-
// Clear both buffers so that we get a clean black border without junk
glClear(GL_COLOR_BUFFER_BIT);
- SDL_GL_SwapBuffers();
+ SDL_GL_SwapWindow(window);
glClear(GL_COLOR_BUFFER_BIT);
- SDL_GL_SwapBuffers();
-
- glViewport(std::max(0, (screen_size.width - new_size.width) / 2),
- std::max(0, (screen_size.height - new_size.height) / 2),
- std::min(new_size.width, screen_size.width),
- std::min(new_size.height, screen_size.height));
+ SDL_GL_SwapWindow(window);
}
+ glViewport(viewport.x, viewport.y, viewport.w, viewport.h);
+
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
}
void
-GLRenderer::apply_video_mode(const Size& size, bool fullscreen)
+GLRenderer::apply_video_mode()
{
- // Only change video mode when its different from the current one
- if (screen_size != size || fullscreen_active != fullscreen)
+ if (window)
{
- int flags = SDL_OPENGL;
-
- if (fullscreen)
+ if (!g_config->use_fullscreen)
{
- flags |= SDL_FULLSCREEN;
+ SDL_SetWindowFullscreen(window, 0);
}
else
{
- flags |= SDL_RESIZABLE;
+ if (g_config->fullscreen_size.width == 0 &&
+ g_config->fullscreen_size.height == 0)
+ {
+ if (SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP) != 0)
+ {
+ log_warning << "failed to switch to desktop fullscreen mode: "
+ << SDL_GetError() << std::endl;
+ }
+ else
+ {
+ log_info << "switched to desktop fullscreen mode" << std::endl;
+ }
+ }
+ else
+ {
+ SDL_DisplayMode mode;
+ mode.format = SDL_PIXELFORMAT_RGB888;
+ mode.w = g_config->fullscreen_size.width;
+ mode.h = g_config->fullscreen_size.height;
+ mode.refresh_rate = g_config->fullscreen_refresh_rate;
+ mode.driverdata = 0;
+
+ if (SDL_SetWindowDisplayMode(window, &mode) != 0)
+ {
+ log_warning << "failed to set display mode: "
+ << mode.w << "x" << mode.h << "@" << mode.refresh_rate << ": "
+ << SDL_GetError() << std::endl;
+ }
+ else
+ {
+ if (SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN) != 0)
+ {
+ log_warning << "failed to switch to fullscreen mode: "
+ << mode.w << "x" << mode.h << "@" << mode.refresh_rate << ": "
+ << SDL_GetError() << std::endl;
+ }
+ else
+ {
+ log_info << "switched to fullscreen mode: "
+ << mode.w << "x" << mode.h << "@" << mode.refresh_rate << std::endl;
+ }
+ }
+ }
}
-
- if (SDL_Surface *screen = SDL_SetVideoMode(size.width, size.height, 0, flags))
+ }
+ else
+ {
+ int flags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE;
+ Size size;
+ if (g_config->use_fullscreen)
{
- screen_size = Size(screen->w, screen->h);
- fullscreen_active = fullscreen;
+ if (g_config->fullscreen_size == Size(0, 0))
+ {
+ flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
+ size = desktop_size;
+ }
+ else
+ {
+ flags |= SDL_WINDOW_FULLSCREEN;
+ size.width = g_config->fullscreen_size.width;
+ size.height = g_config->fullscreen_size.height;
+ }
}
else
{
+ size = g_config->window_size;
+ }
+
+ window = SDL_CreateWindow("SuperTux",
+ SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
+ size.width, size.height,
+ flags);
+ if (!window)
+ {
std::ostringstream msg;
msg << "Couldn't set video mode " << size.width << "x" << size.height << ": " << SDL_GetError();
throw std::runtime_error(msg.str());
}
+ else
+ {
+ glcontext = SDL_GL_CreateContext(window);
+
+ SCREEN_WIDTH = size.width;
+ SCREEN_HEIGHT = size.height;
+
+ fullscreen_active = g_config->use_fullscreen;
+ }
}
}
+Vector
+GLRenderer::to_logical(int physical_x, int physical_y)
+{
+ return Vector(static_cast<float>(physical_x - viewport.x) * SCREEN_WIDTH / viewport.w,
+ static_cast<float>(physical_y - viewport.y) * SCREEN_HEIGHT / viewport.h);
+}
+
+void
+GLRenderer::set_gamma(float gamma)
+{
+ Uint16 ramp[256];
+ SDL_CalculateGammaRamp(gamma, ramp);
+ SDL_SetWindowGammaRamp(window, ramp, ramp, ramp);
+}
+
/* EOF */