view engine/core/view/renderers/lightrenderer.cpp @ 680:60621d858548

* Fixed the image/animation functions in GenericRenderer and LightRenderer. The functions now consider the camera zoom level. * Added the forgotten LightEdit plugin for the editor.
author helios2000@33b003aa-7bff-0310-803a-e67f0ece8222
date Sun, 21 Nov 2010 15:58:14 +0000
parents e3140f01749d
children
line wrap: on
line source

/***************************************************************************
 *   Copyright (C) 2005-2008 by the FIFE team                              *
 *   http://www.fifengine.de                                               *
 *   This file is part of FIFE.                                            *
 *                                                                         *
 *   FIFE is free software; you can redistribute it and/or                 *
 *   modify it under the terms of the GNU Lesser General Public            *
 *   License as published by the Free Software Foundation; either          *
 *   version 2.1 of the License, or (at your option) any later version.    *
 *                                                                         *
 *   This library is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
 *   Lesser General Public License for more details.                       *
 *                                                                         *
 *   You should have received a copy of the GNU Lesser General Public      *
 *   License along with this library; if not, write to the                 *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA          *
 ***************************************************************************/

// Standard C++ library includes
#include <SDL.h>

// 3rd party library includes

// FIFE includes
// These includes are split up in two parts, separated by one empty line
// First block: files included from the FIFE root src directory
// Second block: files included from the same folder
#include "video/renderbackend.h"
#include "video/imagepool.h"
#include "video/animation.h"
#include "video/animationpool.h"
#include "video/fonts/abstractfont.h"
#include "video/image.h"
#include "video/opengl/glimage.h"
#include "util/math/fife_math.h"
#include "util/log/logger.h"
#include "util/time/timemanager.h"
#include "model/metamodel/grids/cellgrid.h"
#include "model/metamodel/timeprovider.h"
#include "model/structures/instance.h"
#include "model/structures/layer.h"
#include "model/structures/location.h"

#include "view/camera.h"
#include "lightrenderer.h"


namespace FIFE {
	static Logger _log(LM_VIEWVIEW);

	LightRendererNode::LightRendererNode(Instance* attached_instance, const Location &relative_location, Layer* relative_layer, const Point &relative_point):
		m_instance(attached_instance),
		m_location(relative_location),
		m_layer(relative_layer),
		m_point(relative_point) {
	}
	LightRendererNode::LightRendererNode(Instance* attached_instance, const Location &relative_location, const Point &relative_point):
		m_instance(attached_instance),
		m_location(relative_location),
		m_layer(NULL),
		m_point(relative_point) {
	}
	LightRendererNode::LightRendererNode(Instance* attached_instance, Layer* relative_layer, const Point &relative_point):
		m_instance(attached_instance),
		m_location(NULL),
		m_layer(relative_layer),
		m_point(relative_point) {
	}
	LightRendererNode::LightRendererNode(Instance* attached_instance, const Point &relative_point):
		m_instance(attached_instance),
		m_location(NULL),
		m_layer(NULL),
		m_point(relative_point) {
	}
	LightRendererNode::LightRendererNode(const Location &attached_location, Layer* relative_layer, const Point &relative_point):
		m_instance(NULL),
		m_location(attached_location),
		m_layer(relative_layer),
		m_point(relative_point) {
	}
	LightRendererNode::LightRendererNode(const Location &attached_location, const Point &relative_point):
		m_instance(NULL),
		m_location(attached_location),
		m_layer(NULL),
		m_point(relative_point) {
	}
	LightRendererNode::LightRendererNode(Layer* attached_layer, const Point &relative_point):
		m_instance(NULL),
		m_location(NULL),
		m_layer(attached_layer),
		m_point(relative_point) {
	}
	LightRendererNode::LightRendererNode(const Point &attached_point):
		m_instance(NULL),
		m_location(NULL),
		m_layer(NULL),
		m_point(attached_point) {
	}
	LightRendererNode::~LightRendererNode() {
	}

	void LightRendererNode::setAttached(Instance* attached_instance, const Location &relative_location, const Point &relative_point) {
		m_instance = attached_instance;
		m_location = relative_location;
		m_point = relative_point;
	}
	void LightRendererNode::setAttached(Instance* attached_instance, const Location &relative_location) {
		m_instance = attached_instance;
		m_location = relative_location;
	}
	void LightRendererNode::setAttached(Instance* attached_instance, const Point &relative_point) {
		m_instance = attached_instance;
		m_point = relative_point;
	}
	void LightRendererNode::setAttached(Instance* attached_instance) {
		m_instance = attached_instance;
	}
	void LightRendererNode::setAttached(const Location &attached_location, const Point &relative_point) {
		m_instance = NULL;
		m_location = attached_location;
		m_point = relative_point;
	}
	void LightRendererNode::setAttached(const Location &attached_location) {
		m_instance = NULL;
		m_location = attached_location;
	}
	void LightRendererNode::setAttached(Layer* attached_layer) {
		m_layer = attached_layer;
	}
	void LightRendererNode::setAttached(const Point &attached_point) {
		m_instance = NULL;
		m_location = NULL;
		m_point = attached_point;
	}

	void LightRendererNode::setRelative(const Location &relative_location) {
		if(m_instance == NULL) {
			throw NotSupported("No instance attached.");
		}
		m_location = relative_location;
	}
	void LightRendererNode::setRelative(const Location &relative_location, Point relative_point) {
		if(m_instance == NULL) {
			throw NotSupported("No instance attached.");
		}
		m_location = relative_location;
		m_point = relative_point;
	}
	void LightRendererNode::setRelative(const Point &relative_point) {
		if(m_instance == NULL || m_location == NULL) {
			throw NotSupported("No instance or location attached.");
		}
		m_point = relative_point;
	}

	Instance* LightRendererNode::getAttachedInstance() {
		if(m_instance == NULL) {
			throw NotSupported("No instance attached.");
		}
		return m_instance;
	}
	Location LightRendererNode::getAttachedLocation() {
		if(m_instance != NULL || m_location == NULL) {
			throw NotSupported("No location attached.");
		}
		return m_location;
	}
	Layer* LightRendererNode::getAttachedLayer() {
		if(m_layer == NULL) {
			throw NotSupported("No layer attached.");
		}
		return m_layer;
	}
	Point LightRendererNode::getAttachedPoint() {
		if(m_instance != NULL || m_location != NULL) {
			throw NotSupported("No point attached.");
		}
		return m_point;
	}

	Location LightRendererNode::getOffsetLocation() {
		if(m_instance == NULL || m_location == NULL) {
			throw NotSupported("No location as offset used.");
		}
		return m_location;
	}
	Point LightRendererNode::getOffsetPoint() {
		if(m_instance == NULL && m_location == NULL) {
			throw NotSupported("No point as offset used.");
		}
		return m_point;
	}

	Instance* LightRendererNode::getInstance() {
		return m_instance;
	}
	Location LightRendererNode::getLocation() {
		return m_location;
	}
	Layer* LightRendererNode::getLayer() {
		return m_layer;
	}
	Point LightRendererNode::getPoint() {
		return m_point;
	}

	Point LightRendererNode::getCalculatedPoint(Camera* cam, Layer* layer) {
		ScreenPoint p;
		if(m_instance != NULL) {
			if(m_layer == NULL) {
				m_layer = m_instance->getLocation().getLayer();
			}
			if(m_location != NULL) {
				p = cam->toScreenCoordinates(m_instance->getLocationRef().getMapCoordinates() + m_location.getMapCoordinates());
			} else {
				p = cam->toScreenCoordinates(m_instance->getLocation().getMapCoordinates());
			}
		} else if(m_location != NULL) {
			if(m_layer == NULL) {
				m_layer = m_location.getLayer();
			}
			p = cam->toScreenCoordinates(m_location.getMapCoordinates());
		} else if(m_layer == NULL) {
			const std::list<Layer*>& layers = cam->getRenderer("LightRenderer")->getActiveLayers();
			std::list<Layer*>::const_reverse_iterator layer_it = layers.rbegin();
			setAttached(*layer_it);
		}
		return Point(m_point.x + p.x, m_point.y + p.y);
	}

	LightRendererImageInfo::LightRendererImageInfo(LightRendererNode anchor, int image, int src, int dst):
		LightRendererElementInfo(),
		m_anchor(anchor),
		m_image(image),
		m_src(src),
		m_dst(dst),
		m_stencil(false),
		m_stencil_ref(0),
		m_alpha_ref(0.0) {
	}
	void LightRendererImageInfo::render(Camera* cam, Layer* layer, RenderList& instances, RenderBackend* renderbackend, ImagePool* imagepool, AnimationPool* animpool) {
		Point p = m_anchor.getCalculatedPoint(cam, layer);
		if(m_anchor.getLayer() == layer) {
			Image* img = &imagepool->getImage(m_image);
			Rect r;
			Rect viewport = cam->getViewPort();
			unsigned int widtht = round(img->getWidth() * cam->getZoom());
			unsigned int height = round(img->getHeight() * cam->getZoom());
			r.x = p.x-widtht/2;
			r.y = p.y-height/2;
			r.w = widtht;
			r.h = height;
			renderbackend->changeBlending(m_src, m_dst);
			if(r.intersects(viewport))
				img->render(r);
		}
	}
	void LightRendererImageInfo::setStencil(uint8_t stencil_ref, float alpha_ref) {
		m_stencil = true;
		m_stencil_ref = stencil_ref;
		m_alpha_ref = alpha_ref;
	}
	int LightRendererImageInfo::getStencil() {
		if(!m_stencil) {
			return -1;
		}
		return m_stencil_ref;
	}
	float LightRendererImageInfo::getAlpha() {
		return m_alpha_ref;
	}
	void LightRendererImageInfo::removeStencil() {
		m_stencil = false;
		m_stencil_ref = 0;
		m_alpha_ref = 0.0;
	}
	
	LightRendererAnimationInfo::LightRendererAnimationInfo(LightRendererNode anchor, int animation, int src, int dst):
		LightRendererElementInfo(),
		m_anchor(anchor),
		m_animation(animation),
		m_src(src),
		m_dst(dst),
		m_start_time(TimeManager::instance()->getTime()),
		m_time_scale(1.0),
		m_stencil(false),
		m_stencil_ref(0),
		m_alpha_ref(0.0) {
	}
	void LightRendererAnimationInfo::render(Camera* cam, Layer* layer, RenderList& instances, RenderBackend* renderbackend, ImagePool* imagepool, AnimationPool* animpool) { 
		Point p = m_anchor.getCalculatedPoint(cam, layer);
		if(m_anchor.getLayer() == layer) {
			Animation& animation = animpool->getAnimation(m_animation);
			int animtime = scaleTime(m_time_scale, TimeManager::instance()->getTime() - m_start_time) % animation.getDuration();
			Image* img = animation.getFrameByTimestamp(animtime);
			Rect r;
			Rect viewport = cam->getViewPort();
			unsigned int widtht = round(img->getWidth() * cam->getZoom());
			unsigned int height = round(img->getHeight() * cam->getZoom());
			r.x = p.x-widtht/2;
			r.y = p.y-height/2;
			r.w = widtht;
			r.h = height;
			renderbackend->changeBlending(m_src, m_dst);
			if(r.intersects(viewport))
				img->render(r);
		}
	}
	void LightRendererAnimationInfo::setStencil(uint8_t stencil_ref, float alpha_ref) {
		m_stencil = true;
		m_stencil_ref = stencil_ref;
		m_alpha_ref = alpha_ref;
	}
	int LightRendererAnimationInfo::getStencil() {
		if(!m_stencil) {
			return -1;
		}
		return m_stencil_ref;
	}
	float LightRendererAnimationInfo::getAlpha() {
		return m_alpha_ref;
	}
	void LightRendererAnimationInfo::removeStencil() {
		m_stencil = false;
		m_stencil_ref = 0;
		m_alpha_ref = 0.0;
	}

	LightRendererResizeInfo::LightRendererResizeInfo(LightRendererNode anchor, int image, int width, int height, int src, int dst):
		LightRendererElementInfo(),
		m_anchor(anchor),
		m_image(image),
		m_width(width),
		m_height(height),
		m_src(src),
		m_dst(dst),
		m_stencil(false),
		m_stencil_ref(0),
		m_alpha_ref(0.0) {
	}
	void LightRendererResizeInfo::render(Camera* cam, Layer* layer, RenderList& instances, RenderBackend* renderbackend, ImagePool* imagepool, AnimationPool* animpool) {
		Point p = m_anchor.getCalculatedPoint(cam, layer);
		if(m_anchor.getLayer() == layer) {
			Image* img = &imagepool->getImage(m_image);
			Rect r;
			Rect viewport = cam->getViewPort();
			unsigned int widtht = round(m_width * cam->getZoom());
			unsigned int height = round(m_height * cam->getZoom());
			r.x = p.x-widtht/2;
			r.y = p.y-height/2;
			r.w = widtht;
			r.h = height;
			renderbackend->changeBlending(m_src, m_dst);
			if(r.intersects(viewport))
				img->render(r);
		}
	}
	void LightRendererResizeInfo::setStencil(uint8_t stencil_ref, float alpha_ref) {
		m_stencil = true;
		m_stencil_ref = stencil_ref;
		m_alpha_ref = alpha_ref;
	}
	int LightRendererResizeInfo::getStencil() {
		if(!m_stencil) {
			return -1;
		}
		return m_stencil_ref;
	}
	float LightRendererResizeInfo::getAlpha() {
		return m_alpha_ref;
	}
	void LightRendererResizeInfo::removeStencil() {
		m_stencil = false;
		m_stencil_ref = 0;
		m_alpha_ref = 0.0;
	}

	LightRendererSimpleLightInfo::LightRendererSimpleLightInfo(LightRendererNode anchor, uint8_t intensity, float radius, int subdivisions, float xstretch, float ystretch, uint8_t r, uint8_t g, uint8_t b, int src, int dst):
		LightRendererElementInfo(),
		m_anchor(anchor),
		m_intensity(intensity),
		m_radius(radius),
		m_subdivisions(subdivisions),
		m_xstretch(xstretch),
		m_ystretch(ystretch),
		m_red(r),
		m_green(g),
		m_blue(b),
		m_src(src),
		m_dst(dst),
		m_stencil(false),
		m_stencil_ref(0),
		m_alpha_ref(0.0) {
	}
	void LightRendererSimpleLightInfo::render(Camera* cam, Layer* layer, RenderList& instances, RenderBackend* renderbackend, ImagePool* imagepool, AnimationPool* animpool) {
		Point p = m_anchor.getCalculatedPoint(cam, layer);
		if(m_anchor.getLayer() == layer) {
			double zoom = cam->getZoom();
			renderbackend->changeBlending(m_src, m_dst);
			renderbackend->drawLightPrimitive(p, m_intensity, m_radius, m_subdivisions, m_xstretch * zoom, m_ystretch * zoom, m_red, m_green, m_blue);
		}
	}
	void LightRendererSimpleLightInfo::setStencil(uint8_t stencil_ref, float alpha_ref) {
		m_stencil = true;
		m_stencil_ref = stencil_ref;
		m_alpha_ref = alpha_ref;
	}
	int LightRendererSimpleLightInfo::getStencil() {
		if(!m_stencil) {
			return -1;
		}
		return m_stencil_ref;
	}
	float LightRendererSimpleLightInfo::getAlpha() {
		return m_alpha_ref;
	}
	void LightRendererSimpleLightInfo::removeStencil() {
		m_stencil = false;
		m_stencil_ref = 0;
		m_alpha_ref = 0.0;
	}
	std::vector<uint8_t> LightRendererSimpleLightInfo::getColor() {
		std::vector<uint8_t> colors;
		colors.push_back(m_red);
		colors.push_back(m_green);
		colors.push_back(m_blue);
		colors.push_back(m_intensity);
		return colors;
	}

	LightRenderer* LightRenderer::getInstance(IRendererContainer* cnt) {
		return dynamic_cast<LightRenderer*>(cnt->getRenderer("LightRenderer"));
	}
	
	LightRenderer::LightRenderer(RenderBackend* renderbackend, int position, ImagePool* imagepool, AnimationPool* animpool):
		RendererBase(renderbackend, position),
		m_imagepool(imagepool),
		m_animationpool(animpool),
		m_groups() {
		setEnabled(false);
	}

	LightRenderer::LightRenderer(const LightRenderer& old):
		RendererBase(old),
		m_imagepool(old.m_imagepool),
		m_animationpool(old.m_animationpool),
		m_groups() {
		setEnabled(false);
	}

	RendererBase* LightRenderer::clone() {
		return new LightRenderer(*this);
	}

	LightRenderer::~LightRenderer() {
	}
	// Add a static lightmap
	void LightRenderer::addImage(const std::string &group, LightRendererNode n, int image, int src, int dst) {
		LightRendererElementInfo* info = new LightRendererImageInfo(n, image, src, dst);
		m_groups[group].push_back(info);
	}
	// Add a animation lightmap
	void LightRenderer::addAnimation(const std::string &group, LightRendererNode n, int animation, int src, int dst) {
		LightRendererElementInfo* info = new LightRendererAnimationInfo(n, animation, src, dst);
		m_groups[group].push_back(info);
	}
	// Add a simple light
	void LightRenderer::addSimpleLight(const std::string &group, LightRendererNode n, uint8_t intensity, float radius, int subdivisions, float xstretch, float ystretch, uint8_t r, uint8_t g, uint8_t b, int src, int dst) {
		LightRendererElementInfo* info = new LightRendererSimpleLightInfo(n, intensity, radius, subdivisions, xstretch, ystretch, r, g, b, src, dst);
		m_groups[group].push_back(info);
	}
	// Resize an Image
	void LightRenderer::resizeImage(const std::string &group, LightRendererNode n, int image, int width, int height, int src, int dst) {
		LightRendererElementInfo* info = new LightRendererResizeInfo(n, image, width, height, src, dst);
		m_groups[group].push_back(info);
	}
	// Enable stencil test for the group
	void LightRenderer::addStencilTest(const std::string &group, uint8_t stencil_ref, float alpha_ref) {
		std::vector<LightRendererElementInfo*>::const_iterator info_it = m_groups[group].begin();
		for (;info_it != m_groups[group].end(); ++info_it) {
			(*info_it)->setStencil(stencil_ref, alpha_ref);
		}
	}
	// Disable stencil test for the group
	void LightRenderer::removeStencilTest(const std::string &group) {
		std::vector<LightRendererElementInfo*>::const_iterator info_it = m_groups[group].begin();
		for (;info_it != m_groups[group].end(); ++info_it) {
			(*info_it)->removeStencil();
		}
	}
	// Return a list of all groups
	std::list<std::string> LightRenderer::getGroups() {
		std::list<std::string> groups;
		std::map<std::string, std::vector<LightRendererElementInfo*> >::iterator group_it = m_groups.begin();
		for(; group_it != m_groups.end(); ++group_it) {
			groups.push_back(group_it->first);
		}
		groups.sort();
		groups.unique();
		return groups;
	}
	// Return a vector of all LightElementInfos
	std::vector<LightRendererElementInfo*> LightRenderer::getLightInfo(const std::string &group) {
		std::vector<LightRendererElementInfo*> info;
		std::vector<LightRendererElementInfo*>::const_iterator info_it = m_groups[group].begin();
		for (;info_it != m_groups[group].end(); ++info_it) {
			info.push_back(*info_it);
		}
		return info;
	}
	// Remove the group
	void LightRenderer::removeAll(const std::string &group) {
		std::vector<LightRendererElementInfo*>::const_iterator info_it = m_groups[group].begin();
		for (;info_it != m_groups[group].end(); ++info_it) {
			delete *info_it;
		}
		m_groups[group].clear();
		m_groups.erase(group);
	}
	// Render
	void LightRenderer::render(Camera* cam, Layer* layer, RenderList& instances) {
		uint8_t lm = m_renderbackend->getLightingModel();
		
		if (!layer->areInstancesVisible()) {
			return;
		}
		m_renderbackend->disableLighting();
		std::map<std::string, std::vector<LightRendererElementInfo*> >::iterator group_it = m_groups.begin();
		for (; group_it != m_groups.end(); ++group_it) {
			std::vector<LightRendererElementInfo*>::const_iterator info_it = group_it->second.begin();
			for (;info_it != group_it->second.end(); ++info_it) {
				if (lm != 0) {
					if ((*info_it)->getStencil() != -1) {
						uint8_t sref = (*info_it)->getStencil();
						float aref = (*info_it)->getAlpha();
						if(info_it != group_it->second.begin())
							sref += 1;
						m_renderbackend->setStencilTest(sref, 3, 4);
						m_renderbackend->setAlphaTest(aref);
					} else if(lm == 1) {
						m_renderbackend->setStencilTest(255, 0, 6);
						m_renderbackend->setAlphaTest(0);
					} else if(lm == 2) {
						m_renderbackend->setStencilTest(1, 2, 4);
						m_renderbackend->setAlphaTest(0);
					}
					(*info_it)->render(cam, layer, instances, m_renderbackend, m_imagepool, m_animationpool);
					m_renderbackend->disableAlphaTest();
					m_renderbackend->disableStencilTest();
				} else {
					(*info_it)->render(cam, layer, instances, m_renderbackend, m_imagepool, m_animationpool);
				}
			}
		}
		m_renderbackend->changeBlending(4, 5);
		m_renderbackend->enableLighting();
	}

}