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 
26 // FIFE includes
27 // These includes are split up in two parts, separated by one empty line
28 // First block: files included from the FIFE root src directory
29 // Second block: files included from the same folder
30 #include "video/renderbackend.h"
31 #include "video/image.h"
32 #include "video/sdl/sdlimage.h"
33 #include "video/imagepool.h"
34 #include "video/animation.h"
35 #include "video/animationpool.h"
36 #include "util/math/fife_math.h"
37 #include "util/log/logger.h"
38 #include "model/metamodel/grids/cellgrid.h"
39 #include "model/metamodel/action.h"
40 #include "model/structures/instance.h"
41 #include "model/structures/layer.h"
42 #include "model/structures/location.h"
43 #include "video/opengl/fife_opengl.h"
44 
45 #include "view/camera.h"
46 #include "view/visual.h"
47 #include "instancerenderer.h"
48 
49 
50 namespace {
51  unsigned int scale(unsigned int val, double factor) {
52  return static_cast<unsigned int>(ceil(static_cast<double>(val) * factor));
53  }
54 }
55 
56 namespace FIFE {
57  static Logger _log(LM_VIEWVIEW);
58 
59  InstanceRenderer::OutlineInfo::OutlineInfo():
60  r(0),
61  g(0),
62  b(0),
63  width(1),
64  dirty(false),
65  outline(NULL),
66  curimg(NULL) {
67  }
68  InstanceRenderer::ColoringInfo::ColoringInfo():
69  r(0),
70  g(0),
71  b(0),
72  dirty(false),
73  overlay(NULL),
74  curimg(NULL) {
75  }
76 
77  InstanceRenderer::AreaInfo::AreaInfo():
78  instance(NULL),
79  groups(),
80  w(1),
81  h(1),
82  trans(0),
83  front(true),
84  z(0) {
85  }
86 
87  InstanceRenderer::OutlineInfo::~OutlineInfo() {
88  delete outline;
89  }
90 
91  InstanceRenderer::ColoringInfo::~ColoringInfo() {
92  delete overlay;
93  }
94 
95  InstanceRenderer::AreaInfo::~AreaInfo() {
96  }
97 
98  InstanceRenderer* InstanceRenderer::getInstance(IRendererContainer* cnt) {
99  return dynamic_cast<InstanceRenderer*>(cnt->getRenderer("InstanceRenderer"));
100  }
101 
102  InstanceRenderer::InstanceRenderer(RenderBackend* renderbackend, int position, ImagePool* imagepool, AnimationPool* animpool):
103  RendererBase(renderbackend, position),
104  m_imagepool(imagepool),
105  m_animationpool(animpool),
106  m_area_layer(false) {
107  setEnabled(true);
108  }
109 
110  InstanceRenderer::InstanceRenderer(const InstanceRenderer& old):
111  RendererBase(old),
112  m_imagepool(old.m_imagepool),
113  m_animationpool(old.m_animationpool),
114  m_area_layer(old.m_area_layer) {
115  setEnabled(true);
116  }
117 
118  RendererBase* InstanceRenderer::clone() {
119  return new InstanceRenderer(*this);
120  }
121 
122  InstanceRenderer::~InstanceRenderer() {
123  }
124 
125  void InstanceRenderer::render(Camera* cam, Layer* layer, RenderList& instances) {
126  FL_DBG(_log, "Iterating layer...");
127  CellGrid* cg = layer->getCellGrid();
128  if (!cg) {
129  FL_WARN(_log, "No cellgrid assigned to layer, cannot draw instances");
130  return;
131  }
132 
133  const bool any_effects = !(m_instance_outlines.empty() && m_instance_colorings.empty());
134  const bool unlit = !m_unlit_groups.empty();
135  unsigned int lm = m_renderbackend->getLightingModel();
136 
137  m_area_layer = false;
138  if(!m_instance_areas.empty()) {
139  InstanceToAreas_t::iterator area_it = m_instance_areas.begin();
140  for(;area_it != m_instance_areas.end(); area_it++) {
141  AreaInfo& info = area_it->second;
142  if(info.instance->getLocation().getLayer() == layer) {
143  if(info.front) {
144  DoublePoint3D instance_posv = cam->toVirtualScreenCoordinates(info.instance->getLocation().getMapCoordinates());
145  info.z = instance_posv.z;
146  }
147  m_area_layer = true;
148  }
149  }
150  }
151 
152  RenderList::iterator instance_it = instances.begin();
153  for (;instance_it != instances.end(); ++instance_it) {
154  FL_DBG(_log, "Iterating instances...");
155  Instance* instance = (*instance_it)->instance;
156  RenderItem& vc = **instance_it;
157 
158  if(m_area_layer) {
159  InstanceToAreas_t::iterator areas_it = m_instance_areas.begin();
160  for(;areas_it != m_instance_areas.end(); areas_it++) {
161  AreaInfo& infoa = areas_it->second;
162  if(infoa.front) {
163  if(infoa.z >= vc.screenpoint.z) {
164  continue;
165  }
166  }
167 
168  std::string str_name = instance->getObject()->getNamespace();
169  std::list<std::string>::iterator group_it = infoa.groups.begin();
170  for(;group_it != infoa.groups.end(); ++group_it) {
171  if(str_name.find((*group_it)) != std::string::npos) {
172  ScreenPoint p;
173  Rect rec;
174  p = cam->toScreenCoordinates(infoa.instance->getLocation().getMapCoordinates());
175  rec.x = p.x - infoa.w / 2;
176  rec.y = p.y - infoa.h / 2;
177  rec.w = infoa.w;
178  rec.h = infoa.h;
179  if(infoa.instance != instance && vc.dimensions.intersects(rec)) {
180  vc.transparency = 255 - infoa.trans;
181  }
182  }
183  }
184  }
185  }
186 
187  FL_DBG(_log, LMsg("Instance layer coordinates = ") << instance->getLocationRef().getLayerCoordinates());
188 
189  if (any_effects) {
190  InstanceToOutlines_t::iterator outline_it = m_instance_outlines.find(instance);
191  if (outline_it != m_instance_outlines.end()) {
192  if (lm != 0) {
193  m_renderbackend->disableLighting();
194  m_renderbackend->setStencilTest(255, 2, 7);
195  m_renderbackend->setAlphaTest(0.0);
196  bindOutline(outline_it->second, vc, cam)->render(vc.dimensions, vc.transparency);
197  m_renderbackend->enableLighting();
198  m_renderbackend->setStencilTest(0, 2, 7);
199  vc.image->render(vc.dimensions, vc.transparency);
200  m_renderbackend->disableAlphaTest();
201  m_renderbackend->disableStencilTest();
202  continue;
203  }
204  bindOutline(outline_it->second, vc, cam)->render(vc.dimensions, vc.transparency);
205  }
206 
207  InstanceToColoring_t::iterator coloring_it = m_instance_colorings.find(instance);
208  if (coloring_it != m_instance_colorings.end()) {
209  m_renderbackend->disableLighting();
210  bindColoring(coloring_it->second, vc, cam)->render(vc.dimensions, vc.transparency);
211  m_renderbackend->enableLighting();
212  continue; // Skip normal rendering after drawing overlay
213  }
214  }
215  if(lm != 0) {
216  if(unlit) {
217  bool found = false;
218  std::string lit_name = instance->getObject()->getNamespace();
219  std::list<std::string>::iterator unlit_it = m_unlit_groups.begin();
220  for(;unlit_it != m_unlit_groups.end(); ++unlit_it) {
221  if(lit_name.find(*unlit_it) != std::string::npos) {
222  m_renderbackend->setStencilTest(255, 2, 7);
223  found = true;
224  break;
225  }
226  }
227  // This is very expensiv, we have to change it
228  if(!found)
229  m_renderbackend->setStencilTest(0, 1, 7);
230 
231  m_renderbackend->setAlphaTest(0.0);
232  vc.image->render(vc.dimensions, vc.transparency);
233  continue;
234  }
235  }
236  vc.image->render(vc.dimensions, vc.transparency);
237 
238  }
239  if(lm != 0) {
240  m_renderbackend->disableAlphaTest();
241  m_renderbackend->disableStencilTest();
242  }
243  }
244 
245  Image* InstanceRenderer::bindOutline(OutlineInfo& info, RenderItem& vc, Camera* cam) {
246  if (!info.dirty && info.curimg == vc.image) {
247  // optimization for outline that has not changed
248  return info.outline;
249  } else {
250  info.curimg = vc.image;
251  }
252 
253  if (info.outline) {
254  delete info.outline; // delete old mask
255  info.outline = NULL;
256  }
257  SDL_Surface* surface = vc.image->getSurface();
258  SDL_Surface* outline_surface = SDL_ConvertSurface(surface, surface->format, surface->flags);
259 
260  // needs to use SDLImage here, since GlImage does not support drawing primitives atm
261  SDLImage* img = new SDLImage(outline_surface);
262 
263  // TODO: optimize...
264  uint8_t r, g, b, a = 0;
265 
266  // vertical sweep
267  for (unsigned int x = 0; x < img->getWidth(); x ++) {
268  uint8_t prev_a = 0;
269  for (unsigned int y = 0; y < img->getHeight(); y ++) {
270  vc.image->getPixelRGBA(x, y, &r, &g, &b, &a);
271  if ((a == 0 || prev_a == 0) && (a != prev_a)) {
272  if (a < prev_a) {
273  for (unsigned int yy = y; yy < y + info.width; yy++) {
274  img->putPixel(x, yy, info.r, info.g, info.b);
275  }
276  } else {
277  for (unsigned int yy = y - info.width; yy < y; yy++) {
278  img->putPixel(x, yy, info.r, info.g, info.b);
279  }
280  }
281  }
282  prev_a = a;
283  }
284  }
285  // horizontal sweep
286  for (unsigned int y = 0; y < img->getHeight(); y ++) {
287  uint8_t prev_a = 0;
288  for (unsigned int x = 0; x < img->getWidth(); x ++) {
289  vc.image->getPixelRGBA(x, y, &r, &g, &b, &a);
290  if ((a == 0 || prev_a == 0) && (a != prev_a)) {
291  if (a < prev_a) {
292  for (unsigned int xx = x; xx < x + info.width; xx++) {
293  img->putPixel(xx, y, info.r, info.g, info.b);
294  }
295  } else {
296  for (unsigned int xx = x - info.width; xx < x; xx++) {
297  img->putPixel(xx, y, info.r, info.g, info.b);
298  }
299  }
300  }
301  prev_a = a;
302  }
303  }
304 
305  // In case of OpenGL backend, SDLImage needs to be converted
306  info.outline = m_renderbackend->createImage(img->detachSurface());
307  delete img;
308 
309  if (info.outline) {
310  // mark outline as not dirty since we created it here
311  info.dirty = false;
312  }
313 
314  return info.outline;
315  }
316 
317  Image* InstanceRenderer::bindColoring(ColoringInfo& info, RenderItem& vc, Camera* cam) {
318  if (!info.dirty && info.curimg == vc.image) {
319  // optimization for coloring overlay that has not changed
320  return info.overlay;
321  }
322  else {
323  info.curimg = vc.image;
324  }
325 
326  if (info.overlay) {
327  delete info.overlay; // delete old mask
328  info.overlay = NULL;
329  }
330 
331  SDL_Surface* surface = vc.image->getSurface();
332  SDL_Surface* overlay_surface = SDL_ConvertSurface(surface, surface->format, surface->flags);
333 
334  // needs to use SDLImage here, since GlImage does not support drawing primitives atm
335  SDLImage* img = new SDLImage(overlay_surface);
336 
337  uint8_t r, g, b, a = 0;
338 
339  for (unsigned int x = 0; x < img->getWidth(); x ++) {
340  for (unsigned int y = 0; y < img->getHeight(); y ++) {
341  vc.image->getPixelRGBA(x, y, &r, &g, &b, &a);
342  if (a > 0) {
343  img->putPixel(x, y, (r + info.r) >> 1, (g + info.g) >> 1, (b + info.b) >> 1);
344  }
345  }
346  }
347 
348  // In case of OpenGL backend, SDLImage needs to be converted
349  info.overlay = m_renderbackend->createImage(img->detachSurface());
350  delete img;
351 
352  if (info.overlay) {
353  // mark overlay coloring as not dirty since we created it here
354  info.dirty = false;
355  }
356 
357  return info.overlay;
358  }
359 
360  void InstanceRenderer::addOutlined(Instance* instance, int r, int g, int b, int width) {
361  OutlineInfo newinfo;
362  newinfo.r = r;
363  newinfo.g = g;
364  newinfo.b = b;
365  newinfo.width = width;
366  newinfo.dirty = true;
367 
368  // attempts to insert the element into the outline map
369  // will return false in the second value of the pair if the instance already exists
370  // in the map and the first value of the pair will then be an iterator to the
371  // existing data for the instance
372  std::pair<InstanceToOutlines_t::iterator, bool> insertiter = m_instance_outlines.insert(std::make_pair(instance, newinfo));
373 
374  if (insertiter.second == false) {
375  // the insertion did not happen because the instance
376  // already exists in the map so lets just update its outline info
377  OutlineInfo& info = insertiter.first->second;
378 
379  if (info.r != r || info.g != g || info.b != b || info.width != width) {
380  // only update the outline info if its changed since the last call
381  // flag the outline info as dirty so it will get processed during rendering
382  info.r = r;
383  info.b = b;
384  info.g = g;
385  info.width = width;
386  info.dirty = true;
387  }
388  }
389  }
390 
391  void InstanceRenderer::addColored(Instance* instance, int r, int g, int b) {
392  ColoringInfo newinfo;
393  newinfo.r = r;
394  newinfo.g = g;
395  newinfo.b = b;
396  newinfo.dirty = true;
397 
398  // attempts to insert the element into the coloring map
399  // will return false in the second value of the pair if the instance already exists
400  // in the map and the first value of the pair will then be an iterator to the
401  // existing data for the instance
402  std::pair<InstanceToColoring_t::iterator, bool> insertiter = m_instance_colorings.insert(std::make_pair(instance, newinfo));
403 
404  if (insertiter.second == false) {
405  // the insertion did not happen because the instance
406  // already exists in the map so lets just update its coloring info
407  ColoringInfo& info = insertiter.first->second;
408 
409  if (info.r != r || info.g != g || info.b != b) {
410  // only update the coloring info if its changed since the last call
411  // flag the coloring info as dirty so it will get processed during rendering
412  info.r = r;
413  info.b = b;
414  info.g = g;
415  info.dirty = true;
416  }
417  }
418  }
419 
420  void InstanceRenderer::addTransparentArea(Instance* instance, const std::list<std::string> &groups, unsigned int w, unsigned int h, unsigned char trans, bool front) {
421  AreaInfo newinfo;
422  newinfo.instance = instance;
423  newinfo.groups = groups;
424 
425  newinfo.w = w;
426  newinfo.h = h;
427  newinfo.trans = trans;
428  newinfo.front = front;
429 
430 
431  // attempts to insert the element into the area map
432  // will return false in the second value of the pair if the instance already exists
433  // in the map and the first value of the pair will then be an iterator to the
434  // existing data for the instance
435  std::pair<InstanceToAreas_t::iterator, bool> insertiter = m_instance_areas.insert(std::make_pair(instance, newinfo));
436 
437  if (insertiter.second == false) {
438  // the insertion did not happen because the instance
439  // already exists in the map so lets just update its area info
440  AreaInfo& info = insertiter.first->second;
441  }
442  }
443 
444  void InstanceRenderer::removeOutlined(Instance* instance) {
445  m_instance_outlines.erase(instance);
446  }
447 
448  void InstanceRenderer::removeColored(Instance* instance) {
449  m_instance_colorings.erase(instance);
450  }
451 
452  void InstanceRenderer::removeTransparentArea(Instance* instance) {
453  m_instance_areas.erase(instance);
454  }
455 
456  void InstanceRenderer::removeAllOutlines() {
457  m_instance_outlines.clear();
458  }
459 
460  void InstanceRenderer::removeAllColored() {
461  m_instance_colorings.clear();
462  }
463 
464  void InstanceRenderer::removeAllTransparentAreas() {
465  m_instance_areas.clear();
466  }
467 
468  void InstanceRenderer::addIgnoreLight(const std::list<std::string> &groups) {
469  std::list<std::string>::const_iterator group_it = groups.begin();
470  for(;group_it != groups.end(); ++group_it) {
471  m_unlit_groups.push_back(*group_it);
472  }
473  m_unlit_groups.sort();
474  m_unlit_groups.unique();
475  }
476 
477  void InstanceRenderer::removeIgnoreLight(const std::list<std::string> &groups) {
478  std::list<std::string>::const_iterator group_it = groups.begin();
479  for(;group_it != groups.end(); ++group_it) {
480  std::list<std::string>::iterator unlit_it = m_unlit_groups.begin();
481  for(;unlit_it != m_unlit_groups.end(); ++unlit_it) {
482  if((*group_it).find(*unlit_it) != std::string::npos) {
483  m_unlit_groups.remove(*unlit_it);
484  break;
485  }
486  }
487  }
488  }
489 
490  void InstanceRenderer::removeAllIgnoreLight() {
491  m_unlit_groups.clear();
492  }
493 
494  void InstanceRenderer::reset() {
495  removeAllOutlines();
496  removeAllColored();
497  removeAllTransparentAreas();
498  removeAllIgnoreLight();
499  }
500 
501 }