SpriteSheet2D.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. //
  2. // Copyright (c) 2008-2020 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 "../Core/Context.h"
  24. #include "../Graphics/Texture2D.h"
  25. #include "../IO/Deserializer.h"
  26. #include "../IO/FileSystem.h"
  27. #include "../IO/Log.h"
  28. #include "../Resource/PListFile.h"
  29. #include "../Resource/ResourceCache.h"
  30. #include "../Resource/XMLFile.h"
  31. #include "../Resource/JSONFile.h"
  32. #include "../Urho2D/Sprite2D.h"
  33. #include "../Urho2D/SpriteSheet2D.h"
  34. #include "../DebugNew.h"
  35. namespace Urho3D
  36. {
  37. SpriteSheet2D::SpriteSheet2D(Context* context) :
  38. Resource(context)
  39. {
  40. }
  41. SpriteSheet2D::~SpriteSheet2D() = default;
  42. void SpriteSheet2D::RegisterObject(Context* context)
  43. {
  44. context->RegisterFactory<SpriteSheet2D>();
  45. }
  46. bool SpriteSheet2D::BeginLoad(Deserializer& source)
  47. {
  48. if (GetName().Empty())
  49. SetName(source.GetName());
  50. loadTextureName_.Clear();
  51. spriteMapping_.Clear();
  52. String extension = GetExtension(source.GetName());
  53. if (extension == ".plist")
  54. return BeginLoadFromPListFile(source);
  55. if (extension == ".xml")
  56. return BeginLoadFromXMLFile(source);
  57. if (extension == ".json")
  58. return BeginLoadFromJSONFile(source);
  59. URHO3D_LOGERROR("Unsupported file type");
  60. return false;
  61. }
  62. bool SpriteSheet2D::EndLoad()
  63. {
  64. if (loadPListFile_)
  65. return EndLoadFromPListFile();
  66. if (loadXMLFile_)
  67. return EndLoadFromXMLFile();
  68. if (loadJSONFile_)
  69. return EndLoadFromJSONFile();
  70. return false;
  71. }
  72. void SpriteSheet2D::SetTexture(Texture2D* texture)
  73. {
  74. loadTextureName_.Clear();
  75. texture_ = texture;
  76. }
  77. void SpriteSheet2D::DefineSprite(const String& name, const IntRect& rectangle, const Vector2& hotSpot, const IntVector2& offset)
  78. {
  79. if (!texture_)
  80. return;
  81. if (GetSprite(name))
  82. return;
  83. SharedPtr<Sprite2D> sprite(new Sprite2D(context_));
  84. sprite->SetName(name);
  85. sprite->SetTexture(texture_);
  86. sprite->SetRectangle(rectangle);
  87. sprite->SetHotSpot(hotSpot);
  88. sprite->SetOffset(offset);
  89. sprite->SetSpriteSheet(this);
  90. spriteMapping_[name] = sprite;
  91. }
  92. Sprite2D* SpriteSheet2D::GetSprite(const String& name) const
  93. {
  94. HashMap<String, SharedPtr<Sprite2D> >::ConstIterator i = spriteMapping_.Find(name);
  95. if (i == spriteMapping_.End())
  96. return nullptr;
  97. return i->second_;
  98. }
  99. bool SpriteSheet2D::BeginLoadFromPListFile(Deserializer& source)
  100. {
  101. loadPListFile_ = new PListFile(context_);
  102. if (!loadPListFile_->Load(source))
  103. {
  104. URHO3D_LOGERROR("Could not load sprite sheet");
  105. loadPListFile_.Reset();
  106. return false;
  107. }
  108. SetMemoryUse(source.GetSize());
  109. const PListValueMap& root = loadPListFile_->GetRoot();
  110. const PListValueMap& metadata = root["metadata"]->GetValueMap();
  111. const String& textureFileName = metadata["realTextureFileName"]->GetString();
  112. // If we're async loading, request the texture now. Finish during EndLoad().
  113. loadTextureName_ = GetParentPath(GetName()) + textureFileName;
  114. if (GetAsyncLoadState() == ASYNC_LOADING)
  115. GetSubsystem<ResourceCache>()->BackgroundLoadResource<Texture2D>(loadTextureName_, true, this);
  116. return true;
  117. }
  118. bool SpriteSheet2D::EndLoadFromPListFile()
  119. {
  120. auto* cache = GetSubsystem<ResourceCache>();
  121. texture_ = cache->GetResource<Texture2D>(loadTextureName_);
  122. if (!texture_)
  123. {
  124. URHO3D_LOGERROR("Could not load texture " + loadTextureName_);
  125. loadPListFile_.Reset();
  126. loadTextureName_.Clear();
  127. return false;
  128. }
  129. const PListValueMap& root = loadPListFile_->GetRoot();
  130. const PListValueMap& frames = root["frames"]->GetValueMap();
  131. for (PListValueMap::ConstIterator i = frames.Begin(); i != frames.End(); ++i)
  132. {
  133. String name = i->first_.Split('.')[0];
  134. const PListValueMap& frameInfo = i->second_.GetValueMap();
  135. if (frameInfo["rotated"]->GetBool())
  136. {
  137. URHO3D_LOGWARNING("Rotated sprite is not support now");
  138. continue;
  139. }
  140. IntRect rectangle = frameInfo["frame"]->GetIntRect();
  141. Vector2 hotSpot(0.5f, 0.5f);
  142. IntVector2 offset(0, 0);
  143. IntRect sourceColorRect = frameInfo["sourceColorRect"]->GetIntRect();
  144. if (sourceColorRect.left_ != 0 && sourceColorRect.top_ != 0)
  145. {
  146. offset.x_ = -sourceColorRect.left_;
  147. offset.y_ = -sourceColorRect.top_;
  148. IntVector2 sourceSize = frameInfo["sourceSize"]->GetIntVector2();
  149. hotSpot.x_ = (offset.x_ + sourceSize.x_ / 2.f) / rectangle.Width();
  150. hotSpot.y_ = 1.0f - (offset.y_ + sourceSize.y_ / 2.f) / rectangle.Height();
  151. }
  152. DefineSprite(name, rectangle, hotSpot, offset);
  153. }
  154. loadPListFile_.Reset();
  155. loadTextureName_.Clear();
  156. return true;
  157. }
  158. bool SpriteSheet2D::BeginLoadFromXMLFile(Deserializer& source)
  159. {
  160. loadXMLFile_ = new XMLFile(context_);
  161. if (!loadXMLFile_->Load(source))
  162. {
  163. URHO3D_LOGERROR("Could not load sprite sheet");
  164. loadXMLFile_.Reset();
  165. return false;
  166. }
  167. SetMemoryUse(source.GetSize());
  168. XMLElement rootElem = loadXMLFile_->GetRoot("TextureAtlas");
  169. if (!rootElem)
  170. {
  171. URHO3D_LOGERROR("Invalid sprite sheet");
  172. loadXMLFile_.Reset();
  173. return false;
  174. }
  175. // If we're async loading, request the texture now. Finish during EndLoad().
  176. loadTextureName_ = GetParentPath(GetName()) + rootElem.GetAttribute("imagePath");
  177. if (GetAsyncLoadState() == ASYNC_LOADING)
  178. GetSubsystem<ResourceCache>()->BackgroundLoadResource<Texture2D>(loadTextureName_, true, this);
  179. return true;
  180. }
  181. bool SpriteSheet2D::EndLoadFromXMLFile()
  182. {
  183. auto* cache = GetSubsystem<ResourceCache>();
  184. texture_ = cache->GetResource<Texture2D>(loadTextureName_);
  185. if (!texture_)
  186. {
  187. URHO3D_LOGERROR("Could not load texture " + loadTextureName_);
  188. loadXMLFile_.Reset();
  189. loadTextureName_.Clear();
  190. return false;
  191. }
  192. XMLElement rootElem = loadXMLFile_->GetRoot("TextureAtlas");
  193. XMLElement subTextureElem = rootElem.GetChild("SubTexture");
  194. while (subTextureElem)
  195. {
  196. String name = subTextureElem.GetAttribute("name");
  197. int x = subTextureElem.GetInt("x");
  198. int y = subTextureElem.GetInt("y");
  199. int width = subTextureElem.GetInt("width");
  200. int height = subTextureElem.GetInt("height");
  201. IntRect rectangle(x, y, x + width, y + height);
  202. Vector2 hotSpot(0.5f, 0.5f);
  203. IntVector2 offset(0, 0);
  204. if (subTextureElem.HasAttribute("frameWidth") && subTextureElem.HasAttribute("frameHeight"))
  205. {
  206. offset.x_ = subTextureElem.GetInt("frameX");
  207. offset.y_ = subTextureElem.GetInt("frameY");
  208. int frameWidth = subTextureElem.GetInt("frameWidth");
  209. int frameHeight = subTextureElem.GetInt("frameHeight");
  210. hotSpot.x_ = (offset.x_ + frameWidth / 2.f) / width;
  211. hotSpot.y_ = 1.0f - (offset.y_ + frameHeight / 2.f) / height;
  212. }
  213. DefineSprite(name, rectangle, hotSpot, offset);
  214. subTextureElem = subTextureElem.GetNext("SubTexture");
  215. }
  216. loadXMLFile_.Reset();
  217. loadTextureName_.Clear();
  218. return true;
  219. }
  220. bool SpriteSheet2D::BeginLoadFromJSONFile(Deserializer& source)
  221. {
  222. loadJSONFile_ = new JSONFile(context_);
  223. if (!loadJSONFile_->Load(source))
  224. {
  225. URHO3D_LOGERROR("Could not load sprite sheet");
  226. loadJSONFile_.Reset();
  227. return false;
  228. }
  229. SetMemoryUse(source.GetSize());
  230. JSONValue rootElem = loadJSONFile_->GetRoot();
  231. if (rootElem.IsNull())
  232. {
  233. URHO3D_LOGERROR("Invalid sprite sheet");
  234. loadJSONFile_.Reset();
  235. return false;
  236. }
  237. // If we're async loading, request the texture now. Finish during EndLoad().
  238. loadTextureName_ = GetParentPath(GetName()) + rootElem.Get("imagePath").GetString();
  239. if (GetAsyncLoadState() == ASYNC_LOADING)
  240. GetSubsystem<ResourceCache>()->BackgroundLoadResource<Texture2D>(loadTextureName_, true, this);
  241. return true;
  242. }
  243. bool SpriteSheet2D::EndLoadFromJSONFile()
  244. {
  245. auto* cache = GetSubsystem<ResourceCache>();
  246. texture_ = cache->GetResource<Texture2D>(loadTextureName_);
  247. if (!texture_)
  248. {
  249. URHO3D_LOGERROR("Could not load texture " + loadTextureName_);
  250. loadJSONFile_.Reset();
  251. loadTextureName_.Clear();
  252. return false;
  253. }
  254. JSONValue rootVal = loadJSONFile_->GetRoot();
  255. JSONArray subTextureArray = rootVal.Get("subtextures").GetArray();
  256. for (unsigned i = 0; i < subTextureArray.Size(); i++)
  257. {
  258. const JSONValue& subTextureVal = subTextureArray.At(i);
  259. String name = subTextureVal.Get("name").GetString();
  260. int x = subTextureVal.Get("x").GetInt();
  261. int y = subTextureVal.Get("y").GetInt();
  262. int width = subTextureVal.Get("width").GetInt();
  263. int height = subTextureVal.Get("height").GetInt();
  264. IntRect rectangle(x, y, x + width, y + height);
  265. Vector2 hotSpot(0.5f, 0.5f);
  266. IntVector2 offset(0, 0);
  267. JSONValue frameWidthVal = subTextureVal.Get("frameWidth");
  268. JSONValue frameHeightVal = subTextureVal.Get("frameHeight");
  269. if (!frameWidthVal.IsNull() && !frameHeightVal.IsNull())
  270. {
  271. offset.x_ = subTextureVal.Get("frameX").GetInt();
  272. offset.y_ = subTextureVal.Get("frameY").GetInt();
  273. int frameWidth = frameWidthVal.GetInt();
  274. int frameHeight = frameHeightVal.GetInt();
  275. hotSpot.x_ = (offset.x_ + frameWidth / 2.f) / width;
  276. hotSpot.y_ = 1.0f - (offset.y_ + frameHeight / 2.f) / height;
  277. }
  278. DefineSprite(name, rectangle, hotSpot, offset);
  279. }
  280. loadJSONFile_.Reset();
  281. loadTextureName_.Clear();
  282. return true;
  283. }
  284. }