1 // $Id: sdl_lightmap.cpp 5063 2007-05-27 11:32:00Z matzeb $
4 // Copyright (C) 2006 Matthias Braun <matze@braunis.de>
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25 #include <SDL_image.h>
31 #include "sdl_lightmap.hpp"
32 #include "sdl_texture.hpp"
33 #include "sdl_surface_data.hpp"
34 #include "drawing_context.hpp"
35 #include "drawing_request.hpp"
36 #include "renderer.hpp"
37 #include "surface.hpp"
40 #include "gameconfig.hpp"
41 #include "texture.hpp"
42 #include "texture_manager.hpp"
43 #include "obstack/obstackpp.hpp"
49 screen = SDL_GetVideoSurface();
51 //float xfactor = 1.0f; // FIXME: (float) config->screenwidth / SCREEN_WIDTH;
52 //float yfactor = 1.0f; // FIXME: (float) config->screenheight / SCREEN_HEIGHT;
60 numerator = config->screenwidth;
61 denominator = SCREEN_WIDTH;
65 numerator = config->screenheight;
66 denominator = SCREEN_HEIGHT;
70 LIGHTMAP_DIV = 8 * numerator / denominator;
72 width = screen->w / LIGHTMAP_DIV;
73 height = screen->h / LIGHTMAP_DIV;
75 red_channel = (Uint8 *)malloc(width * height * sizeof(Uint8));
76 green_channel = (Uint8 *)malloc(width * height * sizeof(Uint8));
77 blue_channel = (Uint8 *)malloc(width * height * sizeof(Uint8));
88 Lightmap::start_draw(const Color &ambient_color)
90 memset(red_channel, (Uint8) (ambient_color.red * 255), width * height * sizeof(Uint8));
91 memset(green_channel, (Uint8) (ambient_color.green * 255), width * height * sizeof(Uint8));
92 memset(blue_channel, (Uint8) (ambient_color.blue * 255), width * height * sizeof(Uint8));
105 void merge(Uint8 color[3], Uint8 color0[3], Uint8 color1[3], int rem, int total)
107 color[0] = (color0[0] * (total - rem) + color1[0] * rem) / total;
108 color[1] = (color0[1] * (total - rem) + color1[1] * rem) / total;
109 color[2] = (color0[2] * (total - rem) + color1[2] * rem) / total;
117 // FIXME: This is really slow
118 if(LIGHTMAP_DIV == 1)
120 int bpp = screen->format->BytesPerPixel;
121 if(SDL_MUSTLOCK(screen))
123 SDL_LockSurface(screen);
125 Uint8 *pixel = (Uint8 *) screen->pixels;
127 for(int y = 0;y < height;y++) {
128 for(int x = 0;x < width;x++, pixel += bpp, loc++) {
129 if(red_channel[loc] == 0xff && green_channel[loc] == 0xff && blue_channel[loc] == 0xff)
139 mapped = *(Uint16 *)pixel;
142 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
143 mapped |= pixel[0] << 16;
144 mapped |= pixel[1] << 8;
145 mapped |= pixel[2] << 0;
147 mapped |= pixel[0] << 0;
148 mapped |= pixel[1] << 8;
149 mapped |= pixel[2] << 16;
153 mapped = *(Uint32 *)pixel;
156 Uint8 red, green, blue, alpha;
157 SDL_GetRGBA(mapped, screen->format, &red, &green, &blue, &alpha);
158 red = (red * red_channel[loc]) >> 8;
159 green = (green * green_channel[loc]) >> 8;
160 blue = (blue * blue_channel[loc]) >> 8;
161 mapped = SDL_MapRGBA(screen->format, red, green, blue, alpha);
167 *(Uint16 *)pixel = mapped;
170 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
171 pixel[0] = (mapped >> 16) & 0xff;
172 pixel[1] = (mapped >> 8) & 0xff;
173 pixel[2] = (mapped >> 0) & 0xff;
175 pixel[0] = (mapped >> 0) & 0xff;
176 pixel[1] = (mapped >> 8) & 0xff;
177 pixel[2] = (mapped >> 16) & 0xff;
181 *(Uint32 *)pixel = mapped;
185 pixel += screen->pitch - width * bpp;
187 if(SDL_MUSTLOCK(screen))
189 SDL_UnlockSurface(screen);
194 int bpp = screen->format->BytesPerPixel;
195 if(SDL_MUSTLOCK(screen))
197 SDL_LockSurface(screen);
199 Uint8 *div_pixel = (Uint8 *) screen->pixels;
201 for(int y = 0;y < height;y++) {
202 for(int x = 0;x < width;x++, div_pixel += bpp * LIGHTMAP_DIV, loc++) {
203 if(red_channel[loc] == 0xff && green_channel[loc] == 0xff && blue_channel[loc] == 0xff)
207 Uint8 *pixel = div_pixel;
208 for(int div_y = 0;div_y < LIGHTMAP_DIV;div_y++) {
209 for(int div_x = 0;div_x < LIGHTMAP_DIV;pixel += bpp, div_x++) {
216 mapped = *(Uint16 *)pixel;
219 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
220 mapped |= pixel[0] << 16;
221 mapped |= pixel[1] << 8;
222 mapped |= pixel[2] << 0;
224 mapped |= pixel[0] << 0;
225 mapped |= pixel[1] << 8;
226 mapped |= pixel[2] << 16;
230 mapped = *(Uint32 *)pixel;
233 Uint8 red, green, blue, alpha;
234 SDL_GetRGBA(mapped, screen->format, &red, &green, &blue, &alpha);
237 int xinc = (x + 1 != width ? 1 : 0);
238 int yinc = (y + 1 != height ? width : 0);
239 Uint8 color00[3], color01[3], color10[3], color11[3];
241 color00[0] = red_channel[loc];
242 color00[1] = green_channel[loc];
243 color00[2] = blue_channel[loc];
246 color01[0] = red_channel[loc + xinc];
247 color01[1] = green_channel[loc + xinc];
248 color01[2] = blue_channel[loc + xinc];
251 color10[0] = red_channel[loc + yinc];
252 color10[1] = green_channel[loc + yinc];
253 color10[2] = blue_channel[loc + yinc];
256 color11[0] = red_channel[loc + yinc + xinc];
257 color11[1] = green_channel[loc + yinc + xinc];
258 color11[2] = blue_channel[loc + yinc + xinc];
260 Uint8 color0[3], color1[3], color[3];
261 merge(color0, color00, color01, div_x, LIGHTMAP_DIV);
262 merge(color1, color10, color11, div_x, LIGHTMAP_DIV);
263 merge(color, color0, color1, div_y, LIGHTMAP_DIV);
264 red = (red * color[0]) >> 8;
265 green = (green * color[1]) >> 8;
266 blue = (blue * color[2]) >> 8;
268 red = (red * red_channel[loc]) >> 8;
269 green = (green * green_channel[loc]) >> 8;
270 blue = (blue * blue_channel[loc]) >> 8;
273 mapped = SDL_MapRGBA(screen->format, red, green, blue, alpha);
279 *(Uint16 *)pixel = mapped;
282 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
283 pixel[0] = (mapped >> 16) & 0xff;
284 pixel[1] = (mapped >> 8) & 0xff;
285 pixel[2] = (mapped >> 0) & 0xff;
287 pixel[0] = (mapped >> 0) & 0xff;
288 pixel[1] = (mapped >> 8) & 0xff;
289 pixel[2] = (mapped >> 16) & 0xff;
293 *(Uint32 *)pixel = mapped;
297 pixel += screen->pitch - LIGHTMAP_DIV * bpp;
300 div_pixel += (screen->pitch - width * bpp) * LIGHTMAP_DIV;
302 if(SDL_MUSTLOCK(screen))
304 SDL_UnlockSurface(screen);
309 void Lightmap::light_blit(SDL_Surface *src, SDL_Rect *src_rect, int dstx, int dsty)
311 dstx /= LIGHTMAP_DIV;
312 dsty /= LIGHTMAP_DIV;
313 int srcx = src_rect->x / LIGHTMAP_DIV;
314 int srcy = src_rect->y / LIGHTMAP_DIV;
315 int blit_width = src_rect->w / LIGHTMAP_DIV;
316 int blit_height = src_rect->h / LIGHTMAP_DIV;
317 int bpp = src->format->BytesPerPixel;
318 if(SDL_MUSTLOCK(src))
320 SDL_LockSurface(src);
322 Uint8 *pixel = (Uint8 *) src->pixels + srcy * src->pitch + srcx * bpp;
323 int loc = dsty * width + dstx;
324 for(int y = 0;y < blit_height;y++) {
325 for(int x = 0;x < blit_width;x++, pixel += bpp * LIGHTMAP_DIV, loc++) {
326 if(x + dstx < 0 || y + dsty < 0 || x + dstx >= width || y + dsty >= height)
330 if(red_channel[loc] == 0xff && green_channel[loc] == 0xff && blue_channel[loc] == 0xff)
341 mapped = *(Uint16 *)pixel;
344 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
345 mapped |= pixel[0] << 16;
346 mapped |= pixel[1] << 8;
347 mapped |= pixel[2] << 0;
349 mapped |= pixel[0] << 0;
350 mapped |= pixel[1] << 8;
351 mapped |= pixel[2] << 16;
355 mapped = *(Uint32 *)pixel;
358 Uint8 red, green, blue, alpha;
359 SDL_GetRGBA(mapped, src->format, &red, &green, &blue, &alpha);
363 int redsum = red_channel[loc] + (red * alpha >> 8);
364 red_channel[loc] = redsum & ~0xff ? 0xff : redsum;
368 int greensum = green_channel[loc] + (green * alpha >> 8);
369 green_channel[loc] = greensum & ~0xff ? 0xff : greensum;
373 int bluesum = blue_channel[loc] + (blue * alpha >> 8);
374 blue_channel[loc] = bluesum & ~0xff ? 0xff : bluesum;
377 pixel += (src->pitch - blit_width * bpp) * LIGHTMAP_DIV;
378 loc += width - blit_width;
380 if(SDL_MUSTLOCK(src))
382 SDL_UnlockSurface(src);
386 /*void Lightmap::light_blit(SDL_Surface *src, SDL_Rect *src_rect, int dstx, int dsty)
388 int bpp = src->format->BytesPerPixel;
389 if(SDL_MUSTLOCK(src))
391 SDL_LockSurface(src);
393 Uint8 *pixel = (Uint8 *) src->pixels + src_rect->y * src->pitch + src_rect->x * bpp;
394 int loc = dsty * width + dstx;
395 for(int y = 0;y < src_rect->h;y++) {
396 for(int x = 0;x < src_rect->w;x++, pixel += bpp, loc++) {
397 if(x + dstx < 0 || y + dsty < 0 || x + dstx >= width || y + dsty >= height)
401 if(red_channel[loc] == 0xff && green_channel[loc] == 0xff && blue_channel[loc] == 0xff)
412 mapped = *(Uint16 *)pixel;
415 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
416 mapped |= pixel[0] << 16;
417 mapped |= pixel[1] << 8;
418 mapped |= pixel[2] << 0;
420 mapped |= pixel[0] << 0;
421 mapped |= pixel[1] << 8;
422 mapped |= pixel[2] << 16;
426 mapped = *(Uint32 *)pixel;
429 Uint8 red, green, blue, alpha;
430 SDL_GetRGBA(mapped, src->format, &red, &green, &blue, &alpha);
434 int redsum = red_channel[loc] + (red * alpha >> 8);
435 red_channel[loc] = redsum & ~0xff ? 0xff : redsum;
439 int greensum = green_channel[loc] + (green * alpha >> 8);
440 green_channel[loc] = greensum & ~0xff ? 0xff : greensum;
444 int bluesum = blue_channel[loc] + (blue * alpha >> 8);
445 blue_channel[loc] = bluesum & ~0xff ? 0xff : bluesum;
448 pixel += src->pitch - src_rect->w * bpp;
449 loc += width - src_rect->w;
451 if(SDL_MUSTLOCK(src))
453 SDL_UnlockSurface(src);
458 Lightmap::draw_surface(const DrawingRequest& request)
460 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)
464 //FIXME: support parameters request.alpha, request.angle, request.blend
466 const Surface* surface = (const Surface*) request.request_data;
467 SDL::Texture *sdltexture = dynamic_cast<SDL::Texture *>(surface->get_texture());
468 SDL::SurfaceData *surface_data = reinterpret_cast<SDL::SurfaceData *>(surface->get_surface_data());
470 DrawingEffect effect = request.drawing_effect;
471 if (surface->get_flipx()) effect = HORIZONTAL_FLIP;
473 SDL_Surface *transform = sdltexture->get_transform(request.color, effect);
475 // get and check SDL_Surface
476 if (transform == 0) {
477 std::cerr << "Warning: Tried to draw NULL surface, skipped draw" << std::endl;
481 SDL_Rect *src_rect = surface_data->get_src_rect(effect);
482 int dstx = (int) request.pos.x * numerator / denominator;
483 int dsty = (int) request.pos.y * numerator / denominator;
484 light_blit(transform, src_rect, dstx, dsty);
488 Lightmap::draw_surface_part(const DrawingRequest& request)
490 const SurfacePartRequest* surfacepartrequest
491 = (SurfacePartRequest*) request.request_data;
493 const Surface* surface = surfacepartrequest->surface;
494 SDL::Texture *sdltexture = dynamic_cast<SDL::Texture *>(surface->get_texture());
496 DrawingEffect effect = request.drawing_effect;
497 if (surface->get_flipx()) effect = HORIZONTAL_FLIP;
499 SDL_Surface *transform = sdltexture->get_transform(Color(1.0, 1.0, 1.0), effect);
501 // get and check SDL_Surface
502 if (transform == 0) {
503 std::cerr << "Warning: Tried to draw NULL surface, skipped draw" << std::endl;
508 if (effect == HORIZONTAL_FLIP)
510 ox = sdltexture->get_texture_width() - surface->get_x() - (int) surfacepartrequest->size.x;
514 ox = surface->get_x();
516 if (effect == VERTICAL_FLIP)
518 oy = sdltexture->get_texture_height() - surface->get_y() - (int) surfacepartrequest->size.y;
522 oy = surface->get_y();
526 src_rect.x = (ox + (int) surfacepartrequest->source.x) * numerator / denominator;
527 src_rect.y = (oy + (int) surfacepartrequest->source.y) * numerator / denominator;
528 src_rect.w = (int) surfacepartrequest->size.x * numerator / denominator;
529 src_rect.h = (int) surfacepartrequest->size.y * numerator / denominator;
530 int dstx = (int) request.pos.x * numerator / denominator;
531 int dsty = (int) request.pos.y * numerator / denominator;
532 light_blit(transform, &src_rect, dstx, dsty);
536 Lightmap::draw_gradient(const DrawingRequest& request)
538 const GradientRequest* gradientrequest
539 = (GradientRequest*) request.request_data;
540 const Color& top = gradientrequest->top;
541 const Color& bottom = gradientrequest->bottom;
544 for(int y = 0;y < height;++y)
546 Uint8 red = (Uint8)((((float)(top.red-bottom.red)/(0-height)) * y + top.red) * 255);
547 Uint8 green = (Uint8)((((float)(top.green-bottom.green)/(0-height)) * y + top.green) * 255);
548 Uint8 blue = (Uint8)((((float)(top.blue-bottom.blue)/(0-height)) * y + top.blue) * 255);
549 Uint8 alpha = (Uint8)((((float)(top.alpha-bottom.alpha)/(0-height)) * y + top.alpha) * 255);
550 for(int x = 0;x < width;x++, loc++) {
553 int redsum = red_channel[loc] + (red * alpha >> 8);
554 red_channel[loc] = redsum & ~0xff ? 0xff : redsum;
558 int greensum = green_channel[loc] + (green * alpha >> 8);
559 green_channel[loc] = greensum & ~0xff ? 0xff : greensum;
563 int bluesum = blue_channel[loc] + (blue * alpha >> 8);
564 blue_channel[loc] = bluesum & ~0xff ? 0xff : bluesum;
571 Lightmap::draw_filled_rect(const DrawingRequest& request)
573 const FillRectRequest* fillrectrequest
574 = (FillRectRequest*) request.request_data;
576 int rect_x = (int) (request.pos.x * width / SCREEN_WIDTH);
577 int rect_y = (int) (request.pos.y * height / SCREEN_HEIGHT);
578 int rect_w = (int) (fillrectrequest->size.x * width / SCREEN_WIDTH);
579 int rect_h = (int) (fillrectrequest->size.y * height / SCREEN_HEIGHT);
580 Uint8 red = (Uint8) (fillrectrequest->color.red * fillrectrequest->color.alpha * 255);
581 Uint8 green = (Uint8) (fillrectrequest->color.green * fillrectrequest->color.alpha * 255);
582 Uint8 blue = (Uint8) (fillrectrequest->color.blue * fillrectrequest->color.alpha * 255);
583 if(red == 0 && green == 0 && blue == 0)
587 for(int y = rect_y;y < rect_y + rect_h;y++) {
588 for(int x = rect_x;x < rect_x + rect_w;x++) {
589 int loc = y * width + x;
592 int redsum = red_channel[loc] + red;
593 red_channel[loc] = redsum & ~0xff ? 0xff : redsum;
597 int greensum = green_channel[loc] + green;
598 green_channel[loc] = greensum & ~0xff ? 0xff : greensum;
602 int bluesum = blue_channel[loc] + blue;
603 blue_channel[loc] = bluesum & ~0xff ? 0xff : bluesum;
610 Lightmap::get_light(const DrawingRequest& request) const
612 const GetLightRequest* getlightrequest
613 = (GetLightRequest*) request.request_data;
615 int x = (int) (request.pos.x * width / SCREEN_WIDTH);
616 int y = (int) (request.pos.y * height / SCREEN_HEIGHT);
617 int loc = y * width + x;
618 *(getlightrequest->color_ptr) = Color(((float)red_channel[loc])/255, ((float)green_channel[loc])/255, ((float)blue_channel[loc])/255);