Remove bogus assert
[supertux.git] / src / video / drawing_context.cpp
1 //  SuperTux
2 //  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
3 //
4 //  This program is free software: you can redistribute it and/or modify
5 //  it under the terms of the GNU General Public License as published by
6 //  the Free Software Foundation, either version 3 of the License, or
7 //  (at your option) any later version.
8 //
9 //  This program is distributed in the hope that it will be useful,
10 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
11 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 //  GNU General Public License for more details.
13 //
14 //  You should have received a copy of the GNU General Public License
15 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
17 #include "video/drawing_context.hpp"
18
19 #include <algorithm>
20 #include <config.h>
21
22 #include "math/sizef.hpp"
23 #include "supertux/gameconfig.hpp"
24 #include "supertux/globals.hpp"
25 #include "util/obstackpp.hpp"
26 #include "video/drawing_request.hpp"
27 #include "video/lightmap.hpp"
28 #include "video/renderer.hpp"
29 #include "video/surface.hpp"
30 #include "video/texture.hpp"
31 #include "video/texture_manager.hpp"
32 #include "video/video_system.hpp"
33
34 DrawingContext::DrawingContext(VideoSystem& video_system_) :
35   video_system(video_system_),
36   transformstack(),
37   transform(),
38   blend_stack(),
39   blend_mode(),
40   drawing_requests(),
41   lightmap_requests(),
42   requests(),
43   ambient_color(1.0f, 1.0f, 1.0f, 1.0f),
44   target(NORMAL),
45   target_stack(),
46   obst(),
47   screenshot_requested(false)
48 {
49   requests = &drawing_requests;
50   obstack_init(&obst);
51 }
52
53 DrawingContext::~DrawingContext()
54 {
55   clear_drawing_requests(lightmap_requests);
56   clear_drawing_requests(drawing_requests);
57
58   obstack_free(&obst, NULL);
59 }
60
61 void
62 DrawingContext::clear_drawing_requests(DrawingRequests& requests_)
63 {
64   for(auto& request : requests_)
65   {
66     if (request->request_data)
67     {
68       request->request_data->~DrawingRequestData();
69     }
70     request->~DrawingRequest();
71   }
72   requests_.clear();
73 }
74
75 void
76 DrawingContext::draw_surface(SurfacePtr surface, const Vector& position,
77                              float angle, const Color& color, const Blend& blend,
78                              int layer)
79 {
80   assert(surface != 0);
81
82   DrawingRequest* request = new(obst) DrawingRequest();
83
84   request->target = target;
85   request->type = SURFACE;
86   request->pos = transform.apply(position);
87
88   if(request->pos.x >= SCREEN_WIDTH || request->pos.y >= SCREEN_HEIGHT
89      || request->pos.x + surface->get_width() < 0
90      || request->pos.y + surface->get_height() < 0)
91     return;
92
93   request->layer = layer;
94   request->drawing_effect = transform.drawing_effect;
95   request->alpha = transform.alpha;
96   request->angle = angle;
97   request->color = color;
98   request->blend = blend;
99
100   SurfaceRequest* surfacerequest = new(obst) SurfaceRequest();
101   surfacerequest->surface = surface.get();
102   request->request_data = surfacerequest;
103
104   requests->push_back(request);
105 }
106
107 void
108 DrawingContext::draw_surface(SurfacePtr 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(SurfacePtr surface,
116                                   const Rectf& srcrect, const Rectf& dstrect,
117                                   int layer)
118 {
119   assert(surface != 0);
120
121   DrawingRequest* request = new(obst) DrawingRequest();
122
123   request->target = target;
124   request->type = SURFACE_PART;
125   request->pos = transform.apply(dstrect.p1);
126   request->layer = layer;
127   request->drawing_effect = transform.drawing_effect;
128   request->alpha = transform.alpha;
129
130   SurfacePartRequest* surfacepartrequest = new(obst) SurfacePartRequest();
131   surfacepartrequest->srcrect = srcrect;
132   surfacepartrequest->dstsize = dstrect.get_size();
133   surfacepartrequest->surface = surface.get();
134
135   request->request_data = surfacepartrequest;
136
137   requests->push_back(request);
138 }
139
140 void
141 DrawingContext::draw_text(FontPtr font, const std::string& text,
142                           const Vector& position, FontAlignment alignment, int layer, Color color)
143 {
144   DrawingRequest* request = new(obst) DrawingRequest();
145
146   request->target = target;
147   request->type = TEXT;
148   request->pos = transform.apply(position);
149   request->layer = layer;
150   request->drawing_effect = transform.drawing_effect;
151   request->alpha = transform.alpha;
152   request->color = color;
153
154   TextRequest* textrequest = new(obst) TextRequest();
155   textrequest->font = font.get();
156   textrequest->text = text;
157   textrequest->alignment = alignment;
158   request->request_data = textrequest;
159
160   requests->push_back(request);
161 }
162
163 void
164 DrawingContext::draw_center_text(FontPtr font, const std::string& text,
165                                  const Vector& position, int layer, Color color)
166 {
167   draw_text(font, text, Vector(position.x + SCREEN_WIDTH/2, position.y),
168             ALIGN_CENTER, layer, color);
169 }
170
171 void
172 DrawingContext::draw_gradient(const Color& top, const Color& bottom, int layer)
173 {
174   DrawingRequest* request = new(obst) DrawingRequest();
175
176   request->target = target;
177   request->type = GRADIENT;
178   request->pos = Vector(0,0);
179   request->layer = layer;
180
181   request->drawing_effect = transform.drawing_effect;
182   request->alpha = transform.alpha;
183
184   GradientRequest* gradientrequest = new(obst) GradientRequest();
185   gradientrequest->top = top;
186   gradientrequest->bottom = bottom;
187   request->request_data = gradientrequest;
188
189   requests->push_back(request);
190 }
191
192 void
193 DrawingContext::draw_filled_rect(const Vector& topleft, const Vector& size,
194                                  const Color& color, int layer)
195 {
196   DrawingRequest* request = new(obst) DrawingRequest();
197
198   request->target = target;
199   request->type = FILLRECT;
200   request->pos = transform.apply(topleft);
201   request->layer = layer;
202
203   request->drawing_effect = transform.drawing_effect;
204   request->alpha = transform.alpha;
205
206   FillRectRequest* fillrectrequest = new(obst) FillRectRequest();
207   fillrectrequest->size = size;
208   fillrectrequest->color = color;
209   fillrectrequest->color.alpha = color.alpha * transform.alpha;
210   fillrectrequest->radius = 0.0f;
211   request->request_data = fillrectrequest;
212
213   requests->push_back(request);
214 }
215
216 void
217 DrawingContext::draw_filled_rect(const Rectf& rect, const Color& color,
218                                  int layer)
219 {
220   draw_filled_rect(rect, color, 0.0f, layer);
221 }
222
223 void
224 DrawingContext::draw_filled_rect(const Rectf& rect, const Color& color, float radius, int layer)
225 {
226   DrawingRequest* request = new(obst) DrawingRequest();
227
228   request->target = target;
229   request->type   = FILLRECT;
230   request->pos    = transform.apply(rect.p1);
231   request->layer  = layer;
232
233   request->drawing_effect = transform.drawing_effect;
234   request->alpha = transform.alpha;
235
236   FillRectRequest* fillrectrequest = new(obst) FillRectRequest;
237   fillrectrequest->size = Vector(rect.get_width(), rect.get_height());
238   fillrectrequest->color = color;
239   fillrectrequest->color.alpha = color.alpha * transform.alpha;
240   fillrectrequest->radius = radius;
241   request->request_data = fillrectrequest;
242
243   requests->push_back(request);
244 }
245
246 void
247 DrawingContext::draw_inverse_ellipse(const Vector& pos, const Vector& size, const Color& color, int layer)
248 {
249   DrawingRequest* request = new(obst) DrawingRequest();
250
251   request->target = target;
252   request->type   = INVERSEELLIPSE;
253   request->pos    = transform.apply(pos);
254   request->layer  = layer;
255
256   request->drawing_effect = transform.drawing_effect;
257   request->alpha = transform.alpha;
258
259   InverseEllipseRequest* ellipse = new(obst)InverseEllipseRequest;
260
261   ellipse->color        = color;
262   ellipse->color.alpha  = color.alpha * transform.alpha;
263   ellipse->size         = size;
264   request->request_data = ellipse;
265
266   requests->push_back(request);
267 }
268
269 Rectf
270 DrawingContext::get_cliprect() const
271 {
272   return Rectf(get_translation().x, get_translation().y,
273                get_translation().x + SCREEN_WIDTH,
274                get_translation().y + SCREEN_HEIGHT);
275 }
276
277 void
278 DrawingContext::get_light(const Vector& position, Color* color)
279 {
280   if( ambient_color.red == 1.0f && ambient_color.green == 1.0f
281       && ambient_color.blue  == 1.0f ) {
282     *color = Color( 1.0f, 1.0f, 1.0f);
283     return;
284   }
285
286   DrawingRequest* request = new(obst) DrawingRequest();
287   request->target = target;
288   request->type = GETLIGHT;
289   request->pos = transform.apply(position);
290
291   //There is no light offscreen.
292   if(request->pos.x >= SCREEN_WIDTH || request->pos.y >= SCREEN_HEIGHT
293      || request->pos.x < 0 || request->pos.y < 0){
294     *color = Color( 0, 0, 0);
295     return;
296   }
297
298   request->layer = LAYER_GUI; //make sure all get_light requests are handled last.
299   GetLightRequest* getlightrequest = new(obst) GetLightRequest();
300   getlightrequest->color_ptr = color;
301   request->request_data = getlightrequest;
302   lightmap_requests.push_back(request);
303 }
304
305 void
306 DrawingContext::do_drawing()
307 {
308   assert(transformstack.empty());
309   assert(target_stack.empty());
310   transformstack.clear();
311   target_stack.clear();
312
313   //Use Lightmap if ambient color is not white.
314   bool use_lightmap = ( ambient_color.red != 1.0f ||
315                         ambient_color.green != 1.0f ||
316                         ambient_color.blue != 1.0f );
317
318   // PART1: create lightmap
319   if(use_lightmap) {
320     Lightmap& lightmap = video_system.get_lightmap();
321
322     lightmap.start_draw(ambient_color);
323     handle_drawing_requests(lightmap_requests);
324     lightmap.end_draw();
325
326     DrawingRequest* request = new(obst) DrawingRequest();
327     request->target = NORMAL;
328     request->type = DRAW_LIGHTMAP;
329     request->layer = LAYER_HUD - 1;
330     drawing_requests.push_back(request);
331   }
332
333   Renderer& renderer = video_system.get_renderer();
334   renderer.start_draw();
335   handle_drawing_requests(drawing_requests);
336   renderer.end_draw();
337
338   clear_drawing_requests(lightmap_requests);
339   clear_drawing_requests(drawing_requests);
340
341   obstack_free(&obst, NULL);
342   obstack_init(&obst);
343
344   // if a screenshot was requested, take one
345   if (screenshot_requested) {
346     renderer.do_take_screenshot();
347     screenshot_requested = false;
348   }
349
350   renderer.flip();
351 }
352
353 class RequestPtrCompare
354 {
355 public:
356   bool operator()(const DrawingRequest* r1, const DrawingRequest* r2) const
357   {
358     return *r1 < *r2;
359   }
360 };
361
362 void
363 DrawingContext::handle_drawing_requests(DrawingRequests& requests_)
364 {
365   std::stable_sort(requests_.begin(), requests_.end(), RequestPtrCompare());
366
367   Renderer& renderer = video_system.get_renderer();
368   Lightmap& lightmap = video_system.get_lightmap();
369
370   DrawingRequests::const_iterator i;
371   for(i = requests_.begin(); i != requests_.end(); ++i) {
372     const DrawingRequest& request = **i;
373
374     switch(request.target) {
375       case NORMAL:
376         switch(request.type) {
377           case SURFACE:
378             renderer.draw_surface(request);
379             break;
380           case SURFACE_PART:
381             renderer.draw_surface_part(request);
382             break;
383           case GRADIENT:
384             renderer.draw_gradient(request);
385             break;
386           case TEXT:
387           {
388             const TextRequest* textrequest = static_cast<TextRequest*>(request.request_data);
389             textrequest->font->draw(&renderer, textrequest->text, request.pos,
390                                     textrequest->alignment, request.drawing_effect, request.color, request.alpha);
391           }
392           break;
393           case FILLRECT:
394             renderer.draw_filled_rect(request);
395             break;
396           case INVERSEELLIPSE:
397             renderer.draw_inverse_ellipse(request);
398             break;
399           case DRAW_LIGHTMAP:
400             lightmap.do_draw();
401             break;
402           case GETLIGHT:
403             lightmap.get_light(request);
404             break;
405         }
406         break;
407       case LIGHTMAP:
408         switch(request.type) {
409           case SURFACE:
410             lightmap.draw_surface(request);
411             break;
412           case SURFACE_PART:
413             lightmap.draw_surface_part(request);
414             break;
415           case GRADIENT:
416             lightmap.draw_gradient(request);
417             break;
418           case TEXT:
419           {
420             const TextRequest* textrequest = static_cast<TextRequest*>(request.request_data);
421             textrequest->font->draw(&renderer, textrequest->text, request.pos,
422                                     textrequest->alignment, request.drawing_effect, request.color, request.alpha);
423           }
424           break;
425           case FILLRECT:
426             lightmap.draw_filled_rect(request);
427             break;
428           case INVERSEELLIPSE:
429             assert(!"InverseEllipse doesn't make sense on the lightmap");
430             break;
431           case DRAW_LIGHTMAP:
432             lightmap.do_draw();
433             break;
434           case GETLIGHT:
435             lightmap.get_light(request);
436             break;
437         }
438         break;
439     }
440   }
441 }
442
443 void
444 DrawingContext::push_transform()
445 {
446   transformstack.push_back(transform);
447 }
448
449 void
450 DrawingContext::pop_transform()
451 {
452   assert(!transformstack.empty());
453
454   transform = transformstack.back();
455   transformstack.pop_back();
456 }
457
458 void
459 DrawingContext::set_drawing_effect(DrawingEffect effect)
460 {
461   transform.drawing_effect = effect;
462 }
463
464 DrawingEffect
465 DrawingContext::get_drawing_effect() const
466 {
467   return transform.drawing_effect;
468 }
469
470 void
471 DrawingContext::set_alpha(float alpha)
472 {
473   transform.alpha = alpha;
474 }
475
476 float
477 DrawingContext::get_alpha() const
478 {
479   return transform.alpha;
480 }
481
482 void
483 DrawingContext::push_target()
484 {
485   target_stack.push_back(target);
486 }
487
488 void
489 DrawingContext::pop_target()
490 {
491   set_target(target_stack.back());
492   target_stack.pop_back();
493 }
494
495 void
496 DrawingContext::set_target(Target target_)
497 {
498   this->target = target_;
499   if(target_ == LIGHTMAP) {
500     requests = &lightmap_requests;
501   } else {
502     assert(target_ == NORMAL);
503     requests = &drawing_requests;
504   }
505 }
506
507 void
508 DrawingContext::set_ambient_color( Color new_color )
509 {
510   ambient_color = new_color;
511 }
512
513 void
514 DrawingContext::take_screenshot()
515 {
516   screenshot_requested = true;
517 }
518
519 /* EOF */