19ca6576699a8d59f7467422133fe53079bf1fd0
[supertux.git] / src / video / drawing_context.cpp
1 //  $Id: drawing_context.cpp 2334 2005-04-04 16:26:14Z grumbel $
2 //
3 //  SuperTux -  A Jump'n Run
4 //  Copyright (C) 2004 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 <algorithm>
22 #include <cassert>
23 #include <iostream>
24 #include <SDL_image.h>
25 #include <GL/gl.h>
26
27 #include "drawing_context.hpp"
28 #include "surface.hpp"
29 #include "font.hpp"
30 #include "main.hpp"
31 #include "gameconfig.hpp"
32 #include "glutil.hpp"
33 #include "texture.hpp"
34 #include "texture_manager.hpp"
35
36 #define LIGHTMAP_DIV 4
37
38 static inline int next_po2(int val)
39 {
40   int result = 1;
41   while(result < val)
42     result *= 2;
43   
44   return result;
45 }
46
47 DrawingContext::DrawingContext()
48 {
49   screen = SDL_GetVideoSurface();
50
51   lightmap_width = screen->w / LIGHTMAP_DIV;
52   lightmap_height = screen->h / LIGHTMAP_DIV;
53   unsigned int width = next_po2(lightmap_width);
54   unsigned int height = next_po2(lightmap_height);
55
56   lightmap = new Texture(width, height, GL_RGB);
57
58   lightmap_uv_right = static_cast<float>(lightmap_width) / static_cast<float>(width);
59   lightmap_uv_bottom = static_cast<float>(lightmap_height) / static_cast<float>(height);
60   texture_manager->register_texture(lightmap);
61
62   requests = &drawing_requests;
63 }
64
65 DrawingContext::~DrawingContext()
66 {
67   texture_manager->remove_texture(lightmap);
68   delete lightmap;
69 }
70
71 void
72 DrawingContext::draw_surface(const Surface* surface, const Vector& position,
73     int layer)
74 {
75   assert(surface != 0);
76   
77   DrawingRequest request;
78
79   request.type = SURFACE;
80   request.pos = transform.apply(position);
81
82   if(request.pos.x >= SCREEN_WIDTH || request.pos.y >= SCREEN_HEIGHT
83       || request.pos.x + surface->get_width() < 0 
84       || request.pos.y + surface->get_height() < 0)
85     return;
86
87   request.layer = layer;
88   request.drawing_effect = transform.drawing_effect;
89   request.alpha = transform.alpha;
90   request.request_data = const_cast<Surface*> (surface);  
91
92   requests->push_back(request);
93 }
94
95 void
96 DrawingContext::draw_surface_part(const Surface* surface, const Vector& source,
97     const Vector& size, const Vector& dest, int layer)
98 {
99   assert(surface != 0);
100
101   DrawingRequest request;
102
103   request.type = SURFACE_PART;
104   request.pos = transform.apply(dest);
105   request.layer = layer;
106   request.drawing_effect = transform.drawing_effect;
107   request.alpha = transform.alpha;
108   
109   SurfacePartRequest* surfacepartrequest = new SurfacePartRequest();
110   surfacepartrequest->size = size;
111   surfacepartrequest->source = source;
112   surfacepartrequest->surface = surface;
113
114   // clip on screen borders
115   if(request.pos.x < 0) {
116     surfacepartrequest->size.x += request.pos.x;
117     if(surfacepartrequest->size.x <= 0)
118       return;
119     surfacepartrequest->source.x -= request.pos.x;
120     request.pos.x = 0;
121   }
122   if(request.pos.y < 0) {
123     surfacepartrequest->size.y += request.pos.y;
124     if(surfacepartrequest->size.y <= 0)
125       return;
126     surfacepartrequest->source.y -= request.pos.y;
127     request.pos.y = 0;
128   }
129   request.request_data = surfacepartrequest;
130
131   requests->push_back(request);
132 }
133
134 void
135 DrawingContext::draw_text(const Font* font, const std::string& text,
136     const Vector& position, FontAlignment alignment, int layer)
137 {
138   DrawingRequest request;
139
140   request.type = TEXT;
141   request.pos = transform.apply(position);
142   request.layer = layer;
143   request.drawing_effect = transform.drawing_effect;
144   request.alpha = transform.alpha;
145
146   TextRequest* textrequest = new TextRequest;
147   textrequest->font = font;
148   textrequest->text = text;
149   textrequest->alignment = alignment;
150   request.request_data = textrequest;
151
152   requests->push_back(request);
153 }
154
155 void
156 DrawingContext::draw_center_text(const Font* font, const std::string& text,
157     const Vector& position, int layer)
158 {
159   draw_text(font, text, Vector(position.x + SCREEN_WIDTH/2, position.y),
160       CENTER_ALLIGN, layer);
161 }
162
163 void
164 DrawingContext::draw_gradient(const Color& top, const Color& bottom, int layer)
165 {
166   DrawingRequest request;
167
168   request.type = GRADIENT;
169   request.pos = Vector(0,0);
170   request.layer = layer;
171
172   request.drawing_effect = transform.drawing_effect;
173   request.alpha = transform.alpha;
174
175   GradientRequest* gradientrequest = new GradientRequest;
176   gradientrequest->top = top;
177   gradientrequest->bottom = bottom;
178   request.request_data = gradientrequest;
179
180   requests->push_back(request);
181 }
182
183 void
184 DrawingContext::draw_filled_rect(const Vector& topleft, const Vector& size,
185                                  const Color& color, int layer)
186 {
187   DrawingRequest request;
188
189   request.type = FILLRECT;
190   request.pos = transform.apply(topleft);
191   request.layer = layer;
192
193   request.drawing_effect = transform.drawing_effect;
194   request.alpha = transform.alpha;                    
195
196   FillRectRequest* fillrectrequest = new FillRectRequest;
197   fillrectrequest->size = size;
198   fillrectrequest->color = color;
199   fillrectrequest->color.alpha = color.alpha * transform.alpha;
200   request.request_data = fillrectrequest;
201
202   requests->push_back(request);
203 }
204
205 void
206 DrawingContext::draw_filled_rect(const Rect& rect, const Color& color,
207                                  int layer)
208 {
209   DrawingRequest request;
210
211   request.type = FILLRECT;
212   request.pos = transform.apply(rect.p1);
213   request.layer = layer;
214
215   request.drawing_effect = transform.drawing_effect;
216   request.alpha = transform.alpha;                    
217
218   FillRectRequest* fillrectrequest = new FillRectRequest;
219   fillrectrequest->size = Vector(rect.get_width(), rect.get_height());
220   fillrectrequest->color = color;
221   fillrectrequest->color.alpha = color.alpha * transform.alpha;
222   request.request_data = fillrectrequest;
223
224   requests->push_back(request);
225 }
226
227 void
228 DrawingContext::draw_surface_part(DrawingRequest& request)
229 {
230   SurfacePartRequest* surfacepartrequest
231     = (SurfacePartRequest*) request.request_data;
232
233   surfacepartrequest->surface->draw_part(
234       surfacepartrequest->source.x, surfacepartrequest->source.y,
235       request.pos.x, request.pos.y,
236       surfacepartrequest->size.x, surfacepartrequest->size.y,
237       request.alpha, request.drawing_effect);
238
239   delete surfacepartrequest;
240 }
241
242 void
243 DrawingContext::draw_gradient(DrawingRequest& request)
244 {
245   GradientRequest* gradientrequest = (GradientRequest*) request.request_data;
246   const Color& top = gradientrequest->top;
247   const Color& bottom = gradientrequest->bottom;
248   
249   glDisable(GL_TEXTURE_2D);
250   glBegin(GL_QUADS);
251   glColor4f(top.red, top.green, top.blue, top.alpha);
252   glVertex2f(0, 0);
253   glVertex2f(SCREEN_WIDTH, 0);
254   glColor4f(bottom.red, bottom.green, bottom.blue, bottom.alpha);
255   glVertex2f(SCREEN_WIDTH, SCREEN_HEIGHT);
256   glVertex2f(0, SCREEN_HEIGHT);
257   glEnd();
258   glEnable(GL_TEXTURE_2D);
259
260   delete gradientrequest;
261 }
262
263 void
264 DrawingContext::draw_text(DrawingRequest& request)
265 {
266   TextRequest* textrequest = (TextRequest*) request.request_data;
267
268   textrequest->font->draw(textrequest->text, request.pos,
269       textrequest->alignment, request.drawing_effect, request.alpha);
270
271   delete textrequest;
272 }
273
274 void
275 DrawingContext::draw_filled_rect(DrawingRequest& request)
276 {
277   FillRectRequest* fillrectrequest = (FillRectRequest*) request.request_data;
278
279   float x = request.pos.x;
280   float y = request.pos.y;
281   float w = fillrectrequest->size.x;
282   float h = fillrectrequest->size.y;
283
284   glDisable(GL_TEXTURE_2D);
285   glColor4f(fillrectrequest->color.red, fillrectrequest->color.green,
286             fillrectrequest->color.blue, fillrectrequest->color.alpha);
287  
288   glBegin(GL_QUADS);
289   glVertex2f(x, y);
290   glVertex2f(x+w, y);
291   glVertex2f(x+w, y+h);
292   glVertex2f(x, y+h);
293   glEnd();
294   glEnable(GL_TEXTURE_2D);
295
296   delete fillrectrequest;
297 }
298
299 void
300 DrawingContext::do_drawing()
301 {
302 #ifdef DEBUG
303   assert(transformstack.empty());
304   assert(target_stack.empty());
305 #endif
306   transformstack.clear();
307   target_stack.clear();
308
309   bool use_lightmap = lightmap_requests.size() != 0;
310   
311   // PART1: create lightmap
312   if(use_lightmap) {
313     glViewport(0, screen->h - lightmap_height, lightmap_width, lightmap_height);
314     glMatrixMode(GL_PROJECTION);
315     glLoadIdentity();               
316     glOrtho(0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, -1.0, 1.0);
317     glMatrixMode(GL_MODELVIEW);
318     glLoadIdentity();
319
320     //glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
321     glClearColor(0, 0, 0, 1);
322     glClear(GL_COLOR_BUFFER_BIT);
323     handle_drawing_requests(lightmap_requests);
324     lightmap_requests.clear();
325   
326     glDisable(GL_BLEND);
327     glBindTexture(GL_TEXTURE_2D, lightmap->get_handle());
328     glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, screen->h - lightmap_height, lightmap_width, lightmap_height);
329
330     glViewport(0, 0, screen->w, screen->h);
331     glMatrixMode(GL_PROJECTION);
332     glLoadIdentity();               
333     glOrtho(0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, -1.0, 1.0);
334     glMatrixMode(GL_MODELVIEW);    
335     glLoadIdentity();
336     glEnable(GL_BLEND);
337   }
338
339   //glClear(GL_COLOR_BUFFER_BIT);
340   handle_drawing_requests(drawing_requests);
341   drawing_requests.clear();
342
343   if(use_lightmap) {
344     glBlendFunc(GL_SRC_ALPHA, GL_ONE);
345
346     glBindTexture(GL_TEXTURE_2D, lightmap->get_handle());
347     glBegin(GL_QUADS);
348
349     glTexCoord2f(0, lightmap_uv_bottom);
350     glVertex2f(0, 0);
351
352     glTexCoord2f(lightmap_uv_right, lightmap_uv_bottom);
353     glVertex2f(SCREEN_WIDTH, 0);
354
355     glTexCoord2f(lightmap_uv_right, 0);
356     glVertex2f(SCREEN_WIDTH, SCREEN_HEIGHT);
357
358     glTexCoord2f(0, 0);
359     glVertex2f(0, SCREEN_HEIGHT);
360     
361     glEnd();
362
363     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
364   }
365
366   assert_gl("drawing");
367
368   SDL_GL_SwapBuffers();
369 }
370
371 void
372 DrawingContext::handle_drawing_requests(DrawingRequests& requests)
373 {
374   std::stable_sort(requests.begin(), requests.end());
375   
376   for(DrawingRequests::iterator i = requests.begin();
377       i != requests.end(); ++i) {
378     switch(i->type) {
379       case SURFACE:
380       {
381         const Surface* surface = (const Surface*) i->request_data;
382         surface->draw(i->pos.x, i->pos.y, i->alpha, i->drawing_effect);
383         break;
384       }
385       case SURFACE_PART:
386         draw_surface_part(*i);
387         break;
388       case GRADIENT:
389         draw_gradient(*i);
390         break;
391       case TEXT:
392         draw_text(*i);
393         break;
394       case FILLRECT:
395         draw_filled_rect(*i);
396         break;
397     }
398   }
399 }
400
401 void
402 DrawingContext::push_transform()
403 {
404   transformstack.push_back(transform);
405 }
406
407 void
408 DrawingContext::pop_transform()
409 {
410   assert(!transformstack.empty());
411
412   transform = transformstack.back();
413   transformstack.pop_back();
414 }
415
416 void
417 DrawingContext::set_drawing_effect(DrawingEffect effect)
418 {
419   transform.drawing_effect = effect;
420 }
421
422 DrawingEffect
423 DrawingContext::get_drawing_effect() const
424 {
425   return transform.drawing_effect;
426 }
427
428 void
429 DrawingContext::set_alpha(float alpha)
430 {
431   transform.alpha = alpha;
432 }
433
434 float
435 DrawingContext::get_alpha() const
436 {
437   return transform.alpha;
438 }
439
440 void
441 DrawingContext::push_target()
442 {
443   target_stack.push_back(target);
444 }
445
446 void
447 DrawingContext::pop_target()
448 {
449   set_target(target_stack.back());
450   target_stack.pop_back();
451 }
452
453 void
454 DrawingContext::set_target(Target target)
455 {
456   this->target = target;
457   if(target == LIGHTMAP)
458     requests = &lightmap_requests;
459   else
460     requests = &drawing_requests;
461 }
462