instancerenderer.cpp

00001 /***************************************************************************
00002  *   Copyright (C) 2005-2008 by the FIFE team                              *
00003  *   http://www.fifengine.de                                               *
00004  *   This file is part of FIFE.                                            *
00005  *                                                                         *
00006  *   FIFE is free software; you can redistribute it and/or                 *
00007  *   modify it under the terms of the GNU Lesser General Public            *
00008  *   License as published by the Free Software Foundation; either          *
00009  *   version 2.1 of the License, or (at your option) any later version.    *
00010  *                                                                         *
00011  *   This library is distributed in the hope that it will be useful,       *
00012  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00013  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
00014  *   Lesser General Public License for more details.                       *
00015  *                                                                         *
00016  *   You should have received a copy of the GNU Lesser General Public      *
00017  *   License along with this library; if not, write to the                 *
00018  *   Free Software Foundation, Inc.,                                       *
00019  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA          *
00020  ***************************************************************************/
00021 
00022 // Standard C++ library includes
00023 
00024 // 3rd party library includes
00025 #include <boost/bind.hpp>
00026 
00027 // FIFE includes
00028 // These includes are split up in two parts, separated by one empty line
00029 // First block: files included from the FIFE root src directory
00030 // Second block: files included from the same folder
00031 #include "video/renderbackend.h"
00032 #include "video/image.h"
00033 #include "video/imagemanager.h"
00034 #include "video/sdl/sdlimage.h"
00035 #include "video/animation.h"
00036 #include "util/math/fife_math.h"
00037 #include "util/log/logger.h"
00038 #include "util/time/timemanager.h"
00039 #include "model/metamodel/grids/cellgrid.h"
00040 #include "model/metamodel/action.h"
00041 #include "model/structures/instance.h"
00042 #include "model/structures/layer.h"
00043 #include "model/structures/location.h"
00044 #include "model/structures/map.h"
00045 #include "video/opengl/fife_opengl.h"
00046 
00047 #include "view/camera.h"
00048 #include "view/visual.h"
00049 #include "instancerenderer.h"
00050 
00051 #include "video/opengle/gleimage.h"
00052 
00053 
00054 namespace {
00055     uint32_t scale(uint32_t val, double factor) {
00056         return static_cast<uint32_t>(ceil(static_cast<double>(val) * factor));
00057     }
00058 }
00059 
00060 namespace FIFE {
00061     static Logger _log(LM_VIEWVIEW);
00062 
00063     class InstanceRendererDeleteListener : public InstanceDeleteListener {
00064     public:
00065         InstanceRendererDeleteListener(InstanceRenderer* r) {
00066             m_renderer = r;
00067         }
00068         virtual ~InstanceRendererDeleteListener() {}
00069 
00070         virtual void onInstanceDeleted(Instance* instance) {
00071             m_renderer->removeInstance(instance);
00072         }
00073 
00074     private:
00075         InstanceRenderer* m_renderer;
00076     };
00077 
00078     InstanceRenderer::OutlineInfo::OutlineInfo(InstanceRenderer* r):
00079         r(0),
00080         g(0),
00081         b(0),
00082         width(1),
00083         dirty(false),
00084         curimg(NULL),
00085         renderer(r) {
00086     }
00087     InstanceRenderer::ColoringInfo::ColoringInfo(InstanceRenderer* r):
00088         r(0),
00089         g(0),
00090         b(0),
00091         dirty(false),
00092         curimg(NULL),
00093         renderer(r) {
00094     }
00095 
00096     InstanceRenderer::AreaInfo::AreaInfo():
00097         instance(NULL),
00098         groups(),
00099         w(1),
00100         h(1),
00101         trans(0),
00102         front(true),
00103         z(0) {
00104     }
00105 
00106     InstanceRenderer::OutlineInfo::~OutlineInfo() {
00107         renderer->addToCheck(outline);
00108     }
00109 
00110     InstanceRenderer::ColoringInfo::~ColoringInfo() {
00111         if (renderer->needColorBinding()) {
00112             renderer->addToCheck(overlay);
00113         }
00114     }
00115 
00116     InstanceRenderer::AreaInfo::~AreaInfo() {
00117     }
00118 
00119     InstanceRenderer* InstanceRenderer::getInstance(IRendererContainer* cnt) {
00120         return dynamic_cast<InstanceRenderer*>(cnt->getRenderer("InstanceRenderer"));
00121     }
00122 
00123     InstanceRenderer::InstanceRenderer(RenderBackend* renderbackend, int32_t position):
00124         RendererBase(renderbackend, position),
00125         m_area_layer(false),
00126         m_interval(60*1000),
00127         m_timer_enabled(false) {
00128         setEnabled(true);
00129         if(m_renderbackend->getName() == "OpenGLe") {
00130             m_need_sorting = false;
00131         } else {
00132             m_need_sorting = true;
00133             if(m_renderbackend->getName() == "SDL") {
00134                 m_need_bind_coloring = true;
00135             } else {
00136                 m_need_bind_coloring = false;
00137             }
00138         }
00139         // init timer
00140         m_timer.setInterval(m_interval);
00141         m_timer.setCallback(boost::bind(&InstanceRenderer::check, this));
00142         // create delete listener
00143         m_delete_listener = new InstanceRendererDeleteListener(this);
00144     }
00145 
00146     InstanceRenderer::InstanceRenderer(const InstanceRenderer& old):
00147         RendererBase(old),
00148         m_area_layer(false),
00149         m_interval(old.m_interval),
00150         m_timer_enabled(false) {
00151         setEnabled(true);
00152         if(m_renderbackend->getName() == "OpenGLe") {
00153             m_need_sorting = false;
00154         } else {
00155             m_need_sorting = true;
00156             if(m_renderbackend->getName() == "SDL") {
00157                 m_need_bind_coloring = true;
00158             } else {
00159                 m_need_bind_coloring = false;
00160             }
00161         }
00162         // init timer
00163         m_timer.setInterval(m_interval);
00164         m_timer.setCallback(boost::bind(&InstanceRenderer::check, this));
00165         // create delete listener
00166         m_delete_listener = new InstanceRendererDeleteListener(this);
00167     }
00168 
00169     RendererBase* InstanceRenderer::clone() {
00170         return new InstanceRenderer(*this);
00171     }
00172 
00173     InstanceRenderer::~InstanceRenderer() {
00174         // remove listener from instances
00175         if (!m_assigned_instances.empty()) {
00176             reset();
00177         }
00178         // delete listener
00179         delete m_delete_listener;
00180     }
00181 
00182     void InstanceRenderer::render(Camera* cam, Layer* layer, RenderList& instances) {
00183 //      FL_DBG(_log, "Iterating layer...");
00184         CellGrid* cg = layer->getCellGrid();
00185         if (!cg) {
00186             FL_WARN(_log, "No cellgrid assigned to layer, cannot draw instances");
00187             return;
00188         }
00189 
00190         if(m_need_sorting) {
00191             renderAlreadySorted(cam, layer, instances);
00192         } else {
00193             renderUnsorted(cam, layer, instances);
00194         }
00195     }
00196 
00197     void InstanceRenderer::renderUnsorted(Camera* cam, Layer* layer, RenderList& instances)
00198     {
00199         // TODO transparent area stuff was cut off
00200         const bool any_effects = !(m_instance_outlines.empty() && m_instance_colorings.empty());
00201         const bool unlit = !m_unlit_groups.empty();
00202         uint32_t lm = m_renderbackend->getLightingModel();
00203 
00204         // Get layer index (this is needed for tweaking vertexZ for OpenGL renderbackend)
00205         Map* parent = layer->getMap();
00206         int num_layers = parent->getLayerCount();
00207         int this_layer = 1; // we don't need 0 indexed
00208         const std::list<Layer*>& layers = parent->getLayers();
00209         std::list<Layer*>::const_iterator iter = layers.begin();
00210         for (; iter != layers.end(); ++iter, ++this_layer) {
00211             if (*iter == layer) {
00212                 break;
00213             }
00214         }
00215 
00216         // This values comes from glOrtho settings
00217         static const double global_z_min = -100.0;
00218         static const double global_z_max = 100.0;
00219 #if 0
00220         static const double depth_range = fabs(global_z_min - global_z_max);
00221 
00222         // This is how much depth we can sacrifice for given layer
00223         // f.e: We have 200 depth range and 4 layers. That means we can assign
00224         // each layer 50 depth range:
00225         // 1st layer gets -100..-50
00226         // 2nd layer gets  -50..0
00227         // 3rd layer gets    0..50
00228         // 4th layer gets   50..100
00229         double layer_depth_range = depth_range / static_cast<double>(num_layers);
00230         // What's left, is to compute z offset for given layer
00231         double layer_z_offset = global_z_min +
00232             layer_depth_range * static_cast<double>(this_layer) -
00233             layer_depth_range * 0.5;
00234 #else
00235 
00236         // 2nd version, better usage of depth buffer as values closer to near plane in depth buffer have more precise (1/z)
00237         double layer_z_offset = global_z_max - (num_layers - (this_layer - 1)) * 20;
00238 #endif
00239         // thanks to multimap, we will have transparent instances already sorted by their z value (key)
00240         std::multimap<float, RenderItem*> transparentInstances;
00241 
00242         RenderList::iterator instance_it = instances.begin();
00243         for (;instance_it != instances.end(); ++instance_it) {
00244 //          FL_DBG(_log, "Iterating instances...");
00245             Instance* instance = (*instance_it)->instance;
00246             RenderItem& vc = **instance_it;
00247             float vertexZ = static_cast<float>(layer_z_offset + vc.screenpoint.z);
00248 
00249             // if the instance is opacous
00250             if(vc.transparency == 255) {
00251                 if (any_effects) {
00252                     InstanceToOutlines_t::iterator outline_it = m_instance_outlines.find(instance);
00253                     const bool outline = outline_it != m_instance_outlines.end();
00254                     if (outline) {
00255                         Image* outline = bindOutline(outline_it->second, vc, cam);
00256                         outline->renderZ(vc.dimensions, vertexZ, 255, lm != 0 ? true : false);
00257                         vc.image->renderZ(vc.dimensions, vertexZ, 255);
00258                     }
00259 
00260                     InstanceToColoring_t::iterator coloring_it = m_instance_colorings.find(instance);
00261                     const bool coloring = coloring_it != m_instance_colorings.end();
00262                     if (coloring) {
00263                         uint8_t rgb[3] = { coloring_it->second.r, coloring_it->second.g, coloring_it->second.b };
00264                         vc.image->renderZ(vc.dimensions, vertexZ, 255, false, rgb);
00265                     }
00266 
00267                     if (outline || coloring) {
00268                         continue;
00269                     }
00270                 }
00271 
00272                 // if we use lighting and appointed some of the instances as unlit
00273                 if(lm != 0 && unlit) {
00274                     bool found = false;
00275                     std::string lit_name = instance->getObject()->getNamespace();
00276                     std::list<std::string>::iterator unlit_it = m_unlit_groups.begin();
00277                     for(;unlit_it != m_unlit_groups.end(); ++unlit_it) {
00278                         if(lit_name.find(*unlit_it) != std::string::npos) {
00279                             found = true;
00280                             break;
00281                         }
00282                     }
00283 
00284                     vc.image->renderZ(vc.dimensions, vertexZ, 255, found ? true : false);
00285                     continue;
00286                 }
00287 
00288                 vc.image->renderZ(vc.dimensions, vertexZ, 255);
00289             } else {
00290                 transparentInstances.insert(std::pair<float, RenderItem*>(vertexZ, &vc));
00291             }
00292         }
00293 
00294         // iterate through all (semi) transparent instances
00295         if(!transparentInstances.empty()) {
00296             std::multimap<float, RenderItem*>::iterator it = transparentInstances.begin();
00297             for( ; it != transparentInstances.end(); ++it) {
00298                 RenderItem& vc = *(it->second);
00299                 uint8_t alpha = vc.transparency;// or? 255 - vc.transparency;
00300                 float vertexZ = it->first;
00301 
00302                 if (any_effects) {
00303                     InstanceToOutlines_t::iterator outline_it = m_instance_outlines.find(vc.instance);
00304                     const bool outline = outline_it != m_instance_outlines.end();
00305                     if (outline) {
00306                         Image* outline = bindOutline(outline_it->second, vc, cam);
00307                         outline->renderZ(vc.dimensions, vertexZ, alpha, lm != 0 ? true : false);
00308                         vc.image->renderZ(vc.dimensions, vertexZ, alpha);
00309                     }
00310 
00311                     InstanceToColoring_t::iterator coloring_it = m_instance_colorings.find(vc.instance);
00312                     const bool coloring = coloring_it != m_instance_colorings.end();
00313                     if (coloring) {
00314                         uint8_t rgb[3] = { coloring_it->second.r, coloring_it->second.g, coloring_it->second.b };
00315                         vc.image->renderZ(vc.dimensions, vertexZ, alpha, false, rgb);
00316                     }
00317 
00318                     if (outline || coloring) {
00319                         continue;
00320                     }
00321                 }
00322 
00323                 // if we use lighting and appointed some of the instances as unlit
00324                 if(lm != 0 && unlit) {
00325                     bool found = false;
00326                     std::string lit_name = vc.instance->getObject()->getNamespace();
00327                     std::list<std::string>::iterator unlit_it = m_unlit_groups.begin();
00328                     for(;unlit_it != m_unlit_groups.end(); ++unlit_it) {
00329                         if(lit_name.find(*unlit_it) != std::string::npos) {
00330                             found = true;
00331                             break;
00332                         }
00333                     }
00334 
00335                     vc.image->renderZ(vc.dimensions, vertexZ, alpha, found ? true : false);
00336                     continue;
00337                 }
00338 
00339                 vc.image->renderZ(vc.dimensions, vertexZ, alpha);
00340             }
00341         }
00342     }
00343 
00344     void InstanceRenderer::renderAlreadySorted(Camera* cam, Layer* layer, RenderList& instances) {
00345         const bool any_effects = !(m_instance_outlines.empty() && m_instance_colorings.empty());
00346         const bool unlit = !m_unlit_groups.empty();
00347         uint32_t lm = m_renderbackend->getLightingModel();
00348 
00349         m_area_layer = false;
00350         if(!m_instance_areas.empty()) {
00351             InstanceToAreas_t::iterator area_it = m_instance_areas.begin();
00352             for(;area_it != m_instance_areas.end(); area_it++) {
00353                 AreaInfo& info = area_it->second;
00354                 if(info.instance->getLocation().getLayer() == layer) {
00355                     if(info.front) {
00356                         DoublePoint3D instance_posv = cam->toVirtualScreenCoordinates(info.instance->getLocation().getMapCoordinates());
00357                         info.z = instance_posv.z;
00358                     }
00359                     m_area_layer = true;
00360                 }
00361             }
00362         }
00363 
00364         RenderList::iterator instance_it = instances.begin();
00365         for (;instance_it != instances.end(); ++instance_it) {
00366 //          FL_DBG(_log, "Iterating instances...");
00367             Instance* instance = (*instance_it)->instance;
00368             RenderItem& vc = **instance_it;
00369 
00370             if(m_area_layer) {
00371                 InstanceToAreas_t::iterator areas_it = m_instance_areas.begin();
00372                 for(;areas_it != m_instance_areas.end(); areas_it++) {
00373                     AreaInfo& infoa = areas_it->second;
00374                     if(infoa.front) {
00375                         if(infoa.z >= vc.screenpoint.z) {
00376                             continue;
00377                         }
00378                     }
00379 
00380                     std::string str_name = instance->getObject()->getNamespace();
00381                     std::list<std::string>::iterator group_it = infoa.groups.begin();
00382                     for(;group_it != infoa.groups.end(); ++group_it) {
00383                         if(str_name.find((*group_it)) != std::string::npos) {
00384                             ScreenPoint p;
00385                             Rect rec;
00386                             p = cam->toScreenCoordinates(infoa.instance->getLocation().getMapCoordinates());
00387                             rec.x = p.x - infoa.w / 2;
00388                             rec.y = p.y - infoa.h / 2;
00389                             rec.w = infoa.w;
00390                             rec.h = infoa.h;
00391                             if(infoa.instance != instance && vc.dimensions.intersects(rec)) {
00392                                 vc.transparency = 255 - infoa.trans;
00393                             }
00394                         }
00395                     }
00396                 }
00397             }
00398 
00399 //          FL_DBG(_log, LMsg("Instance layer coordinates = ") << instance->getLocationRef().getLayerCoordinates());
00400 
00401             if (any_effects) {
00402                 InstanceToOutlines_t::iterator outline_it = m_instance_outlines.find(instance);
00403                 const bool outline = outline_it != m_instance_outlines.end();
00404                 if (outline) {
00405                     if (lm != 0) {
00406                         // first render normal image without stencil and alpha test (0)
00407                         // so it wont look aliased and then with alpha test render only outline (its 'binary' image)
00408                         vc.image->render(vc.dimensions, vc.transparency);
00409                         bindOutline(outline_it->second, vc, cam)->render(vc.dimensions, vc.transparency);
00410                         m_renderbackend->changeRenderInfos(1, 4, 5, false, true, 255, REPLACE, ALWAYS);
00411                     } else {
00412                         bindOutline(outline_it->second, vc, cam)->render(vc.dimensions, vc.transparency);
00413                         vc.image->render(vc.dimensions, vc.transparency);
00414                     }
00415                 }
00416 
00417                 InstanceToColoring_t::iterator coloring_it = m_instance_colorings.find(instance);
00418                 const bool coloring = coloring_it != m_instance_colorings.end();
00419                 if (coloring) {
00420                     if(m_need_bind_coloring) {
00421                         bindColoring(coloring_it->second, vc, cam)->render(vc.dimensions, vc.transparency);
00422                         m_renderbackend->changeRenderInfos(1, 4, 5, true, false, 0, KEEP, ALWAYS);
00423                     } else {
00424                         uint8_t rgb[3] = { coloring_it->second.r, coloring_it->second.g, coloring_it->second.b };
00425                         vc.image->render(vc.dimensions, vc.transparency, rgb);
00426                         m_renderbackend->changeRenderInfos(1, 4, 5, true, false, 0, KEEP, ALWAYS);
00427                     }
00428                 }
00429 
00430                 if (outline || coloring) {
00431                     continue;
00432                 }
00433             }
00434             if(lm != 0) {
00435                 if(unlit) {
00436                     bool found = false;
00437                     std::string lit_name = instance->getObject()->getNamespace();
00438                     std::list<std::string>::iterator unlit_it = m_unlit_groups.begin();
00439                     for(;unlit_it != m_unlit_groups.end(); ++unlit_it) {
00440                         if(lit_name.find(*unlit_it) != std::string::npos) {
00441                             found = true;
00442                             break;
00443                         }
00444                     }
00445                     vc.image->render(vc.dimensions, vc.transparency);
00446                     if (found) {
00447                         m_renderbackend->changeRenderInfos(1, 4, 5, true, true, 255, REPLACE, ALWAYS);
00448                     } else {
00449                         m_renderbackend->changeRenderInfos(1, 4, 5, true, true, 0, ZERO, ALWAYS);
00450                     }
00451                     continue;
00452                 }
00453             }
00454             vc.image->render(vc.dimensions, vc.transparency);
00455 
00456         }
00457     }
00458 
00459     inline bool aboveThreshold(int32_t threshold, int32_t alpha, int32_t prev_alpha) {
00460         if(threshold > 1) {
00461             // new behavior
00462             if (((alpha - threshold) >= 0 || (prev_alpha - threshold) >= 0) && (alpha != prev_alpha)) {
00463                 return true;
00464             } else {
00465                 return false;
00466             }
00467         } else {
00468             // old behavior
00469             if((alpha == 0 || prev_alpha == 0) && (alpha != prev_alpha)) {
00470                 return true;
00471             } else {
00472                 return false;
00473             }
00474         }
00475     }
00476 
00477     Image* InstanceRenderer::bindOutline(OutlineInfo& info, RenderItem& vc, Camera* cam) {
00478         bool valid = isValidImage(info.outline);
00479         if (!info.dirty && info.curimg == vc.image.get() && valid) {
00480             removeFromCheck(info.outline);
00481             // optimization for outline that has not changed
00482             return info.outline.get();
00483         } else {
00484             info.curimg = vc.image.get();
00485         }
00486 
00487         // if outline has changed we can maybe free the old effect image
00488         if (valid) {
00489             addToCheck(info.outline);
00490         }
00491 
00492         // NOTE: Since r3721 outline is just the 'border' so to render everything correctly
00493         // we need to first render normal image, and then its outline.
00494         // This helps much with lighting stuff and doesn't require from us to copy image.
00495 
00496         bool found = false;
00497         // create name
00498         std::stringstream sts;
00499         sts << vc.image.get()->getName() << "," << static_cast<uint32_t>(info.r) << "," << 
00500             static_cast<uint32_t>(info.g) << "," << static_cast<uint32_t>(info.b) << "," << info.width;
00501         // search image
00502         if (ImageManager::instance()->exists(sts.str())) {
00503             info.outline = ImageManager::instance()->getPtr(sts.str());
00504             if (isValidImage(info.outline)) {
00505                 removeFromCheck(info.outline);
00506                 // mark outline as not dirty since we found it here
00507                 info.dirty = false;
00508                 return info.outline.get();
00509             }
00510             found = true;
00511         }
00512 
00513             
00514         // With lazy loading we can come upon a situation where we need to generate outline from
00515         // uninitialised shared image
00516         if(vc.image->isSharedImage()) {
00517             vc.image->forceLoadInternal();
00518         }
00519 
00520         SDL_Surface* surface = vc.image->getSurface();
00521         SDL_Surface* outline_surface = SDL_CreateRGBSurface(SDL_SWSURFACE | SDL_SRCALPHA,
00522             vc.image->getWidth(), vc.image->getHeight(), 32,
00523             RMASK, GMASK, BMASK, AMASK);
00524 
00525         // TODO: optimize...
00526         uint8_t r, g, b, a = 0;
00527 
00528         // vertical sweep
00529         for (int32_t x = 0; x < outline_surface->w; x ++) {
00530             int32_t prev_a = 0;
00531             for (int32_t y = 0; y < outline_surface->h; y ++) {
00532                 vc.image->getPixelRGBA(x, y, &r, &g, &b, &a);
00533                 if (aboveThreshold(info.threshold, static_cast<int32_t>(a), prev_a)) {
00534                     if (a < prev_a) {
00535                         for (int32_t yy = y; yy < y + info.width; yy++) {
00536                             Image::putPixel(outline_surface, x, yy, info.r, info.g, info.b);
00537                         }
00538                     } else {
00539                         for (int32_t yy = y - info.width; yy < y; yy++) {
00540                             Image::putPixel(outline_surface, x, yy, info.r, info.g, info.b);
00541                         }
00542                     }
00543                 }
00544                 prev_a = a;
00545             }
00546         }
00547         // horizontal sweep
00548         for (int32_t y = 0; y < outline_surface->h; y ++) {
00549             int32_t prev_a = 0;
00550             for (int32_t x = 0; x < outline_surface->w; x ++) {
00551                 vc.image->getPixelRGBA(x, y, &r, &g, &b, &a);
00552                 if (aboveThreshold(info.threshold, static_cast<int32_t>(a), prev_a)) {
00553                     if (a < prev_a) {
00554                         for (int32_t xx = x; xx < x + info.width; xx++) {
00555                             Image::putPixel(outline_surface, xx, y, info.r, info.g, info.b);
00556                         }
00557                     } else {
00558                         for (int32_t xx = x - info.width; xx < x; xx++) {
00559                             Image::putPixel(outline_surface, xx, y, info.r, info.g, info.b);
00560                         }
00561                     }
00562                 }
00563                 prev_a = a;
00564             }
00565         }
00566 
00567         // In case of OpenGL backend, SDLImage needs to be converted
00568         Image* img = m_renderbackend->createImage(sts.str(), outline_surface);
00569         img->setState(IResource::RES_LOADED);
00570 
00571         if (found) {
00572             // image exists but is not "loaded"
00573             removeFromCheck(info.outline);
00574             ImagePtr temp(img);
00575             info.outline.get()->copySubimage(0, 0, temp);
00576             info.outline.get()->setState(IResource::RES_LOADED);
00577         } else {
00578             // create and add image
00579             info.outline = ImageManager::instance()->add(img);
00580         }
00581         // mark outline as not dirty since we created/recreated it here
00582         info.dirty = false;
00583 
00584         return info.outline.get();
00585     }
00586 
00587     Image* InstanceRenderer::bindColoring(ColoringInfo& info, RenderItem& vc, Camera* cam) {
00588         bool valid = isValidImage(info.overlay);
00589         if (!info.dirty && info.curimg == vc.image.get() && valid) {
00590             removeFromCheck(info.overlay);
00591             // optimization for coloring that has not changed
00592             return info.overlay.get();
00593         } else {
00594             info.curimg = vc.image.get();
00595         }
00596 
00597         // if coloring has changed we can maybe free the old effect image
00598         if (valid) {
00599             addToCheck(info.overlay);
00600         }
00601 
00602         bool found = false;
00603         // create name
00604         std::stringstream sts;
00605         sts << vc.image.get()->getName() << "," << static_cast<uint32_t>(info.r) << "," << 
00606             static_cast<uint32_t>(info.g) << "," << static_cast<uint32_t>(info.b);
00607         // search image
00608         if (ImageManager::instance()->exists(sts.str())) {
00609             info.overlay = ImageManager::instance()->getPtr(sts.str());
00610             valid = isValidImage(info.overlay);
00611             if (valid) {
00612                 removeFromCheck(info.overlay);
00613                 // mark overlay as not dirty since we found it here
00614                 info.dirty = false;
00615                 return info.overlay.get();
00616             }
00617             found = true;
00618         }
00619 
00620         // With lazy loading we can come upon a situation where we need to generate coloring from
00621         // uninitialised shared image
00622         if(vc.image->isSharedImage()) {
00623             vc.image->forceLoadInternal();
00624         }
00625 
00626         // not found so we create it
00627         SDL_Surface* surface = vc.image->getSurface();
00628         SDL_Surface* overlay_surface = SDL_CreateRGBSurface(SDL_SWSURFACE | SDL_SRCALPHA,
00629             vc.image->getWidth(), vc.image->getHeight(), 32,
00630             RMASK, GMASK, BMASK, AMASK);
00631 
00632         uint8_t r, g, b, a = 0;
00633 
00634         for (int32_t x = 0; x < overlay_surface->w; x ++) {
00635             for (int32_t y = 0; y < overlay_surface->h; y ++) {
00636                 vc.image->getPixelRGBA(x, y, &r, &g, &b, &a);
00637                 if (a > 0) {
00638                     Image::putPixel(overlay_surface, x, y, (r + info.r) >> 1, (g + info.g) >> 1, (b + info.b) >> 1, a);
00639                 }
00640             }
00641         }
00642 
00643         // In case of OpenGL backend, SDLImage needs to be converted
00644         Image* img = m_renderbackend->createImage(sts.str(), overlay_surface);
00645 
00646         if (found) {
00647             // image exists but is not "loaded"
00648             removeFromCheck(info.overlay);
00649             ImagePtr temp(img);
00650             info.overlay.get()->copySubimage(0, 0, temp);
00651             info.overlay.get()->setState(IResource::RES_LOADED);
00652         } else {
00653             // add image
00654             img->setState(IResource::RES_LOADED);
00655             info.overlay = ImageManager::instance()->add(img);
00656         }
00657         // mark overlay as not dirty since we created/recreated it here
00658         info.dirty = false;
00659         
00660         return info.overlay.get();
00661     }
00662 
00663     void InstanceRenderer::addOutlined(Instance* instance, int32_t r, int32_t g, int32_t b, int32_t width, int32_t threshold) {
00664         OutlineInfo newinfo(this);
00665         newinfo.r = r;
00666         newinfo.g = g;
00667         newinfo.b = b;
00668         newinfo.threshold = threshold;
00669         newinfo.width = width;
00670         newinfo.dirty = true;
00671 
00672         // attempts to insert the element into the outline map
00673         // will return false in the second value of the pair if the instance already exists
00674         // in the map and the first value of the pair will then be an iterator to the
00675         // existing data for the instance
00676         std::pair<InstanceToOutlines_t::iterator, bool> insertiter = m_instance_outlines.insert(std::make_pair(instance, newinfo));
00677 
00678         if (insertiter.second == false) {
00679             // the insertion did not happen because the instance
00680             // already exists in the map so lets just update its outline info
00681             OutlineInfo& info = insertiter.first->second;
00682 
00683             if (info.r != r || info.g != g || info.b != b || info.width != width) {
00684                 // only update the outline info if its changed since the last call
00685                 // flag the outline info as dirty so it will get processed during rendering
00686                 info.r = r;
00687                 info.b = b;
00688                 info.g = g;
00689                 info.width = width;
00690                 info.threshold = threshold;
00691                 info.dirty = true;
00692             }
00693         } else {
00694             std::pair<InstanceToEffects_t::iterator, bool> iter = m_assigned_instances.insert(std::make_pair(instance, OUTLINE));
00695             if (iter.second) {
00696                 instance->addDeleteListener(m_delete_listener);
00697             } else {
00698                 Effect& effect = iter.first->second;
00699                 if ((effect & OUTLINE) != OUTLINE) {
00700                     effect += OUTLINE;
00701                 }
00702             }
00703         }
00704     }
00705 
00706     void InstanceRenderer::addColored(Instance* instance, int32_t r, int32_t g, int32_t b) {
00707         ColoringInfo newinfo(this);
00708         newinfo.r = r;
00709         newinfo.g = g;
00710         newinfo.b = b;
00711         newinfo.dirty = true;
00712 
00713         // attempts to insert the element into the coloring map
00714         // will return false in the second value of the pair if the instance already exists
00715         // in the map and the first value of the pair will then be an iterator to the
00716         // existing data for the instance
00717         std::pair<InstanceToColoring_t::iterator, bool> insertiter = m_instance_colorings.insert(std::make_pair(instance, newinfo));
00718 
00719         if (insertiter.second == false) {
00720             // the insertion did not happen because the instance
00721             // already exists in the map so lets just update its coloring info
00722             ColoringInfo& info = insertiter.first->second;
00723 
00724             if (info.r != r || info.g != g || info.b != b) {
00725                 // only update the coloring info if its changed since the last call
00726                 info.r = r;
00727                 info.b = b;
00728                 info.g = g;
00729                 info.dirty = true;
00730             }
00731         } else {
00732             std::pair<InstanceToEffects_t::iterator, bool> iter = m_assigned_instances.insert(std::make_pair(instance, COLOR));
00733             if (iter.second) {
00734                 instance->addDeleteListener(m_delete_listener);
00735             } else {
00736                 Effect& effect = iter.first->second;
00737                 if ((effect & COLOR) != COLOR) {
00738                     effect += COLOR;
00739                 }
00740             }
00741         }
00742     }
00743 
00744     void InstanceRenderer::addTransparentArea(Instance* instance, const std::list<std::string> &groups, uint32_t w, uint32_t h, uint8_t trans, bool front) {
00745         AreaInfo newinfo;
00746         newinfo.instance = instance;
00747         newinfo.groups = groups;
00748 
00749         newinfo.w = w;
00750         newinfo.h = h;
00751         newinfo.trans = trans;
00752         newinfo.front = front;
00753 
00754 
00755         // attempts to insert the element into the area map
00756         // will return false in the second value of the pair if the instance already exists
00757         // in the map and the first value of the pair will then be an iterator to the
00758         // existing data for the instance
00759         std::pair<InstanceToAreas_t::iterator, bool> insertiter = m_instance_areas.insert(std::make_pair(instance, newinfo));
00760 
00761         if (insertiter.second == false) {
00762             // the insertion did not happen because the instance
00763             // already exists in the map so lets just update its area info
00764             AreaInfo& info = insertiter.first->second;
00765         } else {
00766             std::pair<InstanceToEffects_t::iterator, bool> iter = m_assigned_instances.insert(std::make_pair(instance, AREA));
00767             if (iter.second) {
00768                 instance->addDeleteListener(m_delete_listener);
00769             } else {
00770                 Effect& effect = iter.first->second;
00771                 if ((effect & AREA) != AREA) {
00772                     effect += AREA;
00773                 }
00774             }
00775         }
00776     }
00777 
00778     void InstanceRenderer::removeOutlined(Instance* instance) {
00779         InstanceToEffects_t::iterator it = m_assigned_instances.find(instance);
00780         if (it != m_assigned_instances.end()) {
00781             if (it->second == OUTLINE) {
00782                 instance->removeDeleteListener(m_delete_listener);
00783                 m_instance_outlines.erase(instance);
00784                 m_assigned_instances.erase(it);
00785             } else if ((it->second & OUTLINE) == OUTLINE) {
00786                 it->second -= OUTLINE;
00787                 m_instance_outlines.erase(instance);
00788             }
00789         }
00790     }
00791 
00792     void InstanceRenderer::removeColored(Instance* instance) {
00793         InstanceToEffects_t::iterator it = m_assigned_instances.find(instance);
00794         if (it != m_assigned_instances.end()) {
00795             if (it->second == COLOR) {
00796                 instance->removeDeleteListener(m_delete_listener);
00797                 m_instance_colorings.erase(instance);
00798                 m_assigned_instances.erase(it);
00799             } else if ((it->second & COLOR) == COLOR) {
00800                 it->second -= COLOR;
00801                 m_instance_colorings.erase(instance);
00802             }
00803         }
00804     }
00805 
00806     void InstanceRenderer::removeTransparentArea(Instance* instance) {
00807         InstanceToEffects_t::iterator it = m_assigned_instances.find(instance);
00808         if (it != m_assigned_instances.end()) {
00809             if (it->second == AREA) {
00810                 instance->removeDeleteListener(m_delete_listener);
00811                 m_instance_areas.erase(instance);
00812                 m_assigned_instances.erase(it);
00813             } else if ((it->second & AREA) == AREA) {
00814                 it->second -= AREA;
00815                 m_instance_areas.erase(instance);
00816             }
00817         }
00818     }
00819 
00820     void InstanceRenderer::removeAllOutlines() {
00821         if (!m_instance_outlines.empty()) {
00822             InstanceToOutlines_t::iterator outline_it = m_instance_outlines.begin();
00823             for (; outline_it != m_instance_outlines.end(); ++outline_it) {
00824                 InstanceToEffects_t::iterator it = m_assigned_instances.find((*outline_it).first);
00825                 if (it != m_assigned_instances.end()) {
00826                     if (it->second == OUTLINE) {
00827                         (*outline_it).first->removeDeleteListener(m_delete_listener);
00828                         m_assigned_instances.erase(it);
00829                     } else if ((it->second & OUTLINE) == OUTLINE) {
00830                         it->second -= OUTLINE;
00831                     }
00832                 }
00833             }
00834             m_instance_outlines.clear();
00835         }
00836     }
00837 
00838     void InstanceRenderer::removeAllColored() {
00839         if (!m_instance_colorings.empty()) {
00840             InstanceToColoring_t::iterator color_it = m_instance_colorings.begin();
00841             for (; color_it != m_instance_colorings.end(); ++color_it) {
00842                 InstanceToEffects_t::iterator it = m_assigned_instances.find((*color_it).first);
00843                 if (it != m_assigned_instances.end()) {
00844                     if (it->second == COLOR) {
00845                         (*color_it).first->removeDeleteListener(m_delete_listener);
00846                         m_assigned_instances.erase(it);
00847                     } else if ((it->second & COLOR) == COLOR) {
00848                         it->second -= COLOR;
00849                     }
00850                 }
00851             }
00852             m_instance_colorings.clear();
00853         }
00854     }
00855 
00856     void InstanceRenderer::removeAllTransparentAreas() {
00857         if (!m_instance_areas.empty()) {
00858             InstanceToAreas_t::iterator area_it = m_instance_areas.begin();
00859             for (; area_it != m_instance_areas.end(); ++area_it) {
00860                 InstanceToEffects_t::iterator it = m_assigned_instances.find((*area_it).first);
00861                 if (it != m_assigned_instances.end()) {
00862                     if (it->second == AREA) {
00863                         (*area_it).first->removeDeleteListener(m_delete_listener);
00864                         m_assigned_instances.erase(it);
00865                     } else if ((it->second & AREA) == AREA) {
00866                         it->second -= AREA;
00867                     }
00868                 }
00869             }
00870             m_instance_areas.clear();
00871         }
00872     }
00873 
00874     void InstanceRenderer::addIgnoreLight(const std::list<std::string> &groups) {
00875         std::list<std::string>::const_iterator group_it = groups.begin();
00876         for(;group_it != groups.end(); ++group_it) {
00877             m_unlit_groups.push_back(*group_it);
00878         }
00879         m_unlit_groups.sort();
00880         m_unlit_groups.unique();
00881     }
00882 
00883     void InstanceRenderer::removeIgnoreLight(const std::list<std::string> &groups) {
00884         std::list<std::string>::const_iterator group_it = groups.begin();
00885         for(;group_it != groups.end(); ++group_it) {
00886             std::list<std::string>::iterator unlit_it = m_unlit_groups.begin();
00887             for(;unlit_it != m_unlit_groups.end(); ++unlit_it) {
00888                 if((*group_it).find(*unlit_it) != std::string::npos) {
00889                     m_unlit_groups.remove(*unlit_it);
00890                     break;
00891                 }
00892             }
00893         }
00894     }
00895 
00896     void InstanceRenderer::removeAllIgnoreLight() {
00897         m_unlit_groups.clear();
00898     }
00899 
00900     void InstanceRenderer::reset() {
00901         // stop timer
00902         if (m_timer_enabled) {
00903             m_timer.stop();
00904         }
00905         // remove all effects and listener
00906         removeAllOutlines();
00907         removeAllColored();
00908         removeAllTransparentAreas();
00909         removeAllIgnoreLight();
00910         // removes the references to the effect images
00911         m_check_images.clear();
00912     }
00913 
00914     void InstanceRenderer::setRemoveInterval(uint32_t interval) {
00915         if (m_interval != interval*1000) {
00916             m_interval = interval*1000;
00917             m_timer.setInterval(m_interval);
00918         }
00919     }
00920 
00921     uint32_t InstanceRenderer::getRemoveInterval() const {
00922         return m_interval/1000;
00923     }
00924 
00925     void InstanceRenderer::addToCheck(const ImagePtr& image) {
00926         if (isValidImage(image)) {
00927             // if image is already inserted then return
00928             ImagesToCheck_t::iterator it = m_check_images.begin();
00929             for (; it != m_check_images.end(); ++it) {
00930                 if (it->image.get()->getName() == image.get()->getName()) {
00931                     return;                 
00932                 }
00933             }
00934             s_image_entry entry;
00935             entry.image = image;
00936             entry.timestamp = TimeManager::instance()->getTime();
00937             m_check_images.push_front(entry);
00938 
00939             if (!m_timer_enabled) {
00940                 m_timer_enabled = true;
00941                 m_timer.start();
00942             }
00943         }
00944     }
00945 
00946     void InstanceRenderer::check() {
00947         uint32_t now = TimeManager::instance()->getTime();
00948         ImagesToCheck_t::iterator it = m_check_images.begin();
00949         // free unused images
00950         while (it != m_check_images.end()) {
00951             if (now - it->timestamp > m_interval) {
00952                 if (isValidImage(it->image)) {
00953                     ImageManager::instance()->free(it->image.get()->getName());
00954                 }
00955                 it = m_check_images.erase(it);
00956             } else {
00957                 ++it;
00958             }
00959         }
00960 
00961         if (m_check_images.empty() && m_timer_enabled) {
00962             m_timer_enabled = false;
00963             m_timer.stop();
00964         }
00965     }
00966 
00967     void InstanceRenderer::removeFromCheck(const ImagePtr& image) {
00968         if (isValidImage(image)) {
00969             // if the image is used then remove it here
00970             ImagesToCheck_t::iterator it = m_check_images.begin();
00971             for (; it != m_check_images.end(); ++it) {
00972                 if (it->image.get()->getName() == image.get()->getName()) {
00973                     m_check_images.erase(it);
00974                     break;
00975                 }
00976             }
00977 
00978             if (m_check_images.empty() && m_timer_enabled) {
00979                 m_timer_enabled = false;
00980                 m_timer.stop();
00981             }
00982         }
00983     }
00984 
00985     void InstanceRenderer::removeInstance(Instance* instance) {
00986         InstanceToEffects_t::iterator it = m_assigned_instances.find(instance);
00987         if (it != m_assigned_instances.end()) {
00988             m_instance_outlines.erase(instance);
00989             m_instance_colorings.erase(instance);
00990             m_instance_areas.erase(instance);
00991             instance->removeDeleteListener(m_delete_listener);
00992             m_assigned_instances.erase(it);
00993         }
00994     }
00995 
00996     bool InstanceRenderer::isValidImage(const ImagePtr& image) {
00997         if (image.get()) {
00998             if (image.get()->getState() == IResource::RES_LOADED) {
00999                 return true;
01000             }
01001         }
01002         return false;
01003     }
01004 }