014fb736c0935ef4cbf349500df982052502e0e1
[supertux.git] / src / src / video / gl_lightmap.cpp
1 //  $Id: gl_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 #ifdef HAVE_OPENGL
22
23 #include <functional>
24 #include <algorithm>
25 #include <cassert>
26 #include <math.h>
27 #include <iostream>
28 #include <SDL_image.h>
29 #include <sstream>
30 #include <iomanip>
31 #include <physfs.h>
32
33 #include "glutil.hpp"
34 #include "gl_lightmap.hpp"
35 #include "gl_surface_data.hpp"
36 #include "drawing_context.hpp"
37 #include "drawing_request.hpp"
38 #include "renderer.hpp"
39 #include "surface.hpp"
40 #include "font.hpp"
41 #include "main.hpp"
42 #include "gameconfig.hpp"
43 #include "gl_texture.hpp"
44 #include "texture_manager.hpp"
45 #include "obstack/obstackpp.hpp"
46
47 namespace
48 {
49   inline void intern_draw(float left, float top, float right, float bottom,
50                                   float uv_left, float uv_top,
51                                   float uv_right, float uv_bottom,
52                                   float angle, float alpha,
53                                   const Color& color,
54                                   const Blend& blend,
55                                   DrawingEffect effect)
56   {
57     if(effect & HORIZONTAL_FLIP)
58       std::swap(uv_left, uv_right);
59     if(effect & VERTICAL_FLIP) {
60       std::swap(uv_top, uv_bottom);
61     }
62
63     float center_x = (left + right) / 2;
64     float center_y = (top + bottom) / 2;
65
66     float sa = sinf(angle/180.0f*M_PI);
67     float ca = cosf(angle/180.0f*M_PI);
68
69     left  -= center_x;
70     right -= center_x;
71
72     top    -= center_y;
73     bottom -= center_y;
74
75     glBlendFunc(blend.sfactor, blend.dfactor);
76     glColor4f(color.red, color.green, color.blue, color.alpha * alpha);
77     glBegin(GL_QUADS);
78     glTexCoord2f(uv_left, uv_top);
79     glVertex2f(left*ca - top*sa + center_x,
80                left*sa + top*ca + center_y);
81
82     glTexCoord2f(uv_right, uv_top);
83     glVertex2f(right*ca - top*sa + center_x,
84                right*sa + top*ca + center_y);
85
86     glTexCoord2f(uv_right, uv_bottom);
87     glVertex2f(right*ca - bottom*sa + center_x,
88                right*sa + bottom*ca + center_y);
89
90     glTexCoord2f(uv_left, uv_bottom);
91     glVertex2f(left*ca - bottom*sa + center_x,
92                left*sa + bottom*ca + center_y);
93     glEnd();
94
95     // FIXME: find a better way to restore the blend mode
96     glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
97     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
98   }
99 }
100
101 namespace GL
102 {
103   static inline int next_po2(int val)
104   {
105     int result = 1;
106     while(result < val)
107       result *= 2;
108
109     return result;
110   }
111
112   Lightmap::Lightmap()
113   {
114     screen = SDL_GetVideoSurface();
115
116     lightmap_width = screen->w / LIGHTMAP_DIV;
117     lightmap_height = screen->h / LIGHTMAP_DIV;
118     unsigned int width = next_po2(lightmap_width);
119     unsigned int height = next_po2(lightmap_height);
120
121     lightmap = new Texture(width, height);
122
123     lightmap_uv_right = static_cast<float>(lightmap_width) / static_cast<float>(width);
124     lightmap_uv_bottom = static_cast<float>(lightmap_height) / static_cast<float>(height);
125     texture_manager->register_texture(lightmap);
126   }
127
128   Lightmap::~Lightmap()
129   {
130     texture_manager->remove_texture(lightmap);
131     delete lightmap;
132   }
133
134   void
135   Lightmap::start_draw(const Color &ambient_color)
136   {
137     glViewport(0, screen->h - lightmap_height, lightmap_width, lightmap_height);
138     glMatrixMode(GL_PROJECTION);
139     glLoadIdentity();
140     glOrtho(0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, -1.0, 1.0);
141     glMatrixMode(GL_MODELVIEW);
142     glLoadIdentity();
143
144     glClearColor( ambient_color.red, ambient_color.green, ambient_color.blue, 1 );
145     glClear(GL_COLOR_BUFFER_BIT);
146   }
147
148   void
149   Lightmap::end_draw()
150   {
151     glDisable(GL_BLEND);
152     glBindTexture(GL_TEXTURE_2D, lightmap->get_handle());
153     glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, screen->h - lightmap_height, lightmap_width, lightmap_height);
154
155     glViewport(0, 0, screen->w, screen->h);
156     glMatrixMode(GL_PROJECTION);
157     glLoadIdentity();
158     glOrtho(0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, -1.0, 1.0);
159     glMatrixMode(GL_MODELVIEW);
160     glLoadIdentity();
161     glEnable(GL_BLEND);
162     //glClear(GL_COLOR_BUFFER_BIT);
163   }
164
165   void
166   Lightmap::do_draw()
167   {
168     const Texture* texture = lightmap;
169
170     // multiple the lightmap with the framebuffer
171     glBlendFunc(GL_DST_COLOR, GL_ZERO);
172
173     glBindTexture(GL_TEXTURE_2D, texture->get_handle());
174     glBegin(GL_QUADS);
175
176     glTexCoord2f(0, lightmap_uv_bottom);
177     glVertex2f(0, 0);
178
179     glTexCoord2f(lightmap_uv_right, lightmap_uv_bottom);
180     glVertex2f(SCREEN_WIDTH, 0);
181
182     glTexCoord2f(lightmap_uv_right, 0);
183     glVertex2f(SCREEN_WIDTH, SCREEN_HEIGHT);
184
185     glTexCoord2f(0, 0);
186     glVertex2f(0, SCREEN_HEIGHT);
187
188     glEnd();
189
190     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
191   }
192
193   void
194   Lightmap::draw_surface(const DrawingRequest& request)
195   {
196     const Surface* surface = (const Surface*) request.request_data;
197     GL::Texture *gltexture = dynamic_cast<GL::Texture *>(surface->get_texture());
198     GL::SurfaceData *surface_data = reinterpret_cast<GL::SurfaceData *>(surface->get_surface_data());
199
200     glBindTexture(GL_TEXTURE_2D, gltexture->get_handle());
201     intern_draw(request.pos.x, request.pos.y,
202                 request.pos.x + surface->get_width(),
203                 request.pos.y + surface->get_height(),
204                 surface_data->get_uv_left(),
205                 surface_data->get_uv_top(),
206                 surface_data->get_uv_right(),
207                 surface_data->get_uv_bottom(),
208                 request.angle,
209                 request.alpha,
210                 request.color,
211                 request.blend,
212                 request.drawing_effect);
213   }
214
215   void
216   Lightmap::draw_surface_part(const DrawingRequest& request)
217   {
218     const SurfacePartRequest* surfacepartrequest
219       = (SurfacePartRequest*) request.request_data;
220     const Surface *surface = surfacepartrequest->surface;
221     GL::Texture *gltexture = dynamic_cast<GL::Texture *>(surface->get_texture());
222     GL::SurfaceData *surface_data = reinterpret_cast<GL::SurfaceData *>(surface->get_surface_data());
223
224     float uv_width = surface_data->get_uv_right() - surface_data->get_uv_left();
225     float uv_height = surface_data->get_uv_bottom() - surface_data->get_uv_top();
226
227     float uv_left = surface_data->get_uv_left() + (uv_width * surfacepartrequest->source.x) / surface->get_width();
228     float uv_top = surface_data->get_uv_top() + (uv_height * surfacepartrequest->source.y) / surface->get_height();
229     float uv_right = surface_data->get_uv_left() + (uv_width * (surfacepartrequest->source.x + surfacepartrequest->size.x)) / surface->get_width();
230     float uv_bottom = surface_data->get_uv_top() + (uv_height * (surfacepartrequest->source.y + surfacepartrequest->size.y)) / surface->get_height();
231
232     glBindTexture(GL_TEXTURE_2D, gltexture->get_handle());
233     intern_draw(request.pos.x, request.pos.y,
234                 request.pos.x + surfacepartrequest->size.x,
235                 request.pos.y + surfacepartrequest->size.y,
236                 uv_left,
237                 uv_top,
238                 uv_right,
239                 uv_bottom,
240                 0.0,
241                 request.alpha,
242                 Color(1.0, 1.0, 1.0),
243                 Blend(),
244                 request.drawing_effect);
245   }
246
247   void
248   Lightmap::draw_gradient(const DrawingRequest& request)
249   {
250     const GradientRequest* gradientrequest 
251       = (GradientRequest*) request.request_data;
252     const Color& top = gradientrequest->top;
253     const Color& bottom = gradientrequest->bottom;
254
255     glDisable(GL_TEXTURE_2D);
256     glBegin(GL_QUADS);
257     glColor4f(top.red, top.green, top.blue, top.alpha);
258     glVertex2f(0, 0);
259     glVertex2f(SCREEN_WIDTH, 0);
260     glColor4f(bottom.red, bottom.green, bottom.blue, bottom.alpha);
261     glVertex2f(SCREEN_WIDTH, SCREEN_HEIGHT);
262     glVertex2f(0, SCREEN_HEIGHT);
263     glEnd();
264     glEnable(GL_TEXTURE_2D);
265     glColor4f(1, 1, 1, 1);
266   }
267
268   void
269   Lightmap::draw_filled_rect(const DrawingRequest& request)
270   {
271     const FillRectRequest* fillrectrequest
272       = (FillRectRequest*) request.request_data;
273
274     float x = request.pos.x;
275     float y = request.pos.y;
276     float w = fillrectrequest->size.x;
277     float h = fillrectrequest->size.y;
278
279     glDisable(GL_TEXTURE_2D);
280     glColor4f(fillrectrequest->color.red, fillrectrequest->color.green,
281               fillrectrequest->color.blue, fillrectrequest->color.alpha);
282
283     glBegin(GL_QUADS);
284     glVertex2f(x, y);
285     glVertex2f(x+w, y);
286     glVertex2f(x+w, y+h);
287     glVertex2f(x, y+h);
288     glEnd();
289     glEnable(GL_TEXTURE_2D);
290     glColor4f(1, 1, 1, 1);
291   }
292
293   void
294   Lightmap::get_light(const DrawingRequest& request) const
295   {
296     const GetLightRequest* getlightrequest 
297       = (GetLightRequest*) request.request_data;
298
299     float pixels[3];
300     for( int i = 0; i<3; i++)
301       pixels[i] = 0.0f; //set to black
302
303     float posX = request.pos.x * lightmap_width / SCREEN_WIDTH;
304     float posY = screen->h - request.pos.y * lightmap_height / SCREEN_HEIGHT;
305     glReadPixels((GLint) posX, (GLint) posY , 1, 1, GL_RGB, GL_FLOAT, pixels);
306     *(getlightrequest->color_ptr) = Color( pixels[0], pixels[1], pixels[2]);
307     //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]);
308   }
309 }
310
311 #endif