AnimationSet2D.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527
  1. //
  2. // Copyright (c) 2008-2015 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 "../Atomic2D/Animation2D.h"
  24. #include "../Atomic2D/AnimationSet2D.h"
  25. #include "../Core/Context.h"
  26. #include "../IO/FileSystem.h"
  27. #include "../IO/Log.h"
  28. #include "../Resource/ResourceCache.h"
  29. #include "../Atomic2D/Sprite2D.h"
  30. #include "../Atomic2D/SpriteSheet2D.h"
  31. #include "../Resource/XMLFile.h"
  32. #include "../DebugNew.h"
  33. namespace Atomic
  34. {
  35. AnimationSet2D::AnimationSet2D(Context* context) :
  36. Resource(context)
  37. {
  38. }
  39. AnimationSet2D::~AnimationSet2D()
  40. {
  41. }
  42. void AnimationSet2D::RegisterObject(Context* context)
  43. {
  44. context->RegisterFactory<AnimationSet2D>();
  45. }
  46. bool AnimationSet2D::BeginLoad(Deserializer& source)
  47. {
  48. if (GetName().Empty())
  49. SetName(source.GetName());
  50. String extension = GetExtension(source.GetName());
  51. if (extension == ".scml")
  52. return BeginLoadSpriter(source);
  53. LOGERROR("Unsupport animation set file: " + source.GetName());
  54. return false;
  55. }
  56. bool AnimationSet2D::EndLoad()
  57. {
  58. if (spriterFile_)
  59. return EndLoadSpriter();
  60. return false;
  61. }
  62. unsigned AnimationSet2D::GetNumAnimations() const
  63. {
  64. return animations_.Size();
  65. }
  66. Animation2D* AnimationSet2D::GetAnimation(unsigned index) const
  67. {
  68. if (index < animations_.Size())
  69. return animations_[index];
  70. return 0;
  71. }
  72. Animation2D* AnimationSet2D::GetAnimation(const String& name) const
  73. {
  74. for (unsigned i = 0; i < animations_.Size(); ++i)
  75. {
  76. if (animations_[i]->GetName() == name)
  77. return animations_[i];
  78. }
  79. return 0;
  80. }
  81. Sprite2D* AnimationSet2D::GetSprite(const StringHash& hash) const
  82. {
  83. HashMap<StringHash, SharedPtr<Sprite2D> >::ConstIterator i = sprites_.Find(hash);
  84. if (i != sprites_.End())
  85. return i->second_;
  86. return 0;
  87. }
  88. bool AnimationSet2D::BeginLoadSpriter(Deserializer &source)
  89. {
  90. spriterFile_ = new XMLFile(context_);
  91. if (!spriterFile_->Load(source))
  92. {
  93. LOGERROR("Load XML failed " + source.GetName());
  94. spriterFile_.Reset();
  95. return false;
  96. }
  97. XMLElement rootElem = spriterFile_->GetRoot("spriter_data");
  98. if (!rootElem)
  99. {
  100. LOGERROR("Invalid spriter file " + source.GetName());
  101. spriterFile_.Reset();
  102. return false;
  103. }
  104. // When async loading, preprocess folders for spritesheet / sprite files and request them for background loading
  105. if (GetAsyncLoadState() == ASYNC_LOADING)
  106. {
  107. if (!LoadSpriterFolders(rootElem))
  108. {
  109. spriterFile_.Reset();
  110. return false;
  111. }
  112. }
  113. return true;
  114. }
  115. bool AnimationSet2D::EndLoadSpriter()
  116. {
  117. XMLElement rootElem = spriterFile_->GetRoot("spriter_data");
  118. if (!LoadSpriterFolders(rootElem))
  119. {
  120. spriterFile_.Reset();
  121. return false;
  122. }
  123. XMLElement entityElem = rootElem.GetChild("entity");
  124. if (!entityElem)
  125. {
  126. LOGERROR("Could not find entity");
  127. spriterFile_.Reset();
  128. return false;
  129. }
  130. for (XMLElement animationElem = entityElem.GetChild("animation"); animationElem; animationElem = animationElem.GetNext("animation"))
  131. {
  132. if (!LoadSpriterAnimation(animationElem))
  133. {
  134. spriterFile_.Reset();
  135. return false;
  136. }
  137. }
  138. spriterFile_.Reset();
  139. return true;
  140. }
  141. bool AnimationSet2D::LoadSpriterFolders(const XMLElement& rootElem)
  142. {
  143. ResourceCache* cache = GetSubsystem<ResourceCache>();
  144. bool async = GetAsyncLoadState() == ASYNC_LOADING;
  145. String parentPath = GetParentPath(GetName());
  146. String spriteSheetFilePathPList = parentPath + GetFileName(GetName()) + ".plist";
  147. String spriteSheetFilePathXML = parentPath + GetFileName(GetName()) + ".xml";
  148. SpriteSheet2D* spriteSheet = 0;
  149. bool hasSpriteSheet = false;
  150. // When async loading, request the sprite sheet for background loading but do not actually get it
  151. if (!async)
  152. {
  153. spriteSheet = cache->GetResource<SpriteSheet2D>(spriteSheetFilePathPList, false);
  154. if (!spriteSheet)
  155. spriteSheet = cache->GetResource<SpriteSheet2D>(spriteSheetFilePathXML, false);
  156. }
  157. else
  158. {
  159. hasSpriteSheet = cache->Exists(spriteSheetFilePathPList);
  160. if (hasSpriteSheet)
  161. cache->BackgroundLoadResource<SpriteSheet2D>(spriteSheetFilePathPList, false, this);
  162. else
  163. {
  164. hasSpriteSheet = cache->Exists(spriteSheetFilePathXML);
  165. if (hasSpriteSheet)
  166. cache->BackgroundLoadResource<SpriteSheet2D>(spriteSheetFilePathXML, false, this);
  167. }
  168. }
  169. for (XMLElement folderElem = rootElem.GetChild("folder"); folderElem; folderElem = folderElem.GetNext("folder"))
  170. {
  171. unsigned folderId = folderElem.GetUInt("id");
  172. for (XMLElement fileElem = folderElem.GetChild("file"); fileElem; fileElem = fileElem.GetNext("file"))
  173. {
  174. unsigned fileId = fileElem.GetUInt("id");
  175. String fileName = fileElem.GetAttribute("name");
  176. // When async loading, request the sprites for background loading but do not actually get them
  177. if (!async)
  178. {
  179. SharedPtr<Sprite2D> sprite;
  180. if (spriteSheet)
  181. sprite = spriteSheet->GetSprite(GetFileName(fileName));
  182. else
  183. sprite = (cache->GetResource<Sprite2D>(parentPath + fileName));
  184. if (!sprite)
  185. {
  186. LOGERROR("Could not load sprite " + fileName);
  187. return false;
  188. }
  189. Vector2 hotSpot(0.0f, 1.0f);
  190. if (fileElem.HasAttribute("pivot_x"))
  191. hotSpot.x_ = fileElem.GetFloat("pivot_x");
  192. if (fileElem.HasAttribute("pivot_y"))
  193. hotSpot.y_ = fileElem.GetFloat("pivot_y");
  194. // If sprite is trimmed, recalculate hot spot
  195. const IntVector2& offset = sprite->GetOffset();
  196. if (offset != IntVector2::ZERO)
  197. {
  198. int width = fileElem.GetInt("width");
  199. int height = fileElem.GetInt("height");
  200. float pivotX = width * hotSpot.x_;
  201. float pivotY = height * (1.0f - hotSpot.y_);
  202. const IntRect& rectangle = sprite->GetRectangle();
  203. hotSpot.x_ = (offset.x_ + pivotX) / rectangle.Width();
  204. hotSpot.y_ = 1.0f - (offset.y_ + pivotY) / rectangle.Height();
  205. }
  206. sprite->SetHotSpot(hotSpot);
  207. sprites_[StringHash((folderId << 16) + fileId)] = sprite;
  208. }
  209. else if (!hasSpriteSheet)
  210. cache->BackgroundLoadResource<Sprite2D>(parentPath + fileName, true, this);
  211. }
  212. }
  213. return true;
  214. }
  215. // Spriter object type.
  216. enum SpriterObjectType2D
  217. {
  218. SOT_BONE = 0,
  219. SOT_SPRITE,
  220. };
  221. // Spriter timeline key.
  222. struct SpriterTimelineKey2D
  223. {
  224. SpriterTimelineKey2D() :
  225. time_(0.0f),
  226. angle_(0.0f),
  227. spin_(1),
  228. scale_(1.0f, 1.0f),
  229. alpha_(1.0f),
  230. useHotSpot_(false)
  231. {
  232. }
  233. float time_;
  234. Vector2 position_;
  235. float angle_;
  236. int spin_;
  237. Vector2 scale_;
  238. SharedPtr<Sprite2D> sprite_;
  239. float alpha_;
  240. bool useHotSpot_;
  241. Vector2 hotSpot_;
  242. };
  243. // Spriter timeline.
  244. struct SpriterTimeline2D
  245. {
  246. SpriterTimeline2D() :
  247. parent_(-1),
  248. type_(SOT_BONE)
  249. {
  250. }
  251. String name_;
  252. int parent_;
  253. SpriterObjectType2D type_;
  254. Vector<SpriterTimelineKey2D> timelineKeys_;
  255. };
  256. // Spriter reference.
  257. struct SpriterReference2D
  258. {
  259. SpriterReference2D() :
  260. type_(SOT_BONE),
  261. timeline_(-1),
  262. key_(-1),
  263. zIndex_(0)
  264. {
  265. }
  266. SpriterObjectType2D type_;
  267. int timeline_;
  268. int key_;
  269. int zIndex_;
  270. };
  271. // Spriter mainline Key.
  272. struct SpriterMainlineKey2D
  273. {
  274. float time_;
  275. PODVector<SpriterReference2D> references_;
  276. };
  277. bool AnimationSet2D::LoadSpriterAnimation(const XMLElement& animationElem)
  278. {
  279. String name = animationElem.GetAttribute("name");
  280. float length = animationElem.GetFloat("length") * 0.001f;
  281. bool looped = true;
  282. if (animationElem.HasAttribute("looping"))
  283. looped = animationElem.GetBool("looping");
  284. float highestKeyTime = 0.0f;
  285. // Load timelines
  286. Vector<SpriterTimeline2D> timelines;
  287. for (XMLElement timelineElem = animationElem.GetChild("timeline"); timelineElem; timelineElem = timelineElem.GetNext("timeline"))
  288. {
  289. SpriterTimeline2D timeline;
  290. timeline.name_ = timelineElem.GetAttribute("name");
  291. if (timelineElem.GetAttribute("object_type") == "bone")
  292. timeline.type_ = SOT_BONE;
  293. else
  294. timeline.type_ = SOT_SPRITE;
  295. for (XMLElement keyElem = timelineElem.GetChild("key"); keyElem; keyElem = keyElem.GetNext("key"))
  296. {
  297. SpriterTimelineKey2D key;
  298. key.time_ = keyElem.GetFloat("time") * 0.001f;
  299. highestKeyTime = Max(highestKeyTime, key.time_);
  300. if (keyElem.HasAttribute("spin"))
  301. key.spin_ = keyElem.GetInt("spin");
  302. XMLElement childElem = keyElem.GetChild();
  303. key.position_.x_ = childElem.GetFloat("x");
  304. key.position_.y_ = childElem.GetFloat("y");
  305. key.angle_ = childElem.GetFloat("angle");
  306. Vector2 scale(Vector2::ONE);
  307. if (childElem.HasAttribute("scale_x"))
  308. key.scale_.x_ = childElem.GetFloat("scale_x");
  309. if (childElem.HasAttribute("scale_y"))
  310. key.scale_.y_ = childElem.GetFloat("scale_y");
  311. if (timeline.type_ == SOT_SPRITE)
  312. {
  313. int folder = childElem.GetUInt("folder");
  314. int file = childElem.GetUInt("file");
  315. key.sprite_ = GetSprite(StringHash((folder << 16) + file));
  316. if (!key.sprite_)
  317. {
  318. LOGERROR("Could not find sprite");
  319. return false;
  320. }
  321. if (childElem.HasAttribute("pivot_x") && childElem.HasAttribute("pivot_y"))
  322. {
  323. key.useHotSpot_ = true;
  324. key.hotSpot_.x_ = childElem.GetFloat("pivot_x");
  325. key.hotSpot_.y_ = childElem.GetFloat("pivot_y");
  326. }
  327. if (childElem.HasAttribute("a"))
  328. key.alpha_ = childElem.GetFloat("a");
  329. }
  330. timeline.timelineKeys_.Push(key);
  331. }
  332. timelines.Push(timeline);
  333. }
  334. // Load main line
  335. Vector<SpriterMainlineKey2D> mainlineKeys;
  336. XMLElement mainlineElem = animationElem.GetChild("mainline");
  337. for (XMLElement keyElem = mainlineElem.GetChild("key"); keyElem; keyElem = keyElem.GetNext("key"))
  338. {
  339. SpriterMainlineKey2D mainlineKey;
  340. mainlineKey.time_ = keyElem.GetFloat("time") * 0.001f;
  341. for (XMLElement refElem = keyElem.GetChild(); refElem; refElem = refElem.GetNext())
  342. {
  343. SpriterReference2D ref;
  344. if (refElem.GetName() == "bone_ref")
  345. ref.type_ = SOT_BONE;
  346. else
  347. ref.type_ = SOT_SPRITE;
  348. ref.timeline_ = refElem.GetInt("timeline");
  349. ref.key_ = refElem.GetInt("key");
  350. if (refElem.HasAttribute("parent"))
  351. {
  352. int parent = refElem.GetInt("parent");
  353. int parentTimeline = mainlineKey.references_[parent].timeline_;
  354. timelines[ref.timeline_].parent_ = parentTimeline;
  355. }
  356. if (refElem.GetName() == "object_ref")
  357. ref.zIndex_ = refElem.GetInt("z_index");
  358. mainlineKey.references_.Push(ref);
  359. }
  360. mainlineKeys.Push(mainlineKey);
  361. }
  362. unsigned numTimelines = timelines.Size();
  363. unsigned numMainlineKeys = mainlineKeys.Size();
  364. if (numTimelines == 0 || numMainlineKeys == 0)
  365. {
  366. LOGERROR("Invalid animation");
  367. return false;
  368. }
  369. // Create animation
  370. SharedPtr<Animation2D> animation(new Animation2D(this));
  371. // Crop animation length if longer than the last keyframe, prevents sprites vanishing in clamp mode, or occasional flashes
  372. // when looped
  373. if (length > highestKeyTime)
  374. length = highestKeyTime;
  375. animation->SetName(name);
  376. animation->SetLength(length);
  377. animation->SetLooped(looped);
  378. Vector<AnimationTrack2D>& tracks = animation->GetAllTracks();
  379. tracks.Resize(numTimelines);
  380. // Setup animation track key frames
  381. for (unsigned i = 0; i < numTimelines; ++i)
  382. {
  383. SpriterTimeline2D& timeline = timelines[i];
  384. AnimationTrack2D& track = tracks[i];
  385. track.name_ = timeline.name_;
  386. track.hasSprite_ = timeline.type_ == SOT_SPRITE;
  387. unsigned numTimelineKeys = timeline.timelineKeys_.Size();
  388. tracks[i].keyFrames_.Resize(numTimelineKeys);
  389. for (unsigned j = 0; j < numTimelineKeys; ++j)
  390. {
  391. SpriterTimelineKey2D& timelineKey = timeline.timelineKeys_[j];
  392. AnimationKeyFrame2D& keyFrame = track.keyFrames_[j];
  393. keyFrame.time_ = timelineKey.time_;
  394. // Set disabled
  395. keyFrame.enabled_ = false;
  396. keyFrame.parent_ = timeline.parent_;
  397. keyFrame.transform_ = Transform2D(timelineKey.position_, timelineKey.angle_, timelineKey.scale_);
  398. keyFrame.spin_ = timelineKey.spin_;
  399. if (track.hasSprite_)
  400. {
  401. keyFrame.sprite_ = timelineKey.sprite_;
  402. keyFrame.alpha_ = timelineKey.alpha_;
  403. keyFrame.useHotSpot_ = timelineKey.useHotSpot_;
  404. if (timelineKey.useHotSpot_)
  405. keyFrame.hotSpot_ = timelineKey.hotSpot_;
  406. }
  407. }
  408. }
  409. // Set animation key frame enabled and set draw order
  410. for (unsigned i = 0; i < numMainlineKeys; ++i)
  411. {
  412. SpriterMainlineKey2D& mainlineKey = mainlineKeys[i];
  413. PODVector<SpriterReference2D>& references = mainlineKey.references_;
  414. for (unsigned j = 0; j < references.Size(); ++j)
  415. {
  416. SpriterReference2D& ref = references[j];
  417. AnimationKeyFrame2D& keyFrame = tracks[ref.timeline_].keyFrames_[ref.key_];
  418. // Set enabled
  419. keyFrame.enabled_ = true;
  420. // Set draw order
  421. keyFrame.zIndex_ = ref.zIndex_;
  422. }
  423. }
  424. // Fix looped animation
  425. if (looped)
  426. {
  427. for (unsigned i = 0; i < numTimelines; ++i)
  428. {
  429. Vector<AnimationKeyFrame2D>& keyFrames = tracks[i].keyFrames_;
  430. if (keyFrames.Front().time_ < 0.01f && !Equals(keyFrames.Back().time_, length))
  431. {
  432. AnimationKeyFrame2D keyFrame = keyFrames.Front();
  433. keyFrame.time_ = length;
  434. keyFrames.Push(keyFrame);
  435. }
  436. }
  437. }
  438. animations_.Push(animation);
  439. return true;
  440. }
  441. }