1 /***************************************************************************
2  *   Copyright (C) 2005-2019 by the FIFE team                              *
3  *   http://www.fifengine.net                                              *
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 "util/log/logger.h"
31 #include "util/base/exception.h"
32 
33 #include "model/structures/instance.h"
34 #include "model/metamodel/object.h"
35 #include "model/metamodel/action.h"
36 
37 #include "visual.h"
38 
39 
40 namespace FIFE {
41 	/** Logger to use for this source file.
42 	 *  @relates Logger
43 	 */
44 	static Logger _log(LM_VIEW);
45 
OverlayColors()46 	OverlayColors::OverlayColors() {
47 	}
48 
OverlayColors(ImagePtr image)49 	OverlayColors::OverlayColors(ImagePtr image):
50 		m_image(image) {
51 	}
52 
OverlayColors(AnimationPtr animation)53 	OverlayColors::OverlayColors(AnimationPtr animation):
54 		m_animation(animation) {
55 	}
56 
~OverlayColors()57 	OverlayColors::~OverlayColors() {
58 	}
59 
setColorOverlayImage(ImagePtr image)60 	void OverlayColors::setColorOverlayImage(ImagePtr image) {
61 		m_image = image;
62 	}
63 
getColorOverlayImage()64 	ImagePtr OverlayColors::getColorOverlayImage() {
65 		return m_image;
66 	}
67 
setColorOverlayAnimation(AnimationPtr animation)68 	void OverlayColors::setColorOverlayAnimation(AnimationPtr animation) {
69 		m_animation = animation;
70 	}
71 
getColorOverlayAnimation()72 	AnimationPtr OverlayColors::getColorOverlayAnimation() {
73 		return m_animation;
74 	}
75 
changeColor(const Color & source,const Color & target)76 	void OverlayColors::changeColor(const Color& source, const Color& target) {
77 		std::pair<std::map<Color, Color>::iterator, bool> inserter = m_colorMap.insert(std::make_pair(source, target));
78 		if (!inserter.second) {
79 			Color& c = inserter.first->second;
80 			c.set(target.getR(), target.getG(), target.getB(), target.getAlpha());
81 		}
82 	}
83 
getColors()84 	const std::map<Color, Color>& OverlayColors::getColors() {
85 		return m_colorMap;
86 	}
87 
resetColors()88 	void OverlayColors::resetColors() {
89 		m_colorMap.clear();
90 	}
91 
Visual2DGfx()92 	Visual2DGfx::Visual2DGfx() {
93 	}
94 
~Visual2DGfx()95 	Visual2DGfx::~Visual2DGfx() {
96 	}
97 
ObjectVisual()98 	ObjectVisual::ObjectVisual() {
99 	}
100 
create(Object * object)101 	ObjectVisual* ObjectVisual::create(Object* object) {
102 		if (object->getVisual<ObjectVisual>()) {
103 			throw Duplicate("Object already contains visualization");
104 		}
105 		ObjectVisual* v = new ObjectVisual();
106 		object->adoptVisual(v);
107 		return v;
108 	}
109 
~ObjectVisual()110 	ObjectVisual::~ObjectVisual() {
111 	}
112 
addStaticImage(uint32_t angle,int32_t image_index)113 	void ObjectVisual::addStaticImage(uint32_t angle, int32_t image_index) {
114 		m_angle2img[angle % 360] = image_index;
115 	}
116 
getStaticImageIndexByAngle(int32_t angle)117 	int32_t ObjectVisual::getStaticImageIndexByAngle(int32_t angle) {
118 		int32_t closestMatch = 0;
119 		return getIndexByAngle(angle, m_angle2img, closestMatch);
120 	}
121 
addStaticColorOverlay(uint32_t angle,const OverlayColors & colors)122 	void ObjectVisual::addStaticColorOverlay(uint32_t angle, const OverlayColors& colors) {
123 		OverlayColors t = colors;
124 		m_map[angle % 360] = angle % 360;
125 		std::pair<AngleColorOverlayMap::iterator, bool> inserter = m_colorOverlayMap.insert(std::make_pair(angle % 360, colors));
126 		if (!inserter.second) {
127 			OverlayColors tmp = colors;
128 			OverlayColors& c = inserter.first->second;
129 			c.setColorOverlayImage(tmp.getColorOverlayImage());
130 
131 			const std::map<Color, Color>& colorMap = tmp.getColors();
132 			std::map<Color, Color>::const_iterator it = colorMap.begin();
133 			for (; it != colorMap.end(); ++it) {
134 				c.changeColor(it->first, it->second);
135 			}
136 		}
137 	}
138 
getStaticColorOverlay(int32_t angle)139 	OverlayColors* ObjectVisual::getStaticColorOverlay(int32_t angle) {
140 		if (m_colorOverlayMap.empty()) {
141 			return 0;
142 		}
143 		int32_t closestMatch = 0;
144 		return &m_colorOverlayMap[getIndexByAngle(angle, m_map, closestMatch)];
145 	}
146 
removeStaticColorOverlay(int32_t angle)147 	void ObjectVisual::removeStaticColorOverlay(int32_t angle) {
148 		if (m_colorOverlayMap.empty()) {
149 			return;
150 		}
151 		int32_t closestMatch = 0;
152 		int32_t index = getIndexByAngle(angle, m_map, closestMatch);
153 		m_colorOverlayMap.erase(index);
154 		m_map.erase(index);
155 	}
156 
getClosestMatchingAngle(int32_t angle)157 	int32_t ObjectVisual::getClosestMatchingAngle(int32_t angle) {
158 		int32_t closestMatch = 0;
159 		getIndexByAngle(angle, m_angle2img, closestMatch);
160 		return closestMatch;
161 	}
162 
getStaticImageAngles(std::vector<int32_t> & angles)163 	void ObjectVisual::getStaticImageAngles(std::vector<int32_t>& angles) {
164 		angles.clear();
165 		type_angle2id::const_iterator i(m_angle2img.begin());
166 		while (i != m_angle2img.end()) {
167 			angles.push_back(i->first);
168 			++i;
169 		}
170 	}
171 
InstanceVisual()172 	InstanceVisual::InstanceVisual():
173 		m_transparency(0),
174 		m_visible(true),
175 		m_stackposition(0),
176 		m_instance(NULL) {
177 	}
178 
create(Instance * instance)179 	InstanceVisual* InstanceVisual::create(Instance* instance) {
180 		if (instance->getVisual<InstanceVisual>()) {
181 			throw Duplicate("Instance already contains visualization");
182 		}
183 		InstanceVisual* v = new InstanceVisual();
184 		instance->setVisual(v);
185 		v->m_instance = instance;
186 		return v;
187 	}
188 
~InstanceVisual()189 	InstanceVisual::~InstanceVisual() {
190 	}
191 
setTransparency(uint8_t transparency)192 	void InstanceVisual::setTransparency(uint8_t transparency) {
193 		if (m_transparency != transparency) {
194 			m_transparency = transparency;
195 			m_instance->callOnTransparencyChange();
196 		}
197 	}
198 
getTransparency()199 	uint8_t InstanceVisual::getTransparency() {
200 		return m_transparency;
201 	}
202 
setVisible(bool visible)203 	void InstanceVisual::setVisible(bool visible) {
204 		if (m_visible != visible) {
205 			m_visible = visible;
206 			m_instance->callOnVisibleChange();
207 		}
208 	}
209 
isVisible()210 	bool InstanceVisual::isVisible() {
211 		return m_visible;
212 	}
213 
setStackPosition(int32_t stackposition)214 	void InstanceVisual::setStackPosition(int32_t stackposition) {
215 		if (m_stackposition != stackposition) {
216 			m_stackposition = stackposition;
217 			m_instance->callOnStackPositionChange();
218 		}
219 	}
220 
getStackPosition()221 	int32_t InstanceVisual::getStackPosition() {
222 		return m_stackposition;
223 	}
224 
ActionVisual()225 	ActionVisual::ActionVisual(): m_animation_map(), m_map() {
226 	}
227 
create(Action * action)228 	ActionVisual* ActionVisual::create(Action* action) {
229 		if (action->getVisual<ActionVisual>()) {
230 			throw Duplicate("Action already contains visualization");
231 		}
232 		ActionVisual* v = new ActionVisual();
233 		action->adoptVisual(v);
234 		return v;
235 	}
236 
~ActionVisual()237 	ActionVisual::~ActionVisual() {
238 	}
239 
addAnimation(uint32_t angle,AnimationPtr animationptr)240 	void ActionVisual::addAnimation(uint32_t angle, AnimationPtr animationptr) {
241 		m_animation_map[angle % 360] = animationptr;
242 		m_map[angle % 360] = angle % 360;
243 	}
244 
getAnimationByAngle(int32_t angle)245 	AnimationPtr ActionVisual::getAnimationByAngle(int32_t angle) {
246 		int32_t closestMatch = 0;
247 		return m_animation_map[getIndexByAngle(angle, m_map, closestMatch)];
248 	}
249 
addAnimationOverlay(uint32_t angle,int32_t order,AnimationPtr animationptr)250 	void ActionVisual::addAnimationOverlay(uint32_t angle, int32_t order, AnimationPtr animationptr) {
251 		std::map<int32_t, AnimationPtr>& orderMap = m_animationOverlayMap[angle % 360];
252 		m_map[angle % 360] = angle % 360;
253 		orderMap.insert(std::pair<int32_t, AnimationPtr>(order, animationptr));
254 	}
255 
getAnimationOverlay(int32_t angle)256 	std::map<int32_t, AnimationPtr> ActionVisual::getAnimationOverlay(int32_t angle) {
257 		int32_t closestMatch = 0;
258 		return m_animationOverlayMap[getIndexByAngle(angle, m_map, closestMatch)];
259 	}
260 
removeAnimationOverlay(uint32_t angle,int32_t order)261 	void ActionVisual::removeAnimationOverlay(uint32_t angle, int32_t order) {
262 		if (m_animationOverlayMap.empty()) {
263 			return;
264 		}
265 		int32_t closestMatch = 0;
266 		AngleAnimationOverlayMap::iterator it = m_animationOverlayMap.find(getIndexByAngle(angle, m_map, closestMatch));
267 		if (it != m_animationOverlayMap.end()) {
268 			it->second.erase(order);
269 			if (it->second.empty()) {
270 				m_animationOverlayMap.erase(it);
271 			}
272 		}
273 	}
274 
addColorOverlay(uint32_t angle,const OverlayColors & colors)275 	void ActionVisual::addColorOverlay(uint32_t angle, const OverlayColors& colors) {
276 		m_map[angle % 360] = angle % 360;
277 		std::pair<AngleColorOverlayMap::iterator, bool> inserter = m_colorOverlayMap.insert(std::make_pair(angle % 360, colors));
278 		if (!inserter.second) {
279 			OverlayColors tmp = colors;
280 			OverlayColors& c = inserter.first->second;
281 			c.setColorOverlayAnimation(tmp.getColorOverlayAnimation());
282 
283 			const std::map<Color, Color>& colorMap = tmp.getColors();
284 			std::map<Color, Color>::const_iterator it = colorMap.begin();
285 			for (; it != colorMap.end(); ++it) {
286 				c.changeColor(it->first, it->second);
287 			}
288 		}
289 	}
290 
getColorOverlay(int32_t angle)291 	OverlayColors* ActionVisual::getColorOverlay(int32_t angle) {
292 		if (m_colorOverlayMap.empty()) {
293 			return 0;
294 		}
295 		int32_t closestMatch = 0;
296 		int32_t index = getIndexByAngle(angle, m_map, closestMatch);
297 		if (m_colorOverlayMap.find(index) == m_colorOverlayMap.end()) {
298 			return 0;
299 		}
300 		return &m_colorOverlayMap[getIndexByAngle(angle, m_map, closestMatch)];
301 	}
302 
removeColorOverlay(int32_t angle)303 	void ActionVisual::removeColorOverlay(int32_t angle) {
304 		if (m_colorOverlayMap.empty()) {
305 			return;
306 		}
307 		int32_t closestMatch = 0;
308 		int32_t index = getIndexByAngle(angle, m_map, closestMatch);
309 		m_colorOverlayMap.erase(index);
310 	}
311 
addColorOverlay(uint32_t angle,int32_t order,const OverlayColors & colors)312 	void ActionVisual::addColorOverlay(uint32_t angle, int32_t order, const OverlayColors& colors) {
313 		std::map<int32_t, OverlayColors>& orderMap = m_colorAnimationOverlayMap[angle % 360];
314 		m_map[angle % 360] = angle % 360;
315 		std::pair<std::map<int32_t, OverlayColors>::iterator, bool> inserter = orderMap.insert(std::make_pair(order, colors));
316 		if (!inserter.second) {
317 			OverlayColors tmp = colors;
318 			OverlayColors& c = inserter.first->second;
319 			c.setColorOverlayAnimation(tmp.getColorOverlayAnimation());
320 
321 			const std::map<Color, Color>& colorMap = tmp.getColors();
322 			std::map<Color, Color>::const_iterator it = colorMap.begin();
323 			for (; it != colorMap.end(); ++it) {
324 				c.changeColor(it->first, it->second);
325 			}
326 		}
327 	}
328 
getColorOverlay(int32_t angle,int32_t order)329 	OverlayColors* ActionVisual::getColorOverlay(int32_t angle, int32_t order) {
330 		if (m_colorAnimationOverlayMap.empty()) {
331 			return 0;
332 		}
333 
334 		int32_t closestMatch = 0;
335 		AngleColorAnimationOverlayMap::iterator it = m_colorAnimationOverlayMap.find(getIndexByAngle(angle, m_map, closestMatch));
336 		if (it != m_colorAnimationOverlayMap.end()) {
337 			std::map<int32_t, OverlayColors>::iterator sit = it->second.find(order);
338 			if (sit != it->second.end()) {
339 				return &it->second[order];
340 			}
341 		}
342 		return 0;
343 	}
344 
removeColorOverlay(int32_t angle,int32_t order)345 	void ActionVisual::removeColorOverlay(int32_t angle, int32_t order) {
346 		if (m_colorAnimationOverlayMap.empty()) {
347 			return;
348 		}
349 
350 		int32_t closestMatch = 0;
351 		AngleColorAnimationOverlayMap::iterator it = m_colorAnimationOverlayMap.find(getIndexByAngle(angle, m_map, closestMatch));
352 		if (it != m_colorAnimationOverlayMap.end()) {
353 			it->second.erase(order);
354 			if (it->second.empty()) {
355 				m_colorAnimationOverlayMap.erase(it);
356 			}
357 		}
358 	}
359 
getActionImageAngles(std::vector<int32_t> & angles)360 	void ActionVisual::getActionImageAngles(std::vector<int32_t>& angles) {
361 		angles.clear();
362 		type_angle2id::const_iterator i(m_map.begin());
363 		while (i != m_map.end()) {
364 			angles.push_back(i->first);
365 			++i;
366 		}
367 	}
368 
convertToOverlays(bool color)369 	void ActionVisual::convertToOverlays(bool color) {
370 		bool colorOverlay = color && !m_colorOverlayMap.empty();
371 		type_angle2id::const_iterator it = m_map.begin();
372 		for (; it != m_map.end(); ++it) {
373 			addAnimationOverlay(it->first, 0, getAnimationByAngle(it->first));
374 			if (colorOverlay) {
375 				OverlayColors* oldC = getColorOverlay(it->first);
376 				if (oldC) {
377 					OverlayColors c = OverlayColors(*oldC);
378 					addColorOverlay(it->first, 0, c);
379 				}
380 			}
381 		}
382 	}
383 }
384