FIFE  2008.0
 All Classes Namespaces Functions Variables Enumerations Enumerator Pages
instancerenderer.cpp
1 /***************************************************************************
2  * Copyright (C) 2005-2008 by the FIFE team *
3  * http://www.fifengine.de *
4  * This file is part of FIFE. *
5  * *
6  * FIFE is free software; you can redistribute it and/or *
7  * modify it under the terms of the GNU Lesser General Public *
8  * License as published by the Free Software Foundation; either *
9  * version 2.1 of the License, or (at your option) any later version. *
10  * *
11  * This library 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 GNU *
14  * Lesser General Public License for more details. *
15  * *
16  * You should have received a copy of the GNU Lesser General Public *
17  * License along with this library; if not, write to the *
18  * Free Software Foundation, Inc., *
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
20  ***************************************************************************/
21 
22 // Standard C++ library includes
23 
24 // 3rd party library includes
25 #include <boost/bind.hpp>
26 
27 // FIFE includes
28 // These includes are split up in two parts, separated by one empty line
29 // First block: files included from the FIFE root src directory
30 // Second block: files included from the same folder
31 #include "video/renderbackend.h"
32 #include "video/image.h"
33 #include "video/imagemanager.h"
34 #include "video/sdl/sdlimage.h"
35 #include "video/animation.h"
36 #include "util/math/fife_math.h"
37 #include "util/log/logger.h"
38 #include "util/time/timemanager.h"
39 #include "model/metamodel/grids/cellgrid.h"
40 #include "model/metamodel/action.h"
41 #include "model/structures/instance.h"
42 #include "model/structures/layer.h"
43 #include "model/structures/location.h"
44 #include "model/structures/map.h"
45 #include "video/opengl/fife_opengl.h"
46 
47 #include "view/camera.h"
48 #include "view/visual.h"
49 #include "instancerenderer.h"
50 
51 #include "video/opengle/gleimage.h"
52 
53 
54 namespace {
55  uint32_t scale(uint32_t val, double factor) {
56  return static_cast<uint32_t>(ceil(static_cast<double>(val) * factor));
57  }
58 }
59 
60 namespace FIFE {
61  static Logger _log(LM_VIEWVIEW);
62 
63  class InstanceRendererDeleteListener : public InstanceDeleteListener {
64  public:
65  InstanceRendererDeleteListener(InstanceRenderer* r) {
66  m_renderer = r;
67  }
68  virtual ~InstanceRendererDeleteListener() {}
69 
70  virtual void onInstanceDeleted(Instance* instance) {
71  m_renderer->removeInstance(instance);
72  }
73 
74  private:
75  InstanceRenderer* m_renderer;
76  };
77 
78  InstanceRenderer::OutlineInfo::OutlineInfo(InstanceRenderer* r):
79  r(0),
80  g(0),
81  b(0),
82  width(1),
83  dirty(false),
84  curimg(NULL),
85  renderer(r) {
86  }
87  InstanceRenderer::ColoringInfo::ColoringInfo(InstanceRenderer* r):
88  r(0),
89  g(0),
90  b(0),
91  dirty(false),
92  curimg(NULL),
93  renderer(r) {
94  }
95 
96  InstanceRenderer::AreaInfo::AreaInfo():
97  instance(NULL),
98  groups(),
99  w(1),
100  h(1),
101  trans(0),
102  front(true),
103  z(0) {
104  }
105 
106  InstanceRenderer::OutlineInfo::~OutlineInfo() {
107  renderer->addToCheck(outline);
108  }
109 
110  InstanceRenderer::ColoringInfo::~ColoringInfo() {
111  if (renderer->needColorBinding()) {
112  renderer->addToCheck(overlay);
113  }
114  }
115 
116  InstanceRenderer::AreaInfo::~AreaInfo() {
117  }
118 
119  InstanceRenderer* InstanceRenderer::getInstance(IRendererContainer* cnt) {
120  return dynamic_cast<InstanceRenderer*>(cnt->getRenderer("InstanceRenderer"));
121  }
122 
123  InstanceRenderer::InstanceRenderer(RenderBackend* renderbackend, int32_t position):
124  RendererBase(renderbackend, position),
125  m_area_layer(false),
126  m_interval(60*1000),
127  m_timer_enabled(false) {
128  setEnabled(true);
129  if(m_renderbackend->getName() == "OpenGLe") {
130  m_need_sorting = false;
131  } else {
132  m_need_sorting = true;
133  if(m_renderbackend->getName() == "SDL") {
134  m_need_bind_coloring = true;
135  } else {
136  m_need_bind_coloring = false;
137  }
138  }
139  // init timer
140  m_timer.setInterval(m_interval);
141  m_timer.setCallback(boost::bind(&InstanceRenderer::check, this));
142  // create delete listener
143  m_delete_listener = new InstanceRendererDeleteListener(this);
144  }
145 
146  InstanceRenderer::InstanceRenderer(const InstanceRenderer& old):
147  RendererBase(old),
148  m_area_layer(false),
149  m_interval(old.m_interval),
150  m_timer_enabled(false) {
151  setEnabled(true);
152  if(m_renderbackend->getName() == "OpenGLe") {
153  m_need_sorting = false;
154  } else {
155  m_need_sorting = true;
156  if(m_renderbackend->getName() == "SDL") {
157  m_need_bind_coloring = true;
158  } else {
159  m_need_bind_coloring = false;
160  }
161  }
162  // init timer
163  m_timer.setInterval(m_interval);
164  m_timer.setCallback(boost::bind(&InstanceRenderer::check, this));
165  // create delete listener
166  m_delete_listener = new InstanceRendererDeleteListener(this);
167  }
168 
169  RendererBase* InstanceRenderer::clone() {
170  return new InstanceRenderer(*this);
171  }
172 
173  InstanceRenderer::~InstanceRenderer() {
174  // remove listener from instances
175  if (!m_assigned_instances.empty()) {
176  reset();
177  }
178  // delete listener
179  delete m_delete_listener;
180  }
181 
182  void InstanceRenderer::render(Camera* cam, Layer* layer, RenderList& instances) {
183 // FL_DBG(_log, "Iterating layer...");
184  CellGrid* cg = layer->getCellGrid();
185  if (!cg) {
186  FL_WARN(_log, "No cellgrid assigned to layer, cannot draw instances");
187  return;
188  }
189 
190  if(m_need_sorting) {
191  renderAlreadySorted(cam, layer, instances);
192  } else {
193  renderUnsorted(cam, layer, instances);
194  }
195  }
196 
197  void InstanceRenderer::renderUnsorted(Camera* cam, Layer* layer, RenderList& instances)
198  {
199  // TODO transparent area stuff was cut off
200  const bool any_effects = !(m_instance_outlines.empty() && m_instance_colorings.empty());
201  const bool unlit = !m_unlit_groups.empty();
202  uint32_t lm = m_renderbackend->getLightingModel();
203 
204  // Get layer index (this is needed for tweaking vertexZ for OpenGL renderbackend)
205  Map* parent = layer->getMap();
206  int num_layers = parent->getLayerCount();
207  int this_layer = 1; // we don't need 0 indexed
208  const std::list<Layer*>& layers = parent->getLayers();
209  std::list<Layer*>::const_iterator iter = layers.begin();
210  for (; iter != layers.end(); ++iter, ++this_layer) {
211  if (*iter == layer) {
212  break;
213  }
214  }
215 
216  // This values comes from glOrtho settings
217  static const double global_z_min = -100.0;
218  static const double global_z_max = 100.0;
219 #if 0
220  static const double depth_range = fabs(global_z_min - global_z_max);
221 
222  // This is how much depth we can sacrifice for given layer
223  // f.e: We have 200 depth range and 4 layers. That means we can assign
224  // each layer 50 depth range:
225  // 1st layer gets -100..-50
226  // 2nd layer gets -50..0
227  // 3rd layer gets 0..50
228  // 4th layer gets 50..100
229  double layer_depth_range = depth_range / static_cast<double>(num_layers);
230  // What's left, is to compute z offset for given layer
231  double layer_z_offset = global_z_min +
232  layer_depth_range * static_cast<double>(this_layer) -
233  layer_depth_range * 0.5;
234 #else
235 
236  // 2nd version, better usage of depth buffer as values closer to near plane in depth buffer have more precise (1/z)
237  double layer_z_offset = global_z_max - (num_layers - (this_layer - 1)) * 20;
238 #endif
239  // thanks to multimap, we will have transparent instances already sorted by their z value (key)
240  std::multimap<float, RenderItem*> transparentInstances;
241 
242  RenderList::iterator instance_it = instances.begin();
243  for (;instance_it != instances.end(); ++instance_it) {
244 // FL_DBG(_log, "Iterating instances...");
245  Instance* instance = (*instance_it)->instance;
246  RenderItem& vc = **instance_it;
247  float vertexZ = static_cast<float>(layer_z_offset + vc.screenpoint.z);
248 
249  // if the instance is opacous
250  if(vc.transparency == 255) {
251  if (any_effects) {
252  InstanceToOutlines_t::iterator outline_it = m_instance_outlines.find(instance);
253  const bool outline = outline_it != m_instance_outlines.end();
254  if (outline) {
255  Image* outline = bindOutline(outline_it->second, vc, cam);
256  outline->renderZ(vc.dimensions, vertexZ, 255, lm != 0 ? true : false);
257  vc.image->renderZ(vc.dimensions, vertexZ, 255);
258  }
259 
260  InstanceToColoring_t::iterator coloring_it = m_instance_colorings.find(instance);
261  const bool coloring = coloring_it != m_instance_colorings.end();
262  if (coloring) {
263  uint8_t rgb[3] = { coloring_it->second.r, coloring_it->second.g, coloring_it->second.b };
264  vc.image->renderZ(vc.dimensions, vertexZ, 255, false, rgb);
265  }
266 
267  if (outline || coloring) {
268  continue;
269  }
270  }
271 
272  // if we use lighting and appointed some of the instances as unlit
273  if(lm != 0 && unlit) {
274  bool found = false;
275  std::string lit_name = instance->getObject()->getNamespace();
276  std::list<std::string>::iterator unlit_it = m_unlit_groups.begin();
277  for(;unlit_it != m_unlit_groups.end(); ++unlit_it) {
278  if(lit_name.find(*unlit_it) != std::string::npos) {
279  found = true;
280  break;
281  }
282  }
283 
284  vc.image->renderZ(vc.dimensions, vertexZ, 255, found ? true : false);
285  continue;
286  }
287 
288  vc.image->renderZ(vc.dimensions, vertexZ, 255);
289  } else {
290  transparentInstances.insert(std::pair<float, RenderItem*>(vertexZ, &vc));
291  }
292  }
293 
294  // iterate through all (semi) transparent instances
295  if(!transparentInstances.empty()) {
296  std::multimap<float, RenderItem*>::iterator it = transparentInstances.begin();
297  for( ; it != transparentInstances.end(); ++it) {
298  RenderItem& vc = *(it->second);
299  uint8_t alpha = vc.transparency;// or? 255 - vc.transparency;
300  float vertexZ = it->first;
301 
302  if (any_effects) {
303  InstanceToOutlines_t::iterator outline_it = m_instance_outlines.find(vc.instance);
304  const bool outline = outline_it != m_instance_outlines.end();
305  if (outline) {
306  Image* outline = bindOutline(outline_it->second, vc, cam);
307  outline->renderZ(vc.dimensions, vertexZ, alpha, lm != 0 ? true : false);
308  vc.image->renderZ(vc.dimensions, vertexZ, alpha);
309  }
310 
311  InstanceToColoring_t::iterator coloring_it = m_instance_colorings.find(vc.instance);
312  const bool coloring = coloring_it != m_instance_colorings.end();
313  if (coloring) {
314  uint8_t rgb[3] = { coloring_it->second.r, coloring_it->second.g, coloring_it->second.b };
315  vc.image->renderZ(vc.dimensions, vertexZ, alpha, false, rgb);
316  }
317 
318  if (outline || coloring) {
319  continue;
320  }
321  }
322 
323  // if we use lighting and appointed some of the instances as unlit
324  if(lm != 0 && unlit) {
325  bool found = false;
326  std::string lit_name = vc.instance->getObject()->getNamespace();
327  std::list<std::string>::iterator unlit_it = m_unlit_groups.begin();
328  for(;unlit_it != m_unlit_groups.end(); ++unlit_it) {
329  if(lit_name.find(*unlit_it) != std::string::npos) {
330  found = true;
331  break;
332  }
333  }
334 
335  vc.image->renderZ(vc.dimensions, vertexZ, alpha, found ? true : false);
336  continue;
337  }
338 
339  vc.image->renderZ(vc.dimensions, vertexZ, alpha);
340  }
341  }
342  }
343 
344  void InstanceRenderer::renderAlreadySorted(Camera* cam, Layer* layer, RenderList& instances) {
345  const bool any_effects = !(m_instance_outlines.empty() && m_instance_colorings.empty());
346  const bool unlit = !m_unlit_groups.empty();
347  uint32_t lm = m_renderbackend->getLightingModel();
348 
349  m_area_layer = false;
350  if(!m_instance_areas.empty()) {
351  InstanceToAreas_t::iterator area_it = m_instance_areas.begin();
352  for(;area_it != m_instance_areas.end(); area_it++) {
353  AreaInfo& info = area_it->second;
354  if(info.instance->getLocation().getLayer() == layer) {
355  if(info.front) {
356  DoublePoint3D instance_posv = cam->toVirtualScreenCoordinates(info.instance->getLocation().getMapCoordinates());
357  info.z = instance_posv.z;
358  }
359  m_area_layer = true;
360  }
361  }
362  }
363 
364  RenderList::iterator instance_it = instances.begin();
365  for (;instance_it != instances.end(); ++instance_it) {
366 // FL_DBG(_log, "Iterating instances...");
367  Instance* instance = (*instance_it)->instance;
368  RenderItem& vc = **instance_it;
369 
370  if(m_area_layer) {
371  InstanceToAreas_t::iterator areas_it = m_instance_areas.begin();
372  for(;areas_it != m_instance_areas.end(); areas_it++) {
373  AreaInfo& infoa = areas_it->second;
374  if(infoa.front) {
375  if(infoa.z >= vc.screenpoint.z) {
376  continue;
377  }
378  }
379 
380  std::string str_name = instance->getObject()->getNamespace();
381  std::list<std::string>::iterator group_it = infoa.groups.begin();
382  for(;group_it != infoa.groups.end(); ++group_it) {
383  if(str_name.find((*group_it)) != std::string::npos) {
384  ScreenPoint p;
385  Rect rec;
386  p = cam->toScreenCoordinates(infoa.instance->getLocation().getMapCoordinates());
387  rec.x = p.x - infoa.w / 2;
388  rec.y = p.y - infoa.h / 2;
389  rec.w = infoa.w;
390  rec.h = infoa.h;
391  if(infoa.instance != instance && vc.dimensions.intersects(rec)) {
392  vc.transparency = 255 - infoa.trans;
393  }
394  }
395  }
396  }
397  }
398 
399 // FL_DBG(_log, LMsg("Instance layer coordinates = ") << instance->getLocationRef().getLayerCoordinates());
400 
401  if (any_effects) {
402  InstanceToOutlines_t::iterator outline_it = m_instance_outlines.find(instance);
403  const bool outline = outline_it != m_instance_outlines.end();
404  if (outline) {
405  if (lm != 0) {
406  // first render normal image without stencil and alpha test (0)
407  // so it wont look aliased and then with alpha test render only outline (its 'binary' image)
408  vc.image->render(vc.dimensions, vc.transparency);
409  bindOutline(outline_it->second, vc, cam)->render(vc.dimensions, vc.transparency);
410  m_renderbackend->changeRenderInfos(1, 4, 5, false, true, 255, REPLACE, ALWAYS);
411  } else {
412  bindOutline(outline_it->second, vc, cam)->render(vc.dimensions, vc.transparency);
413  vc.image->render(vc.dimensions, vc.transparency);
414  }
415  }
416 
417  InstanceToColoring_t::iterator coloring_it = m_instance_colorings.find(instance);
418  const bool coloring = coloring_it != m_instance_colorings.end();
419  if (coloring) {
420  if(m_need_bind_coloring) {
421  bindColoring(coloring_it->second, vc, cam)->render(vc.dimensions, vc.transparency);
422  m_renderbackend->changeRenderInfos(1, 4, 5, true, false, 0, KEEP, ALWAYS);
423  } else {
424  uint8_t rgb[3] = { coloring_it->second.r, coloring_it->second.g, coloring_it->second.b };
425  vc.image->render(vc.dimensions, vc.transparency, rgb);
426  m_renderbackend->changeRenderInfos(1, 4, 5, true, false, 0, KEEP, ALWAYS);
427  }
428  }
429 
430  if (outline || coloring) {
431  continue;
432  }
433  }
434  if(lm != 0) {
435  if(unlit) {
436  bool found = false;
437  std::string lit_name = instance->getObject()->getNamespace();
438  std::list<std::string>::iterator unlit_it = m_unlit_groups.begin();
439  for(;unlit_it != m_unlit_groups.end(); ++unlit_it) {
440  if(lit_name.find(*unlit_it) != std::string::npos) {
441  found = true;
442  break;
443  }
444  }
445  vc.image->render(vc.dimensions, vc.transparency);
446  if (found) {
447  m_renderbackend->changeRenderInfos(1, 4, 5, true, true, 255, REPLACE, ALWAYS);
448  } else {
449  m_renderbackend->changeRenderInfos(1, 4, 5, true, true, 0, ZERO, ALWAYS);
450  }
451  continue;
452  }
453  }
454  vc.image->render(vc.dimensions, vc.transparency);
455 
456  }
457  }
458 
459  inline bool aboveThreshold(int32_t threshold, int32_t alpha, int32_t prev_alpha) {
460  if(threshold > 1) {
461  // new behavior
462  if (((alpha - threshold) >= 0 || (prev_alpha - threshold) >= 0) && (alpha != prev_alpha)) {
463  return true;
464  } else {
465  return false;
466  }
467  } else {
468  // old behavior
469  if((alpha == 0 || prev_alpha == 0) && (alpha != prev_alpha)) {
470  return true;
471  } else {
472  return false;
473  }
474  }
475  }
476 
477  Image* InstanceRenderer::bindOutline(OutlineInfo& info, RenderItem& vc, Camera* cam) {
478  bool valid = isValidImage(info.outline);
479  if (!info.dirty && info.curimg == vc.image.get() && valid) {
480  removeFromCheck(info.outline);
481  // optimization for outline that has not changed
482  return info.outline.get();
483  } else {
484  info.curimg = vc.image.get();
485  }
486 
487  // if outline has changed we can maybe free the old effect image
488  if (valid) {
489  addToCheck(info.outline);
490  }
491 
492  // NOTE: Since r3721 outline is just the 'border' so to render everything correctly
493  // we need to first render normal image, and then its outline.
494  // This helps much with lighting stuff and doesn't require from us to copy image.
495 
496  bool found = false;
497  // create name
498  std::stringstream sts;
499  sts << vc.image.get()->getName() << "," << static_cast<uint32_t>(info.r) << "," <<
500  static_cast<uint32_t>(info.g) << "," << static_cast<uint32_t>(info.b) << "," << info.width;
501  // search image
502  if (ImageManager::instance()->exists(sts.str())) {
503  info.outline = ImageManager::instance()->getPtr(sts.str());
504  if (isValidImage(info.outline)) {
505  removeFromCheck(info.outline);
506  // mark outline as not dirty since we found it here
507  info.dirty = false;
508  return info.outline.get();
509  }
510  found = true;
511  }
512 
513 
514  // With lazy loading we can come upon a situation where we need to generate outline from
515  // uninitialised shared image
516  if(vc.image->isSharedImage()) {
517  vc.image->forceLoadInternal();
518  }
519 
520  SDL_Surface* surface = vc.image->getSurface();
521  SDL_Surface* outline_surface = SDL_CreateRGBSurface(SDL_SWSURFACE | SDL_SRCALPHA,
522  vc.image->getWidth(), vc.image->getHeight(), 32,
523  RMASK, GMASK, BMASK, AMASK);
524 
525  // TODO: optimize...
526  uint8_t r, g, b, a = 0;
527 
528  // vertical sweep
529  for (int32_t x = 0; x < outline_surface->w; x ++) {
530  int32_t prev_a = 0;
531  for (int32_t y = 0; y < outline_surface->h; y ++) {
532  vc.image->getPixelRGBA(x, y, &r, &g, &b, &a);
533  if (aboveThreshold(info.threshold, static_cast<int32_t>(a), prev_a)) {
534  if (a < prev_a) {
535  for (int32_t yy = y; yy < y + info.width; yy++) {
536  Image::putPixel(outline_surface, x, yy, info.r, info.g, info.b);
537  }
538  } else {
539  for (int32_t yy = y - info.width; yy < y; yy++) {
540  Image::putPixel(outline_surface, x, yy, info.r, info.g, info.b);
541  }
542  }
543  }
544  prev_a = a;
545  }
546  }
547  // horizontal sweep
548  for (int32_t y = 0; y < outline_surface->h; y ++) {
549  int32_t prev_a = 0;
550  for (int32_t x = 0; x < outline_surface->w; x ++) {
551  vc.image->getPixelRGBA(x, y, &r, &g, &b, &a);
552  if (aboveThreshold(info.threshold, static_cast<int32_t>(a), prev_a)) {
553  if (a < prev_a) {
554  for (int32_t xx = x; xx < x + info.width; xx++) {
555  Image::putPixel(outline_surface, xx, y, info.r, info.g, info.b);
556  }
557  } else {
558  for (int32_t xx = x - info.width; xx < x; xx++) {
559  Image::putPixel(outline_surface, xx, y, info.r, info.g, info.b);
560  }
561  }
562  }
563  prev_a = a;
564  }
565  }
566 
567  // In case of OpenGL backend, SDLImage needs to be converted
568  Image* img = m_renderbackend->createImage(sts.str(), outline_surface);
569  img->setState(IResource::RES_LOADED);
570 
571  if (found) {
572  // image exists but is not "loaded"
573  removeFromCheck(info.outline);
574  ImagePtr temp(img);
575  info.outline.get()->copySubimage(0, 0, temp);
576  info.outline.get()->setState(IResource::RES_LOADED);
577  } else {
578  // create and add image
579  info.outline = ImageManager::instance()->add(img);
580  }
581  // mark outline as not dirty since we created/recreated it here
582  info.dirty = false;
583 
584  return info.outline.get();
585  }
586 
587  Image* InstanceRenderer::bindColoring(ColoringInfo& info, RenderItem& vc, Camera* cam) {
588  bool valid = isValidImage(info.overlay);
589  if (!info.dirty && info.curimg == vc.image.get() && valid) {
590  removeFromCheck(info.overlay);
591  // optimization for coloring that has not changed
592  return info.overlay.get();
593  } else {
594  info.curimg = vc.image.get();
595  }
596 
597  // if coloring has changed we can maybe free the old effect image
598  if (valid) {
599  addToCheck(info.overlay);
600  }
601 
602  bool found = false;
603  // create name
604  std::stringstream sts;
605  sts << vc.image.get()->getName() << "," << static_cast<uint32_t>(info.r) << "," <<
606  static_cast<uint32_t>(info.g) << "," << static_cast<uint32_t>(info.b);
607  // search image
608  if (ImageManager::instance()->exists(sts.str())) {
609  info.overlay = ImageManager::instance()->getPtr(sts.str());
610  valid = isValidImage(info.overlay);
611  if (valid) {
612  removeFromCheck(info.overlay);
613  // mark overlay as not dirty since we found it here
614  info.dirty = false;
615  return info.overlay.get();
616  }
617  found = true;
618  }
619 
620  // With lazy loading we can come upon a situation where we need to generate coloring from
621  // uninitialised shared image
622  if(vc.image->isSharedImage()) {
623  vc.image->forceLoadInternal();
624  }
625 
626  // not found so we create it
627  SDL_Surface* surface = vc.image->getSurface();
628  SDL_Surface* overlay_surface = SDL_CreateRGBSurface(SDL_SWSURFACE | SDL_SRCALPHA,
629  vc.image->getWidth(), vc.image->getHeight(), 32,
630  RMASK, GMASK, BMASK, AMASK);
631 
632  uint8_t r, g, b, a = 0;
633 
634  for (int32_t x = 0; x < overlay_surface->w; x ++) {
635  for (int32_t y = 0; y < overlay_surface->h; y ++) {
636  vc.image->getPixelRGBA(x, y, &r, &g, &b, &a);
637  if (a > 0) {
638  Image::putPixel(overlay_surface, x, y, (r + info.r) >> 1, (g + info.g) >> 1, (b + info.b) >> 1, a);
639  }
640  }
641  }
642 
643  // In case of OpenGL backend, SDLImage needs to be converted
644  Image* img = m_renderbackend->createImage(sts.str(), overlay_surface);
645 
646  if (found) {
647  // image exists but is not "loaded"
648  removeFromCheck(info.overlay);
649  ImagePtr temp(img);
650  info.overlay.get()->copySubimage(0, 0, temp);
651  info.overlay.get()->setState(IResource::RES_LOADED);
652  } else {
653  // add image
654  img->setState(IResource::RES_LOADED);
655  info.overlay = ImageManager::instance()->add(img);
656  }
657  // mark overlay as not dirty since we created/recreated it here
658  info.dirty = false;
659 
660  return info.overlay.get();
661  }
662 
663  void InstanceRenderer::addOutlined(Instance* instance, int32_t r, int32_t g, int32_t b, int32_t width, int32_t threshold) {
664  OutlineInfo newinfo(this);
665  newinfo.r = r;
666  newinfo.g = g;
667  newinfo.b = b;
668  newinfo.threshold = threshold;
669  newinfo.width = width;
670  newinfo.dirty = true;
671 
672  // attempts to insert the element into the outline map
673  // will return false in the second value of the pair if the instance already exists
674  // in the map and the first value of the pair will then be an iterator to the
675  // existing data for the instance
676  std::pair<InstanceToOutlines_t::iterator, bool> insertiter = m_instance_outlines.insert(std::make_pair(instance, newinfo));
677 
678  if (insertiter.second == false) {
679  // the insertion did not happen because the instance
680  // already exists in the map so lets just update its outline info
681  OutlineInfo& info = insertiter.first->second;
682 
683  if (info.r != r || info.g != g || info.b != b || info.width != width) {
684  // only update the outline info if its changed since the last call
685  // flag the outline info as dirty so it will get processed during rendering
686  info.r = r;
687  info.b = b;
688  info.g = g;
689  info.width = width;
690  info.threshold = threshold;
691  info.dirty = true;
692  }
693  } else {
694  std::pair<InstanceToEffects_t::iterator, bool> iter = m_assigned_instances.insert(std::make_pair(instance, OUTLINE));
695  if (iter.second) {
696  instance->addDeleteListener(m_delete_listener);
697  } else {
698  Effect& effect = iter.first->second;
699  if ((effect & OUTLINE) != OUTLINE) {
700  effect += OUTLINE;
701  }
702  }
703  }
704  }
705 
706  void InstanceRenderer::addColored(Instance* instance, int32_t r, int32_t g, int32_t b) {
707  ColoringInfo newinfo(this);
708  newinfo.r = r;
709  newinfo.g = g;
710  newinfo.b = b;
711  newinfo.dirty = true;
712 
713  // attempts to insert the element into the coloring map
714  // will return false in the second value of the pair if the instance already exists
715  // in the map and the first value of the pair will then be an iterator to the
716  // existing data for the instance
717  std::pair<InstanceToColoring_t::iterator, bool> insertiter = m_instance_colorings.insert(std::make_pair(instance, newinfo));
718 
719  if (insertiter.second == false) {
720  // the insertion did not happen because the instance
721  // already exists in the map so lets just update its coloring info
722  ColoringInfo& info = insertiter.first->second;
723 
724  if (info.r != r || info.g != g || info.b != b) {
725  // only update the coloring info if its changed since the last call
726  info.r = r;
727  info.b = b;
728  info.g = g;
729  info.dirty = true;
730  }
731  } else {
732  std::pair<InstanceToEffects_t::iterator, bool> iter = m_assigned_instances.insert(std::make_pair(instance, COLOR));
733  if (iter.second) {
734  instance->addDeleteListener(m_delete_listener);
735  } else {
736  Effect& effect = iter.first->second;
737  if ((effect & COLOR) != COLOR) {
738  effect += COLOR;
739  }
740  }
741  }
742  }
743 
744  void InstanceRenderer::addTransparentArea(Instance* instance, const std::list<std::string> &groups, uint32_t w, uint32_t h, uint8_t trans, bool front) {
745  AreaInfo newinfo;
746  newinfo.instance = instance;
747  newinfo.groups = groups;
748 
749  newinfo.w = w;
750  newinfo.h = h;
751  newinfo.trans = trans;
752  newinfo.front = front;
753 
754 
755  // attempts to insert the element into the area map
756  // will return false in the second value of the pair if the instance already exists
757  // in the map and the first value of the pair will then be an iterator to the
758  // existing data for the instance
759  std::pair<InstanceToAreas_t::iterator, bool> insertiter = m_instance_areas.insert(std::make_pair(instance, newinfo));
760 
761  if (insertiter.second == false) {
762  // the insertion did not happen because the instance
763  // already exists in the map so lets just update its area info
764  AreaInfo& info = insertiter.first->second;
765  } else {
766  std::pair<InstanceToEffects_t::iterator, bool> iter = m_assigned_instances.insert(std::make_pair(instance, AREA));
767  if (iter.second) {
768  instance->addDeleteListener(m_delete_listener);
769  } else {
770  Effect& effect = iter.first->second;
771  if ((effect & AREA) != AREA) {
772  effect += AREA;
773  }
774  }
775  }
776  }
777 
778  void InstanceRenderer::removeOutlined(Instance* instance) {
779  InstanceToEffects_t::iterator it = m_assigned_instances.find(instance);
780  if (it != m_assigned_instances.end()) {
781  if (it->second == OUTLINE) {
782  instance->removeDeleteListener(m_delete_listener);
783  m_instance_outlines.erase(instance);
784  m_assigned_instances.erase(it);
785  } else if ((it->second & OUTLINE) == OUTLINE) {
786  it->second -= OUTLINE;
787  m_instance_outlines.erase(instance);
788  }
789  }
790  }
791 
792  void InstanceRenderer::removeColored(Instance* instance) {
793  InstanceToEffects_t::iterator it = m_assigned_instances.find(instance);
794  if (it != m_assigned_instances.end()) {
795  if (it->second == COLOR) {
796  instance->removeDeleteListener(m_delete_listener);
797  m_instance_colorings.erase(instance);
798  m_assigned_instances.erase(it);
799  } else if ((it->second & COLOR) == COLOR) {
800  it->second -= COLOR;
801  m_instance_colorings.erase(instance);
802  }
803  }
804  }
805 
806  void InstanceRenderer::removeTransparentArea(Instance* instance) {
807  InstanceToEffects_t::iterator it = m_assigned_instances.find(instance);
808  if (it != m_assigned_instances.end()) {
809  if (it->second == AREA) {
810  instance->removeDeleteListener(m_delete_listener);
811  m_instance_areas.erase(instance);
812  m_assigned_instances.erase(it);
813  } else if ((it->second & AREA) == AREA) {
814  it->second -= AREA;
815  m_instance_areas.erase(instance);
816  }
817  }
818  }
819 
820  void InstanceRenderer::removeAllOutlines() {
821  if (!m_instance_outlines.empty()) {
822  InstanceToOutlines_t::iterator outline_it = m_instance_outlines.begin();
823  for (; outline_it != m_instance_outlines.end(); ++outline_it) {
824  InstanceToEffects_t::iterator it = m_assigned_instances.find((*outline_it).first);
825  if (it != m_assigned_instances.end()) {
826  if (it->second == OUTLINE) {
827  (*outline_it).first->removeDeleteListener(m_delete_listener);
828  m_assigned_instances.erase(it);
829  } else if ((it->second & OUTLINE) == OUTLINE) {
830  it->second -= OUTLINE;
831  }
832  }
833  }
834  m_instance_outlines.clear();
835  }
836  }
837 
838  void InstanceRenderer::removeAllColored() {
839  if (!m_instance_colorings.empty()) {
840  InstanceToColoring_t::iterator color_it = m_instance_colorings.begin();
841  for (; color_it != m_instance_colorings.end(); ++color_it) {
842  InstanceToEffects_t::iterator it = m_assigned_instances.find((*color_it).first);
843  if (it != m_assigned_instances.end()) {
844  if (it->second == COLOR) {
845  (*color_it).first->removeDeleteListener(m_delete_listener);
846  m_assigned_instances.erase(it);
847  } else if ((it->second & COLOR) == COLOR) {
848  it->second -= COLOR;
849  }
850  }
851  }
852  m_instance_colorings.clear();
853  }
854  }
855 
856  void InstanceRenderer::removeAllTransparentAreas() {
857  if (!m_instance_areas.empty()) {
858  InstanceToAreas_t::iterator area_it = m_instance_areas.begin();
859  for (; area_it != m_instance_areas.end(); ++area_it) {
860  InstanceToEffects_t::iterator it = m_assigned_instances.find((*area_it).first);
861  if (it != m_assigned_instances.end()) {
862  if (it->second == AREA) {
863  (*area_it).first->removeDeleteListener(m_delete_listener);
864  m_assigned_instances.erase(it);
865  } else if ((it->second & AREA) == AREA) {
866  it->second -= AREA;
867  }
868  }
869  }
870  m_instance_areas.clear();
871  }
872  }
873 
874  void InstanceRenderer::addIgnoreLight(const std::list<std::string> &groups) {
875  std::list<std::string>::const_iterator group_it = groups.begin();
876  for(;group_it != groups.end(); ++group_it) {
877  m_unlit_groups.push_back(*group_it);
878  }
879  m_unlit_groups.sort();
880  m_unlit_groups.unique();
881  }
882 
883  void InstanceRenderer::removeIgnoreLight(const std::list<std::string> &groups) {
884  std::list<std::string>::const_iterator group_it = groups.begin();
885  for(;group_it != groups.end(); ++group_it) {
886  std::list<std::string>::iterator unlit_it = m_unlit_groups.begin();
887  for(;unlit_it != m_unlit_groups.end(); ++unlit_it) {
888  if((*group_it).find(*unlit_it) != std::string::npos) {
889  m_unlit_groups.remove(*unlit_it);
890  break;
891  }
892  }
893  }
894  }
895 
896  void InstanceRenderer::removeAllIgnoreLight() {
897  m_unlit_groups.clear();
898  }
899 
900  void InstanceRenderer::reset() {
901  // stop timer
902  if (m_timer_enabled) {
903  m_timer.stop();
904  }
905  // remove all effects and listener
906  removeAllOutlines();
907  removeAllColored();
908  removeAllTransparentAreas();
909  removeAllIgnoreLight();
910  // removes the references to the effect images
911  m_check_images.clear();
912  }
913 
914  void InstanceRenderer::setRemoveInterval(uint32_t interval) {
915  if (m_interval != interval*1000) {
916  m_interval = interval*1000;
917  m_timer.setInterval(m_interval);
918  }
919  }
920 
921  uint32_t InstanceRenderer::getRemoveInterval() const {
922  return m_interval/1000;
923  }
924 
925  void InstanceRenderer::addToCheck(const ImagePtr& image) {
926  if (isValidImage(image)) {
927  // if image is already inserted then return
928  ImagesToCheck_t::iterator it = m_check_images.begin();
929  for (; it != m_check_images.end(); ++it) {
930  if (it->image.get()->getName() == image.get()->getName()) {
931  return;
932  }
933  }
934  s_image_entry entry;
935  entry.image = image;
936  entry.timestamp = TimeManager::instance()->getTime();
937  m_check_images.push_front(entry);
938 
939  if (!m_timer_enabled) {
940  m_timer_enabled = true;
941  m_timer.start();
942  }
943  }
944  }
945 
946  void InstanceRenderer::check() {
947  uint32_t now = TimeManager::instance()->getTime();
948  ImagesToCheck_t::iterator it = m_check_images.begin();
949  // free unused images
950  while (it != m_check_images.end()) {
951  if (now - it->timestamp > m_interval) {
952  if (isValidImage(it->image)) {
953  ImageManager::instance()->free(it->image.get()->getName());
954  }
955  it = m_check_images.erase(it);
956  } else {
957  ++it;
958  }
959  }
960 
961  if (m_check_images.empty() && m_timer_enabled) {
962  m_timer_enabled = false;
963  m_timer.stop();
964  }
965  }
966 
967  void InstanceRenderer::removeFromCheck(const ImagePtr& image) {
968  if (isValidImage(image)) {
969  // if the image is used then remove it here
970  ImagesToCheck_t::iterator it = m_check_images.begin();
971  for (; it != m_check_images.end(); ++it) {
972  if (it->image.get()->getName() == image.get()->getName()) {
973  m_check_images.erase(it);
974  break;
975  }
976  }
977 
978  if (m_check_images.empty() && m_timer_enabled) {
979  m_timer_enabled = false;
980  m_timer.stop();
981  }
982  }
983  }
984 
985  void InstanceRenderer::removeInstance(Instance* instance) {
986  InstanceToEffects_t::iterator it = m_assigned_instances.find(instance);
987  if (it != m_assigned_instances.end()) {
988  m_instance_outlines.erase(instance);
989  m_instance_colorings.erase(instance);
990  m_instance_areas.erase(instance);
991  instance->removeDeleteListener(m_delete_listener);
992  m_assigned_instances.erase(it);
993  }
994  }
995 
996  bool InstanceRenderer::isValidImage(const ImagePtr& image) {
997  if (image.get()) {
998  if (image.get()->getState() == IResource::RES_LOADED) {
999  return true;
1000  }
1001  }
1002  return false;
1003  }
1004 }
virtual const std::string & getName() const =0
virtual void setEnabled(bool enabled)