Precalculated renderer specific surface data, better management of multiple renderers...
[supertux.git] / src / video / drawing_context.cpp
1 //  $Id$
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 "drawing_context.hpp"
31 #include "drawing_request.hpp"
32 #include "video_systems.hpp"
33 #include "renderer.hpp"
34 #include "lightmap.hpp"
35 #include "surface.hpp"
36 #include "main.hpp"
37 #include "gameconfig.hpp"
38 #include "texture.hpp"
39 #include "texture_manager.hpp"
40 #include "obstack/obstackpp.hpp"
41 #define LIGHTMAP_DIV 5
42
43 static inline int next_po2(int val)
44 {
45   int result = 1;
46   while(result < val)
47     result *= 2;
48
49   return result;
50 }
51
52 DrawingContext::DrawingContext() :
53   renderer(0), lightmap(0), ambient_color(1.0f, 1.0f, 1.0f, 1.0f), target(NORMAL), screenshot_requested(false)
54 {
55   requests = &drawing_requests;
56   obstack_init(&obst);
57 }
58
59 DrawingContext::~DrawingContext()
60 {
61   delete renderer;
62   delete lightmap;
63
64   obstack_free(&obst, NULL);
65 }
66
67 void
68 DrawingContext::init_renderer()
69 {
70   delete renderer;
71   delete lightmap;
72
73   renderer = new_renderer();
74   lightmap = new_lightmap();
75 }
76
77 void
78 DrawingContext::draw_surface(const Surface* surface, const Vector& position,
79                              float angle, const Color& color, const Blend& blend,
80                              int layer)
81 {
82   assert(surface != 0);
83
84   DrawingRequest* request = new(obst) DrawingRequest();
85
86   request->target = target;
87   request->type = SURFACE;
88   request->pos = transform.apply(position);
89
90   if(request->pos.x >= SCREEN_WIDTH || request->pos.y >= SCREEN_HEIGHT
91       || request->pos.x + surface->get_width() < 0
92       || request->pos.y + surface->get_height() < 0)
93     return;
94
95   request->layer = layer;
96   request->drawing_effect = transform.drawing_effect;
97   request->alpha = transform.alpha;
98   request->angle = angle;
99   request->color = color;
100   request->blend = blend;
101
102   request->request_data = const_cast<Surface*> (surface);
103
104   requests->push_back(request);
105 }
106
107 void
108 DrawingContext::draw_surface(const Surface* surface, const Vector& position,
109     int layer)
110 {
111   draw_surface(surface, position, 0.0f, Color(1.0f, 1.0f, 1.0f), Blend(), layer);
112 }
113
114 void
115 DrawingContext::draw_surface_part(const Surface* surface, const Vector& source,
116     const Vector& size, const Vector& dest, int layer)
117 {
118   assert(surface != 0);
119
120   DrawingRequest* request = new(obst) DrawingRequest();
121
122   request->target = target;
123   request->type = SURFACE_PART;
124   request->pos = transform.apply(dest);
125   request->layer = layer;
126   request->drawing_effect = transform.drawing_effect;
127   request->alpha = transform.alpha;
128
129   SurfacePartRequest* surfacepartrequest = new(obst) SurfacePartRequest();
130   surfacepartrequest->size = size;
131   surfacepartrequest->source = source;
132   surfacepartrequest->surface = surface;
133
134   // clip on screen borders
135   if(request->pos.x < 0) {
136     surfacepartrequest->size.x += request->pos.x;
137     if(surfacepartrequest->size.x <= 0)
138       return;
139     surfacepartrequest->source.x -= request->pos.x;
140     request->pos.x = 0;
141   }
142   if(request->pos.y < 0) {
143     surfacepartrequest->size.y += request->pos.y;
144     if(surfacepartrequest->size.y <= 0)
145       return;
146     surfacepartrequest->source.y -= request->pos.y;
147     request->pos.y = 0;
148   }
149   request->request_data = surfacepartrequest;
150
151   requests->push_back(request);
152 }
153
154 void
155 DrawingContext::draw_text(const Font* font, const std::string& text,
156     const Vector& position, FontAlignment alignment, int layer)
157 {
158   DrawingRequest* request = new(obst) DrawingRequest();
159
160   request->target = target;
161   request->type = TEXT;
162   request->pos = transform.apply(position);
163   request->layer = layer;
164   request->drawing_effect = transform.drawing_effect;
165   request->alpha = transform.alpha;
166
167   TextRequest* textrequest = new(obst) TextRequest();
168   textrequest->font = font;
169   textrequest->text = text;
170   textrequest->alignment = alignment;
171   request->request_data = textrequest;
172
173   requests->push_back(request);
174 }
175
176 void
177 DrawingContext::draw_center_text(const Font* font, const std::string& text,
178     const Vector& position, int layer)
179 {
180   draw_text(font, text, Vector(position.x + SCREEN_WIDTH/2, position.y),
181       ALIGN_CENTER, layer);
182 }
183
184 void
185 DrawingContext::draw_gradient(const Color& top, const Color& bottom, int layer)
186 {
187   DrawingRequest* request = new(obst) DrawingRequest();
188
189   request->target = target;
190   request->type = GRADIENT;
191   request->pos = Vector(0,0);
192   request->layer = layer;
193
194   request->drawing_effect = transform.drawing_effect;
195   request->alpha = transform.alpha;
196
197   GradientRequest* gradientrequest = new(obst) GradientRequest();
198   gradientrequest->top = top;
199   gradientrequest->bottom = bottom;
200   request->request_data = gradientrequest;
201
202   requests->push_back(request);
203 }
204
205 void
206 DrawingContext::draw_filled_rect(const Vector& topleft, const Vector& size,
207                                  const Color& color, int layer)
208 {
209   DrawingRequest* request = new(obst) DrawingRequest();
210
211   request->target = target;
212   request->type = FILLRECT;
213   request->pos = transform.apply(topleft);
214   request->layer = layer;
215
216   request->drawing_effect = transform.drawing_effect;
217   request->alpha = transform.alpha;
218
219   FillRectRequest* fillrectrequest = new(obst) FillRectRequest();
220   fillrectrequest->size = size;
221   fillrectrequest->color = color;
222   fillrectrequest->color.alpha = color.alpha * transform.alpha;
223   request->request_data = fillrectrequest;
224
225   requests->push_back(request);
226 }
227
228 void
229 DrawingContext::draw_filled_rect(const Rect& rect, const Color& color,
230                                  int layer)
231 {
232   DrawingRequest* request = new(obst) DrawingRequest();
233
234   request->target = target;
235   request->type = FILLRECT;
236   request->pos = transform.apply(rect.p1);
237   request->layer = layer;
238
239   request->drawing_effect = transform.drawing_effect;
240   request->alpha = transform.alpha;
241
242   FillRectRequest* fillrectrequest = new(obst) FillRectRequest;
243   fillrectrequest->size = Vector(rect.get_width(), rect.get_height());
244   fillrectrequest->color = color;
245   fillrectrequest->color.alpha = color.alpha * transform.alpha;
246   request->request_data = fillrectrequest;
247
248   requests->push_back(request);
249 }
250
251 void
252 DrawingContext::get_light(const Vector& position, Color* color)
253 {
254   if( ambient_color.red == 1.0f && ambient_color.green == 1.0f
255       && ambient_color.blue  == 1.0f ) {
256     *color = Color( 1.0f, 1.0f, 1.0f);
257     return;
258   }
259
260   DrawingRequest* request = new(obst) DrawingRequest();
261   request->target = target;
262   request->type = GETLIGHT;
263   request->pos = transform.apply(position);
264
265   //There is no light offscreen.
266   if(request->pos.x >= SCREEN_WIDTH || request->pos.y >= SCREEN_HEIGHT
267       || request->pos.x < 0 || request->pos.y < 0){
268     *color = Color( 0, 0, 0);
269     return;
270   }
271
272   request->layer = LAYER_GUI; //make sure all get_light requests are handled last.
273   GetLightRequest* getlightrequest = new(obst) GetLightRequest();
274   getlightrequest->color_ptr = color;
275   request->request_data = getlightrequest;
276   lightmap_requests.push_back(request);
277 }
278
279 void
280 DrawingContext::do_drawing()
281 {
282 #ifdef DEBUG
283   assert(transformstack.empty());
284   assert(target_stack.empty());
285 #endif
286   transformstack.clear();
287   target_stack.clear();
288
289   //Use Lightmap if ambient color is not white.
290   bool use_lightmap = ( ambient_color.red != 1.0f   || ambient_color.green != 1.0f ||
291                         ambient_color.blue  != 1.0f );
292
293   // PART1: create lightmap
294   if(use_lightmap) {
295     lightmap->start_draw(ambient_color);
296     handle_drawing_requests(lightmap_requests);
297     lightmap->end_draw();
298   }
299
300   handle_drawing_requests(drawing_requests);
301   if(use_lightmap) {
302     lightmap->do_draw();
303   }
304   obstack_free(&obst, NULL);
305   obstack_init(&obst);
306
307   // if a screenshot was requested, take one
308   if (screenshot_requested) {
309     renderer->do_take_screenshot();
310     screenshot_requested = false;
311   }
312
313   renderer->flip();
314 }
315
316 class RequestPtrCompare
317   :  public std::binary_function<const DrawingRequest*,
318                                  const DrawingRequest*, 
319                                  bool>
320 {
321 public:
322   bool operator()(const DrawingRequest* r1, const DrawingRequest* r2) const
323   {
324     return *r1 < *r2;
325   }
326 };
327
328 void
329 DrawingContext::handle_drawing_requests(DrawingRequests& requests)
330 {
331   std::stable_sort(requests.begin(), requests.end(), RequestPtrCompare());
332
333   DrawingRequests::const_iterator i;
334   for(i = requests.begin(); i != requests.end(); ++i) {
335     const DrawingRequest& request = **i;
336
337     switch(request.target) {
338       case NORMAL:
339         switch(request.type) {
340           case SURFACE:
341             renderer->draw_surface(request);
342             break;
343           case SURFACE_PART:
344             renderer->draw_surface_part(request);
345             break;
346           case GRADIENT:
347             renderer->draw_gradient(request);
348             break;
349           case TEXT:
350             renderer->draw_text(request);
351             break;
352           case FILLRECT:
353             renderer->draw_filled_rect(request);
354             break;
355           case GETLIGHT:
356             lightmap->get_light(request);
357             break;
358         }
359         break;
360       case LIGHTMAP:
361         switch(request.type) {
362           case SURFACE:
363             lightmap->draw_surface(request);
364             break;
365           case SURFACE_PART:
366             lightmap->draw_surface_part(request);
367             break;
368           case GRADIENT:
369             lightmap->draw_gradient(request);
370             break;
371           case TEXT:
372             lightmap->draw_text(request);
373             break;
374           case FILLRECT:
375             lightmap->draw_filled_rect(request);
376             break;
377           case GETLIGHT:
378             lightmap->get_light(request);
379             break;
380         }
381         break;
382     }
383   }
384   requests.clear();
385 }
386
387 void
388 DrawingContext::push_transform()
389 {
390   transformstack.push_back(transform);
391 }
392
393 void
394 DrawingContext::pop_transform()
395 {
396   assert(!transformstack.empty());
397
398   transform = transformstack.back();
399   transformstack.pop_back();
400 }
401
402 void
403 DrawingContext::set_drawing_effect(DrawingEffect effect)
404 {
405   transform.drawing_effect = effect;
406 }
407
408 DrawingEffect
409 DrawingContext::get_drawing_effect() const
410 {
411   return transform.drawing_effect;
412 }
413
414 void
415 DrawingContext::set_alpha(float alpha)
416 {
417   transform.alpha = alpha;
418 }
419
420 float
421 DrawingContext::get_alpha() const
422 {
423   return transform.alpha;
424 }
425
426 void
427 DrawingContext::push_target()
428 {
429   target_stack.push_back(target);
430 }
431
432 void
433 DrawingContext::pop_target()
434 {
435   set_target(target_stack.back());
436   target_stack.pop_back();
437 }
438
439 void
440 DrawingContext::set_target(Target target)
441 {
442   this->target = target;
443   if(target == LIGHTMAP) {
444     requests = &lightmap_requests;
445   } else {
446     assert(target == NORMAL);
447     requests = &drawing_requests;
448   }
449 }
450
451 void
452 DrawingContext::set_ambient_color( Color new_color )
453 {
454   ambient_color = new_color;
455 }
456
457 void 
458 DrawingContext::take_screenshot()
459 {
460   screenshot_requested = true;
461 }
462