00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
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
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
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;
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
00323 bool use_lightmap = ( ambient_color.red != 1.0f || ambient_color.green != 1.0f ||
00324 ambient_color.blue != 1.0f );
00325
00326
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
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