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