From 20efd7620620892d92b1c7df124c3a0c8df22a82 Mon Sep 17 00:00:00 2001 From: Tim Goya Date: Wed, 15 Aug 2007 01:02:22 +0000 Subject: [PATCH] Refactored video/ subsystem to make adding other methods of rendering (in particular pure SDL) easier and more maintainable. Implemented pure SDL rendering based on the nogl patch. Lightmaps are implemented for pure SDL as well. SVN-Revision: 5138 --- configure.ac | 12 +- src/gameconfig.cpp | 9 +- src/gameconfig.hpp | 3 + src/main.cpp | 59 +----- src/mainloop.cpp | 4 +- src/mainloop.hpp | 2 +- src/sprite/sprite_data.cpp | 8 +- src/video/color.hpp | 10 + src/video/drawing_context.cpp | 409 +++++++++----------------------------- src/video/drawing_context.hpp | 61 ++---- src/video/drawing_request.hpp | 133 +++++++++++++ src/video/font.cpp | 41 ++-- src/video/font.hpp | 8 +- src/video/gl_lightmap.cpp | 317 +++++++++++++++++++++++++++++ src/video/gl_lightmap.hpp | 61 ++++++ src/video/gl_renderer.cpp | 336 +++++++++++++++++++++++++++++++ src/video/gl_renderer.hpp | 48 +++++ src/video/gl_texture.cpp | 146 ++++++++++++++ src/video/gl_texture.hpp | 97 +++++++++ src/video/glutil.hpp | 16 ++ src/video/image_texture.cpp | 2 + src/video/image_texture.hpp | 2 + src/video/lightmap.hpp | 59 ++++++ src/video/renderer.hpp | 57 ++++++ src/video/sdl_lightmap.cpp | 389 ++++++++++++++++++++++++++++++++++++ src/video/sdl_lightmap.hpp | 60 ++++++ src/video/sdl_renderer.cpp | 307 ++++++++++++++++++++++++++++ src/video/sdl_renderer.hpp | 47 +++++ src/video/sdl_texture.cpp | 451 ++++++++++++++++++++++++++++++++++++++++++ src/video/sdl_texture.hpp | 109 ++++++++++ src/video/surface.cpp | 233 ---------------------- src/video/surface.hpp | 148 ++++++++++---- src/video/texture.cpp | 109 ---------- src/video/texture.hpp | 60 ++++-- src/video/texture_manager.cpp | 84 +++----- src/video/texture_manager.hpp | 30 +-- 36 files changed, 3013 insertions(+), 914 deletions(-) create mode 100644 src/video/drawing_request.hpp create mode 100644 src/video/gl_lightmap.cpp create mode 100644 src/video/gl_lightmap.hpp create mode 100644 src/video/gl_renderer.cpp create mode 100644 src/video/gl_renderer.hpp create mode 100644 src/video/gl_texture.cpp create mode 100644 src/video/gl_texture.hpp create mode 100644 src/video/lightmap.hpp create mode 100644 src/video/renderer.hpp create mode 100644 src/video/sdl_lightmap.cpp create mode 100644 src/video/sdl_lightmap.hpp create mode 100644 src/video/sdl_renderer.cpp create mode 100644 src/video/sdl_renderer.hpp create mode 100644 src/video/sdl_texture.cpp create mode 100644 src/video/sdl_texture.hpp delete mode 100644 src/video/surface.cpp delete mode 100644 src/video/texture.cpp diff --git a/configure.ac b/configure.ac index 0627bf2f7..f6aca78ee 100644 --- a/configure.ac +++ b/configure.ac @@ -154,9 +154,15 @@ NP_FINDLIB([OPENAL], [OpenAL], [OpenAL], [AC_MSG_ERROR([Please intall OpenAL])], [], []) -AX_CHECK_GL -if test "$no_gl" = "yes"; then - AC_MSG_ERROR([Please install opengl libraries and headers]) +AC_ARG_ENABLE(opengl, + AC_HELP_STRING([--enable-opengl], [enable opengl support]), + [enable_opengl=$enableval], [enable_opengl=yes]) + +if test "$enable_opengl" = "yes"; then + AX_CHECK_GL + if test "$no_gl" != "yes"; then + AC_DEFINE_UNQUOTED(HAVE_OPENGL, 1, Define if OpenGL is present on the system) + fi fi dnl Checks for library functions. diff --git a/src/gameconfig.cpp b/src/gameconfig.cpp index 2373d700d..80f27c020 100644 --- a/src/gameconfig.cpp +++ b/src/gameconfig.cpp @@ -36,6 +36,11 @@ Config* config = 0; Config::Config() { use_fullscreen = true; +#ifdef HAVE_OPENGL + video = "opengl"; +#else + video = "sdl"; +#endif try_vsync = true; show_fps = false; sound_enabled = true; @@ -73,7 +78,8 @@ Config::load() const lisp::Lisp* config_video_lisp = config_lisp->get_lisp("video"); if(config_video_lisp) { config_video_lisp->get("fullscreen", use_fullscreen); - config_video_lisp->get("vsync", try_vsync); + config_video_lisp->get("video", video); + config_video_lisp->get("vsync", try_vsync); config_video_lisp->get("width", screenwidth); config_video_lisp->get("height", screenheight); config_video_lisp->get("aspect_ratio", aspect_ratio); @@ -104,6 +110,7 @@ Config::save() writer.start_list("video"); writer.write_bool("fullscreen", use_fullscreen); + writer.write_string("video", video); writer.write_bool("vsync", try_vsync); writer.write_int("width", screenwidth); writer.write_int("height", screenheight); diff --git a/src/gameconfig.hpp b/src/gameconfig.hpp index 4263bda55..0efeacd50 100644 --- a/src/gameconfig.hpp +++ b/src/gameconfig.hpp @@ -19,6 +19,8 @@ #ifndef SUPERTUX_CONFIG_H #define SUPERTUX_CONFIG_H +#include + #include class Config @@ -39,6 +41,7 @@ public: float aspect_ratio; bool use_fullscreen; + std::string video; bool try_vsync; bool show_fps; bool sound_enabled; diff --git a/src/main.cpp b/src/main.cpp index 8780ddca8..c3b68052a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -33,7 +33,6 @@ #include #include #include -#include #include "gameconfig.hpp" #include "resources.hpp" @@ -41,6 +40,7 @@ #include "audio/sound_manager.hpp" #include "video/surface.hpp" #include "video/texture_manager.hpp" +#include "video/drawing_context.hpp" #include "video/glutil.hpp" #include "control/joystickkeyboardcontroller.hpp" #include "options_menu.hpp" @@ -55,7 +55,8 @@ #include "worldmap/worldmap.hpp" #include "binreloc/binreloc.h" -SDL_Surface* screen = 0; +DrawingContext context; +SDL_Surface *screen; JoystickKeyboardController* main_controller = 0; TinyGetText::DictionaryManager dictionary_manager; @@ -365,9 +366,6 @@ void init_video() static int desktop_width = 0; static int desktop_height = 0; - if(texture_manager != NULL) - texture_manager->save_textures(); - /* unfortunately only newer SDLs have these infos */ #if SDL_MAJOR_VERSION > 1 || SDL_MINOR_VERSION > 2 || (SDL_MINOR_VERSION == 2 && SDL_PATCHLEVEL >= 10) /* find which resolution the user normally uses */ @@ -376,32 +374,10 @@ void init_video() desktop_width = info->current_w; desktop_height = info->current_h; } - - if(config->try_vsync) { - /* we want vsync for smooth scrolling */ - SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1); - } #endif - SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); - SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5); - SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5); - SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5); - - int flags = SDL_OPENGL; - if(config->use_fullscreen) - flags |= SDL_FULLSCREEN; - int width = config->screenwidth; - int height = config->screenheight; - int bpp = 0; - - screen = SDL_SetVideoMode(width, height, bpp, flags); - if(screen == 0) { - std::stringstream msg; - msg << "Couldn't set video mode (" << width << "x" << height - << "-" << bpp << "bpp): " << SDL_GetError(); - throw std::runtime_error(msg.str()); - } + context.init_renderer(); + screen = SDL_GetVideoSurface(); SDL_WM_SetCaption(PACKAGE_NAME " " PACKAGE_VERSION, 0); @@ -441,29 +417,6 @@ void init_video() } log_info << (config->use_fullscreen?"fullscreen ":"window ") << SCREEN_WIDTH << "x" << SCREEN_HEIGHT << " Ratio: " << aspect_ratio << "\n"; - - // setup opengl state and transform - glDisable(GL_DEPTH_TEST); - glDisable(GL_CULL_FACE); - glEnable(GL_TEXTURE_2D); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - glViewport(0, 0, screen->w, screen->h); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - // logical resolution here not real monitor resolution - glOrtho(0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, -1.0, 1.0); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - glTranslatef(0, 0, 0); - - check_gl_error("Setting up view matrices"); - - if(texture_manager != NULL) - texture_manager->reload_textures(); - else - texture_manager = new TextureManager(); } static void init_audio() @@ -612,7 +565,7 @@ int main(int argc, char** argv) } //init_rand(); PAK: this call might subsume the above 3, but I'm chicken! - main_loop->run(); + main_loop->run(context); #ifndef NO_CATCH } catch(std::exception& e) { log_fatal << "Unexpected exception: " << e.what() << std::endl; diff --git a/src/mainloop.cpp b/src/mainloop.cpp index 314de1576..e5db0246a 100644 --- a/src/mainloop.cpp +++ b/src/mainloop.cpp @@ -228,10 +228,8 @@ MainLoop::handle_screen_switch() } void -MainLoop::run() +MainLoop::run(DrawingContext &context) { - DrawingContext context; - Uint32 last_ticks = 0; Uint32 elapsed_ticks = 0; diff --git a/src/mainloop.hpp b/src/mainloop.hpp index e9fa1a11a..bd03372b9 100644 --- a/src/mainloop.hpp +++ b/src/mainloop.hpp @@ -34,7 +34,7 @@ public: MainLoop(); ~MainLoop(); - void run(); + void run(DrawingContext &context); void exit_screen(ScreenFade* fade = NULL); void quit(ScreenFade* fade = NULL); void set_speed(float speed); diff --git a/src/sprite/sprite_data.cpp b/src/sprite/sprite_data.cpp index 8d223af4c..52e404a30 100644 --- a/src/sprite/sprite_data.cpp +++ b/src/sprite/sprite_data.cpp @@ -104,8 +104,8 @@ SpriteData::parse_action(const lisp::Lisp* lisp, const std::string& basedir) i++) { Surface* surface = new Surface(*(act_tmp->surfaces[i])); surface->hflip(); - max_w = std::max(max_w, surface->get_width()); - max_h = std::max(max_h, surface->get_height()); + max_w = std::max(max_w, (float) surface->get_width()); + max_h = std::max(max_h, (float) surface->get_height()); action->surfaces.push_back(surface); } if (action->hitbox_w < 1) action->hitbox_w = max_w; @@ -124,8 +124,8 @@ SpriteData::parse_action(const lisp::Lisp* lisp, const std::string& basedir) float max_h = 0; for(std::vector::size_type i = 0; i < images.size(); i++) { Surface* surface = new Surface(basedir + images[i]); - max_w = std::max(max_w, surface->get_width()); - max_h = std::max(max_h, surface->get_height()); + max_w = std::max(max_w, (float) surface->get_width()); + max_h = std::max(max_h, (float) surface->get_height()); action->surfaces.push_back(surface); } if (action->hitbox_w < 1) action->hitbox_w = max_w; diff --git a/src/video/color.hpp b/src/video/color.hpp index e94b362d0..e3ff67804 100644 --- a/src/video/color.hpp +++ b/src/video/color.hpp @@ -66,6 +66,16 @@ public: log_warning << "color value out of range: " << red << ", " << green << ", " << blue << ", " << alpha << std::endl; } + float greyscale() const + { + return red * 0.30 + green * 0.59 + blue * 0.11; + } + + bool operator < (const Color& other) const + { + return greyscale() < other.greyscale(); + } + float red, green, blue, alpha; }; diff --git a/src/video/drawing_context.cpp b/src/video/drawing_context.cpp index 8633956dc..428454da5 100644 --- a/src/video/drawing_context.cpp +++ b/src/video/drawing_context.cpp @@ -23,82 +23,24 @@ #include #include #include -#include #include #include #include #include "drawing_context.hpp" +#include "drawing_request.hpp" +#include "gl_renderer.hpp" +#include "gl_lightmap.hpp" +#include "sdl_renderer.hpp" +#include "sdl_lightmap.hpp" #include "surface.hpp" -#include "font.hpp" #include "main.hpp" #include "gameconfig.hpp" -#include "glutil.hpp" #include "texture.hpp" #include "texture_manager.hpp" #include "obstack/obstackpp.hpp" #define LIGHTMAP_DIV 5 -enum RequestType -{ - SURFACE, SURFACE_PART, TEXT, GRADIENT, FILLRECT, LIGHTMAPREQUEST, GETLIGHT -}; - -struct SurfacePartRequest -{ - const Surface* surface; - Vector source, size; -}; - -struct TextRequest -{ - const Font* font; - std::string text; - FontAlignment alignment; -}; - -struct GradientRequest -{ - Color top, bottom; - Vector size; -}; - -struct FillRectRequest -{ - Color color; - Vector size; -}; - -struct DrawingRequest -{ - RequestType type; - Vector pos; - - int layer; - DrawingEffect drawing_effect; - float alpha; - Blend blend; - float angle; - Color color; - - void* request_data; - - DrawingRequest() - : angle(0.0f), - color(1.0f, 1.0f, 1.0f, 1.0f) - {} - - bool operator<(const DrawingRequest& other) const - { - return layer < other.layer; - } -}; - -struct GetLightRequest -{ - Color* color_ptr; -}; - static inline int next_po2(int val) { int result = 1; @@ -108,33 +50,41 @@ static inline int next_po2(int val) return result; } -DrawingContext::DrawingContext() - : ambient_color(1.0f, 1.0f, 1.0f, 1.0f), target(NORMAL), screenshot_requested(false) +DrawingContext::DrawingContext() : + renderer(0), lightmap(0), ambient_color(1.0f, 1.0f, 1.0f, 1.0f), target(NORMAL), screenshot_requested(false) { - screen = SDL_GetVideoSurface(); - - lightmap_width = screen->w / LIGHTMAP_DIV; - lightmap_height = screen->h / LIGHTMAP_DIV; - unsigned int width = next_po2(lightmap_width); - unsigned int height = next_po2(lightmap_height); - - lightmap = new Texture(width, height, GL_RGB); - - lightmap_uv_right = static_cast(lightmap_width) / static_cast(width); - lightmap_uv_bottom = static_cast(lightmap_height) / static_cast(height); - texture_manager->register_texture(lightmap); - requests = &drawing_requests; - obstack_init(&obst); } DrawingContext::~DrawingContext() { + delete renderer; + delete lightmap; + obstack_free(&obst, NULL); +} - texture_manager->remove_texture(lightmap); - delete lightmap; +void +DrawingContext::init_renderer() +{ + if(renderer) + delete renderer; + if(lightmap) + delete lightmap; + +#ifdef HAVE_OPENGL + if(config->video == "opengl") + { + renderer = new GL::Renderer(); + lightmap = new GL::Lightmap(); + } + else +#endif + { + renderer = new SDL::Renderer(); + lightmap = new SDL::Lightmap(); + } } void @@ -146,6 +96,7 @@ DrawingContext::draw_surface(const Surface* surface, const Vector& position, DrawingRequest* request = new(obst) DrawingRequest(); + request->target = target; request->type = SURFACE; request->pos = transform.apply(position); @@ -181,6 +132,7 @@ DrawingContext::draw_surface_part(const Surface* surface, const Vector& source, DrawingRequest* request = new(obst) DrawingRequest(); + request->target = target; request->type = SURFACE_PART; request->pos = transform.apply(dest); request->layer = layer; @@ -218,6 +170,7 @@ DrawingContext::draw_text(const Font* font, const std::string& text, { DrawingRequest* request = new(obst) DrawingRequest(); + request->target = target; request->type = TEXT; request->pos = transform.apply(position); request->layer = layer; @@ -246,6 +199,7 @@ DrawingContext::draw_gradient(const Color& top, const Color& bottom, int layer) { DrawingRequest* request = new(obst) DrawingRequest(); + request->target = target; request->type = GRADIENT; request->pos = Vector(0,0); request->layer = layer; @@ -267,6 +221,7 @@ DrawingContext::draw_filled_rect(const Vector& topleft, const Vector& size, { DrawingRequest* request = new(obst) DrawingRequest(); + request->target = target; request->type = FILLRECT; request->pos = transform.apply(topleft); request->layer = layer; @@ -289,6 +244,7 @@ DrawingContext::draw_filled_rect(const Rect& rect, const Color& color, { DrawingRequest* request = new(obst) DrawingRequest(); + request->target = target; request->type = FILLRECT; request->pos = transform.apply(rect.p1); request->layer = layer; @@ -315,6 +271,7 @@ DrawingContext::get_light(const Vector& position, Color* color) } DrawingRequest* request = new(obst) DrawingRequest(); + request->target = target; request->type = GETLIGHT; request->pos = transform.apply(position); @@ -333,120 +290,6 @@ DrawingContext::get_light(const Vector& position, Color* color) } void -DrawingContext::get_light(const DrawingRequest& request) const -{ - const GetLightRequest* getlightrequest - = (GetLightRequest*) request.request_data; - - float pixels[3]; - for( int i = 0; i<3; i++) - pixels[i] = 0.0f; //set to black - - float posX = request.pos.x * lightmap_width / SCREEN_WIDTH; - float posY = screen->h - request.pos.y * lightmap_height / SCREEN_HEIGHT; - glReadPixels((GLint) posX, (GLint) posY , 1, 1, GL_RGB, GL_FLOAT, pixels); - *(getlightrequest->color_ptr) = Color( pixels[0], pixels[1], pixels[2]); - //printf("get_light %f/%f =>%f/%f r%f g%f b%f\n", request.pos.x, request.pos.y, posX, posY, pixels[0], pixels[1], pixels[2]); -} - -void -DrawingContext::draw_surface_part(const DrawingRequest& request) const -{ - const SurfacePartRequest* surfacepartrequest - = (SurfacePartRequest*) request.request_data; - - surfacepartrequest->surface->draw_part( - surfacepartrequest->source.x, surfacepartrequest->source.y, - request.pos.x, request.pos.y, - surfacepartrequest->size.x, surfacepartrequest->size.y, - request.alpha, request.drawing_effect); -} - -void -DrawingContext::draw_gradient(const DrawingRequest& request) const -{ - const GradientRequest* gradientrequest - = (GradientRequest*) request.request_data; - const Color& top = gradientrequest->top; - const Color& bottom = gradientrequest->bottom; - - glDisable(GL_TEXTURE_2D); - glBegin(GL_QUADS); - glColor4f(top.red, top.green, top.blue, top.alpha); - glVertex2f(0, 0); - glVertex2f(SCREEN_WIDTH, 0); - glColor4f(bottom.red, bottom.green, bottom.blue, bottom.alpha); - glVertex2f(SCREEN_WIDTH, SCREEN_HEIGHT); - glVertex2f(0, SCREEN_HEIGHT); - glEnd(); - glEnable(GL_TEXTURE_2D); - glColor4f(1, 1, 1, 1); -} - -void -DrawingContext::draw_text(const DrawingRequest& request) const -{ - const TextRequest* textrequest = (TextRequest*) request.request_data; - - textrequest->font->draw(textrequest->text, request.pos, - textrequest->alignment, request.drawing_effect, request.alpha); -} - -void -DrawingContext::draw_filled_rect(const DrawingRequest& request) const -{ - const FillRectRequest* fillrectrequest - = (FillRectRequest*) request.request_data; - - float x = request.pos.x; - float y = request.pos.y; - float w = fillrectrequest->size.x; - float h = fillrectrequest->size.y; - - glDisable(GL_TEXTURE_2D); - glColor4f(fillrectrequest->color.red, fillrectrequest->color.green, - fillrectrequest->color.blue, fillrectrequest->color.alpha); - - glBegin(GL_QUADS); - glVertex2f(x, y); - glVertex2f(x+w, y); - glVertex2f(x+w, y+h); - glVertex2f(x, y+h); - glEnd(); - glEnable(GL_TEXTURE_2D); - - glColor4f(1, 1, 1, 1); -} - -void -DrawingContext::draw_lightmap(const DrawingRequest& request) const -{ - const Texture* texture = reinterpret_cast (request.request_data); - - // multiple the lightmap with the framebuffer - glBlendFunc(GL_DST_COLOR, GL_ZERO); - - glBindTexture(GL_TEXTURE_2D, texture->get_handle()); - glBegin(GL_QUADS); - - glTexCoord2f(0, lightmap_uv_bottom); - glVertex2f(0, 0); - - glTexCoord2f(lightmap_uv_right, lightmap_uv_bottom); - glVertex2f(SCREEN_WIDTH, 0); - - glTexCoord2f(lightmap_uv_right, 0); - glVertex2f(SCREEN_WIDTH, SCREEN_HEIGHT); - - glTexCoord2f(0, 0); - glVertex2f(0, SCREEN_HEIGHT); - - glEnd(); - - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); -} - -void DrawingContext::do_drawing() { #ifdef DEBUG @@ -462,52 +305,25 @@ DrawingContext::do_drawing() // PART1: create lightmap if(use_lightmap) { - glViewport(0, screen->h - lightmap_height, lightmap_width, lightmap_height); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, -1.0, 1.0); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - - glClearColor( ambient_color.red, ambient_color.green, ambient_color.blue, 1 ); - glClear(GL_COLOR_BUFFER_BIT); + lightmap->start_draw(ambient_color); handle_drawing_requests(lightmap_requests); - lightmap_requests.clear(); - - glDisable(GL_BLEND); - glBindTexture(GL_TEXTURE_2D, lightmap->get_handle()); - glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, screen->h - lightmap_height, lightmap_width, lightmap_height); - - glViewport(0, 0, screen->w, screen->h); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, -1.0, 1.0); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - glEnable(GL_BLEND); - - // add a lightmap drawing request into the queue - DrawingRequest* request = new(obst) DrawingRequest(); - request->type = LIGHTMAPREQUEST; - request->layer = LAYER_HUD - 1; - request->request_data = lightmap; - requests->push_back(request); + lightmap->end_draw(); } - //glClear(GL_COLOR_BUFFER_BIT); handle_drawing_requests(drawing_requests); - drawing_requests.clear(); + if(use_lightmap) { + lightmap->do_draw(); + } obstack_free(&obst, NULL); obstack_init(&obst); - assert_gl("drawing"); // if a screenshot was requested, take one if (screenshot_requested) { - do_take_screenshot(); + renderer->do_take_screenshot(); screenshot_requested = false; } - SDL_GL_SwapBuffers(); + renderer->flip(); } class RequestPtrCompare @@ -523,7 +339,7 @@ public: }; void -DrawingContext::handle_drawing_requests(DrawingRequests& requests) const +DrawingContext::handle_drawing_requests(DrawingRequests& requests) { std::stable_sort(requests.begin(), requests.end(), RequestPtrCompare()); @@ -531,42 +347,54 @@ DrawingContext::handle_drawing_requests(DrawingRequests& requests) const for(i = requests.begin(); i != requests.end(); ++i) { const DrawingRequest& request = **i; - switch(request.type) { - case SURFACE: - { - const Surface* surface = (const Surface*) request.request_data; - if (request.angle == 0.0f && - request.color.red == 1.0f && request.color.green == 1.0f && - request.color.blue == 1.0f && request.color.alpha == 1.0f) { - surface->draw(request.pos.x, request.pos.y, request.alpha, - request.drawing_effect); - } else { - surface->draw(request.pos.x, request.pos.y, - request.alpha, request.angle, request.color, - request.blend, request.drawing_effect); + switch(request.target) { + case NORMAL: + switch(request.type) { + case SURFACE: + renderer->draw_surface(request); + break; + case SURFACE_PART: + renderer->draw_surface_part(request); + break; + case GRADIENT: + renderer->draw_gradient(request); + break; + case TEXT: + renderer->draw_text(request); + break; + case FILLRECT: + renderer->draw_filled_rect(request); + break; + case GETLIGHT: + lightmap->get_light(request); + break; } break; - } - case SURFACE_PART: - draw_surface_part(request); - break; - case GRADIENT: - draw_gradient(request); - break; - case TEXT: - draw_text(request); - break; - case FILLRECT: - draw_filled_rect(request); - break; - case LIGHTMAPREQUEST: - draw_lightmap(request); - break; - case GETLIGHT: - get_light(request); + case LIGHTMAP: + switch(request.type) { + case SURFACE: + lightmap->draw_surface(request); + break; + case SURFACE_PART: + lightmap->draw_surface_part(request); + break; + case GRADIENT: + lightmap->draw_gradient(request); + break; + case TEXT: + lightmap->draw_text(request); + break; + case FILLRECT: + lightmap->draw_filled_rect(request); + break; + case GETLIGHT: + lightmap->get_light(request); + break; + } break; } } + requests.clear(); } void @@ -640,65 +468,6 @@ DrawingContext::set_ambient_color( Color new_color ) } void -DrawingContext::do_take_screenshot() -{ - // [Christoph] TODO: Yes, this method also takes care of the actual disk I/O. Split it? - - // create surface to hold screenshot - #if SDL_BYTEORDER == SDL_BIG_ENDIAN - SDL_Surface* shot_surf = SDL_CreateRGBSurface(SDL_SWSURFACE, SCREEN_WIDTH, SCREEN_HEIGHT, 24, 0x00FF0000, 0x0000FF00, 0x000000FF, 0); - #else - SDL_Surface* shot_surf = SDL_CreateRGBSurface(SDL_SWSURFACE, 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; - return; - } - - // read pixels into array - char* pixels = new char[3 * SCREEN_WIDTH * SCREEN_HEIGHT]; - if (!pixels) { - log_warning << "Could not allocate memory to store screenshot" << std::endl; - SDL_FreeSurface(shot_surf); - return; - } - glReadPixels(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, GL_RGB, GL_UNSIGNED_BYTE, pixels); - - // copy array line-by-line - for (int i = 0; i < SCREEN_HEIGHT; i++) { - char* src = pixels + (3 * SCREEN_WIDTH * (SCREEN_HEIGHT - i - 1)); - char* dst = ((char*)shot_surf->pixels) + i * shot_surf->pitch; - memcpy(dst, src, 3 * SCREEN_WIDTH); - } - - // free array - delete[](pixels); - - // save screenshot - static const std::string writeDir = PHYSFS_getWriteDir(); - static const std::string dirSep = PHYSFS_getDirSeparator(); - static const std::string baseName = "screenshot"; - static const std::string fileExt = ".bmp"; - std::string fullFilename; - for (int num = 0; num < 1000; num++) { - std::ostringstream oss; - oss << baseName; - oss << std::setw(3) << std::setfill('0') << num; - oss << fileExt; - std::string fileName = oss.str(); - fullFilename = writeDir + dirSep + fileName; - if (!PHYSFS_exists(fileName.c_str())) { - SDL_SaveBMP(shot_surf, fullFilename.c_str()); - log_debug << "Wrote screenshot to \"" << fullFilename << "\"" << std::endl; - SDL_FreeSurface(shot_surf); - return; - } - } - log_warning << "Did not save screenshot, because all files up to \"" << fullFilename << "\" already existed" << std::endl; - SDL_FreeSurface(shot_surf); -} - -void DrawingContext::take_screenshot() { screenshot_requested = true; diff --git a/src/video/drawing_context.hpp b/src/video/drawing_context.hpp index 296ea8eb7..c8f90127e 100644 --- a/src/video/drawing_context.hpp +++ b/src/video/drawing_context.hpp @@ -25,49 +25,21 @@ #include -#include #include +#include "glutil.hpp" #include "obstack/obstack.h" #include "math/vector.hpp" #include "math/rect.hpp" -#include "surface.hpp" +#include "drawing_request.hpp" #include "font.hpp" #include "color.hpp" class Surface; class Texture; struct DrawingRequest; - -// some constants for predefined layer values -enum { - LAYER_BACKGROUND0 = -300, - LAYER_BACKGROUND1 = -200, - LAYER_BACKGROUNDTILES = -100, - LAYER_TILES = 0, - LAYER_OBJECTS = 50, - LAYER_FLOATINGOBJECTS = 150, - LAYER_FOREGROUNDTILES = 200, - LAYER_FOREGROUND0 = 300, - LAYER_FOREGROUND1 = 400, - LAYER_HUD = 500, - LAYER_GUI = 600 -}; - -class Blend -{ -public: - GLenum sfactor; - GLenum dfactor; - - Blend() - : sfactor(GL_SRC_ALPHA), dfactor(GL_ONE_MINUS_SRC_ALPHA) - {} - - Blend(GLenum s, GLenum d) - : sfactor(s), dfactor(d) - {} -}; +class Renderer; +class Lightmap; /** * This class provides functions for drawing things on screen. It also @@ -79,6 +51,8 @@ public: DrawingContext(); ~DrawingContext(); + void init_renderer(); + /// Adds a drawing request for a surface into the request list. void draw_surface(const Surface* surface, const Vector& position, int layer); @@ -129,9 +103,9 @@ public: /// on next update, set color to lightmap's color at position void get_light(const Vector& position, Color* color ); - enum Target { - NORMAL, LIGHTMAP - }; + typedef ::Target Target; + static const Target NORMAL = ::NORMAL; + static const Target LIGHTMAP = ::LIGHTMAP; void push_target(); void pop_target(); void set_target(Target target); @@ -161,6 +135,9 @@ private: } }; + Renderer *renderer; + Lightmap *lightmap; + /// the transform stack std::vector transformstack; /// the currently active transform @@ -171,15 +148,7 @@ private: typedef std::vector DrawingRequests; - void handle_drawing_requests(DrawingRequests& requests) const; - void draw_surface_part(const DrawingRequest& request) const; - void draw_text(const DrawingRequest& request) const; - void draw_text_center(const DrawingRequest& request) const; - void draw_gradient(const DrawingRequest& request) const; - void draw_filled_rect(const DrawingRequest& request) const; - void draw_lightmap(const DrawingRequest& request) const; - void get_light(const DrawingRequest& request) const; - void do_take_screenshot(); + void handle_drawing_requests(DrawingRequests& requests); DrawingRequests drawing_requests; DrawingRequests lightmap_requests; @@ -187,12 +156,8 @@ private: DrawingRequests* requests; Color ambient_color; - SDL_Surface* screen; Target target; std::vector target_stack; - Texture* lightmap; - int lightmap_width, lightmap_height; - float lightmap_uv_right, lightmap_uv_bottom; /* obstack holding the memory of the drawing requests */ struct obstack obst; diff --git a/src/video/drawing_request.hpp b/src/video/drawing_request.hpp new file mode 100644 index 000000000..24e0c0204 --- /dev/null +++ b/src/video/drawing_request.hpp @@ -0,0 +1,133 @@ +// $Id: drawing_request.hpp 4986 2007-04-16 17:48:28Z matzeb $ +// +// SuperTux +// Copyright (C) 2006 Matthias Braun +// +// 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 the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +#ifndef SUPERTUX_DRAWINGREQUEST_H +#define SUPERTUX_DRAWINGREQUEST_H + +#include +#include +#include + +#include + +#include + +#include "glutil.hpp" +#include "math/vector.hpp" +#include "color.hpp" +#include "font.hpp" + +class Surface; + +// some constants for predefined layer values +enum { + LAYER_BACKGROUND0 = -300, + LAYER_BACKGROUND1 = -200, + LAYER_BACKGROUNDTILES = -100, + LAYER_TILES = 0, + LAYER_OBJECTS = 50, + LAYER_FLOATINGOBJECTS = 150, + LAYER_FOREGROUNDTILES = 200, + LAYER_FOREGROUND0 = 300, + LAYER_FOREGROUND1 = 400, + LAYER_HUD = 500, + LAYER_GUI = 600 +}; + +class Blend +{ +public: + GLenum sfactor; + GLenum dfactor; + + Blend() + : sfactor(GL_SRC_ALPHA), dfactor(GL_ONE_MINUS_SRC_ALPHA) + {} + + Blend(GLenum s, GLenum d) + : sfactor(s), dfactor(d) + {} +}; + +enum Target { + NORMAL, LIGHTMAP +}; + +enum RequestType +{ + SURFACE, SURFACE_PART, TEXT, GRADIENT, FILLRECT, GETLIGHT +}; + +struct SurfacePartRequest +{ + const Surface* surface; + Vector source, size; +}; + +struct TextRequest +{ + const Font* font; + std::string text; + FontAlignment alignment; +}; + +struct GradientRequest +{ + Color top, bottom; + Vector size; +}; + +struct FillRectRequest +{ + Color color; + Vector size; +}; + +struct DrawingRequest +{ + Target target; + RequestType type; + Vector pos; + + int layer; + DrawingEffect drawing_effect; + float alpha; + Blend blend; + float angle; + Color color; + + void* request_data; + + DrawingRequest() + : angle(0.0f), + color(1.0f, 1.0f, 1.0f, 1.0f) + {} + + bool operator<(const DrawingRequest& other) const + { + return layer < other.layer; + } +}; + +struct GetLightRequest +{ + Color* color_ptr; +}; + +#endif + diff --git a/src/video/font.cpp b/src/video/font.cpp index f967892d3..e5b6bb12a 100644 --- a/src/video/font.cpp +++ b/src/video/font.cpp @@ -31,6 +31,7 @@ #include "lisp/lisp.hpp" #include "screen.hpp" #include "font.hpp" +#include "renderer.hpp" #include "drawing_context.hpp" #include "log.hpp" @@ -266,8 +267,9 @@ Font::wrap_to_width(const std::string& s, float width, std::string* overflow) } void -Font::draw(const std::string& text, const Vector& pos_, FontAlignment alignment, - DrawingEffect drawing_effect, float alpha) const +Font::draw(Renderer *renderer, const std::string& text, const Vector& pos_, + FontAlignment alignment, DrawingEffect drawing_effect, + float alpha) const { float x = pos_.x; float y = pos_.y; @@ -291,7 +293,7 @@ Font::draw(const std::string& text, const Vector& pos_, FontAlignment alignment, // no bluring as we would get with subpixel positions pos.x = static_cast(pos.x); - draw_text(temp, pos, drawing_effect, alpha); + draw_text(renderer, temp, pos, drawing_effect, alpha); if (i == text.size()) break; @@ -303,7 +305,7 @@ Font::draw(const std::string& text, const Vector& pos_, FontAlignment alignment, } void -Font::draw_text(const std::string& text, const Vector& pos, +Font::draw_text(Renderer *renderer, const std::string& text, const Vector& pos, DrawingEffect drawing_effect, float alpha) const { if(shadowsize > 0) @@ -311,11 +313,11 @@ Font::draw_text(const std::string& text, const Vector& pos, // FIXME: shadow_glyph_surface and glyph_surface do currently // share the same glyph array, this is incorrect and should be // fixed, it is however hardly noticable - draw_chars(shadow_glyph_surface, text, pos + Vector(shadowsize, shadowsize), - drawing_effect, alpha); + draw_chars(renderer, shadow_glyph_surface, text, + pos + Vector(shadowsize, shadowsize), drawing_effect, alpha); } - draw_chars(glyph_surface, text, pos, drawing_effect, alpha); + draw_chars(renderer, glyph_surface, text, pos, drawing_effect, alpha); } int @@ -341,8 +343,9 @@ Font::chr2glyph(uint32_t chr) const } void -Font::draw_chars(Surface* pchars, const std::string& text, const Vector& pos, - DrawingEffect drawing_effect, float alpha) const +Font::draw_chars(Renderer *renderer, Surface* pchars, const std::string& text, + const Vector& pos, DrawingEffect drawing_effect, + float alpha) const { Vector p = pos; @@ -362,12 +365,20 @@ Font::draw_chars(Surface* pchars, const std::string& text, const Vector& pos, else { const Glyph& glyph = glyphs[font_index]; - pchars->draw_part(glyph.rect.get_left(), - glyph.rect.get_top(), - p.x + glyph.offset.y, - p.y + glyph.offset.y, - glyph.rect.get_width(), glyph.rect.get_height(), - alpha, drawing_effect); + DrawingRequest request; + + request.pos = p + glyph.offset; + request.drawing_effect = drawing_effect; + request.alpha = alpha; + + SurfacePartRequest surfacepartrequest; + surfacepartrequest.size = glyph.rect.p2 - glyph.rect.p1; + surfacepartrequest.source = glyph.rect.p1; + surfacepartrequest.surface = pchars; + + request.request_data = &surfacepartrequest; + renderer->draw_surface_part(request); + p.x += glyphs[font_index].advance; } } diff --git a/src/video/font.hpp b/src/video/font.hpp index 1cf38ebe7..5625be185 100644 --- a/src/video/font.hpp +++ b/src/video/font.hpp @@ -28,6 +28,8 @@ #include "math/vector.hpp" #include "math/rect.hpp" +class Renderer; + enum FontAlignment { ALIGN_LEFT, ALIGN_CENTER, @@ -85,7 +87,7 @@ public: /** Draws the given text to the screen. Also needs the position. * Type of alignment, drawing effect and alpha are optional. */ - void draw(const std::string& text, const Vector& pos, + void draw(Renderer *renderer, const std::string& text, const Vector& pos, FontAlignment allignment = ALIGN_LEFT, DrawingEffect drawing_effect = NO_EFFECT, float alpha = 1.0f) const; @@ -93,11 +95,11 @@ public: private: friend class DrawingContext; - void draw_text(const std::string& text, const Vector& pos, + void draw_text(Renderer *renderer, const std::string& text, const Vector& pos, DrawingEffect drawing_effect = NO_EFFECT, float alpha = 1.0f) const; - void draw_chars(Surface* pchars, const std::string& text, + void draw_chars(Renderer *renderer, Surface* pchars, const std::string& text, const Vector& position, DrawingEffect drawing_effect, float alpha) const; diff --git a/src/video/gl_lightmap.cpp b/src/video/gl_lightmap.cpp new file mode 100644 index 000000000..6649a0e57 --- /dev/null +++ b/src/video/gl_lightmap.cpp @@ -0,0 +1,317 @@ +// $Id: gl_lightmap.cpp 5063 2007-05-27 11:32:00Z matzeb $ +// +// SuperTux +// Copyright (C) 2006 Matthias Braun +// +// 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 the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +#include + +#ifdef HAVE_OPENGL + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "glutil.hpp" +#include "gl_lightmap.hpp" +#include "drawing_context.hpp" +#include "drawing_request.hpp" +#include "renderer.hpp" +#include "surface.hpp" +#include "font.hpp" +#include "main.hpp" +#include "gameconfig.hpp" +#include "gl_texture.hpp" +#include "texture_manager.hpp" +#include "obstack/obstackpp.hpp" +#define LIGHTMAP_DIV 5 + +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); + } + + 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; + + glBlendFunc(blend.sfactor, blend.dfactor); + glColor4f(color.red, color.green, color.blue, color.alpha * alpha); + glBegin(GL_QUADS); + glTexCoord2f(uv_left, uv_top); + glVertex2f(left*ca - top*sa + center_x, + left*sa + top*ca + center_y); + + glTexCoord2f(uv_right, uv_top); + glVertex2f(right*ca - top*sa + center_x, + right*sa + top*ca + center_y); + + glTexCoord2f(uv_right, uv_bottom); + glVertex2f(right*ca - bottom*sa + center_x, + right*sa + bottom*ca + center_y); + + glTexCoord2f(uv_left, uv_bottom); + glVertex2f(left*ca - bottom*sa + center_x, + left*sa + bottom*ca + center_y); + glEnd(); + + // 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 GL +{ + static inline int next_po2(int val) + { + int result = 1; + while(result < val) + result *= 2; + + return result; + } + + Lightmap::Lightmap() + { + screen = SDL_GetVideoSurface(); + + lightmap_width = screen->w / LIGHTMAP_DIV; + lightmap_height = screen->h / LIGHTMAP_DIV; + unsigned int width = next_po2(lightmap_width); + unsigned int height = next_po2(lightmap_height); + + lightmap = new Texture(width, height); + + lightmap_uv_right = static_cast(lightmap_width) / static_cast(width); + lightmap_uv_bottom = static_cast(lightmap_height) / static_cast(height); + texture_manager->register_texture(lightmap); + } + + Lightmap::~Lightmap() + { + texture_manager->remove_texture(lightmap); + delete lightmap; + } + + void + Lightmap::start_draw(const Color &ambient_color) + { + glViewport(0, screen->h - lightmap_height, lightmap_width, lightmap_height); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, -1.0, 1.0); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + glClearColor( ambient_color.red, ambient_color.green, ambient_color.blue, 1 ); + glClear(GL_COLOR_BUFFER_BIT); + } + + void + Lightmap::end_draw() + { + glDisable(GL_BLEND); + glBindTexture(GL_TEXTURE_2D, lightmap->get_handle()); + glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, screen->h - lightmap_height, lightmap_width, lightmap_height); + + glViewport(0, 0, screen->w, screen->h); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, -1.0, 1.0); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glEnable(GL_BLEND); + //glClear(GL_COLOR_BUFFER_BIT); + } + + void + Lightmap::do_draw() + { + const Texture* texture = lightmap; + + // multiple the lightmap with the framebuffer + glBlendFunc(GL_DST_COLOR, GL_ZERO); + + glBindTexture(GL_TEXTURE_2D, texture->get_handle()); + glBegin(GL_QUADS); + + glTexCoord2f(0, lightmap_uv_bottom); + glVertex2f(0, 0); + + glTexCoord2f(lightmap_uv_right, lightmap_uv_bottom); + glVertex2f(SCREEN_WIDTH, 0); + + glTexCoord2f(lightmap_uv_right, 0); + glVertex2f(SCREEN_WIDTH, SCREEN_HEIGHT); + + glTexCoord2f(0, 0); + glVertex2f(0, SCREEN_HEIGHT); + + glEnd(); + + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + + void + Lightmap::draw_surface(const DrawingRequest& request) + { + const Surface* surface = (const Surface*) request.request_data; + GL::Texture *gltexture = dynamic_cast(surface->get_texture()); + glBindTexture(GL_TEXTURE_2D, gltexture->get_handle()); + intern_draw(request.pos.x, request.pos.y, + request.pos.x + surface->get_width(), + request.pos.y + surface->get_height(), + surface->get_uv_left(), + surface->get_uv_top(), + surface->get_uv_right(), + surface->get_uv_bottom(), + request.angle, + request.alpha, + request.color, + request.blend, + request.drawing_effect); + } + + void + Lightmap::draw_surface_part(const DrawingRequest& request) + { + const SurfacePartRequest* surfacepartrequest + = (SurfacePartRequest*) request.request_data; + const Surface *surface = surfacepartrequest->surface; + GL::Texture *gltexture = dynamic_cast(surface->get_texture()); + + float uv_width = surface->get_uv_right() - surface->get_uv_left(); + float uv_height = surface->get_uv_bottom() - surface->get_uv_top(); + + float uv_left = surface->get_uv_left() + (uv_width * surfacepartrequest->source.x) / surface->get_width(); + float uv_top = surface->get_uv_top() + (uv_height * surfacepartrequest->source.y) / surface->get_height(); + float uv_right = surface->get_uv_left() + (uv_width * (surfacepartrequest->source.x + surfacepartrequest->size.x)) / surface->get_width(); + float uv_bottom = surface->get_uv_top() + (uv_height * (surfacepartrequest->source.y + surfacepartrequest->size.y)) / surface->get_height(); + + glBindTexture(GL_TEXTURE_2D, gltexture->get_handle()); + intern_draw(request.pos.x, request.pos.y, + request.pos.x + surfacepartrequest->size.x, + request.pos.y + surfacepartrequest->size.y, + uv_left, + uv_top, + uv_right, + uv_bottom, + 0.0, + request.alpha, + Color(1.0, 1.0, 1.0), + Blend(), + request.drawing_effect); + } + + void + Lightmap::draw_gradient(const DrawingRequest& request) + { + const GradientRequest* gradientrequest + = (GradientRequest*) request.request_data; + const Color& top = gradientrequest->top; + const Color& bottom = gradientrequest->bottom; + + glDisable(GL_TEXTURE_2D); + glBegin(GL_QUADS); + glColor4f(top.red, top.green, top.blue, top.alpha); + glVertex2f(0, 0); + glVertex2f(SCREEN_WIDTH, 0); + glColor4f(bottom.red, bottom.green, bottom.blue, bottom.alpha); + glVertex2f(SCREEN_WIDTH, SCREEN_HEIGHT); + glVertex2f(0, SCREEN_HEIGHT); + glEnd(); + glEnable(GL_TEXTURE_2D); + glColor4f(1, 1, 1, 1); + } + + void + Lightmap::draw_text(const DrawingRequest& /*request*/) + { + //const TextRequest* textrequest = (TextRequest*) request.request_data; + + //textrequest->font->draw(textrequest->text, request.pos, + // textrequest->alignment, request.drawing_effect, request.alpha); + } + + void + Lightmap::draw_filled_rect(const DrawingRequest& request) + { + const FillRectRequest* fillrectrequest + = (FillRectRequest*) request.request_data; + + float x = request.pos.x; + float y = request.pos.y; + float w = fillrectrequest->size.x; + float h = fillrectrequest->size.y; + + glDisable(GL_TEXTURE_2D); + glColor4f(fillrectrequest->color.red, fillrectrequest->color.green, + fillrectrequest->color.blue, fillrectrequest->color.alpha); + + glBegin(GL_QUADS); + glVertex2f(x, y); + glVertex2f(x+w, y); + glVertex2f(x+w, y+h); + glVertex2f(x, y+h); + glEnd(); + glEnable(GL_TEXTURE_2D); + glColor4f(1, 1, 1, 1); + } + + void + Lightmap::get_light(const DrawingRequest& request) const + { + const GetLightRequest* getlightrequest + = (GetLightRequest*) request.request_data; + + float pixels[3]; + for( int i = 0; i<3; i++) + pixels[i] = 0.0f; //set to black + + float posX = request.pos.x * lightmap_width / SCREEN_WIDTH; + float posY = screen->h - request.pos.y * lightmap_height / SCREEN_HEIGHT; + glReadPixels((GLint) posX, (GLint) posY , 1, 1, GL_RGB, GL_FLOAT, pixels); + *(getlightrequest->color_ptr) = Color( pixels[0], pixels[1], pixels[2]); + //printf("get_light %f/%f =>%f/%f r%f g%f b%f\n", request.pos.x, request.pos.y, posX, posY, pixels[0], pixels[1], pixels[2]); + } +} + +#endif diff --git a/src/video/gl_lightmap.hpp b/src/video/gl_lightmap.hpp new file mode 100644 index 000000000..dd5b131d5 --- /dev/null +++ b/src/video/gl_lightmap.hpp @@ -0,0 +1,61 @@ +// $Id: gl_lightmap.hpp 4986 2007-04-16 17:48:28Z matzeb $ +// +// SuperTux +// Copyright (C) 2006 Matthias Braun +// +// 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 the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +#include + +#ifdef HAVE_OPENGL + +#ifndef SUPERTUX_GL_LIGHTMAP_H +#define SUPERTUX_GL_LIGHTMAP_H + +#include + +#include "lightmap.hpp" + +struct DrawingRequest; + +namespace GL +{ + class Texture; + class Lightmap : public ::Lightmap + { + public: + Lightmap(); + ~Lightmap(); + + void start_draw(const Color &ambient_color); + void end_draw(); + void do_draw(); + void draw_surface(const DrawingRequest& request); + void draw_surface_part(const DrawingRequest& request); + void draw_text(const DrawingRequest& request); + void draw_gradient(const DrawingRequest& request); + void draw_filled_rect(const DrawingRequest& request); + void get_light(const DrawingRequest& request) const; + + private: + SDL_Surface* screen; + Texture* lightmap; + int lightmap_width, lightmap_height; + float lightmap_uv_right, lightmap_uv_bottom; + }; +} + +#endif + +#endif diff --git a/src/video/gl_renderer.cpp b/src/video/gl_renderer.cpp new file mode 100644 index 000000000..8158874c5 --- /dev/null +++ b/src/video/gl_renderer.cpp @@ -0,0 +1,336 @@ +// $Id: gl_renderer.cpp 5063 2007-05-27 11:32:00Z matzeb $ +// +// SuperTux +// Copyright (C) 2006 Matthias Braun +// +// 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 the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +#include + +#ifdef HAVE_OPENGL + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "glutil.hpp" +#include "gl_renderer.hpp" +#include "gl_texture.hpp" +#include "drawing_context.hpp" +#include "drawing_request.hpp" +#include "surface.hpp" +#include "font.hpp" +#include "main.hpp" +#include "gameconfig.hpp" +#include "texture.hpp" +#include "texture_manager.hpp" +#include "obstack/obstackpp.hpp" +#define LIGHTMAP_DIV 5 + +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); + } + + 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; + + glBlendFunc(blend.sfactor, blend.dfactor); + glColor4f(color.red, color.green, color.blue, color.alpha * alpha); + glBegin(GL_QUADS); + glTexCoord2f(uv_left, uv_top); + glVertex2f(left*ca - top*sa + center_x, + left*sa + top*ca + center_y); + + glTexCoord2f(uv_right, uv_top); + glVertex2f(right*ca - top*sa + center_x, + right*sa + top*ca + center_y); + + glTexCoord2f(uv_right, uv_bottom); + glVertex2f(right*ca - bottom*sa + center_x, + right*sa + bottom*ca + center_y); + + glTexCoord2f(uv_left, uv_bottom); + glVertex2f(left*ca - bottom*sa + center_x, + left*sa + bottom*ca + center_y); + glEnd(); + + // 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 GL +{ + Renderer::Renderer() + { + if(texture_manager != 0) + texture_manager->save_textures(); + + if(config->try_vsync) { + /* we want vsync for smooth scrolling */ + SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1); + } + + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5); + SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5); + SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5); + + int flags = SDL_OPENGL; + if(config->use_fullscreen) + flags |= SDL_FULLSCREEN; + int width = config->screenwidth; + int height = config->screenheight; + int bpp = 0; + + SDL_Surface *screen = SDL_SetVideoMode(width, height, bpp, flags); + if(screen == 0) { + std::stringstream msg; + msg << "Couldn't set video mode (" << width << "x" << height + << "-" << bpp << "bpp): " << SDL_GetError(); + throw std::runtime_error(msg.str()); + } + + // setup opengl state and transform + glDisable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); + glEnable(GL_TEXTURE_2D); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glViewport(0, 0, screen->w, screen->h); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + // logical resolution here not real monitor resolution + glOrtho(0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, -1.0, 1.0); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef(0, 0, 0); + + check_gl_error("Setting up view matrices"); + + + if(texture_manager == 0) + texture_manager = new TextureManager(); + else + texture_manager->reload_textures(); + } + + Renderer::~Renderer() + { + } + + void + Renderer::draw_surface(const DrawingRequest& request) + { + const Surface* surface = (const Surface*) request.request_data; + GL::Texture *gltexture = dynamic_cast(surface->get_texture()); + glBindTexture(GL_TEXTURE_2D, gltexture->get_handle()); + intern_draw(request.pos.x, request.pos.y, + request.pos.x + surface->get_width(), + request.pos.y + surface->get_height(), + surface->get_uv_left(), + surface->get_uv_top(), + surface->get_uv_right(), + surface->get_uv_bottom(), + request.angle, + request.alpha, + request.color, + request.blend, + request.drawing_effect); + } + + void + Renderer::draw_surface_part(const DrawingRequest& request) + { + const SurfacePartRequest* surfacepartrequest + = (SurfacePartRequest*) request.request_data; + const Surface *surface = surfacepartrequest->surface; + GL::Texture *gltexture = dynamic_cast(surface->get_texture()); + + float uv_width = surface->get_uv_right() - surface->get_uv_left(); + float uv_height = surface->get_uv_bottom() - surface->get_uv_top(); + + float uv_left = surface->get_uv_left() + (uv_width * surfacepartrequest->source.x) / surface->get_width(); + float uv_top = surface->get_uv_top() + (uv_height * surfacepartrequest->source.y) / surface->get_height(); + float uv_right = surface->get_uv_left() + (uv_width * (surfacepartrequest->source.x + surfacepartrequest->size.x)) / surface->get_width(); + float uv_bottom = surface->get_uv_top() + (uv_height * (surfacepartrequest->source.y + surfacepartrequest->size.y)) / surface->get_height(); + + glBindTexture(GL_TEXTURE_2D, gltexture->get_handle()); + intern_draw(request.pos.x, request.pos.y, + request.pos.x + surfacepartrequest->size.x, + request.pos.y + surfacepartrequest->size.y, + uv_left, + uv_top, + uv_right, + uv_bottom, + 0.0, + request.alpha, + Color(1.0, 1.0, 1.0), + Blend(), + request.drawing_effect); + } + + void + Renderer::draw_gradient(const DrawingRequest& request) + { + const GradientRequest* gradientrequest + = (GradientRequest*) request.request_data; + const Color& top = gradientrequest->top; + const Color& bottom = gradientrequest->bottom; + + glDisable(GL_TEXTURE_2D); + glBegin(GL_QUADS); + glColor4f(top.red, top.green, top.blue, top.alpha); + glVertex2f(0, 0); + glVertex2f(SCREEN_WIDTH, 0); + glColor4f(bottom.red, bottom.green, bottom.blue, bottom.alpha); + glVertex2f(SCREEN_WIDTH, SCREEN_HEIGHT); + glVertex2f(0, SCREEN_HEIGHT); + glEnd(); + glEnable(GL_TEXTURE_2D); + glColor4f(1, 1, 1, 1); + } + + void + Renderer::draw_text(const DrawingRequest& request) + { + const TextRequest* textrequest = (TextRequest*) request.request_data; + + textrequest->font->draw(this, textrequest->text, request.pos, + textrequest->alignment, request.drawing_effect, request.alpha); + } + + void + Renderer::draw_filled_rect(const DrawingRequest& request) + { + const FillRectRequest* fillrectrequest + = (FillRectRequest*) request.request_data; + + float x = request.pos.x; + float y = request.pos.y; + float w = fillrectrequest->size.x; + float h = fillrectrequest->size.y; + + glDisable(GL_TEXTURE_2D); + glColor4f(fillrectrequest->color.red, fillrectrequest->color.green, + fillrectrequest->color.blue, fillrectrequest->color.alpha); + + glBegin(GL_QUADS); + glVertex2f(x, y); + glVertex2f(x+w, y); + glVertex2f(x+w, y+h); + glVertex2f(x, y+h); + glEnd(); + glEnable(GL_TEXTURE_2D); + glColor4f(1, 1, 1, 1); + } + + void + Renderer::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); + #else + shot_surf = SDL_CreateRGBSurface(SDL_SWSURFACE, 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; + return; + } + + // read pixels into array + char* pixels = new char[3 * SCREEN_WIDTH * SCREEN_HEIGHT]; + if (!pixels) { + log_warning << "Could not allocate memory to store screenshot" << std::endl; + SDL_FreeSurface(shot_surf); + return; + } + glReadPixels(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, GL_RGB, GL_UNSIGNED_BYTE, pixels); + + // copy array line-by-line + for (int i = 0; i < SCREEN_HEIGHT; i++) { + char* src = pixels + (3 * SCREEN_WIDTH * (SCREEN_HEIGHT - i - 1)); + char* dst = ((char*)shot_surf->pixels) + i * shot_surf->pitch; + memcpy(dst, src, 3 * SCREEN_WIDTH); + } + + // free array + delete[](pixels); + + // save screenshot + static const std::string writeDir = PHYSFS_getWriteDir(); + static const std::string dirSep = PHYSFS_getDirSeparator(); + static const std::string baseName = "screenshot"; + static const std::string fileExt = ".bmp"; + std::string fullFilename; + for (int num = 0; num < 1000; num++) { + std::ostringstream oss; + oss << baseName; + oss << std::setw(3) << std::setfill('0') << num; + oss << fileExt; + std::string fileName = oss.str(); + fullFilename = writeDir + dirSep + fileName; + if (!PHYSFS_exists(fileName.c_str())) { + SDL_SaveBMP(shot_surf, fullFilename.c_str()); + log_debug << "Wrote screenshot to \"" << fullFilename << "\"" << std::endl; + SDL_FreeSurface(shot_surf); + return; + } + } + log_warning << "Did not save screenshot, because all files up to \"" << fullFilename << "\" already existed" << std::endl; + SDL_FreeSurface(shot_surf); + } + + void + Renderer::flip() + { + assert_gl("drawing"); + SDL_GL_SwapBuffers(); + } +} + +#endif diff --git a/src/video/gl_renderer.hpp b/src/video/gl_renderer.hpp new file mode 100644 index 000000000..1eef6b489 --- /dev/null +++ b/src/video/gl_renderer.hpp @@ -0,0 +1,48 @@ +// $Id: gl_renderer.hpp 4986 2007-04-16 17:48:28Z matzeb $ +// +// SuperTux +// Copyright (C) 2006 Matthias Braun +// +// 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 the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +#include + +#ifdef HAVE_OPENGL + +#ifndef SUPERTUX_GL_RENDERER_H +#define SUPERTUX_GL_RENDERER_H + +#include "renderer.hpp" + +namespace GL +{ + class Renderer : public ::Renderer + { + public: + Renderer(); + ~Renderer(); + + void draw_surface(const DrawingRequest& request); + void draw_surface_part(const DrawingRequest& request); + void draw_text(const DrawingRequest& request); + void draw_gradient(const DrawingRequest& request); + void draw_filled_rect(const DrawingRequest& request); + void do_take_screenshot(); + void flip(); + }; +} + +#endif + +#endif diff --git a/src/video/gl_texture.cpp b/src/video/gl_texture.cpp new file mode 100644 index 000000000..e9d72d0ab --- /dev/null +++ b/src/video/gl_texture.cpp @@ -0,0 +1,146 @@ +// $Id: gl_texture.cpp 4063 2006-07-21 21:05:23Z anmaster $ +// +// SuperTux +// Copyright (C) 2006 Matthias Braun +// +// 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 the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include + +#ifdef HAVE_OPENGL + +#include "gl_texture.hpp" +#include "gameconfig.hpp" +#include "glutil.hpp" +#include "log.hpp" + +#include +#include + +namespace +{ + inline bool is_power_of_2(int v) + { + return (v & (v-1)) == 0; + } + + inline int next_power_of_two(int val) + { + int result = 1; + while(result < val) + result *= 2; + return result; + } +} + +namespace GL +{ + Texture::Texture(unsigned int width, unsigned int height) + { + assert(is_power_of_2(width)); + assert(is_power_of_2(height)); + texture_width = width; + texture_height = height; + image_width = width; + image_height = height; + + assert_gl("before creating texture"); + glGenTextures(1, &handle); + + try { + glBindTexture(GL_TEXTURE_2D, handle); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture_width, + texture_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + + set_texture_params(); + } catch(...) { + glDeleteTextures(1, &handle); + throw; + } + } + + Texture::Texture(SDL_Surface* image) + { + texture_width = next_power_of_two(image->w); + texture_height = next_power_of_two(image->h); + image_width = image->w; + image_height = image->h; + +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + SDL_Surface* convert = SDL_CreateRGBSurface(SDL_SWSURFACE, + texture_width, texture_height, 32, + 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff); +#else + SDL_Surface* convert = SDL_CreateRGBSurface(SDL_SWSURFACE, + texture_width, texture_height, 32, + 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000); +#endif + + if(convert == 0) { + throw std::runtime_error("Couldn't create texture: out of memory"); + } + + SDL_SetAlpha(image, 0, 0); + SDL_BlitSurface(image, 0, convert, 0); + + assert_gl("before creating texture"); + glGenTextures(1, &handle); + + try { + GLenum sdl_format; + if(convert->format->BytesPerPixel == 3) + sdl_format = GL_RGB; + else if(convert->format->BytesPerPixel == 4) + sdl_format = GL_RGBA; + else + assert(false); + + glBindTexture(GL_TEXTURE_2D, handle); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glPixelStorei(GL_UNPACK_ROW_LENGTH, convert->pitch/convert->format->BytesPerPixel); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture_width, + texture_height, 0, sdl_format, + GL_UNSIGNED_BYTE, convert->pixels); + + assert_gl("creating texture"); + + set_texture_params(); + } catch(...) { + glDeleteTextures(1, &handle); + SDL_FreeSurface(convert); + throw; + } + SDL_FreeSurface(convert); + } + + Texture::~Texture() + { + glDeleteTextures(1, &handle); + } + + void + Texture::set_texture_params() + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + + assert_gl("set texture params"); + } +} + +#endif diff --git a/src/video/gl_texture.hpp b/src/video/gl_texture.hpp new file mode 100644 index 000000000..14532ccbe --- /dev/null +++ b/src/video/gl_texture.hpp @@ -0,0 +1,97 @@ +// $Id: gl_texture.hpp 4063 2006-07-21 21:05:23Z anmaster $ +// +// SuperTux +// Copyright (C) 2006 Matthias Braun +// +// 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 the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +#include + +#ifdef HAVE_OPENGL + +#ifndef __GL_TEXTURE_HPP__ +#define __GL_TEXTURE_HPP__ + +#include + +#include "texture.hpp" +#include "glutil.hpp" + +/** + * This class is a wrapper around a texture handle. It stores the texture width + * and height and provides convenience functions for uploading SDL_Surfaces + * into the texture + */ +namespace GL +{ + class Texture : public ::Texture + { + protected: + GLuint handle; + unsigned int texture_width; + unsigned int texture_height; + unsigned int image_width; + unsigned int image_height; + + public: + Texture(unsigned int width, unsigned int height); + Texture(SDL_Surface* image); + ~Texture(); + + const GLuint &get_handle() const { + return handle; + } + + void set_handle(GLuint handle) { + this->handle = handle; + } + + unsigned int get_texture_width() const + { + return texture_width; + } + + unsigned int get_texture_height() const + { + return texture_height; + } + + unsigned int get_image_width() const + { + return image_width; + } + + unsigned int get_image_height() const + { + return image_height; + } + + void set_image_width(unsigned int width) + { + image_width = width; + } + + void set_image_height(unsigned int height) + { + image_height = height; + } + + private: + void set_texture_params(); + }; +} + +#endif + +#endif diff --git a/src/video/glutil.hpp b/src/video/glutil.hpp index 90dd5255d..b19ce8b10 100644 --- a/src/video/glutil.hpp +++ b/src/video/glutil.hpp @@ -19,9 +19,14 @@ #ifndef __GLUTIL_HPP__ #define __GLUTIL_HPP__ +#include + +#ifdef HAVE_OPENGL + #include #include #include +#include static inline void check_gl_error(const char* message) { @@ -77,4 +82,15 @@ static inline void assert_gl(const char* message) #endif } +#else + +#define GLenum int +#define GLint int +#define GL_SRC_ALPHA 0 +#define GL_ONE_MINUS_SRC_ALPHA 1 +#define GL_RGBA 2 +#define GL_ONE 3 + +#endif + #endif diff --git a/src/video/image_texture.cpp b/src/video/image_texture.cpp index 445fdc8b6..c9a0de1d3 100644 --- a/src/video/image_texture.cpp +++ b/src/video/image_texture.cpp @@ -17,6 +17,7 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +#if 0 #include #include "image_texture.hpp" @@ -36,3 +37,4 @@ ImageTexture::release() { texture_manager->release(this); } +#endif diff --git a/src/video/image_texture.hpp b/src/video/image_texture.hpp index dccc9bfcc..9d32f761e 100644 --- a/src/video/image_texture.hpp +++ b/src/video/image_texture.hpp @@ -17,6 +17,7 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +#if 0 #ifndef __SURFACE_TEXTURE_HPP__ #define __SURFACE_TEXTURE_HPP__ @@ -76,3 +77,4 @@ private: }; #endif +#endif diff --git a/src/video/lightmap.hpp b/src/video/lightmap.hpp new file mode 100644 index 000000000..bca28d46b --- /dev/null +++ b/src/video/lightmap.hpp @@ -0,0 +1,59 @@ +// $Id: lightmap.hpp 4986 2007-04-16 17:48:28Z matzeb $ +// +// SuperTux +// Copyright (C) 2006 Matthias Braun +// +// 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 the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +#ifndef SUPERTUX_LIGHTMAP_H +#define SUPERTUX_LIGHTMAP_H + +#include +#include +#include + +#include + +#include + +#include "glutil.hpp" +#include "obstack/obstack.h" +#include "math/vector.hpp" +#include "math/rect.hpp" +#include "drawing_request.hpp" +#include "surface.hpp" +#include "font.hpp" +#include "color.hpp" + +class Texture; +struct DrawingRequest; + +class Lightmap +{ +public: + virtual ~Lightmap() {} + + virtual void start_draw(const Color &ambient_color) = 0; + virtual void end_draw() = 0; + virtual void do_draw() = 0; + virtual void draw_surface(const DrawingRequest& request) = 0; + virtual void draw_surface_part(const DrawingRequest& request) = 0; + virtual void draw_text(const DrawingRequest& request) = 0; + virtual void draw_gradient(const DrawingRequest& request) = 0; + virtual void draw_filled_rect(const DrawingRequest& request) = 0; + virtual void get_light(const DrawingRequest& request) const = 0; +}; + +#endif + diff --git a/src/video/renderer.hpp b/src/video/renderer.hpp new file mode 100644 index 000000000..c1f10bf4b --- /dev/null +++ b/src/video/renderer.hpp @@ -0,0 +1,57 @@ +// $Id: drawing_context.hpp 4986 2007-04-16 17:48:28Z matzeb $ +// +// SuperTux +// Copyright (C) 2006 Matthias Braun +// +// 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 the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +#ifndef SUPERTUX_RENDERER_H +#define SUPERTUX_RENDERER_H + +#include +#include +#include + +#include + +#include + +#include "glutil.hpp" +#include "obstack/obstack.h" +#include "math/vector.hpp" +#include "math/rect.hpp" +#include "surface.hpp" +#include "font.hpp" +#include "color.hpp" + +class Surface; +class Texture; +struct DrawingRequest; + +class Renderer +{ +public: + virtual ~Renderer() {} + + virtual void draw_surface(const DrawingRequest& request) = 0; + virtual void draw_surface_part(const DrawingRequest& request) = 0; + virtual void draw_text(const DrawingRequest& request) = 0; + virtual void draw_gradient(const DrawingRequest& request) = 0; + virtual void draw_filled_rect(const DrawingRequest& request)= 0; + virtual void do_take_screenshot() = 0; + virtual void flip() = 0; +}; + +#endif + diff --git a/src/video/sdl_lightmap.cpp b/src/video/sdl_lightmap.cpp new file mode 100644 index 000000000..0d19c489f --- /dev/null +++ b/src/video/sdl_lightmap.cpp @@ -0,0 +1,389 @@ +// $Id: sdl_lightmap.cpp 5063 2007-05-27 11:32:00Z matzeb $ +// +// SuperTux +// Copyright (C) 2006 Matthias Braun +// +// 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 the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "glutil.hpp" +#include "sdl_lightmap.hpp" +#include "sdl_texture.hpp" +#include "drawing_context.hpp" +#include "drawing_request.hpp" +#include "renderer.hpp" +#include "surface.hpp" +#include "font.hpp" +#include "main.hpp" +#include "gameconfig.hpp" +#include "texture.hpp" +#include "texture_manager.hpp" +#include "obstack/obstackpp.hpp" + +namespace SDL +{ + Lightmap::Lightmap() + { + screen = SDL_GetVideoSurface(); + + width = screen->w; + height = screen->h; + + red_channel = (Uint8 *)malloc(width * height * sizeof(Uint8)); + green_channel = (Uint8 *)malloc(width * height * sizeof(Uint8)); + blue_channel = (Uint8 *)malloc(width * height * sizeof(Uint8)); + } + + Lightmap::~Lightmap() + { + free(red_channel); + free(green_channel); + free(blue_channel); + } + + void + Lightmap::start_draw(const Color &ambient_color) + { + memset(red_channel, (Uint8) (ambient_color.red * 255), width * height * sizeof(Uint8)); + memset(green_channel, (Uint8) (ambient_color.green * 255), width * height * sizeof(Uint8)); + memset(blue_channel, (Uint8) (ambient_color.blue * 255), width * height * sizeof(Uint8)); + } + + void + Lightmap::end_draw() + { + } + + void + Lightmap::do_draw() + { + // FIXME: This is really slow + int bpp = screen->format->BytesPerPixel; + for(int y = 0;y < height;y++) { + for(int x = 0;x < width;x++) { + int loc = y * width + x; + if(red_channel[loc] == 255 && green_channel[loc] == 255 && blue_channel[loc] == 255) + { + continue; + } + Uint8 *pixel = (Uint8 *) screen->pixels + y * screen->pitch + x * bpp; + Uint32 mapped = 0; + switch(bpp) { + case 1: + mapped = *pixel; + break; + case 2: + mapped = *(Uint16 *)pixel; + break; + case 3: +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + mapped |= pixel[0] << 16; + mapped |= pixel[1] << 8; + mapped |= pixel[2] << 0; +#else + mapped |= pixel[0] << 0; + mapped |= pixel[1] << 8; + mapped |= pixel[2] << 16; +#endif + break; + case 4: + mapped = *(Uint32 *)pixel; + break; + } + Uint8 red, green, blue, alpha; + SDL_GetRGBA(mapped, screen->format, &red, &green, &blue, &alpha); + red = (red * red_channel[loc]) / 255; + green = (green * green_channel[loc]) / 255; + blue = (blue * blue_channel[loc]) / 255; + mapped = SDL_MapRGBA(screen->format, red, green, blue, alpha); + switch(bpp) { + case 1: + *pixel = mapped; + break; + case 2: + *(Uint16 *)pixel = mapped; + break; + case 3: +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + pixel[0] = (mapped >> 16) & 0xff; + pixel[1] = (mapped >> 8) & 0xff; + pixel[2] = (mapped >> 0) & 0xff; +#else + pixel[0] = (mapped >> 0) & 0xff; + pixel[1] = (mapped >> 8) & 0xff; + pixel[2] = (mapped >> 16) & 0xff; +#endif + break; + case 4: + *(Uint32 *)pixel = mapped; + break; + } + } + } + } + + void Lightmap::light_blit(SDL_Surface *src, int dstx, int dsty, + int srcx, int srcy, int blit_width, int blit_height) + { + for(int y = 0;y < blit_height;y++) { + for(int x = 0;x < blit_width;x++) { + if(x + dstx < 0 || y + dsty < 0 || x + dstx >= width || y + dsty >= height) + { + continue; + } + int loc = (y + dsty) * width + (x + dstx); + if(red_channel[loc] == 255 && green_channel[loc] == 255 && blue_channel[loc]) + { + continue; + } + + Uint8 *pixel = (Uint8 *) src->pixels + (y + srcy) * src->pitch + (x + srcx) * src->format->BytesPerPixel; + Uint32 mapped = 0; + switch(src->format->BytesPerPixel) { + case 1: + mapped = *pixel; + break; + case 2: + mapped = *(Uint16 *)pixel; + break; + case 3: +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + mapped |= pixel[0] << 16; + mapped |= pixel[1] << 8; + mapped |= pixel[2] << 0; +#else + mapped |= pixel[0] << 0; + mapped |= pixel[1] << 8; + mapped |= pixel[2] << 16; +#endif + break; + case 4: + mapped = *(Uint32 *)pixel; + break; + } + Uint8 red, green, blue, alpha; + SDL_GetRGBA(mapped, src->format, &red, &green, &blue, &alpha); + + red_channel[loc] = std::min(red_channel[loc] + (red * alpha / 255), 255); + green_channel[loc] = std::min(green_channel[loc] + (green * alpha / 255), 255); + blue_channel[loc] = std::min(blue_channel[loc] + (blue * alpha / 255), 255); + } + } + } + + void + Lightmap::draw_surface(const DrawingRequest& request) + { + if((request.color.red == 0.0 && request.color.green == 0.0 && request.color.blue == 0.0) || request.color.alpha == 0.0 || request.alpha == 0.0) + { + return; + } + //FIXME: support parameters request.alpha, request.angle, request.blend + + const Surface* surface = (const Surface*) request.request_data; + SDL::Texture *sdltexture = dynamic_cast(surface->get_texture()); + DrawingEffect effect = request.drawing_effect; + if (surface->get_flipx()) effect = HORIZONTAL_FLIP; + + SDL_Surface *transform = sdltexture->get_transform(request.color, effect); + + // get and check SDL_Surface + if (transform == 0) { + std::cerr << "Warning: Tried to draw NULL surface, skipped draw" << std::endl; + return; + } + + int ox, oy; + if (effect == HORIZONTAL_FLIP) + { + ox = sdltexture->get_texture_width() - surface->get_x() - surface->get_width(); + } + else + { + ox = surface->get_x(); + } + if (effect == VERTICAL_FLIP) + { + oy = sdltexture->get_texture_height() - surface->get_y() - surface->get_height(); + } + else + { + oy = surface->get_y(); + } + + int numerator, denominator; + float xfactor = (float) config->screenwidth / SCREEN_WIDTH; + float yfactor = (float) config->screenheight / SCREEN_HEIGHT; + if(xfactor < yfactor) + { + numerator = config->screenwidth; + denominator = SCREEN_WIDTH; + } + else + { + numerator = config->screenheight; + denominator = SCREEN_HEIGHT; + } + + int dstx = (int) request.pos.x * numerator / denominator; + int dsty = (int) request.pos.y * numerator / denominator; + int srcx = ox * numerator / denominator; + int srcy = oy * numerator / denominator; + int blit_width = surface->get_width() * numerator / denominator; + int blit_height = surface->get_height() * numerator / denominator; + light_blit(transform, dstx, dsty, srcx, srcy, blit_width, blit_height); + } + + void + Lightmap::draw_surface_part(const DrawingRequest& request) + { + const SurfacePartRequest* surfacepartrequest + = (SurfacePartRequest*) request.request_data; + + const Surface* surface = surfacepartrequest->surface; + SDL::Texture *sdltexture = dynamic_cast(surface->get_texture()); + DrawingEffect effect = request.drawing_effect; + if (surface->get_flipx()) effect = HORIZONTAL_FLIP; + + SDL_Surface *transform = sdltexture->get_transform(Color(1.0, 1.0, 1.0), effect); + + // get and check SDL_Surface + if (transform == 0) { + std::cerr << "Warning: Tried to draw NULL surface, skipped draw" << std::endl; + return; + } + + int ox, oy; + if (effect == HORIZONTAL_FLIP) + { + ox = sdltexture->get_texture_width() - surface->get_x() - (int) surfacepartrequest->size.x; + } + else + { + ox = surface->get_x(); + } + if (effect == VERTICAL_FLIP) + { + oy = sdltexture->get_texture_height() - surface->get_y() - (int) surfacepartrequest->size.y; + } + else + { + oy = surface->get_y(); + } + + int numerator, denominator; + float xfactor = (float) config->screenwidth / SCREEN_WIDTH; + float yfactor = (float) config->screenheight / SCREEN_HEIGHT; + if(xfactor < yfactor) + { + numerator = config->screenwidth; + denominator = SCREEN_WIDTH; + } + else + { + numerator = config->screenheight; + denominator = SCREEN_HEIGHT; + } + + int dstx = (int) request.pos.x * numerator / denominator; + int dsty = (int) request.pos.y * numerator / denominator; + int srcx = (ox + (int) surfacepartrequest->source.x) * numerator / denominator; + int srcy = (oy + (int) surfacepartrequest->source.y) * numerator / denominator; + int blit_width = (int) surfacepartrequest->size.x * numerator / denominator; + int blit_height = (int) surfacepartrequest->size.y * numerator / denominator; + light_blit(transform, dstx, dsty, srcx, srcy, blit_width, blit_height); + } + + void + Lightmap::draw_gradient(const DrawingRequest& request) + { + const GradientRequest* gradientrequest + = (GradientRequest*) request.request_data; + const Color& top = gradientrequest->top; + const Color& bottom = gradientrequest->bottom; + + for(int y = 0;y < height;++y) + { + Uint8 r = (Uint8)((((float)(top.red-bottom.red)/(0-height)) * y + top.red) * 255); + Uint8 g = (Uint8)((((float)(top.green-bottom.green)/(0-height)) * y + top.green) * 255); + Uint8 b = (Uint8)((((float)(top.blue-bottom.blue)/(0-height)) * y + top.blue) * 255); + // FIXME + //Uint8 a = (Uint8)((((float)(top.alpha-bottom.alpha)/(0-height)) * y + top.alpha) * 255); + for(int x = 0;x < width;x++) { + int loc = y * width + x; + red_channel[loc] = std::min(red_channel[loc] + r, 255); + green_channel[loc] = std::min(green_channel[loc] + g, 255); + blue_channel[loc] = std::min(blue_channel[loc] + b, 255); + } + } + } + + void + Lightmap::draw_text(const DrawingRequest& /*request*/) + { + //const TextRequest* textrequest = (TextRequest*) request.request_data; + + //textrequest->font->draw(textrequest->text, request.pos, + // textrequest->alignment, request.drawing_effect, request.alpha); + } + + void + Lightmap::draw_filled_rect(const DrawingRequest& request) + { + const FillRectRequest* fillrectrequest + = (FillRectRequest*) request.request_data; + + int rect_x = (int) (request.pos.x * width / SCREEN_WIDTH); + int rect_y = (int) (request.pos.y * height / SCREEN_HEIGHT); + int rect_w = (int) (fillrectrequest->size.x * width / SCREEN_WIDTH); + int rect_h = (int) (fillrectrequest->size.y * height / SCREEN_HEIGHT); + Uint8 red = (Uint8) (fillrectrequest->color.red * fillrectrequest->color.alpha * 255); + Uint8 green = (Uint8) (fillrectrequest->color.green * fillrectrequest->color.alpha * 255); + Uint8 blue = (Uint8) (fillrectrequest->color.blue * fillrectrequest->color.alpha * 255); + if(red == 0 && green == 0 && blue == 0) + { + return; + } + for(int y = rect_y;y < rect_y + rect_h;y++) { + for(int x = rect_x;x < rect_x + rect_w;x++) { + int loc = y * width + x; + red_channel[loc] = std::min(red_channel[loc] + red, 255); + green_channel[loc] = std::min(green_channel[loc] + green, 255); + blue_channel[loc] = std::min(blue_channel[loc] + blue, 255); + } + } + } + + void + Lightmap::get_light(const DrawingRequest& request) const + { + const GetLightRequest* getlightrequest + = (GetLightRequest*) request.request_data; + + int x = (int) (request.pos.x * width / SCREEN_WIDTH); + int y = (int) (request.pos.y * height / SCREEN_HEIGHT); + int loc = y * width + x; + *(getlightrequest->color_ptr) = Color(((float)red_channel[loc])/255, ((float)green_channel[loc])/255, ((float)blue_channel[loc])/255); + } +} diff --git a/src/video/sdl_lightmap.hpp b/src/video/sdl_lightmap.hpp new file mode 100644 index 000000000..985615ab6 --- /dev/null +++ b/src/video/sdl_lightmap.hpp @@ -0,0 +1,60 @@ +// $Id: sdl_lightmap.hpp 4986 2007-04-16 17:48:28Z matzeb $ +// +// SuperTux +// Copyright (C) 2006 Matthias Braun +// +// 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 the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +#ifndef SUPERTUX_SDL_LIGHTMAP_H +#define SUPERTUX_SDL_LIGHTMAP_H + +#include + +#include "lightmap.hpp" + +class Color; +struct DrawingRequest; + +namespace SDL +{ + class Lightmap : public ::Lightmap + { + public: + Lightmap(); + ~Lightmap(); + + void start_draw(const Color &ambient_color); + void end_draw(); + void do_draw(); + void draw_surface(const DrawingRequest& request); + void draw_surface_part(const DrawingRequest& request); + void draw_text(const DrawingRequest& request); + void draw_gradient(const DrawingRequest& request); + void draw_filled_rect(const DrawingRequest& request); + void get_light(const DrawingRequest& request) const; + + private: + SDL_Surface* screen; + Uint8 *red_channel; + Uint8 *blue_channel; + Uint8 *green_channel; + int width, height; + + void light_blit(SDL_Surface *src, int dstx, int dsty, + int srcx, int srcy, int blit_width, int blit_height); + }; +} + +#endif + diff --git a/src/video/sdl_renderer.cpp b/src/video/sdl_renderer.cpp new file mode 100644 index 000000000..f4bae6377 --- /dev/null +++ b/src/video/sdl_renderer.cpp @@ -0,0 +1,307 @@ +// $Id: sdl_renderer.cpp 5063 2007-05-27 11:32:00Z matzeb $ +// +// SuperTux +// Copyright (C) 2006 Matthias Braun +// +// 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 the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "glutil.hpp" +#include "sdl_renderer.hpp" +#include "sdl_texture.hpp" +#include "drawing_context.hpp" +#include "drawing_request.hpp" +#include "surface.hpp" +#include "font.hpp" +#include "main.hpp" +#include "gameconfig.hpp" +#include "texture.hpp" +#include "texture_manager.hpp" +#include "obstack/obstackpp.hpp" + +namespace SDL +{ + Renderer::Renderer() + { + int flags = SDL_SWSURFACE; + if(config->use_fullscreen) + flags |= SDL_FULLSCREEN; + int width = config->screenwidth; + int height = config->screenheight; + int bpp = 0; + + screen = SDL_SetVideoMode(width, height, bpp, flags); + if(screen == 0) { + std::stringstream msg; + msg << "Couldn't set video mode (" << width << "x" << height + << "-" << bpp << "bpp): " << SDL_GetError(); + throw std::runtime_error(msg.str()); + } + + if(texture_manager == 0) + texture_manager = new TextureManager(); + } + + Renderer::~Renderer() + { + } + + void + Renderer::draw_surface(const DrawingRequest& request) + { + //FIXME: support parameters request.alpha, request.angle, request.blend + const Surface* surface = (const Surface*) request.request_data; + SDL::Texture *sdltexture = dynamic_cast(surface->get_texture()); + DrawingEffect effect = request.drawing_effect; + if (surface->get_flipx()) effect = HORIZONTAL_FLIP; + + SDL_Surface *transform = sdltexture->get_transform(request.color, effect); + + // get and check SDL_Surface + if (transform == 0) { + std::cerr << "Warning: Tried to draw NULL surface, skipped draw" << std::endl; + return; + } + + int ox, oy; + if (effect == HORIZONTAL_FLIP) + { + ox = sdltexture->get_texture_width() - surface->get_x() - surface->get_width(); + } + else + { + ox = surface->get_x(); + } + if (effect == VERTICAL_FLIP) + { + oy = sdltexture->get_texture_height() - surface->get_y() - surface->get_height(); + } + else + { + oy = surface->get_y(); + } + + int numerator, denominator; + float xfactor = (float) config->screenwidth / SCREEN_WIDTH; + float yfactor = (float) config->screenheight / SCREEN_HEIGHT; + if(xfactor < yfactor) + { + numerator = config->screenwidth; + denominator = SCREEN_WIDTH; + } + else + { + numerator = config->screenheight; + denominator = SCREEN_HEIGHT; + } + + SDL_Rect srcRect; + srcRect.x = ox * numerator / denominator; + srcRect.y = oy * numerator / denominator; + srcRect.w = surface->get_width() * numerator / denominator; + srcRect.h = surface->get_height() * numerator / denominator; + + SDL_Rect dstRect; + dstRect.x = (int) request.pos.x * numerator / denominator; + dstRect.y = (int) request.pos.y * numerator / denominator; + + SDL_BlitSurface(transform, &srcRect, screen, &dstRect); + } + + void + Renderer::draw_surface_part(const DrawingRequest& request) + { + const SurfacePartRequest* surfacepartrequest + = (SurfacePartRequest*) request.request_data; + + const Surface* surface = surfacepartrequest->surface; + SDL::Texture *sdltexture = dynamic_cast(surface->get_texture()); + DrawingEffect effect = request.drawing_effect; + if (surface->get_flipx()) effect = HORIZONTAL_FLIP; + + SDL_Surface *transform = sdltexture->get_transform(Color(1.0, 1.0, 1.0), effect); + + // get and check SDL_Surface + if (transform == 0) { + std::cerr << "Warning: Tried to draw NULL surface, skipped draw" << std::endl; + return; + } + + int ox, oy; + if (effect == HORIZONTAL_FLIP) + { + ox = sdltexture->get_texture_width() - surface->get_x() - (int) surfacepartrequest->size.x; + } + else + { + ox = surface->get_x(); + } + if (effect == VERTICAL_FLIP) + { + oy = sdltexture->get_texture_height() - surface->get_y() - (int) surfacepartrequest->size.y; + } + else + { + oy = surface->get_y(); + } + + int numerator, denominator; + float xfactor = (float) config->screenwidth / SCREEN_WIDTH; + float yfactor = (float) config->screenheight / SCREEN_HEIGHT; + if(xfactor < yfactor) + { + numerator = config->screenwidth; + denominator = SCREEN_WIDTH; + } + else + { + numerator = config->screenheight; + denominator = SCREEN_HEIGHT; + } + + SDL_Rect srcRect; + srcRect.x = (ox + (int) surfacepartrequest->source.x) * numerator / denominator; + srcRect.y = (oy + (int) surfacepartrequest->source.y) * numerator / denominator; + srcRect.w = (int) surfacepartrequest->size.x * numerator / denominator; + srcRect.h = (int) surfacepartrequest->size.y * numerator / denominator; + + SDL_Rect dstRect; + dstRect.x = (int) request.pos.x * numerator / denominator; + dstRect.y = (int) request.pos.y * numerator / denominator; + + SDL_BlitSurface(transform, &srcRect, screen, &dstRect); + } + + void + Renderer::draw_gradient(const DrawingRequest& request) + { + const GradientRequest* gradientrequest + = (GradientRequest*) request.request_data; + const Color& top = gradientrequest->top; + const Color& bottom = gradientrequest->bottom; + + for(int y = 0;y < screen->h;++y) + { + Uint8 r = (Uint8)((((float)(top.red-bottom.red)/(0-screen->h)) * y + top.red) * 255); + Uint8 g = (Uint8)((((float)(top.green-bottom.green)/(0-screen->h)) * y + top.green) * 255); + Uint8 b = (Uint8)((((float)(top.blue-bottom.blue)/(0-screen->h)) * y + top.blue) * 255); + Uint8 a = (Uint8)((((float)(top.alpha-bottom.alpha)/(0-screen->h)) * y + top.alpha) * 255); + Uint32 color = SDL_MapRGB(screen->format, r, g, b); + + SDL_Rect rect; + rect.x = 0; + rect.y = y; + rect.w = screen->w; + rect.h = 1; + + if(a == SDL_ALPHA_OPAQUE) { + SDL_FillRect(screen, &rect, color); + } else if(a != SDL_ALPHA_TRANSPARENT) { + SDL_Surface *temp = SDL_CreateRGBSurface(screen->flags, rect.w, rect.h, screen->format->BitsPerPixel, screen->format->Rmask, screen->format->Gmask, screen->format->Bmask, screen->format->Amask); + + SDL_FillRect(temp, 0, color); + SDL_SetAlpha(temp, SDL_SRCALPHA, a); + SDL_BlitSurface(temp, 0, screen, &rect); + SDL_FreeSurface(temp); + } + } + } + + void + Renderer::draw_text(const DrawingRequest& request) + { + const TextRequest* textrequest = (TextRequest*) request.request_data; + + textrequest->font->draw(this, textrequest->text, request.pos, + textrequest->alignment, request.drawing_effect, request.alpha); + } + + void + Renderer::draw_filled_rect(const DrawingRequest& request) + { + const FillRectRequest* fillrectrequest + = (FillRectRequest*) request.request_data; + + SDL_Rect rect; + rect.x = (Sint16)request.pos.x * screen->w / SCREEN_WIDTH; + rect.y = (Sint16)request.pos.y * screen->h / SCREEN_HEIGHT; + rect.w = (Uint16)fillrectrequest->size.x * screen->w / SCREEN_WIDTH; + rect.h = (Uint16)fillrectrequest->size.y * screen->h / SCREEN_HEIGHT; + Uint8 r = static_cast(fillrectrequest->color.red * 255); + Uint8 g = static_cast(fillrectrequest->color.green * 255); + Uint8 b = static_cast(fillrectrequest->color.blue * 255); + Uint8 a = static_cast(fillrectrequest->color.alpha * 255); + Uint32 color = SDL_MapRGB(screen->format, r, g, b); + if(a == SDL_ALPHA_OPAQUE) { + SDL_FillRect(screen, &rect, color); + } else if(a != SDL_ALPHA_TRANSPARENT) { + SDL_Surface *temp = SDL_CreateRGBSurface(screen->flags, rect.w, rect.h, screen->format->BitsPerPixel, screen->format->Rmask, screen->format->Gmask, screen->format->Bmask, screen->format->Amask); + + SDL_FillRect(temp, 0, color); + SDL_SetAlpha(temp, SDL_SRCALPHA, a); + SDL_BlitSurface(temp, 0, screen, &rect); + SDL_FreeSurface(temp); + } + } + + void + Renderer::do_take_screenshot() + { + // [Christoph] TODO: Yes, this method also takes care of the actual disk I/O. Split it? + + SDL_Surface *shot_surf; + shot_surf = SDL_GetVideoSurface(); + shot_surf->refcount++; + + // save screenshot + static const std::string writeDir = PHYSFS_getWriteDir(); + static const std::string dirSep = PHYSFS_getDirSeparator(); + static const std::string baseName = "screenshot"; + static const std::string fileExt = ".bmp"; + std::string fullFilename; + for (int num = 0; num < 1000; num++) { + std::ostringstream oss; + oss << baseName; + oss << std::setw(3) << std::setfill('0') << num; + oss << fileExt; + std::string fileName = oss.str(); + fullFilename = writeDir + dirSep + fileName; + if (!PHYSFS_exists(fileName.c_str())) { + SDL_SaveBMP(shot_surf, fullFilename.c_str()); + log_debug << "Wrote screenshot to \"" << fullFilename << "\"" << std::endl; + SDL_FreeSurface(shot_surf); + return; + } + } + log_warning << "Did not save screenshot, because all files up to \"" << fullFilename << "\" already existed" << std::endl; + SDL_FreeSurface(shot_surf); + } + + void + Renderer::flip() + { + SDL_Flip(screen); + } +} diff --git a/src/video/sdl_renderer.hpp b/src/video/sdl_renderer.hpp new file mode 100644 index 000000000..8c943f1e1 --- /dev/null +++ b/src/video/sdl_renderer.hpp @@ -0,0 +1,47 @@ +// $Id: sdl_renderer.hpp 4986 2007-04-16 17:48:28Z matzeb $ +// +// SuperTux +// Copyright (C) 2006 Matthias Braun +// +// 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 the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +#ifndef SUPERTUX_SDL_RENDERER_H +#define SUPERTUX_SDL_RENDERER_H + +#include + +#include "renderer.hpp" + +namespace SDL +{ + class Renderer : public ::Renderer + { + public: + Renderer(); + ~Renderer(); + + void draw_surface(const DrawingRequest& request); + void draw_surface_part(const DrawingRequest& request); + void draw_text(const DrawingRequest& request); + void draw_gradient(const DrawingRequest& request); + void draw_filled_rect(const DrawingRequest& request); + void do_take_screenshot(); + void flip(); + private: + SDL_Surface *screen; + }; +} + +#endif + diff --git a/src/video/sdl_texture.cpp b/src/video/sdl_texture.cpp new file mode 100644 index 000000000..d5a9d9cf5 --- /dev/null +++ b/src/video/sdl_texture.cpp @@ -0,0 +1,451 @@ +// $Id: sdl_texture.cpp 4063 2006-07-21 21:05:23Z anmaster $ +// +// SuperTux +// Copyright (C) 2006 Matthias Braun +// +// 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 the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include + +#include "sdl_texture.hpp" +#include "color.hpp" +#include "gameconfig.hpp" +#include "main.hpp" + +#include + +#include + +namespace +{ +#define BILINEAR + +#ifdef NAIVE + SDL_Surface *scale(SDL_Surface *src, int numerator, int denominator) + { + if(numerator == denominator) + { + src->refcount++; + return src; + } + else + { + SDL_Surface *dst = SDL_CreateRGBSurface(src->flags, src->w * numerator / denominator, src->h * numerator / denominator, src->format->BitsPerPixel, src->format->Rmask, src->format->Gmask, src->format->Bmask, src->format->Amask); + int bpp = dst->format->BytesPerPixel; + for(int y = 0;y < dst->h;y++) { + for(int x = 0;x < dst->w;x++) { + Uint8 *srcpixel = (Uint8 *) src->pixels + (y * denominator / numerator) * src->pitch + (x * denominator / numerator) * bpp; + Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + x * bpp; + switch(bpp) { + case 4: + dstpixel[3] = srcpixel[3]; + case 3: + dstpixel[2] = srcpixel[2]; + case 2: + dstpixel[1] = srcpixel[1]; + case 1: + dstpixel[0] = srcpixel[0]; + } + } + } + return dst; + } + } +#endif + +#ifdef BILINEAR + void getpixel(SDL_Surface *src, int srcx, int srcy, Uint8 color[4]) + { + int bpp = src->format->BytesPerPixel; + Uint8 *srcpixel = (Uint8 *) src->pixels + srcy * src->pitch + srcx * bpp; + Uint32 mapped = 0; + switch(bpp) { + case 1: + mapped = *srcpixel; + break; + case 2: + mapped = *(Uint16 *)srcpixel; + break; + case 3: +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + mapped |= srcpixel[0] << 16; + mapped |= srcpixel[1] << 8; + mapped |= srcpixel[2] << 0; +#else + mapped |= srcpixel[0] << 0; + mapped |= srcpixel[1] << 8; + mapped |= srcpixel[2] << 16; +#endif + break; + case 4: + mapped = *(Uint32 *)srcpixel; + break; + } + SDL_GetRGBA(mapped, src->format, &color[0], &color[1], &color[2], &color[3]); + } + + void merge(Uint8 color[4], Uint8 color0[4], Uint8 color1[4], int rem, int total) + { + color[0] = (color0[0] * (total - rem) + color1[0] * rem) / total; + color[1] = (color0[1] * (total - rem) + color1[1] * rem) / total; + color[2] = (color0[2] * (total - rem) + color1[2] * rem) / total; + color[3] = (color0[3] * (total - rem) + color1[3] * rem) / total; + } + + SDL_Surface *scale(SDL_Surface *src, int numerator, int denominator) + { + if(numerator == denominator) + { + src->refcount++; + return src; + } + else + { + SDL_Surface *dst = SDL_CreateRGBSurface(src->flags, src->w * numerator / denominator, src->h * numerator / denominator, src->format->BitsPerPixel, src->format->Rmask, src->format->Gmask, src->format->Bmask, src->format->Amask); + int bpp = dst->format->BytesPerPixel; + for(int y = 0;y < dst->h;y++) { + for(int x = 0;x < dst->w;x++) { + int srcx = x * denominator / numerator; + int srcy = y * denominator / numerator; + Uint8 color00[4], color01[4], color10[4], color11[4]; + getpixel(src, srcx, srcy, color00); + getpixel(src, srcx + 1, srcy, color01); + getpixel(src, srcx, srcy + 1, color10); + getpixel(src, srcx + 1, srcy + 1, color11); + Uint8 color0[4], color1[4], color[4]; + int remx = x * denominator % numerator; + merge(color0, color00, color01, remx, numerator); + merge(color1, color10, color11, remx, numerator); + int remy = y * denominator % numerator; + merge(color, color0, color1, remy, numerator); + Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + x * bpp; + Uint32 mapped = SDL_MapRGBA(dst->format, color[0], color[1], color[2], color[3]); + switch(bpp) { + case 1: + *dstpixel = mapped; + break; + case 2: + *(Uint16 *)dstpixel = mapped; + break; + case 3: +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + dstpixel[0] = (mapped >> 16) & 0xff; + dstpixel[1] = (mapped >> 8) & 0xff; + dstpixel[2] = (mapped >> 0) & 0xff; +#else + dstpixel[0] = (mapped >> 0) & 0xff; + dstpixel[1] = (mapped >> 8) & 0xff; + dstpixel[2] = (mapped >> 16) & 0xff; +#endif + break; + case 4: + *(Uint32 *)dstpixel = mapped; + break; + } + } + } + return dst; + } + } +#endif + + // FIXME: Horizontal and vertical line problem +#ifdef BRESENHAM + void accumulate(SDL_Surface *src, int srcx, int srcy, int color[4], int weight) + { + if(srcx < 0 || srcy < 0 || weight == 0) { + return; + } + int bpp = src->format->BytesPerPixel; + Uint8 *srcpixel = (Uint8 *) src->pixels + srcy * src->pitch + srcx * bpp; + Uint32 mapped = 0; + switch(bpp) { + case 1: + mapped = *srcpixel; + break; + case 2: + mapped = *(Uint16 *)srcpixel; + break; + case 3: +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + mapped |= srcpixel[0] << 16; + mapped |= srcpixel[1] << 8; + mapped |= srcpixel[2] << 0; +#else + mapped |= srcpixel[0] << 0; + mapped |= srcpixel[1] << 8; + mapped |= srcpixel[2] << 16; +#endif + break; + case 4: + mapped = *(Uint32 *)srcpixel; + break; + } + Uint8 red, green, blue, alpha; + SDL_GetRGBA(mapped, src->format, &red, &green, &blue, &alpha); + color[0] += red * weight; + color[1] += green * weight; + color[2] += blue * weight; + color[3] += alpha * weight; + } + + void accumulate_line(SDL_Surface *src, int srcy, int line[][4], int linesize, int weight, int numerator, int denominator) + { + int intpart = denominator / numerator; + int fractpart = denominator % numerator; + for(int x = 0, xe = 0, srcx = 0;x < linesize;x++) { + accumulate(src, srcx, srcy, line[x], (numerator - xe) * weight); + srcx++; + for(int i = 0;i < intpart - 1;i++) { + accumulate(src, srcx, srcy, line[x], numerator * weight); + srcx++; + } + xe += fractpart; + if(xe >= numerator) { + xe -= numerator; + srcx++; + } + accumulate(src, srcx, srcy, line[x], xe * weight); + } + } + + SDL_Surface *scale(SDL_Surface *src, int numerator, int denominator) + { + if(numerator == denominator) + { + src->refcount++; + return src; + } + else + { + SDL_Surface *dst = SDL_CreateRGBSurface(src->flags, src->w * numerator / denominator, src->h * numerator / denominator, src->format->BitsPerPixel, src->format->Rmask, src->format->Gmask, src->format->Bmask, src->format->Amask); + int bpp = dst->format->BytesPerPixel; + int intpart = denominator / numerator; + int fractpart = denominator % numerator; + for(int y = 0, ye = 0, srcy = 0;y < dst->h;y++) { + int line[dst->w][4]; + memset(line, 0, sizeof(int) * dst->w * 4); + accumulate_line(src, srcy, line, dst->w, numerator - ye, numerator, denominator); + srcy++; + for(int i = 0;i < intpart - 1;i++) { + accumulate_line(src, srcy, line, dst->w, numerator, numerator, denominator); + srcy++; + } + ye += fractpart; + if(ye >= numerator) { + ye -= numerator; + srcy++; + } + accumulate_line(src, srcy, line, dst->w, ye, numerator, denominator); + for(int x = 0;x < dst->w;x++) { + Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + x * bpp; + Uint32 mapped = SDL_MapRGBA(dst->format, line[x][0] / (denominator * denominator), line[x][1] / (denominator * denominator), line[x][2] / (denominator * denominator), line[x][3] / (denominator * denominator)); + switch(bpp) { + case 1: + *dstpixel = mapped; + break; + case 2: + *(Uint16 *)dstpixel = mapped; + break; + case 3: +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + dstpixel[0] = (mapped >> 16) & 0xff; + dstpixel[1] = (mapped >> 8) & 0xff; + dstpixel[2] = (mapped >> 0) & 0xff; +#else + dstpixel[0] = (mapped >> 0) & 0xff; + dstpixel[1] = (mapped >> 8) & 0xff; + dstpixel[2] = (mapped >> 16) & 0xff; +#endif + break; + case 4: + *(Uint32 *)dstpixel = mapped; + break; + } + } + } + return dst; + } + } +#endif + + SDL_Surface *horz_flip(SDL_Surface *src) + { + SDL_Surface *dst = SDL_CreateRGBSurface(src->flags, src->w, src->h, src->format->BitsPerPixel, src->format->Rmask, src->format->Gmask, src->format->Bmask, src->format->Amask); + int bpp = dst->format->BytesPerPixel; + for(int y = 0;y < dst->h;y++) { + for(int x = 0;x < dst->w;x++) { + Uint8 *srcpixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp; + Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + (dst->w - x - 1) * bpp; + switch(bpp) { + case 4: + dstpixel[3] = srcpixel[3]; + case 3: + dstpixel[2] = srcpixel[2]; + case 2: + dstpixel[1] = srcpixel[1]; + case 1: + dstpixel[0] = srcpixel[0]; + } + } + } + return dst; + } + + SDL_Surface *vert_flip(SDL_Surface *src) + { + SDL_Surface *dst = SDL_CreateRGBSurface(src->flags, src->w, src->h, src->format->BitsPerPixel, src->format->Rmask, src->format->Gmask, src->format->Bmask, src->format->Amask); + int bpp = dst->format->BytesPerPixel; + for(int y = 0;y < dst->h;y++) { + for(int x = 0;x < dst->w;x++) { + Uint8 *srcpixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp; + Uint8 *dstpixel = (Uint8 *) dst->pixels + (dst->h - y - 1) * dst->pitch + x * bpp; + switch(bpp) { + case 4: + dstpixel[3] = srcpixel[3]; + case 3: + dstpixel[2] = srcpixel[2]; + case 2: + dstpixel[1] = srcpixel[1]; + case 1: + dstpixel[0] = srcpixel[0]; + } + } + } + return dst; + } + + const Color white(1.0, 1.0, 1.0); + + SDL_Surface *colorize(SDL_Surface *src, const Color &color) + { + // FIXME: This is really slow + assert(color.red != 1.0 || color.green != 1.0 || color.blue != 1.0); + SDL_Surface *dst = SDL_CreateRGBSurface(src->flags, src->w, src->h, src->format->BitsPerPixel, src->format->Rmask, src->format->Gmask, src->format->Bmask, src->format->Amask); + int bpp = dst->format->BytesPerPixel; + for(int y = 0;y < dst->h;y++) { + for(int x = 0;x < dst->w;x++) { + Uint8 *srcpixel = (Uint8 *) src->pixels + y * src->pitch + x * bpp; + Uint8 *dstpixel = (Uint8 *) dst->pixels + y * dst->pitch + x * bpp; + Uint32 mapped = 0; + switch(bpp) { + case 1: + mapped = *srcpixel; + break; + case 2: + mapped = *(Uint16 *)srcpixel; + break; + case 3: +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + mapped |= srcpixel[0] << 16; + mapped |= srcpixel[1] << 8; + mapped |= srcpixel[2] << 0; +#else + mapped |= srcpixel[0] << 0; + mapped |= srcpixel[1] << 8; + mapped |= srcpixel[2] << 16; +#endif + break; + case 4: + mapped = *(Uint32 *)srcpixel; + break; + } + Uint8 red, green, blue, alpha; + SDL_GetRGBA(mapped, src->format, &red, &green, &blue, &alpha); + red = (Uint8) (red * color.red); + green = (Uint8) (green * color.green); + blue = (Uint8) (blue * color.blue); + mapped = SDL_MapRGBA(dst->format, red, green, blue, alpha); + switch(bpp) { + case 1: + *dstpixel = mapped; + break; + case 2: + *(Uint16 *)dstpixel = mapped; + break; + case 3: +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + dstpixel[0] = (mapped >> 16) & 0xff; + dstpixel[1] = (mapped >> 8) & 0xff; + dstpixel[2] = (mapped >> 0) & 0xff; +#else + dstpixel[0] = (mapped >> 0) & 0xff; + dstpixel[1] = (mapped >> 8) & 0xff; + dstpixel[2] = (mapped >> 16) & 0xff; +#endif + break; + case 4: + *(Uint32 *)dstpixel = mapped; + break; + } + } + } + return dst; + } +} + +namespace SDL +{ + Texture::Texture(SDL_Surface* image) + { + texture = SDL_DisplayFormatAlpha(image); + //image->refcount++; + //texture = image; + + float xfactor = (float) config->screenwidth / SCREEN_WIDTH; + float yfactor = (float) config->screenheight / SCREEN_HEIGHT; + if(xfactor < yfactor) + { + numerator = config->screenwidth; + denominator = SCREEN_WIDTH; + } + else + { + numerator = config->screenheight; + denominator = SCREEN_HEIGHT; + } + cache[white][NO_EFFECT] = scale(texture, numerator, denominator); + } + + Texture::~Texture() + { + SDL_FreeSurface(texture); + } + + SDL_Surface *Texture::get_transform(const Color &color, DrawingEffect effect) + { + if(cache.find(color) == cache.end()) + { + cache[color][NO_EFFECT] = colorize(cache[white][NO_EFFECT], color); + } + if(cache[color][effect] == 0) { + assert(cache[color][NO_EFFECT]); + switch(effect) { + case NO_EFFECT: + break; + case HORIZONTAL_FLIP: + cache[color][HORIZONTAL_FLIP] = horz_flip(cache[color][NO_EFFECT]); + break; + case VERTICAL_FLIP: + cache[color][VERTICAL_FLIP] = vert_flip(cache[color][NO_EFFECT]); + break; + default: + std::cerr << "Warning: transformation unknown" << std::endl; + return 0; + } + } + return cache[color][effect]; + } +} diff --git a/src/video/sdl_texture.hpp b/src/video/sdl_texture.hpp new file mode 100644 index 000000000..0a8045fca --- /dev/null +++ b/src/video/sdl_texture.hpp @@ -0,0 +1,109 @@ +// $Id: sdl_texture.hpp 4063 2006-07-21 21:05:23Z anmaster $ +// +// SuperTux +// Copyright (C) 2006 Matthias Braun +// +// 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 the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef __SDL_TEXTURE_HPP__ +#define __SDL_TEXTURE_HPP__ + +#include + +#include + +#include "texture.hpp" + +class Color; + +namespace SDL +{ + class Texture : public ::Texture + { + protected: + SDL_Surface *texture; + int numerator; + int denominator; + + struct Cache + { + static void ref(SDL_Surface *surface) + { + if(surface) + { + surface->refcount++; + } + } + + SDL_Surface *data[NUM_EFFECTS]; + + Cache() + { + memset(data, 0, NUM_EFFECTS * sizeof(SDL_Surface *)); + } + + ~Cache() + { + std::for_each(data, data + NUM_EFFECTS, SDL_FreeSurface); + } + + void operator = (const Cache &other) + { + std::for_each(other.data, other.data + NUM_EFFECTS, ref); + std::for_each(data, data + NUM_EFFECTS, SDL_FreeSurface); + memcpy(data, other.data, sizeof(Cache)); + } + + SDL_Surface *&operator [] (DrawingEffect effect) + { + return data[effect]; + } + }; + mutable std::map cache; /**< Cache for processed surfaces */ + + public: + Texture(SDL_Surface* sdlsurface); + virtual ~Texture(); + + SDL_Surface *get_transform(const Color &color, DrawingEffect effect); + + SDL_Surface *get_texture() const + { + return texture; + } + + unsigned int get_texture_width() const + { + return texture->w; + } + + unsigned int get_texture_height() const + { + return texture->h; + } + + unsigned int get_image_width() const + { + return texture->w; + } + + unsigned int get_image_height() const + { + return texture->h; + } + }; +} + +#endif diff --git a/src/video/surface.cpp b/src/video/surface.cpp deleted file mode 100644 index fc0f870bc..000000000 --- a/src/video/surface.cpp +++ /dev/null @@ -1,233 +0,0 @@ -// $Id$ -// -// SuperTux -// Copyright (C) 2006 Matthias Braun -// -// 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 the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#include - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "gameconfig.hpp" -#include "physfs/physfs_sdl.hpp" -#include "video/surface.hpp" -#include "video/drawing_context.hpp" -#include "video/color.hpp" -#include "image_texture.hpp" -#include "texture_manager.hpp" - -Surface::Surface(const std::string& file) -{ - texture = texture_manager->get(file); - texture->ref(); - uv_left = 0; - uv_top = 0; - uv_right = texture->get_uv_right(); - uv_bottom = texture->get_uv_bottom(); - - width = texture->get_image_width(); - height = texture->get_image_height(); -} - -Surface::Surface(const std::string& file, int x, int y, int w, int h) -{ - texture = texture_manager->get(file); - texture->ref(); - - float tex_w = static_cast (texture->get_width()); - float tex_h = static_cast (texture->get_height()); - uv_left = static_cast(x) / tex_w; - uv_top = static_cast(y) / tex_h; - uv_right = static_cast(x+w) / tex_w; - uv_bottom = static_cast(y+h) / tex_h; - - width = w; - height = h; -} - -Surface::Surface(const Surface& other) -{ - texture = other.texture; - texture->ref(); - - uv_left = other.uv_left; - uv_top = other.uv_top; - uv_right = other.uv_right; - uv_bottom = other.uv_bottom; - width = other.width; - height = other.height; -} - -const Surface& -Surface::operator= (const Surface& other) -{ - other.texture->ref(); - texture->unref(); - texture = other.texture; - - uv_left = other.uv_left; - uv_top = other.uv_top; - uv_right = other.uv_right; - uv_bottom = other.uv_bottom; - width = other.width; - height = other.height; - - return *this; -} - -Surface::~Surface() -{ - texture->unref(); -} - -void -Surface::hflip() -{ - std::swap(uv_left, uv_right); -} - -static inline void intern_draw(float left, float top, float right, float bottom, float uv_left, float uv_top, - float uv_right, float uv_bottom, - DrawingEffect effect) -{ - if(effect & HORIZONTAL_FLIP) - std::swap(uv_left, uv_right); - if(effect & VERTICAL_FLIP) { - std::swap(uv_top, uv_bottom); - } - - glBegin(GL_QUADS); - glTexCoord2f(uv_left, uv_top); - glVertex2f(left, top); - - glTexCoord2f(uv_right, uv_top); - glVertex2f(right, top); - - glTexCoord2f(uv_right, uv_bottom); - glVertex2f(right, bottom); - - glTexCoord2f(uv_left, uv_bottom); - glVertex2f(left, bottom); - glEnd(); -} - -static inline void intern_draw2(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); - } - - 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; - - glBlendFunc(blend.sfactor, blend.dfactor); - glColor4f(color.red, color.green, color.blue, color.alpha * alpha); - glBegin(GL_QUADS); - glTexCoord2f(uv_left, uv_top); - glVertex2f(left*ca - top*sa + center_x, - left*sa + top*ca + center_y); - - glTexCoord2f(uv_right, uv_top); - glVertex2f(right*ca - top*sa + center_x, - right*sa + top*ca + center_y); - - glTexCoord2f(uv_right, uv_bottom); - glVertex2f(right*ca - bottom*sa + center_x, - right*sa + bottom*ca + center_y); - - glTexCoord2f(uv_left, uv_bottom); - glVertex2f(left*ca - bottom*sa + center_x, - left*sa + bottom*ca + center_y); - glEnd(); - - // 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); -} - -void -Surface::draw(float x, float y, float alpha, float angle, const Color& color, const Blend& blend, DrawingEffect effect) const -{ - glBindTexture(GL_TEXTURE_2D, texture->get_handle()); - - intern_draw2(x, y, - x + width, y + height, - uv_left, uv_top, uv_right, uv_bottom, - angle, - alpha, - color, - blend, - effect); -} - -void -Surface::draw(float x, float y, float alpha, DrawingEffect effect) const -{ - glBindTexture(GL_TEXTURE_2D, texture->get_handle()); - - glColor4f(1, 1, 1, alpha); - intern_draw(x, y, - x + width, y + height, - uv_left, uv_top, uv_right, uv_bottom, effect); - glColor4f(1, 1, 1, 1); -} - -void -Surface::draw_part(float src_x, float src_y, float dst_x, float dst_y, - float width, float height, float alpha, - DrawingEffect effect) const -{ - float uv_width = uv_right - uv_left; - float uv_height = uv_bottom - uv_top; - - float uv_left = this->uv_left + (uv_width * src_x) / this->width; - float uv_top = this->uv_top + (uv_height * src_y) / this->height; - float uv_right = this->uv_left + (uv_width * (src_x + width)) / this->width; - float uv_bottom = this->uv_top + (uv_height * (src_y + height)) / this->height; - - glBindTexture(GL_TEXTURE_2D, texture->get_handle()); - - glColor4f(1, 1, 1, alpha); - intern_draw(dst_x, dst_y, - dst_x + width, dst_y + height, - uv_left, uv_top, uv_right, uv_bottom, effect); - glColor4f(1, 1, 1, 1); -} diff --git a/src/video/surface.hpp b/src/video/surface.hpp index d7021815d..e1b7cd425 100644 --- a/src/video/surface.hpp +++ b/src/video/surface.hpp @@ -20,22 +20,12 @@ #ifndef __SURFACE_HPP__ #define __SURFACE_HPP__ +#include + #include +#include #include "math/vector.hpp" - -class Color; -class Blend; -class ImageTexture; - -/// bitset for drawing effects -enum DrawingEffect { - /** Don't apply anything */ - NO_EFFECT = 0x0000, - /** Draw the Surface upside down */ - VERTICAL_FLIP = 0x0001, - /** Draw the Surface from left to down */ - HORIZONTAL_FLIP = 0x0002, -}; +#include "texture.hpp" /** * A rectangular image. @@ -45,50 +35,126 @@ enum DrawingEffect { class Surface { private: - friend class DrawingContext; - friend class Font; - ImageTexture* texture; - - float uv_left; - float uv_top; - float uv_right; - float uv_bottom; - - void draw(float x, float y, float alpha, float angle, const Color& color, const Blend& blend, DrawingEffect effect) const; - void draw(float x, float y, float alpha, DrawingEffect effect) const; - void draw_part(float src_x, float src_y, float dst_x, float dst_y, - float width, float height, - float alpha, DrawingEffect effect) const; - - float width; - float height; + Texture* texture; + int x; + int y; + int w; + int h; + bool flipx; + public: - Surface(const std::string& file); - Surface(const std::string& file, int x, int y, int w, int h); - Surface(const Surface& other); - ~Surface(); + Surface(const std::string& file) : + texture(texture_manager->get(file)), + x(0), y(0), w(0), h(0), + flipx(false) + { + texture->ref(); + w = texture->get_image_width(); + h = texture->get_image_height(); + } + + Surface(const std::string& file, int x, int y, int w, int h) : + texture(texture_manager->get(file)), + x(x), y(y), w(w), h(h), + flipx(false) + { + texture->ref(); + } + + Surface(const Surface& other) : + texture(other.texture), + x(other.x), y(other.y), + w(other.w), h(other.h), + flipx(false) + { + texture->ref(); + } + + ~Surface() + { + texture->unref(); + } /** flip the surface horizontally */ - void hflip(); + void hflip() + { + flipx = !flipx; + } + + bool get_flipx() const + { + return flipx; + } + + const Surface& operator= (const Surface& other) + { + other.texture->ref(); + texture->unref(); + texture = other.texture; + x = other.x; + y = other.y; + w = other.w; + h = other.h; + return *this; + } + + Texture *get_texture() const + { + return texture; + } - const Surface& operator= (const Surface& other); + int get_x() const + { + return x; + } - float get_width() const + int get_y() const { - return width; + return y; } - float get_height() const + int get_width() const { - return height; + return w; } + int get_height() const + { + return h; + } + + Vector get_position() const + { return Vector(get_x(), get_y()); } + /** * returns a vector containing width and height */ Vector get_size() const { return Vector(get_width(), get_height()); } + float get_uv_left() const + { + return (float) (x + (flipx ? w : 0)) / texture->get_texture_width(); + } + + float get_uv_top() const + { + return (float) y / texture->get_texture_height(); + } + + float get_uv_right() const + { + return (float) (x + (flipx ? 0 : w)) / texture->get_texture_width(); + } + + float get_uv_bottom() const + { + return (float) (y + h) / texture->get_texture_height(); + } + + //void draw_part(float src_x, float src_y, float dst_x, float dst_y, + // float width, float height, float alpha, + // DrawingEffect effect) const; }; #endif diff --git a/src/video/texture.cpp b/src/video/texture.cpp deleted file mode 100644 index 9c41ddde9..000000000 --- a/src/video/texture.cpp +++ /dev/null @@ -1,109 +0,0 @@ -// $Id$ -// -// SuperTux -// Copyright (C) 2006 Matthias Braun -// -// 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 the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#include - -#include "texture.hpp" - -#include -#include -#include "glutil.hpp" - -static inline bool is_power_of_2(int v) -{ - return (v & (v-1)) == 0; -} - -Texture::Texture(unsigned int w, unsigned int h, GLenum glformat) -{ - assert(is_power_of_2(w)); - assert(is_power_of_2(h)); - - this->width = w; - this->height = h; - - assert_gl("before creating texture"); - glGenTextures(1, &handle); - - try { - glBindTexture(GL_TEXTURE_2D, handle); - - glTexImage2D(GL_TEXTURE_2D, 0, glformat, width, height, 0, GL_RGBA, - GL_UNSIGNED_BYTE, 0); - - set_texture_params(); - } catch(...) { - glDeleteTextures(1, &handle); - throw; - } -} - -Texture::Texture(SDL_Surface* image, GLenum glformat) -{ - const SDL_PixelFormat* format = image->format; - if(!is_power_of_2(image->w) || !is_power_of_2(image->h)) - throw std::runtime_error("image has no power of 2 size"); - if(format->BitsPerPixel != 24 && format->BitsPerPixel != 32) - throw std::runtime_error("image has no 24 or 32 bit color depth"); - - this->width = image->w; - this->height = image->h; - - assert_gl("before creating texture"); - glGenTextures(1, &handle); - - try { - GLenum sdl_format; - if(format->BytesPerPixel == 3) - sdl_format = GL_RGB; - else if(format->BytesPerPixel == 4) - sdl_format = GL_RGBA; - else - assert(false); - - glBindTexture(GL_TEXTURE_2D, handle); - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - glPixelStorei(GL_UNPACK_ROW_LENGTH, image->pitch/format->BytesPerPixel); - glTexImage2D(GL_TEXTURE_2D, 0, glformat, width, height, 0, sdl_format, - GL_UNSIGNED_BYTE, image->pixels); - - assert_gl("creating texture"); - - set_texture_params(); - } catch(...) { - glDeleteTextures(1, &handle); - throw; - } -} - -Texture::~Texture() -{ - glDeleteTextures(1, &handle); -} - -void -Texture::set_texture_params() -{ - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); - - assert_gl("set texture params"); -} diff --git a/src/video/texture.hpp b/src/video/texture.hpp index bf87c65df..6e4c462fe 100644 --- a/src/video/texture.hpp +++ b/src/video/texture.hpp @@ -20,8 +20,23 @@ #ifndef __TEXTURE_HPP__ #define __TEXTURE_HPP__ -#include -#include +#include + +#include +#include + +#include "texture_manager.hpp" + +/// bitset for drawing effects +enum DrawingEffect { + /** Don't apply anything */ + NO_EFFECT, + /** Draw the Surface upside down */ + VERTICAL_FLIP, + /** Draw the Surface from left to down */ + HORIZONTAL_FLIP, + NUM_EFFECTS +}; /** * This class is a wrapper around a texture handle. It stores the texture width @@ -31,33 +46,46 @@ class Texture { protected: - friend class TextureManager; - GLuint handle; - unsigned int width; - unsigned int height; + int refcount; + std::string filename; public: - Texture(unsigned int width, unsigned int height, GLenum glformat); - Texture(SDL_Surface* surface, GLenum glformat); - virtual ~Texture(); + Texture() : refcount(0), filename() {} + virtual ~Texture() {} + + virtual unsigned int get_texture_width() const = 0; + virtual unsigned int get_texture_height() const = 0; + virtual unsigned int get_image_width() const = 0; + virtual unsigned int get_image_height() const = 0; - GLuint get_handle() const + std::string get_filename() const { - return handle; + return filename; } - unsigned int get_width() const + void set_filename(std::string filename) { - return width; + this->filename = filename; } - unsigned int get_height() const + void ref() { - return height; + refcount++; + } + + void unref() + { + assert(refcount > 0); + refcount--; + if(refcount == 0) + release(); } private: - void set_texture_params(); + void release() + { + texture_manager->release(this); + } }; #endif diff --git a/src/video/texture_manager.cpp b/src/video/texture_manager.cpp index 6326d0455..7b556a642 100644 --- a/src/video/texture_manager.cpp +++ b/src/video/texture_manager.cpp @@ -24,14 +24,14 @@ #include #include #include -#include -#include #include #include #include #include "physfs/physfs_sdl.hpp" -#include "image_texture.hpp" +#include "gl_texture.hpp" +#include "sdl_texture.hpp" #include "glutil.hpp" +#include "gameconfig.hpp" #include "file_system.hpp" #include "log.hpp" @@ -52,13 +52,13 @@ TextureManager::~TextureManager() } } -ImageTexture* +Texture* TextureManager::get(const std::string& _filename) { std::string filename = FileSystem::normalize(_filename); ImageTextures::iterator i = image_textures.find(filename); - ImageTexture* texture = NULL; + Texture* texture = NULL; if(i != image_textures.end()) texture = i->second; @@ -71,81 +71,60 @@ TextureManager::get(const std::string& _filename) } void -TextureManager::release(ImageTexture* texture) +TextureManager::release(Texture* texture) { - image_textures.erase(texture->filename); + image_textures.erase(texture->get_filename()); delete texture; } +#ifdef HAVE_OPENGL void -TextureManager::register_texture(Texture* texture) +TextureManager::register_texture(GL::Texture* texture) { textures.insert(texture); } void -TextureManager::remove_texture(Texture* texture) +TextureManager::remove_texture(GL::Texture* texture) { textures.erase(texture); } +#endif -static inline int next_power_of_two(int val) -{ - int result = 1; - while(result < val) - result *= 2; - return result; -} - -ImageTexture* +Texture* TextureManager::create_image_texture(const std::string& filename) { SDL_Surface* image = IMG_Load_RW(get_physfs_SDLRWops(filename), 1); - if(image == NULL) { + if(image == 0) { std::ostringstream msg; msg << "Couldn't load image '" << filename << "' :" << SDL_GetError(); throw std::runtime_error(msg.str()); } - int texture_w = next_power_of_two(image->w); - int texture_h = next_power_of_two(image->h); - -#if SDL_BYTEORDER == SDL_BIG_ENDIAN - SDL_Surface* convert = SDL_CreateRGBSurface(SDL_SWSURFACE, - texture_w, texture_h, 32, - 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff); -#else - SDL_Surface* convert = SDL_CreateRGBSurface(SDL_SWSURFACE, - texture_w, texture_h, 32, - 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000); -#endif - - if(convert == 0) { - SDL_FreeSurface(image); - throw std::runtime_error("Couldn't create texture: out of memory"); - } - - SDL_SetAlpha(image, 0, 0); - SDL_BlitSurface(image, 0, convert, 0); - - ImageTexture* result = NULL; + Texture* result = 0; try { - result = new ImageTexture(convert); - result->filename = filename; - result->image_width = image->w; - result->image_height = image->h; +#ifdef HAVE_OPENGL + if(config->video == "opengl") + { + result = new GL::Texture(image); + } + else +#endif + { + result = new SDL::Texture(image); + } + result->set_filename(filename); } catch(...) { delete result; SDL_FreeSurface(image); - SDL_FreeSurface(convert); throw; } SDL_FreeSurface(image); - SDL_FreeSurface(convert); return result; } +#ifdef HAVE_OPENGL void TextureManager::save_textures() { @@ -160,12 +139,12 @@ TextureManager::save_textures() } for(ImageTextures::iterator i = image_textures.begin(); i != image_textures.end(); ++i) { - save_texture(i->second); + save_texture(dynamic_cast(i->second)); } } void -TextureManager::save_texture(Texture* texture) +TextureManager::save_texture(GL::Texture* texture) { SavedTexture saved_texture; saved_texture.texture = texture; @@ -193,8 +172,8 @@ TextureManager::save_texture(Texture* texture) saved_textures.push_back(saved_texture); - glDeleteTextures(1, &(texture->handle)); - texture->handle = 0; + glDeleteTextures(1, &(texture->get_handle())); + texture->set_handle(0); assert_gl("retrieving texture for save"); } @@ -235,8 +214,9 @@ TextureManager::reload_textures() saved_texture.wrap_t); assert_gl("setting texture_params"); - saved_texture.texture->handle = handle; + saved_texture.texture->set_handle(handle); } saved_textures.clear(); } +#endif diff --git a/src/video/texture_manager.hpp b/src/video/texture_manager.hpp index ee75655ed..1431c68ea 100644 --- a/src/video/texture_manager.hpp +++ b/src/video/texture_manager.hpp @@ -20,14 +20,16 @@ #ifndef __IMAGE_TEXTURE_MANAGER_HPP__ #define __IMAGE_TEXTURE_MANAGER_HPP__ -#include +#include + +#include "glutil.hpp" #include #include #include #include class Texture; -class ImageTexture; +namespace GL { class Texture; } class TextureManager { @@ -35,29 +37,32 @@ public: TextureManager(); ~TextureManager(); - ImageTexture* get(const std::string& filename); + Texture* get(const std::string& filename); - void register_texture(Texture* texture); - void remove_texture(Texture* texture); +#ifdef HAVE_OPENGL + void register_texture(GL::Texture* texture); + void remove_texture(GL::Texture* texture); void save_textures(); void reload_textures(); +#endif private: - friend class ImageTexture; - void release(ImageTexture* texture); + friend class Texture; + void release(Texture* texture); - typedef std::map ImageTextures; + typedef std::map ImageTextures; ImageTextures image_textures; - ImageTexture* create_image_texture(const std::string& filename); + Texture* create_image_texture(const std::string& filename); - typedef std::set Textures; +#ifdef HAVE_OPENGL + typedef std::set Textures; Textures textures; struct SavedTexture { - Texture* texture; + GL::Texture* texture; GLint width; GLint height; char* pixels; @@ -70,7 +75,8 @@ private: }; std::vector saved_textures; - void save_texture(Texture* texture); + void save_texture(GL::Texture* texture); +#endif }; extern TextureManager* texture_manager; -- 2.11.0