New grow and skid sounds from remaxim
[supertux.git] / src / video / sdl_lightmap.cpp
1 //  $Id: sdl_lightmap.cpp 5063 2007-05-27 11:32:00Z matzeb $
2 //
3 //  SuperTux
4 //  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
5 //
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.
10 //
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.
15 //
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.
19 #include <config.h>
20
21 #include <functional>
22 #include <algorithm>
23 #include <cassert>
24 #include <iostream>
25 #include <SDL_image.h>
26 #include <sstream>
27 #include <iomanip>
28 #include <physfs.h>
29
30 #include "glutil.hpp"
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"
38 #include "font.hpp"
39 #include "main.hpp"
40 #include "gameconfig.hpp"
41 #include "texture.hpp"
42 #include "texture_manager.hpp"
43 #include "obstack/obstackpp.hpp"
44
45 namespace SDL
46 {
47   Lightmap::Lightmap()
48   {
49     screen = SDL_GetVideoSurface();
50
51     //float xfactor = 1.0f; // FIXME: (float) config->screenwidth / SCREEN_WIDTH;
52     //float yfactor = 1.0f; // FIXME: (float) config->screenheight / SCREEN_HEIGHT;
53
54     numerator = 1;
55     denominator = 1;
56
57     /* FIXME:
58     if(xfactor < yfactor)
59     {
60       numerator = config->screenwidth;
61       denominator = SCREEN_WIDTH;
62     }
63     else
64     {
65       numerator = config->screenheight;
66       denominator = SCREEN_HEIGHT;
67     }
68     */
69
70     LIGHTMAP_DIV = 8 * numerator / denominator;
71
72     width = screen->w / LIGHTMAP_DIV;
73     height = screen->h / LIGHTMAP_DIV;
74
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));
78   }
79
80   Lightmap::~Lightmap()
81   {
82     free(red_channel);
83     free(green_channel);
84     free(blue_channel);
85   }
86
87   void
88   Lightmap::start_draw(const Color &ambient_color)
89   {
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));
93   }
94
95   void
96   Lightmap::end_draw()
97   {
98   }
99
100 //#define BILINEAR
101
102 #ifdef BILINEAR
103   namespace
104   {
105     void merge(Uint8 color[3], Uint8 color0[3], Uint8 color1[3], int rem, int total)
106     {
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;
110     }
111   }
112 #endif
113
114   void
115   Lightmap::do_draw()
116   {
117     // FIXME: This is really slow
118     if(LIGHTMAP_DIV == 1)
119     {
120       int bpp = screen->format->BytesPerPixel;
121       if(SDL_MUSTLOCK(screen))
122       {
123         SDL_LockSurface(screen);
124       }
125       Uint8 *pixel = (Uint8 *) screen->pixels;
126       int loc = 0;
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)
130           {
131             continue;
132           }
133           Uint32 mapped = 0;
134           switch(bpp) {
135             case 1:
136               mapped = *pixel;
137               break;
138             case 2:
139               mapped = *(Uint16 *)pixel;
140               break;
141             case 3:
142 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
143               mapped |= pixel[0] << 16;
144               mapped |= pixel[1] << 8;
145               mapped |= pixel[2] << 0;
146 #else
147               mapped |= pixel[0] << 0;
148               mapped |= pixel[1] << 8;
149               mapped |= pixel[2] << 16;
150 #endif
151               break;
152             case 4:
153               mapped = *(Uint32 *)pixel;
154               break;
155           }
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);
162           switch(bpp) {
163             case 1:
164               *pixel = mapped;
165               break;
166             case 2:
167               *(Uint16 *)pixel = mapped;
168               break;
169             case 3:
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;
174 #else
175               pixel[0] = (mapped >> 0) & 0xff;
176               pixel[1] = (mapped >> 8) & 0xff;
177               pixel[2] = (mapped >> 16) & 0xff;
178 #endif
179               break;
180             case 4:
181               *(Uint32 *)pixel = mapped;
182               break;
183           }
184         }
185         pixel += screen->pitch - width * bpp;
186       }
187       if(SDL_MUSTLOCK(screen))
188       {
189         SDL_UnlockSurface(screen);
190       }
191     }
192     else
193     {
194       int bpp = screen->format->BytesPerPixel;
195       if(SDL_MUSTLOCK(screen))
196       {
197         SDL_LockSurface(screen);
198       }
199       Uint8 *div_pixel = (Uint8 *) screen->pixels;
200       int loc = 0;
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)
204           {
205             continue;
206           }
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++) {
210               Uint32 mapped = 0;
211               switch(bpp) {
212                 case 1:
213                   mapped = *pixel;
214                   break;
215                 case 2:
216                   mapped = *(Uint16 *)pixel;
217                   break;
218                 case 3:
219 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
220                   mapped |= pixel[0] << 16;
221                   mapped |= pixel[1] << 8;
222                   mapped |= pixel[2] << 0;
223 #else
224                   mapped |= pixel[0] << 0;
225                   mapped |= pixel[1] << 8;
226                   mapped |= pixel[2] << 16;
227 #endif
228                   break;
229                 case 4:
230                   mapped = *(Uint32 *)pixel;
231                   break;
232               }
233               Uint8 red, green, blue, alpha;
234               SDL_GetRGBA(mapped, screen->format, &red, &green, &blue, &alpha);
235
236 #ifdef BILINEAR
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];
240               {
241                 color00[0] = red_channel[loc];
242                 color00[1] = green_channel[loc];
243                 color00[2] = blue_channel[loc];
244               }
245               {
246                 color01[0] = red_channel[loc + xinc];
247                 color01[1] = green_channel[loc + xinc];
248                 color01[2] = blue_channel[loc + xinc];
249               }
250               {
251                 color10[0] = red_channel[loc + yinc];
252                 color10[1] = green_channel[loc + yinc];
253                 color10[2] = blue_channel[loc + yinc];
254               }
255               {
256                 color11[0] = red_channel[loc + yinc + xinc];
257                 color11[1] = green_channel[loc + yinc + xinc];
258                 color11[2] = blue_channel[loc + yinc + xinc];
259               }
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;
267 #else
268               red = (red * red_channel[loc]) >> 8;
269               green = (green * green_channel[loc]) >> 8;
270               blue = (blue * blue_channel[loc]) >> 8;
271 #endif
272
273               mapped = SDL_MapRGBA(screen->format, red, green, blue, alpha);
274               switch(bpp) {
275                 case 1:
276                   *pixel = mapped;
277                   break;
278                 case 2:
279                   *(Uint16 *)pixel = mapped;
280                   break;
281                 case 3:
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;
286 #else
287                   pixel[0] = (mapped >> 0) & 0xff;
288                   pixel[1] = (mapped >> 8) & 0xff;
289                   pixel[2] = (mapped >> 16) & 0xff;
290 #endif
291                   break;
292                 case 4:
293                   *(Uint32 *)pixel = mapped;
294                   break;
295               }
296             }
297             pixel += screen->pitch - LIGHTMAP_DIV * bpp;
298           }
299         }
300         div_pixel += (screen->pitch - width * bpp) * LIGHTMAP_DIV;
301       }
302       if(SDL_MUSTLOCK(screen))
303       {
304         SDL_UnlockSurface(screen);
305       }
306     }
307   }
308
309   void Lightmap::light_blit(SDL_Surface *src, SDL_Rect *src_rect, int dstx, int dsty)
310   {
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))
319     {
320       SDL_LockSurface(src);
321     }
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)
327         {
328           continue;
329         }
330         if(red_channel[loc] == 0xff && green_channel[loc] == 0xff && blue_channel[loc] == 0xff)
331         {
332           continue;
333         }
334
335         Uint32 mapped = 0;
336         switch(bpp) {
337           case 1:
338             mapped = *pixel;
339             break;
340           case 2:
341             mapped = *(Uint16 *)pixel;
342             break;
343           case 3:
344 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
345             mapped |= pixel[0] << 16;
346             mapped |= pixel[1] << 8;
347             mapped |= pixel[2] << 0;
348 #else
349             mapped |= pixel[0] << 0;
350             mapped |= pixel[1] << 8;
351             mapped |= pixel[2] << 16;
352 #endif
353             break;
354           case 4:
355             mapped = *(Uint32 *)pixel;
356             break;
357         }
358         Uint8 red, green, blue, alpha;
359         SDL_GetRGBA(mapped, src->format, &red, &green, &blue, &alpha);
360
361         if(red != 0)
362         {
363           int redsum = red_channel[loc] + (red * alpha >> 8);
364           red_channel[loc] = redsum & ~0xff ? 0xff : redsum;
365         }
366         if(green != 0)
367         {
368           int greensum = green_channel[loc] + (green * alpha >> 8);
369           green_channel[loc] = greensum & ~0xff ? 0xff : greensum;
370         }
371         if(blue != 0)
372         {
373           int bluesum = blue_channel[loc] + (blue * alpha >> 8);
374           blue_channel[loc] = bluesum & ~0xff ? 0xff : bluesum;
375         }
376       }
377       pixel += (src->pitch - blit_width * bpp) * LIGHTMAP_DIV;
378       loc += width - blit_width;
379     }
380     if(SDL_MUSTLOCK(src))
381     {
382       SDL_UnlockSurface(src);
383     }
384   }
385
386   /*void Lightmap::light_blit(SDL_Surface *src, SDL_Rect *src_rect, int dstx, int dsty)
387   {
388     int bpp = src->format->BytesPerPixel;
389     if(SDL_MUSTLOCK(src))
390     {
391       SDL_LockSurface(src);
392     }
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)
398         {
399           continue;
400         }
401         if(red_channel[loc] == 0xff && green_channel[loc] == 0xff && blue_channel[loc] == 0xff)
402         {
403           continue;
404         }
405
406         Uint32 mapped = 0;
407         switch(bpp) {
408           case 1:
409             mapped = *pixel;
410             break;
411           case 2:
412             mapped = *(Uint16 *)pixel;
413             break;
414           case 3:
415 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
416             mapped |= pixel[0] << 16;
417             mapped |= pixel[1] << 8;
418             mapped |= pixel[2] << 0;
419 #else
420             mapped |= pixel[0] << 0;
421             mapped |= pixel[1] << 8;
422             mapped |= pixel[2] << 16;
423 #endif
424             break;
425           case 4:
426             mapped = *(Uint32 *)pixel;
427             break;
428         }
429         Uint8 red, green, blue, alpha;
430         SDL_GetRGBA(mapped, src->format, &red, &green, &blue, &alpha);
431
432         if(red != 0)
433         {
434           int redsum = red_channel[loc] + (red * alpha >> 8);
435           red_channel[loc] = redsum & ~0xff ? 0xff : redsum;
436         }
437         if(green != 0)
438         {
439           int greensum = green_channel[loc] + (green * alpha >> 8);
440           green_channel[loc] = greensum & ~0xff ? 0xff : greensum;
441         }
442         if(blue != 0)
443         {
444           int bluesum = blue_channel[loc] + (blue * alpha >> 8);
445           blue_channel[loc] = bluesum & ~0xff ? 0xff : bluesum;
446         }
447       }
448       pixel += src->pitch - src_rect->w * bpp;
449       loc += width - src_rect->w;
450     }
451     if(SDL_MUSTLOCK(src))
452     {
453       SDL_UnlockSurface(src);
454     }
455   }*/
456
457   void
458   Lightmap::draw_surface(const DrawingRequest& request)
459   {
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)
461     {
462       return;
463     }
464     //FIXME: support parameters request.alpha, request.angle, request.blend
465  
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());
469
470     DrawingEffect effect = request.drawing_effect;
471     if (surface->get_flipx()) effect = HORIZONTAL_FLIP;
472
473     SDL_Surface *transform = sdltexture->get_transform(request.color, effect);
474
475     // get and check SDL_Surface
476     if (transform == 0) {
477       std::cerr << "Warning: Tried to draw NULL surface, skipped draw" << std::endl;
478       return;
479     }
480
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);
485   }
486
487   void
488   Lightmap::draw_surface_part(const DrawingRequest& request)
489   {
490     const SurfacePartRequest* surfacepartrequest
491       = (SurfacePartRequest*) request.request_data;
492
493     const Surface* surface = surfacepartrequest->surface;
494     SDL::Texture *sdltexture = dynamic_cast<SDL::Texture *>(surface->get_texture());
495
496     DrawingEffect effect = request.drawing_effect;
497     if (surface->get_flipx()) effect = HORIZONTAL_FLIP;
498
499     SDL_Surface *transform = sdltexture->get_transform(Color(1.0, 1.0, 1.0), effect);
500
501     // get and check SDL_Surface
502     if (transform == 0) {
503       std::cerr << "Warning: Tried to draw NULL surface, skipped draw" << std::endl;
504       return;
505     }
506
507     int ox, oy;
508     if (effect == HORIZONTAL_FLIP)
509     {
510       ox = sdltexture->get_texture_width() - surface->get_x() - (int) surfacepartrequest->size.x;
511     }
512     else
513     {
514       ox = surface->get_x();
515     }
516     if (effect == VERTICAL_FLIP)
517     {
518       oy = sdltexture->get_texture_height() - surface->get_y() - (int) surfacepartrequest->size.y;
519     }
520     else
521     {
522       oy = surface->get_y();
523     }
524
525     SDL_Rect src_rect;
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);
533   }
534
535   void
536   Lightmap::draw_gradient(const DrawingRequest& request)
537   {
538     const GradientRequest* gradientrequest 
539       = (GradientRequest*) request.request_data;
540     const Color& top = gradientrequest->top;
541     const Color& bottom = gradientrequest->bottom;
542
543     int loc = 0;
544     for(int y = 0;y < height;++y)
545     {
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++) {
551         if(red != 0)
552         {
553           int redsum = red_channel[loc] + (red * alpha >> 8);
554           red_channel[loc] = redsum & ~0xff ? 0xff : redsum;
555         }
556         if(green != 0)
557         {
558           int greensum = green_channel[loc] + (green * alpha >> 8);
559           green_channel[loc] = greensum & ~0xff ? 0xff : greensum;
560         }
561         if(blue != 0)
562         {
563           int bluesum = blue_channel[loc] + (blue * alpha >> 8);
564           blue_channel[loc] = bluesum & ~0xff ? 0xff : bluesum;
565         }
566       }
567     }
568   }
569
570   void
571   Lightmap::draw_filled_rect(const DrawingRequest& request)
572   {
573     const FillRectRequest* fillrectrequest
574       = (FillRectRequest*) request.request_data;
575
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)
584     {
585       return;
586     }
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;
590         if(red != 0)
591         {
592           int redsum = red_channel[loc] + red;
593           red_channel[loc] = redsum & ~0xff ? 0xff : redsum;
594         }
595         if(green != 0)
596         {
597           int greensum = green_channel[loc] + green;
598           green_channel[loc] = greensum & ~0xff ? 0xff : greensum;
599         }
600         if(blue != 0)
601         {
602           int bluesum = blue_channel[loc] + blue;
603           blue_channel[loc] = bluesum & ~0xff ? 0xff : bluesum;
604         }
605       }
606     }
607   }
608
609   void
610   Lightmap::get_light(const DrawingRequest& request) const
611   {
612     const GetLightRequest* getlightrequest 
613       = (GetLightRequest*) request.request_data;
614
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);
619   }
620 }