00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017 #include "video/gl/gl_renderer.hpp"
00018
00019 #include <iomanip>
00020 #include <iostream>
00021 #include <physfs.h>
00022
00023 #include "supertux/gameconfig.hpp"
00024 #include "supertux/globals.hpp"
00025 #include "video/drawing_request.hpp"
00026 #include "video/gl/gl_surface_data.hpp"
00027 #include "video/gl/gl_texture.hpp"
00028 #define LIGHTMAP_DIV 5
00029
00030 #ifdef GL_VERSION_ES_CM_1_0
00031 # define glOrtho glOrthof
00032 #endif
00033
00034 GLRenderer::GLRenderer() :
00035 desktop_size(-1, -1),
00036 screen_size(-1, -1),
00037 fullscreen_active(false)
00038 {
00039 Renderer::instance_ = this;
00040
00041 #if SDL_MAJOR_VERSION > 1 || SDL_MINOR_VERSION > 2 || (SDL_MINOR_VERSION == 2 && SDL_PATCHLEVEL >= 10)
00042
00043
00044
00045 const SDL_VideoInfo *info = SDL_GetVideoInfo();
00046 if (info)
00047 {
00048 desktop_size = Size(info->current_w, info->current_h);
00049 }
00050 #endif
00051
00052 if(texture_manager != 0)
00053 texture_manager->save_textures();
00054
00055 #ifdef SDL_GL_SWAP_CONTROL
00056 if(config->try_vsync) {
00057
00058 SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1);
00059 }
00060 #endif
00061
00062 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
00063
00064
00065 SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
00066 SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5);
00067 SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
00068
00069 if(g_config->use_fullscreen)
00070 {
00071 apply_video_mode(g_config->fullscreen_size, true);
00072 }
00073 else
00074 {
00075 apply_video_mode(g_config->window_size, false);
00076 }
00077
00078
00079 glDisable(GL_DEPTH_TEST);
00080 glDisable(GL_CULL_FACE);
00081 glEnable(GL_TEXTURE_2D);
00082 glEnable(GL_BLEND);
00083 glEnableClientState(GL_VERTEX_ARRAY);
00084 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
00085 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
00086
00087
00088 apply_config();
00089
00090 if(texture_manager == 0)
00091 texture_manager = new TextureManager();
00092 else
00093 texture_manager->reload_textures();
00094
00095 #ifndef GL_VERSION_ES_CM_1_0
00096 GLenum err = glewInit();
00097 if (GLEW_OK != err)
00098 {
00099 std::ostringstream out;
00100 out << "GLRenderer: " << glewGetErrorString(err);
00101 throw std::runtime_error(out.str());
00102 }
00103 log_info << "Using GLEW " << glewGetString(GLEW_VERSION) << std::endl;
00104 log_info << "GLEW_ARB_texture_non_power_of_two: " << static_cast<int>(GLEW_ARB_texture_non_power_of_two) << std::endl;
00105 #endif
00106 }
00107
00108 GLRenderer::~GLRenderer()
00109 {
00110 }
00111
00112 void
00113 GLRenderer::draw_surface(const DrawingRequest& request)
00114 {
00115 const Surface* surface = (const Surface*) request.request_data;
00116 GLTexture* gltexture = static_cast<GLTexture*>(surface->get_texture().get());
00117 GLSurfaceData *surface_data = static_cast<GLSurfaceData*>(surface->get_surface_data());
00118
00119 glBindTexture(GL_TEXTURE_2D, gltexture->get_handle());
00120 intern_draw(request.pos.x, request.pos.y,
00121 request.pos.x + surface->get_width(),
00122 request.pos.y + surface->get_height(),
00123 surface_data->get_uv_left(),
00124 surface_data->get_uv_top(),
00125 surface_data->get_uv_right(),
00126 surface_data->get_uv_bottom(),
00127 request.angle,
00128 request.alpha,
00129 request.color,
00130 request.blend,
00131 request.drawing_effect);
00132 }
00133
00134 void
00135 GLRenderer::draw_surface_part(const DrawingRequest& request)
00136 {
00137 const SurfacePartRequest* surfacepartrequest
00138 = (SurfacePartRequest*) request.request_data;
00139 const Surface* surface = surfacepartrequest->surface;
00140 boost::shared_ptr<GLTexture> gltexture = boost::dynamic_pointer_cast<GLTexture>(surface->get_texture());
00141 GLSurfaceData *surface_data = reinterpret_cast<GLSurfaceData *>(surface->get_surface_data());
00142
00143 float uv_width = surface_data->get_uv_right() - surface_data->get_uv_left();
00144 float uv_height = surface_data->get_uv_bottom() - surface_data->get_uv_top();
00145
00146 float uv_left = surface_data->get_uv_left() + (uv_width * surfacepartrequest->source.x) / surface->get_width();
00147 float uv_top = surface_data->get_uv_top() + (uv_height * surfacepartrequest->source.y) / surface->get_height();
00148 float uv_right = surface_data->get_uv_left() + (uv_width * (surfacepartrequest->source.x + surfacepartrequest->size.x)) / surface->get_width();
00149 float uv_bottom = surface_data->get_uv_top() + (uv_height * (surfacepartrequest->source.y + surfacepartrequest->size.y)) / surface->get_height();
00150
00151 glBindTexture(GL_TEXTURE_2D, gltexture->get_handle());
00152 intern_draw(request.pos.x, request.pos.y,
00153 request.pos.x + surfacepartrequest->size.x,
00154 request.pos.y + surfacepartrequest->size.y,
00155 uv_left,
00156 uv_top,
00157 uv_right,
00158 uv_bottom,
00159 0.0,
00160 request.alpha,
00161 request.color,
00162 Blend(),
00163 request.drawing_effect);
00164 }
00165
00166 void
00167 GLRenderer::draw_gradient(const DrawingRequest& request)
00168 {
00169 const GradientRequest* gradientrequest
00170 = (GradientRequest*) request.request_data;
00171 const Color& top = gradientrequest->top;
00172 const Color& bottom = gradientrequest->bottom;
00173
00174 glDisable(GL_TEXTURE_2D);
00175 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
00176 glEnableClientState(GL_COLOR_ARRAY);
00177
00178 float vertices[] = {
00179 0, 0,
00180 SCREEN_WIDTH, 0,
00181 SCREEN_WIDTH, SCREEN_HEIGHT,
00182 0, SCREEN_HEIGHT
00183 };
00184 glVertexPointer(2, GL_FLOAT, 0, vertices);
00185
00186 float colors[] = {
00187 top.red, top.green, top.blue, top.alpha,
00188 top.red, top.green, top.blue, top.alpha,
00189 bottom.red, bottom.green, bottom.blue, bottom.alpha,
00190 bottom.red, bottom.green, bottom.blue, bottom.alpha,
00191 };
00192 glColorPointer(4, GL_FLOAT, 0, colors);
00193
00194 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
00195
00196 glDisableClientState(GL_COLOR_ARRAY);
00197 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
00198
00199 glEnable(GL_TEXTURE_2D);
00200 glColor4f(1, 1, 1, 1);
00201 }
00202
00203 void
00204 GLRenderer::draw_filled_rect(const DrawingRequest& request)
00205 {
00206 const FillRectRequest* fillrectrequest
00207 = (FillRectRequest*) request.request_data;
00208
00209 glDisable(GL_TEXTURE_2D);
00210 glColor4f(fillrectrequest->color.red, fillrectrequest->color.green,
00211 fillrectrequest->color.blue, fillrectrequest->color.alpha);
00212 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
00213
00214 if (fillrectrequest->radius != 0.0f)
00215 {
00216
00217
00218
00219 float radius = std::min(fillrectrequest->radius,
00220 std::min(fillrectrequest->size.x/2,
00221 fillrectrequest->size.y/2));
00222
00223
00224 Rectf irect(request.pos.x + radius,
00225 request.pos.y + radius,
00226 request.pos.x + fillrectrequest->size.x - radius,
00227 request.pos.y + fillrectrequest->size.y - radius);
00228
00229 int n = 8;
00230 int p = 0;
00231 std::vector<float> vertices((n+1) * 4 * 2);
00232
00233 for(int i = 0; i <= n; ++i)
00234 {
00235 float x = sinf(i * (M_PI/2) / n) * radius;
00236 float y = cosf(i * (M_PI/2) / n) * radius;
00237
00238 vertices[p++] = irect.get_left() - x;
00239 vertices[p++] = irect.get_top() - y;
00240
00241 vertices[p++] = irect.get_right() + x;
00242 vertices[p++] = irect.get_top() - y;
00243 }
00244
00245 for(int i = 0; i <= n; ++i)
00246 {
00247 float x = cosf(i * (M_PI/2) / n) * radius;
00248 float y = sinf(i * (M_PI/2) / n) * radius;
00249
00250 vertices[p++] = irect.get_left() - x;
00251 vertices[p++] = irect.get_bottom() + y;
00252
00253 vertices[p++] = irect.get_right() + x;
00254 vertices[p++] = irect.get_bottom() + y;
00255 }
00256
00257 glVertexPointer(2, GL_FLOAT, 0, &*vertices.begin());
00258 glDrawArrays(GL_TRIANGLE_STRIP, 0, vertices.size()/2);
00259 }
00260 else
00261 {
00262 float x = request.pos.x;
00263 float y = request.pos.y;
00264 float w = fillrectrequest->size.x;
00265 float h = fillrectrequest->size.y;
00266
00267 float vertices[] = {
00268 x, y,
00269 x+w, y,
00270 x+w, y+h,
00271 x, y+h
00272 };
00273 glVertexPointer(2, GL_FLOAT, 0, vertices);
00274
00275 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
00276 }
00277
00278 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
00279 glEnable(GL_TEXTURE_2D);
00280 glColor4f(1, 1, 1, 1);
00281 }
00282
00283 void
00284 GLRenderer::draw_inverse_ellipse(const DrawingRequest& request)
00285 {
00286 const InverseEllipseRequest* ellipse = (InverseEllipseRequest*)request.request_data;
00287
00288 glDisable(GL_TEXTURE_2D);
00289 glColor4f(ellipse->color.red, ellipse->color.green,
00290 ellipse->color.blue, ellipse->color.alpha);
00291
00292 float x = request.pos.x;
00293 float y = request.pos.y;
00294 float w = ellipse->size.x/2.0f;
00295 float h = ellipse->size.y/2.0f;
00296
00297 static const int slices = 16;
00298 static const int points = (slices+1) * 12;
00299
00300 float vertices[points * 2];
00301 int p = 0;
00302
00303
00304 vertices[p++] = SCREEN_WIDTH; vertices[p++] = SCREEN_HEIGHT;
00305 vertices[p++] = 0; vertices[p++] = SCREEN_HEIGHT;
00306 vertices[p++] = x; vertices[p++] = y+h;
00307
00308
00309 vertices[p++] = SCREEN_WIDTH; vertices[p++] = 0;
00310 vertices[p++] = 0; vertices[p++] = 0;
00311 vertices[p++] = x; vertices[p++] = y-h;
00312
00313
00314 vertices[p++] = SCREEN_WIDTH; vertices[p++] = 0;
00315 vertices[p++] = SCREEN_WIDTH; vertices[p++] = SCREEN_HEIGHT;
00316 vertices[p++] = x+w; vertices[p++] = y;
00317
00318
00319 vertices[p++] = 0; vertices[p++] = 0;
00320 vertices[p++] = 0; vertices[p++] = SCREEN_HEIGHT;
00321 vertices[p++] = x-w; vertices[p++] = y;
00322
00323 for(int i = 0; i < slices; ++i)
00324 {
00325 float ex1 = sinf(M_PI/2 / slices * i) * w;
00326 float ey1 = cosf(M_PI/2 / slices * i) * h;
00327
00328 float ex2 = sinf(M_PI/2 / slices * (i+1)) * w;
00329 float ey2 = cosf(M_PI/2 / slices * (i+1)) * h;
00330
00331
00332 vertices[p++] = SCREEN_WIDTH; vertices[p++] = SCREEN_HEIGHT;
00333 vertices[p++] = x + ex1; vertices[p++] = y + ey1;
00334 vertices[p++] = x + ex2; vertices[p++] = y + ey2;
00335
00336
00337 vertices[p++] = 0; vertices[p++] = 0;
00338 vertices[p++] = x - ex1; vertices[p++] = y - ey1;
00339 vertices[p++] = x - ex2; vertices[p++] = y - ey2;
00340
00341
00342 vertices[p++] = SCREEN_WIDTH; vertices[p++] = 0;
00343 vertices[p++] = x + ex1; vertices[p++] = y - ey1;
00344 vertices[p++] = x + ex2; vertices[p++] = y - ey2;
00345
00346
00347 vertices[p++] = 0; vertices[p++] = SCREEN_HEIGHT;
00348 vertices[p++] = x - ex1; vertices[p++] = y + ey1;
00349 vertices[p++] = x - ex2; vertices[p++] = y + ey2;
00350 }
00351
00352 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
00353 glVertexPointer(2, GL_FLOAT, 0, vertices);
00354
00355 glDrawArrays(GL_TRIANGLES, 0, points);
00356
00357 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
00358
00359 glEnable(GL_TEXTURE_2D);
00360 glColor4f(1, 1, 1, 1);
00361 }
00362
00363 void
00364 GLRenderer::do_take_screenshot()
00365 {
00366
00367
00368 SDL_Surface *shot_surf;
00369
00370 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
00371 shot_surf = SDL_CreateRGBSurface(SDL_SWSURFACE, SCREEN_WIDTH, SCREEN_HEIGHT, 24, 0x00FF0000, 0x0000FF00, 0x000000FF, 0);
00372 #else
00373 shot_surf = SDL_CreateRGBSurface(SDL_SWSURFACE, SCREEN_WIDTH, SCREEN_HEIGHT, 24, 0x000000FF, 0x0000FF00, 0x00FF0000, 0);
00374 #endif
00375 if (!shot_surf) {
00376 log_warning << "Could not create RGB Surface to contain screenshot" << std::endl;
00377 return;
00378 }
00379
00380
00381 char* pixels = new char[3 * SCREEN_WIDTH * SCREEN_HEIGHT];
00382 if (!pixels) {
00383 log_warning << "Could not allocate memory to store screenshot" << std::endl;
00384 SDL_FreeSurface(shot_surf);
00385 return;
00386 }
00387 glPixelStorei(GL_PACK_ALIGNMENT, 1);
00388 glReadPixels(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, GL_RGB, GL_UNSIGNED_BYTE, pixels);
00389
00390
00391 for (int i = 0; i < SCREEN_HEIGHT; i++) {
00392 char* src = pixels + (3 * SCREEN_WIDTH * (SCREEN_HEIGHT - i - 1));
00393 if(SDL_MUSTLOCK(shot_surf))
00394 {
00395 SDL_LockSurface(shot_surf);
00396 }
00397 char* dst = ((char*)shot_surf->pixels) + i * shot_surf->pitch;
00398 memcpy(dst, src, 3 * SCREEN_WIDTH);
00399 if(SDL_MUSTLOCK(shot_surf))
00400 {
00401 SDL_UnlockSurface(shot_surf);
00402 }
00403 }
00404
00405
00406 delete[](pixels);
00407
00408
00409 static const std::string writeDir = PHYSFS_getWriteDir();
00410 static const std::string dirSep = PHYSFS_getDirSeparator();
00411 static const std::string baseName = "screenshot";
00412 static const std::string fileExt = ".bmp";
00413 std::string fullFilename;
00414 for (int num = 0; num < 1000; num++) {
00415 std::ostringstream oss;
00416 oss << baseName;
00417 oss << std::setw(3) << std::setfill('0') << num;
00418 oss << fileExt;
00419 std::string fileName = oss.str();
00420 fullFilename = writeDir + dirSep + fileName;
00421 if (!PHYSFS_exists(fileName.c_str())) {
00422 SDL_SaveBMP(shot_surf, fullFilename.c_str());
00423 log_debug << "Wrote screenshot to \"" << fullFilename << "\"" << std::endl;
00424 SDL_FreeSurface(shot_surf);
00425 return;
00426 }
00427 }
00428 log_warning << "Did not save screenshot, because all files up to \"" << fullFilename << "\" already existed" << std::endl;
00429 SDL_FreeSurface(shot_surf);
00430 }
00431
00432 void
00433 GLRenderer::flip()
00434 {
00435 assert_gl("drawing");
00436 SDL_GL_SwapBuffers();
00437 }
00438
00439 void
00440 GLRenderer::resize(int w, int h)
00441 {
00442
00443
00444 SDL_SetVideoMode(w, h, 0, SDL_OPENGL | SDL_RESIZABLE);
00445
00446 g_config->window_size = Size(w, h);
00447
00448 apply_config();
00449 }
00450
00451 void
00452 GLRenderer::apply_config()
00453 {
00454 if (false)
00455 {
00456 log_info << "Applying Config:"
00457 << "\n Desktop: " << desktop_size.width << "x" << desktop_size.height
00458 << "\n Window: " << g_config->window_size
00459 << "\n FullRes: " << g_config->fullscreen_size
00460 << "\n Aspect: " << g_config->aspect_size
00461 << "\n Magnif: " << g_config->magnification
00462 << std::endl;
00463 }
00464
00465 float target_aspect = static_cast<float>(desktop_size.width) / static_cast<float>(desktop_size.height);
00466 if (g_config->aspect_size != Size(0, 0))
00467 {
00468 target_aspect = float(g_config->aspect_size.width) / float(g_config->aspect_size.height);
00469 }
00470
00471 float desktop_aspect = 4.0f / 3.0f;
00472 if (desktop_size.width != -1 && desktop_size.height != -1)
00473 {
00474 desktop_aspect = float(desktop_size.width) / float(desktop_size.height);
00475 }
00476
00477 Size screen_size;
00478
00479
00480 if (g_config->use_fullscreen)
00481 {
00482 screen_size = g_config->fullscreen_size;
00483 desktop_aspect = float(screen_size.width) / float(screen_size.height);
00484 }
00485 else
00486 {
00487 screen_size = g_config->window_size;
00488 }
00489
00490 apply_video_mode(screen_size, g_config->use_fullscreen);
00491
00492 if (target_aspect > 1.0f)
00493 {
00494 SCREEN_WIDTH = static_cast<int>(screen_size.width * (target_aspect / desktop_aspect));
00495 SCREEN_HEIGHT = static_cast<int>(screen_size.height);
00496 }
00497 else
00498 {
00499 SCREEN_WIDTH = static_cast<int>(screen_size.width);
00500 SCREEN_HEIGHT = static_cast<int>(screen_size.height * (target_aspect / desktop_aspect));
00501 }
00502
00503 Size max_size(1280, 800);
00504 Size min_size(640, 480);
00505
00506 if (g_config->magnification == 0.0f)
00507 {
00508
00509
00510 if (SCREEN_WIDTH > max_size.width || SCREEN_HEIGHT > max_size.height)
00511 {
00512 float scale1 = float(max_size.width)/SCREEN_WIDTH;
00513 float scale2 = float(max_size.height)/SCREEN_HEIGHT;
00514 float scale = (scale1 < scale2) ? scale1 : scale2;
00515 SCREEN_WIDTH = static_cast<int>(SCREEN_WIDTH * scale);
00516 SCREEN_HEIGHT = static_cast<int>(SCREEN_HEIGHT * scale);
00517 }
00518 else if (SCREEN_WIDTH < min_size.width || SCREEN_HEIGHT < min_size.height)
00519 {
00520 float scale1 = float(min_size.width)/SCREEN_WIDTH;
00521 float scale2 = float(min_size.height)/SCREEN_HEIGHT;
00522 float scale = (scale1 < scale2) ? scale1 : scale2;
00523 SCREEN_WIDTH = static_cast<int>(SCREEN_WIDTH * scale);
00524 SCREEN_HEIGHT = static_cast<int>(SCREEN_HEIGHT * scale);
00525 }
00526
00527
00528 glViewport(0, 0, screen_size.width, screen_size.height);
00529 }
00530 else
00531 {
00532 SCREEN_WIDTH = static_cast<int>(SCREEN_WIDTH / g_config->magnification);
00533 SCREEN_HEIGHT = static_cast<int>(SCREEN_HEIGHT / g_config->magnification);
00534
00535
00536
00537 Size new_size = screen_size;
00538
00539 if (SCREEN_WIDTH > max_size.width)
00540 {
00541 new_size.width = static_cast<int>((float) new_size.width * float(max_size.width)/SCREEN_WIDTH);
00542 SCREEN_WIDTH = static_cast<int>(max_size.width);
00543 }
00544
00545 if (SCREEN_HEIGHT > max_size.height)
00546 {
00547 new_size.height = static_cast<int>((float) new_size.height * float(max_size.height)/SCREEN_HEIGHT);
00548 SCREEN_HEIGHT = static_cast<int>(max_size.height);
00549 }
00550
00551
00552 glClear(GL_COLOR_BUFFER_BIT);
00553 SDL_GL_SwapBuffers();
00554 glClear(GL_COLOR_BUFFER_BIT);
00555 SDL_GL_SwapBuffers();
00556
00557 glViewport(std::max(0, (screen_size.width - new_size.width) / 2),
00558 std::max(0, (screen_size.height - new_size.height) / 2),
00559 std::min(new_size.width, screen_size.width),
00560 std::min(new_size.height, screen_size.height));
00561 }
00562
00563 glMatrixMode(GL_PROJECTION);
00564 glLoadIdentity();
00565
00566 glOrtho(0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, -1, 1);
00567
00568 glMatrixMode(GL_MODELVIEW);
00569 glLoadIdentity();
00570 glTranslatef(0, 0, 0);
00571 check_gl_error("Setting up view matrices");
00572 }
00573
00574 void
00575 GLRenderer::apply_video_mode(const Size& size, bool fullscreen)
00576 {
00577
00578 if (screen_size != size || fullscreen_active != fullscreen)
00579 {
00580 int flags = SDL_OPENGL;
00581
00582 if (fullscreen)
00583 {
00584 flags |= SDL_FULLSCREEN;
00585 }
00586 else
00587 {
00588 flags |= SDL_RESIZABLE;
00589 }
00590
00591 if (SDL_Surface *screen = SDL_SetVideoMode(size.width, size.height, 0, flags))
00592 {
00593 screen_size = Size(screen->w, screen->h);
00594 fullscreen_active = fullscreen;
00595 }
00596 else
00597 {
00598 std::ostringstream msg;
00599 msg << "Couldn't set video mode " << size.width << "x" << size.height << ": " << SDL_GetError();
00600 throw std::runtime_error(msg.str());
00601 }
00602 }
00603 }
00604
00605