src/video/drawing_context.cpp

Go to the documentation of this file.
00001 //  SuperTux
00002 //  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
00003 //
00004 //  This program is free software: you can redistribute it and/or modify
00005 //  it under the terms of the GNU General Public License as published by
00006 //  the Free Software Foundation, either version 3 of the License, or
00007 //  (at your option) any later version.
00008 //
00009 //  This program is distributed in the hope that it will be useful,
00010 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012 //  GNU General Public License for more details.
00013 //
00014 //  You should have received a copy of the GNU General Public License
00015 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
00016 
00017 #include "video/drawing_context.hpp"
00018 
00019 #include <algorithm>
00020 #include <config.h>
00021 
00022 #include "math/sizef.hpp"
00023 #include "supertux/gameconfig.hpp"
00024 #include "supertux/globals.hpp"
00025 #include "util/obstackpp.hpp"
00026 #include "video/drawing_request.hpp"
00027 #include "video/lightmap.hpp"
00028 #include "video/renderer.hpp"
00029 #include "video/surface.hpp"
00030 #include "video/texture.hpp"
00031 #include "video/texture_manager.hpp"
00032 #include "video/video_systems.hpp"
00033 
00034 DrawingContext::DrawingContext() :
00035   renderer(0), 
00036   lightmap(0),
00037   transformstack(),
00038   transform(),
00039   blend_stack(),
00040   blend_mode(),
00041   drawing_requests(),
00042   lightmap_requests(),
00043   requests(),
00044   ambient_color(1.0f, 1.0f, 1.0f, 1.0f),
00045   target(NORMAL),
00046   target_stack(),
00047   obst(),
00048   screenshot_requested(false)
00049 {
00050   requests = &drawing_requests;
00051   obstack_init(&obst);
00052 }
00053 
00054 DrawingContext::~DrawingContext()
00055 {
00056   delete renderer;
00057   delete lightmap;
00058 
00059   obstack_free(&obst, NULL);
00060 }
00061 
00062 void
00063 DrawingContext::init_renderer()
00064 {
00065   delete renderer;
00066   delete lightmap;
00067 
00068   renderer = VideoSystem::new_renderer();
00069   lightmap = VideoSystem::new_lightmap();
00070 }
00071 
00072 void
00073 DrawingContext::draw_surface(SurfacePtr surface, const Vector& position,
00074                              float angle, const Color& color, const Blend& blend,
00075                              int layer)
00076 {
00077   assert(surface != 0);
00078 
00079   DrawingRequest* request = new(obst) DrawingRequest();
00080 
00081   request->target = target;
00082   request->type = SURFACE;
00083   request->pos = transform.apply(position);
00084 
00085   if(request->pos.x >= SCREEN_WIDTH || request->pos.y >= SCREEN_HEIGHT
00086      || request->pos.x + surface->get_width() < 0
00087      || request->pos.y + surface->get_height() < 0)
00088     return;
00089 
00090   request->layer = layer;
00091   request->drawing_effect = transform.drawing_effect;
00092   request->alpha = transform.alpha;
00093   request->angle = angle;
00094   request->color = color;
00095   request->blend = blend;
00096 
00097   request->request_data = surface.get();
00098 
00099   requests->push_back(request);
00100 }
00101 
00102 void
00103 DrawingContext::draw_surface(SurfacePtr surface, const Vector& position,
00104                              int layer)
00105 {
00106   draw_surface(surface, position, 0.0f, Color(1.0f, 1.0f, 1.0f), Blend(), layer);
00107 }
00108 
00109 void
00110 DrawingContext::draw_surface_part(SurfacePtr surface, const Vector& source,
00111                                   const Vector& size, const Vector& dest, int layer)
00112 {
00113   assert(surface != 0);
00114 
00115   DrawingRequest* request = new(obst) DrawingRequest();
00116 
00117   request->target = target;
00118   request->type = SURFACE_PART;
00119   request->pos = transform.apply(dest);
00120   request->layer = layer;
00121   request->drawing_effect = transform.drawing_effect;
00122   request->alpha = transform.alpha;
00123 
00124   SurfacePartRequest* surfacepartrequest = new(obst) SurfacePartRequest();
00125   surfacepartrequest->size = size;
00126   surfacepartrequest->source = source;
00127   surfacepartrequest->surface = surface.get();
00128 
00129   // clip on screen borders
00130   if(request->pos.x < 0) {
00131     surfacepartrequest->size.x += request->pos.x;
00132     if(surfacepartrequest->size.x <= 0)
00133       return;
00134     surfacepartrequest->source.x -= request->pos.x;
00135     request->pos.x = 0;
00136   }
00137   if(request->pos.y < 0) {
00138     surfacepartrequest->size.y += request->pos.y;
00139     if(surfacepartrequest->size.y <= 0)
00140       return;
00141     surfacepartrequest->source.y -= request->pos.y;
00142     request->pos.y = 0;
00143   }
00144   request->request_data = surfacepartrequest;
00145 
00146   requests->push_back(request);
00147 }
00148 
00149 void
00150 DrawingContext::draw_text(FontPtr font, const std::string& text,
00151                           const Vector& position, FontAlignment alignment, int layer, Color color)
00152 {
00153   DrawingRequest* request = new(obst) DrawingRequest();
00154 
00155   request->target = target;
00156   request->type = TEXT;
00157   request->pos = transform.apply(position);
00158   request->layer = layer;
00159   request->drawing_effect = transform.drawing_effect;
00160   request->alpha = transform.alpha;
00161   request->color = color;
00162 
00163   TextRequest* textrequest = new(obst) TextRequest();
00164   textrequest->font = font.get();
00165   textrequest->text = text;
00166   textrequest->alignment = alignment;
00167   request->request_data = textrequest;
00168 
00169   requests->push_back(request);
00170 }
00171 
00172 void
00173 DrawingContext::draw_center_text(FontPtr font, const std::string& text,
00174                                  const Vector& position, int layer, Color color)
00175 {
00176   draw_text(font, text, Vector(position.x + SCREEN_WIDTH/2, position.y),
00177             ALIGN_CENTER, layer, color);
00178 }
00179 
00180 void
00181 DrawingContext::draw_gradient(const Color& top, const Color& bottom, int layer)
00182 {
00183   DrawingRequest* request = new(obst) DrawingRequest();
00184 
00185   request->target = target;
00186   request->type = GRADIENT;
00187   request->pos = Vector(0,0);
00188   request->layer = layer;
00189 
00190   request->drawing_effect = transform.drawing_effect;
00191   request->alpha = transform.alpha;
00192 
00193   GradientRequest* gradientrequest = new(obst) GradientRequest();
00194   gradientrequest->top = top;
00195   gradientrequest->bottom = bottom;
00196   request->request_data = gradientrequest;
00197 
00198   requests->push_back(request);
00199 }
00200 
00201 void
00202 DrawingContext::draw_filled_rect(const Vector& topleft, const Vector& size,
00203                                  const Color& color, int layer)
00204 {
00205   DrawingRequest* request = new(obst) DrawingRequest();
00206 
00207   request->target = target;
00208   request->type = FILLRECT;
00209   request->pos = transform.apply(topleft);
00210   request->layer = layer;
00211 
00212   request->drawing_effect = transform.drawing_effect;
00213   request->alpha = transform.alpha;
00214 
00215   FillRectRequest* fillrectrequest = new(obst) FillRectRequest();
00216   fillrectrequest->size = size;
00217   fillrectrequest->color = color;
00218   fillrectrequest->color.alpha = color.alpha * transform.alpha;
00219   fillrectrequest->radius = 0.0f;
00220   request->request_data = fillrectrequest;
00221 
00222   requests->push_back(request);
00223 }
00224 
00225 void
00226 DrawingContext::draw_filled_rect(const Rectf& rect, const Color& color,
00227                                  int layer)
00228 {
00229   draw_filled_rect(rect, color, 0.0f, layer);
00230 }
00231 
00232 void
00233 DrawingContext::draw_filled_rect(const Rectf& rect, const Color& color, float radius, int layer)
00234 {
00235   DrawingRequest* request = new(obst) DrawingRequest();
00236 
00237   request->target = target;
00238   request->type   = FILLRECT;
00239   request->pos    = transform.apply(rect.p1);
00240   request->layer  = layer;
00241 
00242   request->drawing_effect = transform.drawing_effect;
00243   request->alpha = transform.alpha;
00244 
00245   FillRectRequest* fillrectrequest = new(obst) FillRectRequest;
00246   fillrectrequest->size = Vector(rect.get_width(), rect.get_height());
00247   fillrectrequest->color = color;
00248   fillrectrequest->color.alpha = color.alpha * transform.alpha;
00249   fillrectrequest->radius = radius;
00250   request->request_data = fillrectrequest;
00251 
00252   requests->push_back(request); 
00253 }
00254 
00255 void
00256 DrawingContext::draw_inverse_ellipse(const Vector& pos, const Vector& size, const Color& color, int layer)
00257 {
00258   DrawingRequest* request = new(obst) DrawingRequest();
00259 
00260   request->target = target;
00261   request->type   = INVERSEELLIPSE;
00262   request->pos    = transform.apply(pos);
00263   request->layer  = layer;
00264 
00265   request->drawing_effect = transform.drawing_effect;
00266   request->alpha = transform.alpha;
00267 
00268   InverseEllipseRequest* ellipse = new(obst)InverseEllipseRequest;
00269   
00270   ellipse->color        = color;
00271   ellipse->color.alpha  = color.alpha * transform.alpha;
00272   ellipse->size         = size;
00273   request->request_data = ellipse;
00274 
00275   requests->push_back(request);     
00276 }
00277 
00278 Rectf
00279 DrawingContext::get_cliprect() const
00280 {
00281   return Rectf(get_translation().x, get_translation().y,
00282                get_translation().x + SCREEN_WIDTH, 
00283                get_translation().y + SCREEN_HEIGHT);
00284 }
00285 
00286 void
00287 DrawingContext::get_light(const Vector& position, Color* color)
00288 {
00289   if( ambient_color.red == 1.0f && ambient_color.green == 1.0f
00290       && ambient_color.blue  == 1.0f ) {
00291     *color = Color( 1.0f, 1.0f, 1.0f);
00292     return;
00293   }
00294 
00295   DrawingRequest* request = new(obst) DrawingRequest();
00296   request->target = target;
00297   request->type = GETLIGHT;
00298   request->pos = transform.apply(position);
00299 
00300   //There is no light offscreen.
00301   if(request->pos.x >= SCREEN_WIDTH || request->pos.y >= SCREEN_HEIGHT
00302      || request->pos.x < 0 || request->pos.y < 0){
00303     *color = Color( 0, 0, 0);
00304     return;
00305   }
00306 
00307   request->layer = LAYER_GUI; //make sure all get_light requests are handled last.
00308   GetLightRequest* getlightrequest = new(obst) GetLightRequest();
00309   getlightrequest->color_ptr = color;
00310   request->request_data = getlightrequest;
00311   lightmap_requests.push_back(request);
00312 }
00313 
00314 void
00315 DrawingContext::do_drawing()
00316 {
00317   assert(transformstack.empty());
00318   assert(target_stack.empty());
00319   transformstack.clear();
00320   target_stack.clear();
00321 
00322   //Use Lightmap if ambient color is not white.
00323   bool use_lightmap = ( ambient_color.red != 1.0f   || ambient_color.green != 1.0f ||
00324                         ambient_color.blue  != 1.0f );
00325 
00326   // PART1: create lightmap
00327   if(use_lightmap) {
00328     lightmap->start_draw(ambient_color);
00329     handle_drawing_requests(lightmap_requests);
00330     lightmap->end_draw();
00331 
00332     DrawingRequest* request = new(obst) DrawingRequest();
00333     request->target = NORMAL;
00334     request->type = DRAW_LIGHTMAP;
00335     request->layer = LAYER_HUD - 1;
00336     drawing_requests.push_back(request);
00337   }
00338   lightmap_requests.clear();
00339 
00340   handle_drawing_requests(drawing_requests);
00341   drawing_requests.clear();
00342   obstack_free(&obst, NULL);
00343   obstack_init(&obst);
00344 
00345   // if a screenshot was requested, take one
00346   if (screenshot_requested) {
00347     renderer->do_take_screenshot();
00348     screenshot_requested = false;
00349   }
00350 
00351   renderer->flip();
00352 }
00353 
00354 class RequestPtrCompare
00355 {
00356 public:
00357   bool operator()(const DrawingRequest* r1, const DrawingRequest* r2) const
00358   {
00359     return *r1 < *r2;
00360   }
00361 };
00362 
00363 void
00364 DrawingContext::handle_drawing_requests(DrawingRequests& requests)
00365 {
00366   std::stable_sort(requests.begin(), requests.end(), RequestPtrCompare());
00367 
00368   DrawingRequests::const_iterator i;
00369   for(i = requests.begin(); i != requests.end(); ++i) {
00370     const DrawingRequest& request = **i;
00371 
00372     switch(request.target) {
00373       case NORMAL:
00374         switch(request.type) {
00375           case SURFACE:
00376             renderer->draw_surface(request);
00377             break;
00378           case SURFACE_PART:
00379             renderer->draw_surface_part(request);
00380             break;
00381           case GRADIENT:
00382             renderer->draw_gradient(request);
00383             break;
00384           case TEXT:
00385           {
00386             const TextRequest* textrequest = (TextRequest*) request.request_data;
00387             textrequest->font->draw(renderer, textrequest->text, request.pos,
00388                                     textrequest->alignment, request.drawing_effect, request.color, request.alpha);
00389           }
00390           break;
00391           case FILLRECT:
00392             renderer->draw_filled_rect(request);
00393             break;
00394           case INVERSEELLIPSE:
00395             renderer->draw_inverse_ellipse(request);
00396             break;
00397           case DRAW_LIGHTMAP:
00398             lightmap->do_draw();
00399             break;
00400           case GETLIGHT:
00401             lightmap->get_light(request);
00402             break;
00403         }
00404         break;
00405       case LIGHTMAP:
00406         switch(request.type) {
00407           case SURFACE:
00408             lightmap->draw_surface(request);
00409             break;
00410           case SURFACE_PART:
00411             lightmap->draw_surface_part(request);
00412             break;
00413           case GRADIENT:
00414             lightmap->draw_gradient(request);
00415             break;
00416           case TEXT:
00417           {
00418             const TextRequest* textrequest = (TextRequest*) request.request_data;
00419             textrequest->font->draw(renderer, textrequest->text, request.pos,
00420                                     textrequest->alignment, request.drawing_effect, request.color, request.alpha);
00421           }
00422           break;
00423           case FILLRECT:
00424             lightmap->draw_filled_rect(request);
00425             break;
00426           case INVERSEELLIPSE:
00427             assert(!"InverseEllipse doesn't make sense on the lightmap");
00428             break;
00429           case DRAW_LIGHTMAP:
00430             lightmap->do_draw();
00431             break;
00432           case GETLIGHT:
00433             lightmap->get_light(request);
00434             break;
00435         }
00436         break;
00437     }
00438   }
00439 }
00440 
00441 void
00442 DrawingContext::push_transform()
00443 {
00444   transformstack.push_back(transform);
00445 }
00446 
00447 void
00448 DrawingContext::pop_transform()
00449 {
00450   assert(!transformstack.empty());
00451 
00452   transform = transformstack.back();
00453   transformstack.pop_back();
00454 }
00455 
00456 void
00457 DrawingContext::set_drawing_effect(DrawingEffect effect)
00458 {
00459   transform.drawing_effect = effect;
00460 }
00461 
00462 DrawingEffect
00463 DrawingContext::get_drawing_effect() const
00464 {
00465   return transform.drawing_effect;
00466 }
00467 
00468 void
00469 DrawingContext::set_alpha(float alpha)
00470 {
00471   transform.alpha = alpha;
00472 }
00473 
00474 float
00475 DrawingContext::get_alpha() const
00476 {
00477   return transform.alpha;
00478 }
00479 
00480 void
00481 DrawingContext::push_target()
00482 {
00483   target_stack.push_back(target);
00484 }
00485 
00486 void
00487 DrawingContext::pop_target()
00488 {
00489   set_target(target_stack.back());
00490   target_stack.pop_back();
00491 }
00492 
00493 void
00494 DrawingContext::set_target(Target target)
00495 {
00496   this->target = target;
00497   if(target == LIGHTMAP) {
00498     requests = &lightmap_requests;
00499   } else {
00500     assert(target == NORMAL);
00501     requests = &drawing_requests;
00502   }
00503 }
00504 
00505 void
00506 DrawingContext::set_ambient_color( Color new_color )
00507 {
00508   ambient_color = new_color;
00509 }
00510 
00511 void 
00512 DrawingContext::take_screenshot()
00513 {
00514   screenshot_requested = true;
00515 }
00516 
00517 /* EOF */

Generated on Mon Jun 9 03:38:24 2014 for SuperTux by  doxygen 1.5.1