1 //
2 // C++ Implementation: TerrainMod
3 //
4 // Description:
5 //
6 //
7 // Author: Tamas Bates <rhymer@gmail.com>, (C) 2008
8 // Author: Erik Hjortsberg <erik@worldforge.org>, (C) 2008
9 //
10 // This program is free software; you can redistribute it and/or modify
11 // it under the terms of the GNU General Public License as published by
12 // the Free Software Foundation; either version 2 of the License, or
13 // (at your option) any later version.
14 //
15 // This program is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with this program; if not, write to the Free Software
22 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.//
23 //
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27 
28 #include <Eris/TerrainModTranslator.h>
29 #include <Eris/Log.h>
30 
31 #include <Mercator/TerrainMod.h>
32 
33 #include <wfmath/atlasconv.h>
34 
35 #include <cassert>
36 
37 using Atlas::Message::Element;
38 using Atlas::Message::MapType;
39 using Atlas::Message::ListType;
40 using Atlas::Message::FloatType;
41 
42 namespace Eris {
43 
44 /**
45  * @brief Ctor.
46  */
TerrainModTranslator()47 TerrainModTranslator::TerrainModTranslator() : m_mod(0)
48 {
49 }
50 
51 /**
52  * @brief Parse the shape data and create the terrain mod instance with it
53  * @param pos Position of the mod entity
54  * @param orientation Orientation of the mod entity
55  * @param modElement Atlas data describing the mod
56  * @param typeName Name of the type of mod from the Atlas data
57  * @param shape Reference to the shape object to be populated
58  * @param shapeMap Atlas data containing the shape parameters
59  */
60 template <template <int> class Shape>
parseStuff(const WFMath::Point<3> & pos,const WFMath::Quaternion & orientation,const MapType & modElement,const std::string & typeName,Shape<2> & shape,const Element & shapeMap)61 bool TerrainModTranslator::parseStuff(
62       const WFMath::Point<3> & pos,
63       const WFMath::Quaternion & orientation,
64       const MapType& modElement,
65       const std::string & typeName,
66       Shape<2> & shape,
67       const Element & shapeMap)
68 {
69     if (!parseShape(shapeMap, pos, orientation, shape)) {
70         return false;
71     }
72     if (typeName == "slopemod") {
73         return createInstance<Mercator::SlopeTerrainMod>(shape, pos, modElement, 0, 0);
74     } else if (typeName == "levelmod") {
75         return createInstance<Mercator::LevelTerrainMod>(shape, pos, modElement);
76     } else if (typeName == "adjustmod") {
77         return createInstance<Mercator::AdjustTerrainMod>(shape, pos, modElement);
78     } else if (typeName == "cratermod") {
79         return createInstance<Mercator::CraterTerrainMod>(shape, pos, modElement);
80     }
81     return false;
82 }
83 
84 /**
85  * @brief Parse the Atlas data and create the terrain mod instance with it
86  * @param pos Position of the mod entity
87  * @param orientation Orientation of the mod entity
88  * @param modElement Atlas data describing the mod
89  * @return true if translation succeeds
90  */
parseData(const WFMath::Point<3> & pos,const WFMath::Quaternion & orientation,const MapType & modElement)91 bool TerrainModTranslator::parseData(
92       const WFMath::Point<3> & pos,
93       const WFMath::Quaternion & orientation,
94       const MapType& modElement)
95 {
96     MapType::const_iterator I = modElement.find("type");
97     if (I == modElement.end() || !I->second.isString()) {
98         return false;
99     }
100     const std::string& modType = I->second.String();
101 
102     I = modElement.find("shape");
103     if (I == modElement.end() || !I->second.isMap()) {
104         return false;
105     }
106     const MapType& shapeMap = I->second.Map();
107 
108     // Get shape's type
109     I = shapeMap.find("type");
110     if (I == shapeMap.end() || !I->second.isString()) {
111         return false;
112     }
113     const std::string& shapeType = I->second.String();
114     if (shapeType == "ball") {
115         WFMath::Ball<2> shape;
116         return parseStuff(pos, orientation, modElement, modType, shape, shapeMap);
117     } else if (shapeType == "rotbox") {
118         WFMath::RotBox<2> shape;
119         return parseStuff(pos, orientation, modElement, modType, shape, shapeMap);
120     } else if (shapeType == "polygon") {
121         WFMath::Polygon<2> shape;
122         return parseStuff(pos, orientation, modElement, modType, shape, shapeMap);
123     }
124     return false;
125 }
126 
127 
getModifier()128 Mercator::TerrainMod* TerrainModTranslator::getModifier()
129 {
130     return m_mod;
131 }
132 
133 /**
134  * @brief Parses the changes to the position of the mod
135  * If no height data is given the height of the entity the mod belongs to will
136  * be used. If however a "height" value is set, that will be used instead.
137  * If no "height" value is set, but a "heightoffset" is present, that value
138  * will be added to the height set by the position of the entity the mod
139  * belongs to.
140  * @param pos Position of the mod entity
141  * @param modElement Atlas data describing the mod
142  * @return The adjusted height of the mod
143  */
parsePosition(const WFMath::Point<3> & pos,const MapType & modElement)144 float TerrainModTranslator::parsePosition(
145       const WFMath::Point<3> & pos,
146       const MapType& modElement)
147 {
148     ///If the height is specified use that, else check for a height offset. If none is found, use the default height of the entity position
149     MapType::const_iterator I = modElement.find("height");
150     if (I != modElement.end()) {
151         const Element& modHeightElem = I->second;
152         if (modHeightElem.isNum()) {
153             return modHeightElem.asNum();
154         }
155     } else {
156         I = modElement.find("heightoffset");
157         if (I != modElement.end()) {
158             const Element& modHeightElem = I->second;
159             if (modHeightElem.isNum()) {
160                 float heightoffset = modHeightElem.asNum();
161                 return pos.z() + heightoffset;
162             }
163         }
164     }
165     return pos.z();
166 }
167 
168 /**
169  * @brief Common method for parsing shape data from Atlas.
170  * Since each different shape expects different Atlas data this is a
171  * templated method with specialized implemtations for each available shape.
172  * If you call this and get error regarding missing implementations it
173  * probably means that there's no implementation for the type of shape you're
174  * calling it with. Note that a new shape instance will be put on the heap if
175  * the parsing is successful, and it's up to the calling code to properly
176  * delete it when done.
177  * @param shapeElement The atlas map element which contains the shape data.
178  * Often this is found with the key "shape" in the atlas data.
179  * @param pos The original position of the entity to which this shape will
180  * belong. The shape will be positioned according to this.
181  * @param shape The resulting shape is meant to be put here, if successfully
182  * created. That means that a new shape instance will be created, and it's
183  * then up to the calling method to properly delete it, to avoid memory leaks.
184  * @return True if the atlas data was successfully parsed and a shape was
185  * created.
186  */
187 template<template <int> class Shape>
parseShape(const Element & shapeElement,const WFMath::Point<3> & pos,const WFMath::Quaternion & orientation,Shape<2> & shape)188 bool TerrainModTranslator::parseShape(
189       const Element& shapeElement,
190       const WFMath::Point<3>& pos,
191       const WFMath::Quaternion& orientation,
192       Shape <2> & shape)
193 {
194     try {
195         shape.fromAtlas(shapeElement);
196     } catch (...) {
197         ///Just log an error and return false, this isn't fatal.
198         warning() << "Error when parsing shape from atlas.";
199         return false;
200     }
201 
202     if (!shape.isValid()) {
203         return false;
204     }
205 
206     if (orientation.isValid()) {
207         /// rotation about Z axis
208         WFMath::Vector<3> xVec = WFMath::Vector<3>(1.0, 0.0, 0.0).rotate(orientation);
209         WFMath::CoordType theta = std::atan2(xVec.y(), xVec.x());
210         WFMath::RotMatrix<2> rm;
211         shape.rotatePoint(rm.rotation(theta), WFMath::Point<2>(0, 0));
212     }
213 
214     shape.shift(WFMath::Vector<2>(pos.x(), pos.y()));
215     return true;
216 }
217 
218 /**
219  * @brief Create or update an instance from the passed in atlas data.
220  * @param shape The modified shape of the mod
221  * @param pos Position of the mod entity
222  * @return True if the atlas data could be successfully parsed
223  */
224 template <template <template <int> class Shape> class Mod,
225           template <int> class Shape>
createInstance(Shape<2> & shape,const WFMath::Point<3> & pos,const MapType & modElement,float,float)226 bool TerrainModTranslator::createInstance(
227       Shape <2> & shape,
228       const WFMath::Point<3>& pos,
229       const MapType& modElement,
230       float ,
231       float )
232 {
233     float level = parsePosition(pos, modElement);
234     MapType::const_iterator I = modElement.find("slopes");
235     if (I == modElement.end()) {
236         error() << "SlopeTerrainMod defined without slopes";
237         return false;
238     }
239     const Element& modSlopeElem = I->second;
240     if (!modSlopeElem.isList()) {
241         error() << "SlopeTerrainMod defined with malformed slopes";
242         return false;
243     }
244     const ListType & slopes = modSlopeElem.asList();
245     if (slopes.size() < 2 || !slopes[0].isNum() || !slopes[1].isNum()) {
246         error() << "SlopeTerrainMod defined without slopes";
247         return false;
248     }
249     const float dx = slopes[0].asNum();
250     const float dy = slopes[1].asNum();
251     if (m_mod != 0) {
252         Mod<Shape> * mod = dynamic_cast<Mod<Shape>*>(m_mod);
253         if (mod != 0) {
254             mod->setShape(level, dx, dy, shape);
255             return true;
256         }
257     }
258     m_mod = new Mod<Shape>(level, dx, dy, shape);
259     return true;
260 }
261 
262 /**
263  * @brief Create or update an instance from the passed in atlas data.
264  * @param shape The modified shape of the mod
265  * @param pos Position of the mod entity
266  * @return True if the atlas data could be successfully parsed
267  */
268 template <template <template <int> class S> class Mod,
269           template <int> class Shape>
createInstance(Shape<2> & shape,const WFMath::Point<3> & pos,const MapType & modElement)270 bool TerrainModTranslator::createInstance(
271       Shape <2> & shape,
272       const WFMath::Point<3>& pos,
273       const MapType& modElement)
274 {
275     float level = parsePosition(pos, modElement);
276     if (m_mod != 0) {
277         Mod<Shape> * mod = dynamic_cast<Mod<Shape>*>(m_mod);
278         if (mod != 0) {
279             mod->setShape(level, shape);
280             return true;
281         }
282     }
283     m_mod = new Mod<Shape>(level, shape);
284     return true;
285 }
286 
287 } // close Namespace Eris
288 
289