TmxFile2D.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580
  1. //
  2. // Copyright (c) 2008-2014 the Urho3D project.
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to deal
  6. // in the Software without restriction, including without limitation the rights
  7. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. // copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20. // THE SOFTWARE.
  21. //
  22. #include "Precompiled.h"
  23. #include "Animation2D.h"
  24. #include "Context.h"
  25. #include "FileSystem.h"
  26. #include "Log.h"
  27. #include "ResourceCache.h"
  28. #include "Sprite2D.h"
  29. #include "Texture2D.h"
  30. #include "TmxFile2D.h"
  31. #include "XMLFile.h"
  32. #include "DebugNew.h"
  33. namespace Urho3D
  34. {
  35. extern const float PIXEL_SIZE;
  36. Properties2D::Properties2D()
  37. {
  38. }
  39. Properties2D::~Properties2D()
  40. {
  41. }
  42. void Properties2D::Load(const XMLElement& element)
  43. {
  44. assert(element.GetName() == "properties");
  45. for (XMLElement propertyElem = element.GetChild("property"); propertyElem; propertyElem = propertyElem.GetNext("property"))
  46. properties_[propertyElem.GetAttribute("name")] = propertyElem.GetAttribute("value");
  47. }
  48. bool Properties2D::HasProperty(const String& name) const
  49. {
  50. return properties_.Find(name) != properties_.End();
  51. }
  52. const String& Properties2D::GetProperty(const String& name) const
  53. {
  54. HashMap<String, String>::ConstIterator i = properties_.Find(name);
  55. if (i == properties_.End())
  56. return String::EMPTY;
  57. return i->second_;
  58. }
  59. Tile2D::Tile2D() :
  60. gid_(0)
  61. {
  62. }
  63. Sprite2D* Tile2D::GetSprite() const
  64. {
  65. return sprite_;
  66. }
  67. bool Tile2D::HasProperty(const String& name) const
  68. {
  69. if (!properties_)
  70. return false;
  71. return properties_->HasProperty(name);
  72. }
  73. const String& Tile2D::GetProperty(const String& name) const
  74. {
  75. if (!properties_)
  76. return String::EMPTY;
  77. return properties_->GetProperty(name);
  78. }
  79. TileObject2D::TileObject2D()
  80. {
  81. }
  82. unsigned TileObject2D::GetNumPoints() const
  83. {
  84. return points_.Size();
  85. }
  86. const Vector2& TileObject2D::GetPoint(unsigned index) const
  87. {
  88. if (index >= points_.Size())
  89. return Vector2::ZERO;
  90. return points_[index];
  91. }
  92. Sprite2D* TileObject2D::GetTileSprite() const
  93. {
  94. return sprite_;
  95. }
  96. bool TileObject2D::HasProperty(const String& name) const
  97. {
  98. if (!properties_)
  99. return false;
  100. return properties_->HasProperty(name);
  101. }
  102. const String& TileObject2D::GetProperty(const String& name) const
  103. {
  104. if (!properties_)
  105. return String::EMPTY;
  106. return properties_->GetProperty(name);
  107. }
  108. TmxLayer2D::TmxLayer2D(TmxFile2D* tmxFile, TmxLayerType2D type) :
  109. tmxFile_(tmxFile),
  110. type_(type)
  111. {
  112. }
  113. TmxLayer2D::~TmxLayer2D()
  114. {
  115. }
  116. TmxFile2D* TmxLayer2D::GetTmxFile() const
  117. {
  118. return tmxFile_;
  119. }
  120. bool TmxLayer2D::HasProperty(const String& name) const
  121. {
  122. if (!properties_)
  123. return false;
  124. return properties_->HasProperty(name);
  125. }
  126. const String& TmxLayer2D::GetProperty(const String& name) const
  127. {
  128. if (!properties_)
  129. return String::EMPTY;
  130. return properties_->GetProperty(name);
  131. }
  132. void TmxLayer2D::LoadInfo(const XMLElement& element)
  133. {
  134. name_ = element.GetAttribute("name");
  135. width_ = element.GetInt("width");
  136. height_ = element.GetInt("height");
  137. if (element.HasAttribute("visible"))
  138. visible_ = element.GetInt("visible") != 0;
  139. else
  140. visible_ = true;
  141. }
  142. void TmxLayer2D::LoadProperties(const XMLElement& element)
  143. {
  144. properties_ = new Properties2D();
  145. properties_->Load(element);
  146. }
  147. TmxTileLayer2D::TmxTileLayer2D(TmxFile2D* tmxFile) :
  148. TmxLayer2D(tmxFile, LT_TILE_LAYER)
  149. {
  150. }
  151. bool TmxTileLayer2D::Load(const XMLElement& element)
  152. {
  153. LoadInfo(element);
  154. XMLElement dataElem = element.GetChild("data");
  155. if (!dataElem)
  156. {
  157. LOGERROR("Could not find data in layer");
  158. return false;
  159. }
  160. if (dataElem.HasAttribute("encoding") && dataElem.GetAttribute("encoding") != "xml")
  161. {
  162. LOGERROR("Encoding not support now");
  163. return false;
  164. }
  165. XMLElement tileElem = dataElem.GetChild("tile");
  166. tileGids_.Resize(width_ * height_);
  167. // Flip y
  168. for (int y = height_ - 1; y >= 0; --y)
  169. {
  170. for (int x = 0; x < width_; ++x)
  171. {
  172. if (!tileElem)
  173. return false;
  174. int gid = tileElem.GetInt("gid");
  175. if (gid > 0)
  176. {
  177. SharedPtr<Tile2D> tile(new Tile2D());
  178. tile->gid_ = gid;
  179. tile->sprite_ = tmxFile_->GetTileSprite(gid);
  180. tile->properties_ = tmxFile_->GetTileProperties(gid);
  181. tileGids_[y * width_ + x] = tile;
  182. }
  183. tileElem = tileElem.GetNext("tile");
  184. }
  185. }
  186. if (element.HasChild("properties"))
  187. LoadProperties(element.GetChild("properties"));
  188. return true;
  189. }
  190. Tile2D* TmxTileLayer2D::GetTile(int x, int y) const
  191. {
  192. if (x < 0 || x > width_ || y < 0 || y > height_)
  193. return 0;
  194. return tileGids_[y * width_ + x];
  195. }
  196. TmxObjectGroup2D::TmxObjectGroup2D(TmxFile2D* tmxFile) :
  197. TmxLayer2D(tmxFile, LT_OBJECT_GROUP)
  198. {
  199. }
  200. bool TmxObjectGroup2D::Load(const XMLElement& element)
  201. {
  202. LoadInfo(element);
  203. const float mapHeight = height_ * tmxFile_->GetTileHeight();
  204. for (XMLElement objectElem = element.GetChild("object"); objectElem; objectElem = objectElem.GetNext("object"))
  205. {
  206. SharedPtr<TileObject2D> object(new TileObject2D());
  207. object->position_ = Vector2(objectElem.GetInt("x") * PIXEL_SIZE, mapHeight - objectElem.GetInt("y") * PIXEL_SIZE);
  208. if (objectElem.HasAttribute("width") || objectElem.HasAttribute("height"))
  209. {
  210. if (!objectElem.HasChild("ellipse"))
  211. object->type_ = OT_RECTANGLE;
  212. else
  213. object->type_ = OT_ELLIPSE;
  214. object->size_ = Vector2(objectElem.GetInt("width") * PIXEL_SIZE, objectElem.GetInt("height") * PIXEL_SIZE);
  215. object->position_.y_ -= object->size_.y_;
  216. }
  217. else if (objectElem.HasAttribute("gid"))
  218. {
  219. object->type_ = OT_TILE;
  220. object->gid_ = objectElem.GetInt("gid");
  221. object->sprite_ = tmxFile_->GetTileSprite(object->gid_);
  222. }
  223. else
  224. {
  225. XMLElement childElem = objectElem.GetChild();
  226. if (childElem.GetName() == "polygon")
  227. object->type_ = OT_POLYGON;
  228. else if (childElem.GetName() == "polyline")
  229. object->type_ = OT_POLYLINE;
  230. else
  231. return false;
  232. Vector<String> points = childElem.GetAttribute("points").Split(' ');
  233. object->points_.Resize(points.Size());
  234. for (unsigned i = 0; i < points.Size(); ++i)
  235. {
  236. points[i].Replace(',', ' ');
  237. Vector2 point = ToVector2(points[i]) * PIXEL_SIZE;
  238. point.y_ = mapHeight - point.y_;
  239. object->points_[i] = point;
  240. }
  241. }
  242. if (objectElem.HasChild("properties"))
  243. {
  244. object->properties_ = new Properties2D();
  245. object->properties_->Load(objectElem.GetChild("properties"));
  246. }
  247. objects_.Push(object);
  248. }
  249. if (element.HasChild("properties"))
  250. LoadProperties(element.GetChild("properties"));
  251. return true;
  252. }
  253. TileObject2D* TmxObjectGroup2D::GetObject(unsigned index) const
  254. {
  255. if (index >= objects_.Size())
  256. return 0;
  257. return objects_[index];
  258. }
  259. TmxImageLayer2D::TmxImageLayer2D(TmxFile2D* tmxFile) :
  260. TmxLayer2D(tmxFile, LT_IMAGE_LAYER)
  261. {
  262. }
  263. bool TmxImageLayer2D::Load(const XMLElement& element)
  264. {
  265. LoadInfo(element);
  266. XMLElement imageElem = element.GetChild("image");
  267. if (!imageElem)
  268. return false;
  269. String textureFilePath = GetParentPath(GetName()) + imageElem.GetAttribute("source");
  270. ResourceCache* cache = tmxFile_->GetSubsystem<ResourceCache>();
  271. SharedPtr<Texture2D> texture(cache->GetResource<Texture2D>(textureFilePath));
  272. if (!texture)
  273. {
  274. LOGERROR("Could not load texture " + textureFilePath);
  275. return false;
  276. }
  277. sprite_ = new Sprite2D(tmxFile_->GetContext());
  278. sprite_->SetTexture(texture);
  279. sprite_->SetRectangle(IntRect(0, 0, texture->GetWidth(), texture->GetHeight()));
  280. // Left top
  281. sprite_->SetHotSpot(Vector2(0.0f, 1.0f));
  282. if (element.HasChild("properties"))
  283. LoadProperties(element.GetChild("properties"));
  284. return true;
  285. }
  286. Sprite2D* TmxImageLayer2D::GetSprite() const
  287. {
  288. return sprite_;
  289. }
  290. TmxFile2D::TmxFile2D(Context* context) :
  291. Resource(context),
  292. width_(0),
  293. height_(0),
  294. tileWidth_(0.0f),
  295. tileHeight_(0.0f)
  296. {
  297. }
  298. TmxFile2D::~TmxFile2D()
  299. {
  300. for (unsigned i = 0; i < layers_.Size(); ++i)
  301. delete layers_[i];
  302. }
  303. void TmxFile2D::RegisterObject(Context* context)
  304. {
  305. context->RegisterFactory<TmxFile2D>();
  306. }
  307. bool TmxFile2D::BeginLoad(Deserializer& source)
  308. {
  309. loadXMLFile_ = new XMLFile(context_);
  310. if (!loadXMLFile_->Load(source))
  311. {
  312. LOGERROR("Load XML failed " + source.GetName());
  313. loadXMLFile_.Reset();
  314. return false;
  315. }
  316. XMLElement rootElem = loadXMLFile_->GetRoot("map");
  317. if (!rootElem)
  318. {
  319. LOGERROR("Invalid tmx file " + source.GetName());
  320. loadXMLFile_.Reset();
  321. return false;
  322. }
  323. // If we're async loading, request the texture now. Finish during EndLoad().
  324. if (GetAsyncLoadState() == ASYNC_LOADING)
  325. {
  326. for (XMLElement tileSetElem = rootElem.GetChild("tileset"); tileSetElem; tileSetElem = tileSetElem.GetNext("tileset"))
  327. {
  328. String textureFilePath = GetParentPath(GetName()) + tileSetElem.GetChild("image").GetAttribute("source");
  329. GetSubsystem<ResourceCache>()->BackgroundLoadResource<Texture2D>(textureFilePath, true, this);
  330. }
  331. for (XMLElement imageLayerElem = rootElem.GetChild("imagelayer"); imageLayerElem; imageLayerElem = imageLayerElem.GetNext("imagelayer"))
  332. {
  333. String textureFilePath = GetParentPath(GetName()) + imageLayerElem.GetChild("image").GetAttribute("source");
  334. GetSubsystem<ResourceCache>()->BackgroundLoadResource<Texture2D>(textureFilePath, true, this);
  335. }
  336. }
  337. return true;
  338. }
  339. bool TmxFile2D::EndLoad()
  340. {
  341. if (!loadXMLFile_)
  342. return false;
  343. XMLElement rootElem = loadXMLFile_->GetRoot("map");
  344. String version = rootElem.GetAttribute("version");
  345. if (version != "1.0")
  346. {
  347. LOGERROR("Invalid version");
  348. return false;
  349. }
  350. String orientation = rootElem.GetAttribute("orientation");
  351. if (orientation != "orthogonal")
  352. {
  353. LOGERROR("Unsupported orientation now");
  354. return false;
  355. }
  356. width_ = rootElem.GetInt("width");
  357. height_ = rootElem.GetInt("height");
  358. tileWidth_ = rootElem.GetFloat("tilewidth") * PIXEL_SIZE;
  359. tileHeight_ = rootElem.GetFloat("tileheight") * PIXEL_SIZE;
  360. for (unsigned i = 0; i < layers_.Size(); ++i)
  361. delete layers_[i];
  362. layers_.Clear();
  363. for (XMLElement childElement = rootElem.GetChild(); childElement; childElement = childElement.GetNext())
  364. {
  365. bool ret = true;
  366. String name = childElement.GetName();
  367. if (name == "tileset")
  368. ret = LoadTileSet(childElement);
  369. else if (name == "layer")
  370. ret = LoadLayer(childElement);
  371. else if (name == "objectgroup")
  372. ret = LoadObjectGroup(childElement);
  373. else if (name == "imagelayer")
  374. ret = LoadImageLayer(childElement);
  375. if (!ret)
  376. {
  377. loadXMLFile_.Reset();
  378. return false;
  379. }
  380. }
  381. loadXMLFile_.Reset();
  382. return true;
  383. }
  384. Sprite2D* TmxFile2D::GetTileSprite(int gid) const
  385. {
  386. HashMap<int, SharedPtr<Sprite2D> >::ConstIterator i = tileSprites_.Find(gid);
  387. if (i == tileSprites_.End())
  388. return 0;
  389. return i->second_;
  390. }
  391. Properties2D* TmxFile2D::GetTileProperties(int gid) const
  392. {
  393. HashMap<int, SharedPtr<Properties2D> >::ConstIterator i = tileProperties_.Find(gid);
  394. if (i == tileProperties_.End())
  395. return 0;
  396. return i->second_;
  397. }
  398. const TmxLayer2D* TmxFile2D::GetLayer(unsigned index) const
  399. {
  400. if (index >= layers_.Size())
  401. return 0;
  402. return layers_[index];
  403. }
  404. const TmxLayer2D* TmxFile2D::GetLayerByName(const String& name) const
  405. {
  406. for (unsigned i = 0; i < layers_.Size(); ++i)
  407. {
  408. if (name == layers_[i]->GetName())
  409. return layers_[i];
  410. }
  411. return 0;
  412. }
  413. bool TmxFile2D::LoadTileSet(const XMLElement& element)
  414. {
  415. XMLElement imageElem = element.GetChild("image");
  416. String textureFilePath = GetParentPath(GetName()) + imageElem.GetAttribute("source");
  417. ResourceCache* cache = GetSubsystem<ResourceCache>();
  418. SharedPtr<Texture2D> texture(cache->GetResource<Texture2D>(textureFilePath));
  419. if (!texture)
  420. {
  421. LOGERROR("Could not load texture " + textureFilePath);
  422. return false;
  423. }
  424. tileSetTextures_.Push(texture);
  425. int firstgid = element.GetInt("firstgid");
  426. int tileWidth = element.GetInt("tilewidth");
  427. int tileHeight = element.GetInt("tileheight");
  428. int spacing = element.GetInt("spacing");
  429. int margin = element.GetInt("margin");
  430. int imageWidth = imageElem.GetInt("width");
  431. int imageHeight = imageElem.GetInt("height");
  432. int gid = firstgid;
  433. for (int y = margin; y < imageHeight - margin; y += tileHeight + spacing)
  434. {
  435. for (int x = margin; x < imageWidth - margin; x += tileWidth + spacing)
  436. {
  437. SharedPtr<Sprite2D> sprite(new Sprite2D(context_));
  438. sprite->SetTexture(texture);
  439. sprite->SetRectangle(IntRect(x, y, x + tileWidth, y + tileHeight));
  440. // Set hot spot at left bottom
  441. sprite->SetHotSpot(Vector2(0.0f, 0.0f));
  442. tileSprites_[gid++] = sprite;
  443. }
  444. }
  445. for (XMLElement tileElem = element.GetChild("tile"); tileElem; tileElem = tileElem.GetNext("tile"))
  446. {
  447. if (tileElem.HasChild("properties"))
  448. {
  449. SharedPtr<Properties2D> properties(new Properties2D());
  450. properties->Load(tileElem.GetChild("properties"));
  451. tileProperties_[firstgid + tileElem.GetInt("id")] = properties;
  452. }
  453. }
  454. return true;
  455. }
  456. bool TmxFile2D::LoadLayer(const XMLElement& element)
  457. {
  458. TmxTileLayer2D* tileLayer = new TmxTileLayer2D(this);
  459. layers_.Push(tileLayer);
  460. return tileLayer->Load(element);
  461. }
  462. bool TmxFile2D::LoadObjectGroup(const XMLElement& element)
  463. {
  464. TmxObjectGroup2D* objectGroup = new TmxObjectGroup2D(this);
  465. layers_.Push(objectGroup);
  466. return objectGroup->Load(element);
  467. }
  468. bool TmxFile2D::LoadImageLayer(const XMLElement& element)
  469. {
  470. TmxImageLayer2D* imageLayer = new TmxImageLayer2D(this);
  471. layers_.Push(imageLayer);
  472. return imageLayer->Load(element);
  473. }
  474. }