4 // Copyright (C) 2006 Matthias Braun <matze@braunis.de>
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.
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.
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.
25 #include <SDL_image.h>
31 #include "drawing_context.hpp"
32 #include "surface.hpp"
35 #include "gameconfig.hpp"
37 #include "texture.hpp"
38 #include "texture_manager.hpp"
39 #include "obstack/obstackpp.hpp"
40 #define LIGHTMAP_DIV 5
44 SURFACE, SURFACE_PART, TEXT, GRADIENT, FILLRECT, LIGHTMAPREQUEST, GETLIGHT
47 struct SurfacePartRequest
49 const Surface* surface;
57 FontAlignment alignment;
60 struct GradientRequest
66 struct FillRectRequest
78 DrawingEffect drawing_effect;
88 color(1.0f, 1.0f, 1.0f, 1.0f)
91 bool operator<(const DrawingRequest& other) const
93 return layer < other.layer;
97 struct GetLightRequest
102 static inline int next_po2(int val)
111 DrawingContext::DrawingContext()
112 : ambient_color(1.0f, 1.0f, 1.0f, 1.0f), target(NORMAL), screenshot_requested(false)
114 screen = SDL_GetVideoSurface();
116 lightmap_width = screen->w / LIGHTMAP_DIV;
117 lightmap_height = screen->h / LIGHTMAP_DIV;
118 unsigned int width = next_po2(lightmap_width);
119 unsigned int height = next_po2(lightmap_height);
121 lightmap = new Texture(width, height, GL_RGB);
123 lightmap_uv_right = static_cast<float>(lightmap_width) / static_cast<float>(width);
124 lightmap_uv_bottom = static_cast<float>(lightmap_height) / static_cast<float>(height);
125 texture_manager->register_texture(lightmap);
127 requests = &drawing_requests;
132 DrawingContext::~DrawingContext()
134 obstack_free(&obst, NULL);
136 texture_manager->remove_texture(lightmap);
141 DrawingContext::draw_surface(const Surface* surface, const Vector& position,
142 float angle, const Color& color, const Blend& blend,
145 assert(surface != 0);
147 DrawingRequest* request = new(obst) DrawingRequest();
149 request->type = SURFACE;
150 request->pos = transform.apply(position);
152 if(request->pos.x >= SCREEN_WIDTH || request->pos.y >= SCREEN_HEIGHT
153 || request->pos.x + surface->get_width() < 0
154 || request->pos.y + surface->get_height() < 0)
157 request->layer = layer;
158 request->drawing_effect = transform.drawing_effect;
159 request->alpha = transform.alpha;
160 request->angle = angle;
161 request->color = color;
162 request->blend = blend;
164 request->request_data = const_cast<Surface*> (surface);
166 requests->push_back(request);
170 DrawingContext::draw_surface(const Surface* surface, const Vector& position,
173 draw_surface(surface, position, 0.0f, Color(1.0f, 1.0f, 1.0f), Blend(), layer);
177 DrawingContext::draw_surface_part(const Surface* surface, const Vector& source,
178 const Vector& size, const Vector& dest, int layer)
180 assert(surface != 0);
182 DrawingRequest* request = new(obst) DrawingRequest();
184 request->type = SURFACE_PART;
185 request->pos = transform.apply(dest);
186 request->layer = layer;
187 request->drawing_effect = transform.drawing_effect;
188 request->alpha = transform.alpha;
190 SurfacePartRequest* surfacepartrequest = new(obst) SurfacePartRequest();
191 surfacepartrequest->size = size;
192 surfacepartrequest->source = source;
193 surfacepartrequest->surface = surface;
195 // clip on screen borders
196 if(request->pos.x < 0) {
197 surfacepartrequest->size.x += request->pos.x;
198 if(surfacepartrequest->size.x <= 0)
200 surfacepartrequest->source.x -= request->pos.x;
203 if(request->pos.y < 0) {
204 surfacepartrequest->size.y += request->pos.y;
205 if(surfacepartrequest->size.y <= 0)
207 surfacepartrequest->source.y -= request->pos.y;
210 request->request_data = surfacepartrequest;
212 requests->push_back(request);
216 DrawingContext::draw_text(const Font* font, const std::string& text,
217 const Vector& position, FontAlignment alignment, int layer)
219 DrawingRequest* request = new(obst) DrawingRequest();
221 request->type = TEXT;
222 request->pos = transform.apply(position);
223 request->layer = layer;
224 request->drawing_effect = transform.drawing_effect;
225 request->alpha = transform.alpha;
227 TextRequest* textrequest = new(obst) TextRequest();
228 textrequest->font = font;
229 textrequest->text = text;
230 textrequest->alignment = alignment;
231 request->request_data = textrequest;
233 requests->push_back(request);
237 DrawingContext::draw_center_text(const Font* font, const std::string& text,
238 const Vector& position, int layer)
240 draw_text(font, text, Vector(position.x + SCREEN_WIDTH/2, position.y),
241 ALIGN_CENTER, layer);
245 DrawingContext::draw_gradient(const Color& top, const Color& bottom, int layer)
247 DrawingRequest* request = new(obst) DrawingRequest();
249 request->type = GRADIENT;
250 request->pos = Vector(0,0);
251 request->layer = layer;
253 request->drawing_effect = transform.drawing_effect;
254 request->alpha = transform.alpha;
256 GradientRequest* gradientrequest = new(obst) GradientRequest();
257 gradientrequest->top = top;
258 gradientrequest->bottom = bottom;
259 request->request_data = gradientrequest;
261 requests->push_back(request);
265 DrawingContext::draw_filled_rect(const Vector& topleft, const Vector& size,
266 const Color& color, int layer)
268 DrawingRequest* request = new(obst) DrawingRequest();
270 request->type = FILLRECT;
271 request->pos = transform.apply(topleft);
272 request->layer = layer;
274 request->drawing_effect = transform.drawing_effect;
275 request->alpha = transform.alpha;
277 FillRectRequest* fillrectrequest = new(obst) FillRectRequest();
278 fillrectrequest->size = size;
279 fillrectrequest->color = color;
280 fillrectrequest->color.alpha = color.alpha * transform.alpha;
281 request->request_data = fillrectrequest;
283 requests->push_back(request);
287 DrawingContext::draw_filled_rect(const Rect& rect, const Color& color,
290 DrawingRequest* request = new(obst) DrawingRequest();
292 request->type = FILLRECT;
293 request->pos = transform.apply(rect.p1);
294 request->layer = layer;
296 request->drawing_effect = transform.drawing_effect;
297 request->alpha = transform.alpha;
299 FillRectRequest* fillrectrequest = new(obst) FillRectRequest;
300 fillrectrequest->size = Vector(rect.get_width(), rect.get_height());
301 fillrectrequest->color = color;
302 fillrectrequest->color.alpha = color.alpha * transform.alpha;
303 request->request_data = fillrectrequest;
305 requests->push_back(request);
309 DrawingContext::get_light(const Vector& position, Color* color)
311 if( ambient_color.red == 1.0f && ambient_color.green == 1.0f
312 && ambient_color.blue == 1.0f ) {
313 *color = Color( 1.0f, 1.0f, 1.0f);
317 DrawingRequest* request = new(obst) DrawingRequest();
318 request->type = GETLIGHT;
319 request->pos = transform.apply(position);
321 //There is no light offscreen.
322 if(request->pos.x >= SCREEN_WIDTH || request->pos.y >= SCREEN_HEIGHT
323 || request->pos.x < 0 || request->pos.y < 0){
324 *color = Color( 0, 0, 0);
328 request->layer = LAYER_GUI; //make sure all get_light requests are handled last.
329 GetLightRequest* getlightrequest = new(obst) GetLightRequest();
330 getlightrequest->color_ptr = color;
331 request->request_data = getlightrequest;
332 lightmap_requests.push_back(request);
336 DrawingContext::get_light(const DrawingRequest& request) const
338 const GetLightRequest* getlightrequest
339 = (GetLightRequest*) request.request_data;
342 for( int i = 0; i<3; i++)
343 pixels[i] = 0.0f; //set to black
345 float posX = request.pos.x * lightmap_width / SCREEN_WIDTH;
346 float posY = screen->h - request.pos.y * lightmap_height / SCREEN_HEIGHT;
347 glReadPixels((GLint) posX, (GLint) posY , 1, 1, GL_RGB, GL_FLOAT, pixels);
348 *(getlightrequest->color_ptr) = Color( pixels[0], pixels[1], pixels[2]);
349 //printf("get_light %f/%f =>%f/%f r%f g%f b%f\n", request.pos.x, request.pos.y, posX, posY, pixels[0], pixels[1], pixels[2]);
353 DrawingContext::draw_surface_part(const DrawingRequest& request) const
355 const SurfacePartRequest* surfacepartrequest
356 = (SurfacePartRequest*) request.request_data;
358 surfacepartrequest->surface->draw_part(
359 surfacepartrequest->source.x, surfacepartrequest->source.y,
360 request.pos.x, request.pos.y,
361 surfacepartrequest->size.x, surfacepartrequest->size.y,
362 request.alpha, request.drawing_effect);
366 DrawingContext::draw_gradient(const DrawingRequest& request) const
368 const GradientRequest* gradientrequest
369 = (GradientRequest*) request.request_data;
370 const Color& top = gradientrequest->top;
371 const Color& bottom = gradientrequest->bottom;
373 glDisable(GL_TEXTURE_2D);
375 glColor4f(top.red, top.green, top.blue, top.alpha);
377 glVertex2f(SCREEN_WIDTH, 0);
378 glColor4f(bottom.red, bottom.green, bottom.blue, bottom.alpha);
379 glVertex2f(SCREEN_WIDTH, SCREEN_HEIGHT);
380 glVertex2f(0, SCREEN_HEIGHT);
382 glEnable(GL_TEXTURE_2D);
383 glColor4f(1, 1, 1, 1);
387 DrawingContext::draw_text(const DrawingRequest& request) const
389 const TextRequest* textrequest = (TextRequest*) request.request_data;
391 textrequest->font->draw(textrequest->text, request.pos,
392 textrequest->alignment, request.drawing_effect, request.alpha);
396 DrawingContext::draw_filled_rect(const DrawingRequest& request) const
398 const FillRectRequest* fillrectrequest
399 = (FillRectRequest*) request.request_data;
401 float x = request.pos.x;
402 float y = request.pos.y;
403 float w = fillrectrequest->size.x;
404 float h = fillrectrequest->size.y;
406 glDisable(GL_TEXTURE_2D);
407 glColor4f(fillrectrequest->color.red, fillrectrequest->color.green,
408 fillrectrequest->color.blue, fillrectrequest->color.alpha);
413 glVertex2f(x+w, y+h);
416 glEnable(GL_TEXTURE_2D);
418 glColor4f(1, 1, 1, 1);
422 DrawingContext::draw_lightmap(const DrawingRequest& request) const
424 const Texture* texture = reinterpret_cast<Texture*> (request.request_data);
426 // multiple the lightmap with the framebuffer
427 glBlendFunc(GL_DST_COLOR, GL_ZERO);
429 glBindTexture(GL_TEXTURE_2D, texture->get_handle());
432 glTexCoord2f(0, lightmap_uv_bottom);
435 glTexCoord2f(lightmap_uv_right, lightmap_uv_bottom);
436 glVertex2f(SCREEN_WIDTH, 0);
438 glTexCoord2f(lightmap_uv_right, 0);
439 glVertex2f(SCREEN_WIDTH, SCREEN_HEIGHT);
442 glVertex2f(0, SCREEN_HEIGHT);
446 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
450 DrawingContext::do_drawing()
453 assert(transformstack.empty());
454 assert(target_stack.empty());
456 transformstack.clear();
457 target_stack.clear();
459 //Use Lightmap if ambient color is not white.
460 bool use_lightmap = ( ambient_color.red != 1.0f || ambient_color.green != 1.0f ||
461 ambient_color.blue != 1.0f );
463 // PART1: create lightmap
465 glViewport(0, screen->h - lightmap_height, lightmap_width, lightmap_height);
466 glMatrixMode(GL_PROJECTION);
468 glOrtho(0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, -1.0, 1.0);
469 glMatrixMode(GL_MODELVIEW);
472 glClearColor( ambient_color.red, ambient_color.green, ambient_color.blue, 1 );
473 glClear(GL_COLOR_BUFFER_BIT);
474 handle_drawing_requests(lightmap_requests);
475 lightmap_requests.clear();
478 glBindTexture(GL_TEXTURE_2D, lightmap->get_handle());
479 glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, screen->h - lightmap_height, lightmap_width, lightmap_height);
481 glViewport(0, 0, screen->w, screen->h);
482 glMatrixMode(GL_PROJECTION);
484 glOrtho(0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, -1.0, 1.0);
485 glMatrixMode(GL_MODELVIEW);
489 // add a lightmap drawing request into the queue
490 DrawingRequest* request = new(obst) DrawingRequest();
491 request->type = LIGHTMAPREQUEST;
492 request->layer = LAYER_HUD - 1;
493 request->request_data = lightmap;
494 requests->push_back(request);
497 //glClear(GL_COLOR_BUFFER_BIT);
498 handle_drawing_requests(drawing_requests);
499 drawing_requests.clear();
500 obstack_free(&obst, NULL);
502 assert_gl("drawing");
504 // if a screenshot was requested, take one
505 if (screenshot_requested) {
506 do_take_screenshot();
507 screenshot_requested = false;
510 SDL_GL_SwapBuffers();
513 class RequestPtrCompare
514 : public std::binary_function<const DrawingRequest*,
515 const DrawingRequest*,
519 bool operator()(const DrawingRequest* r1, const DrawingRequest* r2) const
526 DrawingContext::handle_drawing_requests(DrawingRequests& requests) const
528 std::stable_sort(requests.begin(), requests.end(), RequestPtrCompare());
530 DrawingRequests::const_iterator i;
531 for(i = requests.begin(); i != requests.end(); ++i) {
532 const DrawingRequest& request = **i;
534 switch(request.type) {
537 const Surface* surface = (const Surface*) request.request_data;
538 if (request.angle == 0.0f &&
539 request.color.red == 1.0f && request.color.green == 1.0f &&
540 request.color.blue == 1.0f && request.color.alpha == 1.0f) {
541 surface->draw(request.pos.x, request.pos.y, request.alpha,
542 request.drawing_effect);
544 surface->draw(request.pos.x, request.pos.y,
545 request.alpha, request.angle, request.color,
546 request.blend, request.drawing_effect);
551 draw_surface_part(request);
554 draw_gradient(request);
560 draw_filled_rect(request);
562 case LIGHTMAPREQUEST:
563 draw_lightmap(request);
573 DrawingContext::push_transform()
575 transformstack.push_back(transform);
579 DrawingContext::pop_transform()
581 assert(!transformstack.empty());
583 transform = transformstack.back();
584 transformstack.pop_back();
588 DrawingContext::set_drawing_effect(DrawingEffect effect)
590 transform.drawing_effect = effect;
594 DrawingContext::get_drawing_effect() const
596 return transform.drawing_effect;
600 DrawingContext::set_alpha(float alpha)
602 transform.alpha = alpha;
606 DrawingContext::get_alpha() const
608 return transform.alpha;
612 DrawingContext::push_target()
614 target_stack.push_back(target);
618 DrawingContext::pop_target()
620 set_target(target_stack.back());
621 target_stack.pop_back();
625 DrawingContext::set_target(Target target)
627 this->target = target;
628 if(target == LIGHTMAP) {
629 requests = &lightmap_requests;
631 assert(target == NORMAL);
632 requests = &drawing_requests;
637 DrawingContext::set_ambient_color( Color new_color )
639 ambient_color = new_color;
643 DrawingContext::do_take_screenshot()
645 // [Christoph] TODO: Yes, this method also takes care of the actual disk I/O. Split it?
647 // create surface to hold screenshot
648 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
649 SDL_Surface* shot_surf = SDL_CreateRGBSurface(SDL_SWSURFACE, SCREEN_WIDTH, SCREEN_HEIGHT, 24, 0x00FF0000, 0x0000FF00, 0x000000FF, 0);
651 SDL_Surface* shot_surf = SDL_CreateRGBSurface(SDL_SWSURFACE, SCREEN_WIDTH, SCREEN_HEIGHT, 24, 0x000000FF, 0x0000FF00, 0x00FF0000, 0);
654 log_warning << "Could not create RGB Surface to contain screenshot" << std::endl;
658 // read pixels into array
659 char* pixels = new char[3 * SCREEN_WIDTH * SCREEN_HEIGHT];
661 log_warning << "Could not allocate memory to store screenshot" << std::endl;
662 SDL_FreeSurface(shot_surf);
665 glReadPixels(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, GL_RGB, GL_UNSIGNED_BYTE, pixels);
667 // copy array line-by-line
668 for (int i = 0; i < SCREEN_HEIGHT; i++) {
669 char* src = pixels + (3 * SCREEN_WIDTH * (SCREEN_HEIGHT - i - 1));
670 char* dst = ((char*)shot_surf->pixels) + i * shot_surf->pitch;
671 memcpy(dst, src, 3 * SCREEN_WIDTH);
678 static const std::string writeDir = PHYSFS_getWriteDir();
679 static const std::string dirSep = PHYSFS_getDirSeparator();
680 static const std::string baseName = "screenshot";
681 static const std::string fileExt = ".bmp";
682 std::string fullFilename;
683 for (int num = 0; num < 1000; num++) {
684 std::ostringstream oss;
686 oss << std::setw(3) << std::setfill('0') << num;
688 std::string fileName = oss.str();
689 fullFilename = writeDir + dirSep + fileName;
690 if (!PHYSFS_exists(fileName.c_str())) {
691 SDL_SaveBMP(shot_surf, fullFilename.c_str());
692 log_debug << "Wrote screenshot to \"" << fullFilename << "\"" << std::endl;
693 SDL_FreeSurface(shot_surf);
697 log_warning << "Did not save screenshot, because all files up to \"" << fullFilename << "\" already existed" << std::endl;
698 SDL_FreeSurface(shot_surf);
702 DrawingContext::take_screenshot()
704 screenshot_requested = true;