FIFE 2008.0
layercache.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 <SDL.h>
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 
00032 #include "model/metamodel/grids/cellgrid.h"
00033 #include "model/metamodel/action.h"
00034 #include "model/structures/layer.h"
00035 #include "model/structures/instance.h"
00036 #include "model/structures/location.h"
00037 #include "util/base/exception.h"
00038 #include "util/log/logger.h"
00039 #include "util/math/fife_math.h"
00040 #include "util/math/angles.h"
00041 #include "video/renderbackend.h"
00042 #include "video/image.h"
00043 #include "video/imagepool.h"
00044 #include "video/animation.h"
00045 #include "video/animationpool.h"
00046 
00047 #include "camera.h"
00048 #include "layercache.h"
00049 #include "visual.h"
00050 
00051 
00052 namespace FIFE {
00053     static Logger _log(LM_CAMERA);
00054 
00055     class CacheLayerChangeListener : public LayerChangeListener {
00056     public:
00057         CacheLayerChangeListener(LayerCache* cache)
00058         {
00059             m_cache = cache;
00060         }
00061         virtual ~CacheLayerChangeListener() {};
00062 
00063         virtual void onLayerChanged(Layer* layer, std::vector<Instance*>& instances)
00064         {
00065             for(std::vector<Instance*>::iterator i = instances.begin();
00066                 i != instances.end(); ++i) {
00067                 m_cache->updateInstance(*i);
00068             }
00069         }
00070 
00071         virtual void onInstanceCreate(Layer* layer, Instance* instance)
00072         {
00073             m_cache->addInstance(instance);
00074         }
00075 
00076         virtual void onInstanceDelete(Layer* layer, Instance* instance)
00077         {
00078             m_cache->removeInstance(instance);
00079         }
00080     private:
00081         LayerCache* m_cache;
00082     };
00083 
00084     LayerCache::LayerCache(Camera* camera, ImagePool* image_pool, AnimationPool* animation_pool) {
00085         m_camera = camera;
00086         m_image_pool = image_pool;
00087         m_animation_pool = animation_pool;
00088         m_layer = 0;
00089         m_tree = 0;
00090     }
00091 
00092     LayerCache::~LayerCache() {
00093         m_layer->removeChangeListener(m_layer_observer);
00094         delete m_layer_observer;
00095         delete m_tree;
00096     }
00097 
00098     void LayerCache::setLayer(Layer* layer) {
00099         m_layer = layer;
00100         m_layer_observer = new CacheLayerChangeListener(this);
00101         layer->addChangeListener(m_layer_observer);
00102         reset();
00103     }
00104 
00105     void LayerCache::reset() {
00106         m_instances.clear();
00107         delete m_tree;
00108         m_tree = new CacheTree;
00109         const std::vector<Instance*>& instances = m_layer->getInstances();
00110         for(std::vector<Instance*>::const_iterator i = instances.begin();
00111             i != instances.end(); ++i) {
00112             addInstance(*i);
00113         }
00114     }
00115 
00116     void LayerCache::addInstance(Instance* instance) {
00117         if(m_instance_map.find(instance)!=m_instance_map.end()) {
00118             throw new Duplicate(instance->getId());
00119         }
00120 
00121         RenderItem item;
00122         Entry entry;
00123         item.instance = instance;
00124         m_instances.push_back(item);
00125         m_instance_map[instance] = m_instances.size() - 1;
00126 
00127         entry.node = 0;
00128         entry.instance_index = m_instances.size() - 1;
00129         entry.entry_index = m_entries.size();
00130         m_entries.push_back(entry);
00131         updateEntry(m_entries.back());
00132     }
00133 
00134     void LayerCache::removeInstance(Instance* instance) {
00135         // FIXME
00136         // The way LayerCache stores it's data
00137         // it's pretty much impossible to cleanly remove
00138         // added instances.
00139 
00140         // This has to get fixed.
00141         if(m_instance_map.find(instance) == m_instance_map.end()) {
00142             throw new NotFound(instance->getId());
00143         }
00144         Entry& item = m_entries[m_instance_map[instance]];
00145         assert(item.instance_index == m_instance_map[instance]);
00146 
00147         if(item.node)
00148             item.node->data().erase(item.entry_index);
00149         item.node = 0;
00150         item.instance_index = unsigned(-1);
00151         m_instance_map.erase(instance);
00152     }
00153 
00154     void LayerCache::updateInstance(Instance* instance) {
00155         Entry& entry = m_entries[m_instance_map[instance]];
00156         updateEntry(entry);
00157     }
00158 
00159     void LayerCache::updateEntry(LayerCache::Entry& item) {
00160         if(item.instance_index ==  unsigned(-1)) {
00161             return;
00162         }
00163         if(item.node) {
00164             item.node->data().erase(item.entry_index);
00165         }
00166         RenderItem& render_item = m_instances[item.instance_index];
00167         Instance* instance = render_item.instance;
00168 
00169         DoublePoint3D screen_position = m_camera->toVirtualScreenCoordinates(instance->getLocationRef().getMapCoordinates());
00170 
00171         render_item.facing_angle = getAngleBetween(instance->getLocationRef(), instance->getFacingLocation());
00172         int angle = m_camera->getRotation() + render_item.facing_angle + instance->getRotation();
00173 
00174         Image* image = NULL;
00175         Action* action = instance->getCurrentAction();
00176         int w = 0;
00177         int h = 0;
00178         int xshift = 0;
00179         int yshift = 0;
00180 
00181         if(!action) {
00182             // Try static images then default action.
00183             int image_id = render_item.getStaticImageIndexByAngle(angle, instance);
00184             if(image_id == Pool::INVALID_ID) {
00185                 action = instance->getObject()->getDefaultAction();
00186             } else {
00187                 image = &m_image_pool->getImage(image_id);
00188             }
00189         }
00190         item.force_update = bool(action);
00191 
00192         if(action) {
00193             int animation_id = action->getVisual<ActionVisual>()->getAnimationIndexByAngle(render_item.facing_angle + m_camera->getRotation());
00194             Animation& animation = m_animation_pool->getAnimation(animation_id);
00195             unsigned animation_time = instance->getActionRuntime() % animation.getDuration();
00196             image = animation.getFrameByTimestamp(animation_time);
00197 
00198             int facing_angle = render_item.facing_angle;
00199             if (facing_angle < 0){
00200                 facing_angle += 360;
00201             }
00202             instance->setRotation(facing_angle);
00203         }
00204 
00205         if (image) {
00206             w = image->getWidth();
00207             h = image->getHeight();
00208             xshift = image->getXShift();
00209             yshift = image->getYShift();
00210         }
00211 
00212         screen_position.x -= w / 2;
00213         screen_position.x += xshift;
00214         screen_position.y -= h / 2;
00215         screen_position.y += yshift;
00216 
00217         render_item.image = image;
00218         render_item.screenpoint = screen_position;
00219 
00220         render_item.bbox.x = screen_position.x;
00221         render_item.bbox.y = screen_position.y;
00222         render_item.bbox.w = w;
00223         render_item.bbox.h = h;
00224 
00225         render_item.dimensions.x = screen_position.x;
00226         render_item.dimensions.y = screen_position.y;
00227         render_item.dimensions.w = w;
00228         render_item.dimensions.h = h;
00229 
00230         CacheTree::Node* node = m_tree->find_container(render_item.bbox);
00231         item.node = node;
00232         node->data().insert(item.entry_index);
00233     }
00234 
00235     class CacheTreeCollector {
00236             std::vector<int>& m_indices;
00237             Rect m_viewport;
00238         public:
00239             CacheTreeCollector(std::vector<int>& indices, const Rect& _viewport)
00240             : m_indices(indices), m_viewport(_viewport) {
00241             }
00242             bool visit(LayerCache::CacheTree::Node* node, int d = -1);
00243     };
00244 
00245     bool CacheTreeCollector::visit(LayerCache::CacheTree::Node* node, int d) {
00246         if(!m_viewport.intersects(Rect(node->x(), node->y(),node->size(),node->size())))
00247             return false;
00248         std::set<int>& list = node->data();
00249         for(std::set<int>::iterator i = list.begin(); i!=list.end();++i) {
00250             m_indices.push_back(*i);
00251         }
00252         return true;
00253     }
00254 
00255     void LayerCache::collect(const Rect& viewport, std::vector<int>& index_list) {
00256         CacheTree::Node * node = m_tree->find_container(viewport);
00257         CacheTreeCollector collector(index_list, viewport);
00258         node->apply_visitor(collector);
00259         node = node->parent();
00260         while(node) {
00261             collector.visit(node);
00262             node = node->parent();
00263         }
00264     }
00265 
00266     void LayerCache::fullUpdate() {
00267         for(unsigned i=0; i!=m_entries.size(); ++i)
00268             updateEntry(m_entries[i]);
00269     }
00270 
00271     class InstanceDistanceSort {
00272     public:
00273         inline bool operator()(RenderItem* const & lhs, RenderItem* const & rhs) {
00274             if (lhs->screenpoint.z == rhs->screenpoint.z) {
00275                 InstanceVisual* liv = lhs->instance->getVisual<InstanceVisual>();
00276                 InstanceVisual* riv = rhs->instance->getVisual<InstanceVisual>();
00277                 return liv->getStackPosition() < riv->getStackPosition();
00278             }
00279             return lhs->screenpoint.z < rhs->screenpoint.z;
00280         }
00281     };
00282 
00283     void LayerCache::update(Camera::Transform transform, RenderList& renderlist) {
00284         const double OVERDRAW = 2.5;
00285         renderlist.clear();
00286         if(!m_layer->areInstancesVisible()) {
00287             FL_DBG(_log, "Layer instances hidden");
00288             return;
00289         }
00290         bool isWarped = transform == Camera::WarpedTransform;
00291         if( isWarped )
00292             fullUpdate();
00293 
00294         Rect viewport = m_camera->getViewPort();
00295         Rect screen_viewport = viewport;
00296         float zoom = m_camera->getZoom();
00297         DoublePoint3D viewport_a = m_camera->screenToVirtualScreen(Point3D(viewport.x, viewport.y));
00298         DoublePoint3D viewport_b = m_camera->screenToVirtualScreen(Point3D(viewport.right(), viewport.bottom()));
00299         viewport.x = std::min(viewport_a.x, viewport_b.x);
00300         viewport.y = std::min(viewport_a.y, viewport_b.y);
00301         viewport.w = std::max(viewport_a.x, viewport_b.x) - viewport.x;
00302         viewport.h = std::max(viewport_a.y, viewport_b.y) - viewport.y;
00303         unsigned char layer_trans = m_layer->getLayerTransparency();
00304 
00305         // FL_LOG(_log, LMsg("camera-update viewport") << viewport);
00306         std::vector<int> index_list;
00307         collect(viewport, index_list);
00308         for(unsigned i=0; i!=index_list.size();++i) {
00309             Entry& entry = m_entries[index_list[i]];
00310             // NOTE
00311             // An update is forced if the item has an animation/action.
00312             // This update only happens if it is _already_ included in the viewport
00313             // Nevertheless: Moving instances - which might move into the viewport will be updated
00314             // By the layer change listener.
00315             if(entry.force_update)
00316                 updateEntry(entry);
00317 
00318             RenderItem& item = m_instances[entry.instance_index];
00319             InstanceVisual* visual = item.instance->getVisual<InstanceVisual>();
00320             bool visible = visual->isVisible();
00321             unsigned char instance_trans = visual->getTransparency();
00322             if(!item.image || !visible || (instance_trans == 255 && layer_trans == 0)
00323                                        || (instance_trans == 0 && layer_trans == 255)) {
00324                 continue;
00325             }
00326 
00327             if(layer_trans != 0) {
00328                 if(instance_trans != 0) {
00329                     short calc_trans = layer_trans - instance_trans;
00330                     if(calc_trans >= 0) {
00331                         instance_trans = calc_trans;
00332                     } else {
00333                         instance_trans = 0;
00334                     }
00335                 } else {
00336                     instance_trans = layer_trans;
00337                 }
00338             }
00339 
00340             Point3D screen_point = m_camera->virtualScreenToScreen(item.screenpoint);
00341             // NOTE:
00342             // One would expect this to be necessary here,
00343             // however it works the same without, sofar
00344             // m_camera->calculateZValue(screen_point);
00345             // item.screenpoint.z = -screen_point.z;
00346 
00347             item.dimensions.x = screen_point.x;
00348             item.dimensions.y = screen_point.y;
00349             item.dimensions.w = item.bbox.w;
00350             item.dimensions.h = item.bbox.h;
00351 
00352             item.transparency = 255 - instance_trans;
00353 
00354             if (zoom != 1.0) {
00355                 // NOTE: Due to image alignment, there is additional additions on image dimensions 
00356                 //       There's probabaly some better solution for this, but works "good enough" for now. 
00357                 //       In case additions are removed, gaps appear between tiles. 
00358                 item.dimensions.w = unsigned(double(item.bbox.w) * zoom + OVERDRAW);
00359                 item.dimensions.h = unsigned(double(item.bbox.h) * zoom + OVERDRAW);
00360             }
00361 
00362             if(item.dimensions.intersects(screen_viewport))
00363                 renderlist.push_back(&item);
00364         }
00365 
00366         InstanceDistanceSort ids;
00367         std::stable_sort(renderlist.begin(), renderlist.end(), ids);
00368         //  FL_LOG(_log, LMsg("camera-update ") << " N=" <<renderlist.size() << "/" << m_instances.size() << "/" << index_list.size());
00369     }
00370 }
 All Classes Namespaces Functions Variables Enumerations Enumerator