FIFE 2008.0
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 
00026 // FIFE includes
00027 // These includes are split up in two parts, separated by one empty line
00028 // First block: files included from the FIFE root src directory
00029 // Second block: files included from the same folder
00030 #include "video/renderbackend.h"
00031 #include "video/image.h"
00032 #include "video/sdl/sdlimage.h"
00033 #include "video/imagepool.h"
00034 #include "video/animation.h"
00035 #include "video/animationpool.h"
00036 #include "util/math/fife_math.h"
00037 #include "util/log/logger.h"
00038 #include "model/metamodel/grids/cellgrid.h"
00039 #include "model/metamodel/action.h"
00040 #include "model/structures/instance.h"
00041 #include "model/structures/layer.h"
00042 #include "model/structures/location.h"
00043 #include "video/opengl/fife_opengl.h"
00044 
00045 #include "view/camera.h"
00046 #include "view/visual.h"
00047 #include "instancerenderer.h"
00048 
00049 
00050 namespace {
00051     unsigned int scale(unsigned int val, double factor) {
00052         return static_cast<unsigned int>(ceil(static_cast<double>(val) * factor));
00053     }
00054 }
00055 
00056 namespace FIFE {
00057     static Logger _log(LM_VIEWVIEW);
00058 
00059     InstanceRenderer::OutlineInfo::OutlineInfo():
00060         r(0),
00061         g(0),
00062         b(0),
00063         width(1),
00064         dirty(false),
00065         outline(NULL),
00066         curimg(NULL) {
00067     }
00068     InstanceRenderer::ColoringInfo::ColoringInfo():
00069         r(0),
00070         g(0),
00071         b(0),
00072         dirty(false),
00073         overlay(NULL),
00074         curimg(NULL) {
00075     }
00076 
00077     InstanceRenderer::AreaInfo::AreaInfo():
00078         instance(NULL),
00079         groups(),
00080         w(1),
00081         h(1),
00082         trans(0),
00083         front(true),
00084         z(0) {
00085     }
00086 
00087     InstanceRenderer::OutlineInfo::~OutlineInfo() {
00088         delete outline;
00089     }
00090 
00091     InstanceRenderer::ColoringInfo::~ColoringInfo() {
00092         delete overlay;
00093     }
00094 
00095     InstanceRenderer::AreaInfo::~AreaInfo() {
00096     }
00097 
00098     InstanceRenderer* InstanceRenderer::getInstance(IRendererContainer* cnt) {
00099         return dynamic_cast<InstanceRenderer*>(cnt->getRenderer("InstanceRenderer"));
00100     }
00101 
00102     InstanceRenderer::InstanceRenderer(RenderBackend* renderbackend, int position, ImagePool* imagepool, AnimationPool* animpool):
00103         RendererBase(renderbackend, position),
00104         m_imagepool(imagepool),
00105         m_animationpool(animpool),
00106         m_area_layer(false) {
00107         setEnabled(true);
00108     }
00109 
00110     InstanceRenderer::InstanceRenderer(const InstanceRenderer& old):
00111         RendererBase(old),
00112         m_imagepool(old.m_imagepool),
00113         m_animationpool(old.m_animationpool),
00114         m_area_layer(old.m_area_layer) {
00115         setEnabled(true);
00116     }
00117 
00118     RendererBase* InstanceRenderer::clone() {
00119         return new InstanceRenderer(*this);
00120     }
00121 
00122     InstanceRenderer::~InstanceRenderer() {
00123     }
00124 
00125     void InstanceRenderer::render(Camera* cam, Layer* layer, RenderList& instances) {
00126         FL_DBG(_log, "Iterating layer...");
00127         CellGrid* cg = layer->getCellGrid();
00128         if (!cg) {
00129             FL_WARN(_log, "No cellgrid assigned to layer, cannot draw instances");
00130             return;
00131         }
00132 
00133         const bool any_effects = !(m_instance_outlines.empty() && m_instance_colorings.empty());
00134         const bool unlit = !m_unlit_groups.empty();
00135         unsigned int lm = m_renderbackend->getLightingModel();
00136 
00137         m_area_layer = false;
00138         if(!m_instance_areas.empty()) {
00139             InstanceToAreas_t::iterator area_it = m_instance_areas.begin();
00140             for(;area_it != m_instance_areas.end(); area_it++) {
00141                 AreaInfo& info = area_it->second;
00142                 if(info.instance->getLocation().getLayer() == layer) {
00143                     if(info.front) {
00144                         DoublePoint3D instance_posv = cam->toVirtualScreenCoordinates(info.instance->getLocation().getMapCoordinates());
00145                         info.z = instance_posv.z;
00146                     }
00147                     m_area_layer = true;
00148                 }
00149             }
00150         }
00151 
00152         RenderList::iterator instance_it = instances.begin();
00153         for (;instance_it != instances.end(); ++instance_it) {
00154             FL_DBG(_log, "Iterating instances...");
00155             Instance* instance = (*instance_it)->instance;
00156             RenderItem& vc = **instance_it;
00157 
00158             if(m_area_layer) {
00159                 InstanceToAreas_t::iterator areas_it = m_instance_areas.begin();
00160                 for(;areas_it != m_instance_areas.end(); areas_it++) {
00161                     AreaInfo& infoa = areas_it->second;
00162                     if(infoa.front) {
00163                         if(infoa.z >= vc.screenpoint.z) {
00164                             continue;
00165                         }
00166                     }
00167 
00168                     std::string str_name = instance->getObject()->getNamespace();
00169                     std::list<std::string>::iterator group_it = infoa.groups.begin();
00170                     for(;group_it != infoa.groups.end(); ++group_it) {
00171                         if(str_name.find((*group_it)) != std::string::npos) {
00172                             ScreenPoint p;
00173                             Rect rec;
00174                             p = cam->toScreenCoordinates(infoa.instance->getLocation().getMapCoordinates());
00175                             rec.x = p.x - infoa.w / 2;
00176                             rec.y = p.y - infoa.h / 2;
00177                             rec.w = infoa.w;
00178                             rec.h = infoa.h;
00179                             if(infoa.instance != instance && vc.dimensions.intersects(rec)) {
00180                                 vc.transparency = 255 - infoa.trans;
00181                             }
00182                         }
00183                     }
00184                 }
00185             }
00186 
00187             FL_DBG(_log, LMsg("Instance layer coordinates = ") << instance->getLocationRef().getLayerCoordinates());
00188 
00189             if (any_effects) {
00190                 InstanceToOutlines_t::iterator outline_it = m_instance_outlines.find(instance);
00191                 if (outline_it != m_instance_outlines.end()) {
00192                     if (lm != 0) {
00193                         m_renderbackend->disableLighting();
00194                         m_renderbackend->setStencilTest(255, 2, 7);
00195                         m_renderbackend->setAlphaTest(0.0);
00196                         bindOutline(outline_it->second, vc, cam)->render(vc.dimensions, vc.transparency);
00197                         m_renderbackend->enableLighting();
00198                         m_renderbackend->setStencilTest(0, 2, 7);
00199                         vc.image->render(vc.dimensions, vc.transparency);
00200                         m_renderbackend->disableAlphaTest();
00201                         m_renderbackend->disableStencilTest();
00202                         continue;
00203                     }
00204                     bindOutline(outline_it->second, vc, cam)->render(vc.dimensions, vc.transparency);
00205                 }
00206 
00207                 InstanceToColoring_t::iterator coloring_it = m_instance_colorings.find(instance);
00208                 if (coloring_it != m_instance_colorings.end()) {
00209                     m_renderbackend->disableLighting();
00210                     bindColoring(coloring_it->second, vc, cam)->render(vc.dimensions, vc.transparency);
00211                     m_renderbackend->enableLighting();
00212                     continue; // Skip normal rendering after drawing overlay
00213                 }
00214             }
00215             if(lm != 0) {
00216                 if(unlit) {
00217                     bool found = false;
00218                     std::string lit_name = instance->getObject()->getNamespace();
00219                     std::list<std::string>::iterator unlit_it = m_unlit_groups.begin();
00220                     for(;unlit_it != m_unlit_groups.end(); ++unlit_it) {
00221                         if(lit_name.find(*unlit_it) != std::string::npos) {
00222                             m_renderbackend->setStencilTest(255, 2, 7);
00223                             found = true;
00224                             break;
00225                         }
00226                     }
00227                     // This is very expensiv, we have to change it
00228                     if(!found)
00229                         m_renderbackend->setStencilTest(0, 1, 7);
00230 
00231                     m_renderbackend->setAlphaTest(0.0);
00232                     vc.image->render(vc.dimensions, vc.transparency);
00233                     continue;
00234                 }
00235             }
00236             vc.image->render(vc.dimensions, vc.transparency);
00237 
00238         }
00239         if(lm != 0) {
00240             m_renderbackend->disableAlphaTest();
00241             m_renderbackend->disableStencilTest();
00242         }
00243     }
00244 
00245     Image* InstanceRenderer::bindOutline(OutlineInfo& info, RenderItem& vc, Camera* cam) {
00246         if (!info.dirty && info.curimg == vc.image) {
00247             // optimization for outline that has not changed
00248             return info.outline;
00249         } else {
00250             info.curimg = vc.image;
00251         }
00252 
00253         if (info.outline) {
00254             delete info.outline; // delete old mask
00255             info.outline = NULL;
00256         }
00257         SDL_Surface* surface = vc.image->getSurface();
00258         SDL_Surface* outline_surface = SDL_ConvertSurface(surface, surface->format, surface->flags);
00259 
00260         // needs to use SDLImage here, since GlImage does not support drawing primitives atm
00261         SDLImage* img = new SDLImage(outline_surface);
00262 
00263         // TODO: optimize...
00264         uint8_t r, g, b, a = 0;
00265 
00266         // vertical sweep
00267         for (unsigned int x = 0; x < img->getWidth(); x ++) {
00268             uint8_t prev_a = 0;
00269             for (unsigned int y = 0; y < img->getHeight(); y ++) {
00270                 vc.image->getPixelRGBA(x, y, &r, &g, &b, &a);
00271                 if ((a == 0 || prev_a == 0) && (a != prev_a)) {
00272                     if (a < prev_a) {
00273                         for (unsigned int yy = y; yy < y + info.width; yy++) {
00274                             img->putPixel(x, yy, info.r, info.g, info.b);
00275                         }
00276                     } else {
00277                         for (unsigned int yy = y - info.width; yy < y; yy++) {
00278                             img->putPixel(x, yy, info.r, info.g, info.b);
00279                         }
00280                     }
00281                 }
00282                 prev_a = a;
00283             }
00284         }
00285         // horizontal sweep
00286         for (unsigned int y = 0; y < img->getHeight(); y ++) {
00287             uint8_t prev_a = 0;
00288             for (unsigned int x = 0; x < img->getWidth(); x ++) {
00289                 vc.image->getPixelRGBA(x, y, &r, &g, &b, &a);
00290                 if ((a == 0 || prev_a == 0) && (a != prev_a)) {
00291                     if (a < prev_a) {
00292                         for (unsigned int xx = x; xx < x + info.width; xx++) {
00293                             img->putPixel(xx, y, info.r, info.g, info.b);
00294                         }
00295                     } else {
00296                         for (unsigned int xx = x - info.width; xx < x; xx++) {
00297                             img->putPixel(xx, y, info.r, info.g, info.b);
00298                         }
00299                     }
00300                 }
00301                 prev_a = a;
00302             }
00303         }
00304 
00305         // In case of OpenGL backend, SDLImage needs to be converted
00306         info.outline = m_renderbackend->createImage(img->detachSurface());
00307         delete img;
00308 
00309         if (info.outline) {
00310             // mark outline as not dirty since we created it here
00311             info.dirty = false;
00312         }
00313 
00314         return info.outline;
00315     }
00316 
00317     Image* InstanceRenderer::bindColoring(ColoringInfo& info, RenderItem& vc, Camera* cam) {
00318         if (!info.dirty && info.curimg == vc.image) {
00319             // optimization for coloring overlay that has not changed
00320             return info.overlay;
00321         }
00322         else {
00323             info.curimg = vc.image;
00324         }
00325         
00326         if (info.overlay) {
00327             delete info.overlay; // delete old mask
00328             info.overlay = NULL;
00329         }
00330 
00331         SDL_Surface* surface = vc.image->getSurface();
00332         SDL_Surface* overlay_surface = SDL_ConvertSurface(surface, surface->format, surface->flags);
00333 
00334         // needs to use SDLImage here, since GlImage does not support drawing primitives atm
00335         SDLImage* img = new SDLImage(overlay_surface);
00336 
00337         uint8_t r, g, b, a = 0;
00338 
00339         for (unsigned int x = 0; x < img->getWidth(); x ++) {
00340             for (unsigned int y = 0; y < img->getHeight(); y ++) {
00341                 vc.image->getPixelRGBA(x, y, &r, &g, &b, &a);
00342                 if (a > 0) {
00343                     img->putPixel(x, y, (r + info.r) >> 1, (g + info.g) >> 1, (b + info.b) >> 1);
00344                 }
00345             }
00346         }
00347 
00348         // In case of OpenGL backend, SDLImage needs to be converted
00349         info.overlay = m_renderbackend->createImage(img->detachSurface());
00350         delete img;
00351 
00352         if (info.overlay) {
00353             // mark overlay coloring as not dirty since we created it here
00354             info.dirty = false;
00355         }
00356 
00357         return info.overlay;
00358     }
00359 
00360     void InstanceRenderer::addOutlined(Instance* instance, int r, int g, int b, int width) {
00361         OutlineInfo newinfo;
00362         newinfo.r = r;
00363         newinfo.g = g;
00364         newinfo.b = b;
00365         newinfo.width = width;
00366         newinfo.dirty = true;
00367 
00368         // attempts to insert the element into the outline map
00369         // will return false in the second value of the pair if the instance already exists 
00370         // in the map and the first value of the pair will then be an iterator to the 
00371         // existing data for the instance
00372         std::pair<InstanceToOutlines_t::iterator, bool> insertiter = m_instance_outlines.insert(std::make_pair(instance, newinfo));
00373 
00374         if (insertiter.second == false) {
00375             // the insertion did not happen because the instance 
00376             // already exists in the map so lets just update its outline info
00377             OutlineInfo& info = insertiter.first->second;
00378 
00379             if (info.r != r || info.g != g || info.b != b || info.width != width) {
00380                 // only update the outline info if its changed since the last call
00381                 // flag the outline info as dirty so it will get processed during rendering
00382                 info.r = r;
00383                 info.b = b;
00384                 info.g = g;
00385                 info.width = width;
00386                 info.dirty = true;
00387             }
00388         }
00389     }
00390 
00391     void InstanceRenderer::addColored(Instance* instance, int r, int g, int b) {
00392         ColoringInfo newinfo;
00393         newinfo.r = r;
00394         newinfo.g = g;
00395         newinfo.b = b;
00396         newinfo.dirty = true;
00397 
00398         // attempts to insert the element into the coloring map
00399         // will return false in the second value of the pair if the instance already exists 
00400         // in the map and the first value of the pair will then be an iterator to the 
00401         // existing data for the instance
00402         std::pair<InstanceToColoring_t::iterator, bool> insertiter = m_instance_colorings.insert(std::make_pair(instance, newinfo));
00403 
00404         if (insertiter.second == false) {
00405             // the insertion did not happen because the instance 
00406             // already exists in the map so lets just update its coloring info
00407             ColoringInfo& info = insertiter.first->second;
00408 
00409             if (info.r != r || info.g != g || info.b != b) {
00410                 // only update the coloring info if its changed since the last call
00411                 // flag the coloring info as dirty so it will get processed during rendering
00412                 info.r = r;
00413                 info.b = b;
00414                 info.g = g;
00415                 info.dirty = true;
00416             }
00417         }
00418     }
00419 
00420     void InstanceRenderer::addTransparentArea(Instance* instance, const std::list<std::string> &groups, unsigned int w, unsigned int h, unsigned char trans, bool front) {
00421         AreaInfo newinfo;
00422         newinfo.instance = instance;
00423         newinfo.groups = groups;
00424 
00425         newinfo.w = w;
00426         newinfo.h = h;
00427         newinfo.trans = trans;
00428         newinfo.front = front;
00429 
00430 
00431         // attempts to insert the element into the area map
00432         // will return false in the second value of the pair if the instance already exists 
00433         // in the map and the first value of the pair will then be an iterator to the 
00434         // existing data for the instance
00435         std::pair<InstanceToAreas_t::iterator, bool> insertiter = m_instance_areas.insert(std::make_pair(instance, newinfo));
00436 
00437         if (insertiter.second == false) {
00438             // the insertion did not happen because the instance 
00439             // already exists in the map so lets just update its area info
00440             AreaInfo& info = insertiter.first->second;
00441         }
00442     }
00443 
00444     void InstanceRenderer::removeOutlined(Instance* instance) {
00445         m_instance_outlines.erase(instance);
00446     }
00447 
00448     void InstanceRenderer::removeColored(Instance* instance) {
00449         m_instance_colorings.erase(instance);
00450     }
00451 
00452     void InstanceRenderer::removeTransparentArea(Instance* instance) {
00453         m_instance_areas.erase(instance);
00454     }
00455 
00456     void InstanceRenderer::removeAllOutlines() {
00457         m_instance_outlines.clear();
00458     }
00459 
00460     void InstanceRenderer::removeAllColored() {
00461         m_instance_colorings.clear();
00462     }
00463 
00464     void InstanceRenderer::removeAllTransparentAreas() {
00465         m_instance_areas.clear();
00466     }
00467 
00468     void InstanceRenderer::addIgnoreLight(const std::list<std::string> &groups) {
00469         std::list<std::string>::const_iterator group_it = groups.begin();
00470         for(;group_it != groups.end(); ++group_it) {
00471             m_unlit_groups.push_back(*group_it);
00472         }
00473         m_unlit_groups.sort();
00474         m_unlit_groups.unique();
00475     }
00476 
00477     void InstanceRenderer::removeIgnoreLight(const std::list<std::string> &groups) {
00478         std::list<std::string>::const_iterator group_it = groups.begin();
00479         for(;group_it != groups.end(); ++group_it) {
00480             std::list<std::string>::iterator unlit_it = m_unlit_groups.begin();
00481             for(;unlit_it != m_unlit_groups.end(); ++unlit_it) {
00482                 if((*group_it).find(*unlit_it) != std::string::npos) {
00483                     m_unlit_groups.remove(*unlit_it);
00484                     break;
00485                 }
00486             }
00487         }
00488     }
00489 
00490     void InstanceRenderer::removeAllIgnoreLight() {
00491         m_unlit_groups.clear();
00492     }
00493 
00494     void InstanceRenderer::reset() {
00495         removeAllOutlines();
00496         removeAllColored();
00497         removeAllTransparentAreas();
00498         removeAllIgnoreLight();
00499     }
00500 
00501 }
 All Classes Namespaces Functions Variables Enumerations Enumerator