AnimationSet2D.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531
  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 "../Core/Context.h"
  24. #include "../IO/FileSystem.h"
  25. #include "../IO/Log.h"
  26. #include "../Resource/ResourceCache.h"
  27. #include "../Atomic2D/Animation2D.h"
  28. #include "../Atomic2D/AnimationSet2D.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;
  131. animationElem = animationElem.GetNext("animation"))
  132. {
  133. if (!LoadSpriterAnimation(animationElem))
  134. {
  135. spriterFile_.Reset();
  136. return false;
  137. }
  138. }
  139. spriterFile_.Reset();
  140. return true;
  141. }
  142. bool AnimationSet2D::LoadSpriterFolders(const XMLElement& rootElem)
  143. {
  144. ResourceCache* cache = GetSubsystem<ResourceCache>();
  145. bool async = GetAsyncLoadState() == ASYNC_LOADING;
  146. String parentPath = GetParentPath(GetName());
  147. String spriteSheetFilePathPList = parentPath + GetFileName(GetName()) + ".plist";
  148. String spriteSheetFilePathXML = parentPath + GetFileName(GetName()) + ".xml";
  149. SpriteSheet2D* spriteSheet = 0;
  150. bool hasSpriteSheet = false;
  151. // When async loading, request the sprite sheet for background loading but do not actually get it
  152. if (!async)
  153. {
  154. spriteSheet = cache->GetResource<SpriteSheet2D>(spriteSheetFilePathPList, false);
  155. if (!spriteSheet)
  156. spriteSheet = cache->GetResource<SpriteSheet2D>(spriteSheetFilePathXML, false);
  157. }
  158. else
  159. {
  160. hasSpriteSheet = cache->Exists(spriteSheetFilePathPList);
  161. if (hasSpriteSheet)
  162. cache->BackgroundLoadResource<SpriteSheet2D>(spriteSheetFilePathPList, false, this);
  163. else
  164. {
  165. hasSpriteSheet = cache->Exists(spriteSheetFilePathXML);
  166. if (hasSpriteSheet)
  167. cache->BackgroundLoadResource<SpriteSheet2D>(spriteSheetFilePathXML, false, this);
  168. }
  169. }
  170. for (XMLElement folderElem = rootElem.GetChild("folder"); folderElem; folderElem = folderElem.GetNext("folder"))
  171. {
  172. unsigned folderId = folderElem.GetUInt("id");
  173. for (XMLElement fileElem = folderElem.GetChild("file"); fileElem; fileElem = fileElem.GetNext("file"))
  174. {
  175. unsigned fileId = fileElem.GetUInt("id");
  176. String fileName = fileElem.GetAttribute("name");
  177. // When async loading, request the sprites for background loading but do not actually get them
  178. if (!async)
  179. {
  180. SharedPtr<Sprite2D> sprite;
  181. if (spriteSheet)
  182. sprite = spriteSheet->GetSprite(GetFileName(fileName));
  183. else
  184. sprite = (cache->GetResource<Sprite2D>(parentPath + fileName));
  185. if (!sprite)
  186. {
  187. LOGERROR("Could not load sprite " + fileName);
  188. return false;
  189. }
  190. Vector2 hotSpot(0.0f, 1.0f);
  191. if (fileElem.HasAttribute("pivot_x"))
  192. hotSpot.x_ = fileElem.GetFloat("pivot_x");
  193. if (fileElem.HasAttribute("pivot_y"))
  194. hotSpot.y_ = fileElem.GetFloat("pivot_y");
  195. // If sprite is trimmed, recalculate hot spot
  196. const IntVector2& offset = sprite->GetOffset();
  197. if (offset != IntVector2::ZERO)
  198. {
  199. int width = fileElem.GetInt("width");
  200. int height = fileElem.GetInt("height");
  201. float pivotX = width * hotSpot.x_;
  202. float pivotY = height * (1.0f - hotSpot.y_);
  203. const IntRect& rectangle = sprite->GetRectangle();
  204. hotSpot.x_ = (offset.x_ + pivotX) / rectangle.Width();
  205. hotSpot.y_ = 1.0f - (offset.y_ + pivotY) / rectangle.Height();
  206. }
  207. sprite->SetHotSpot(hotSpot);
  208. sprites_[StringHash((folderId << 16) + fileId)] = sprite;
  209. }
  210. else if (!hasSpriteSheet)
  211. cache->BackgroundLoadResource<Sprite2D>(parentPath + fileName, true, this);
  212. }
  213. }
  214. return true;
  215. }
  216. // Spriter object type.
  217. enum SpriterObjectType2D
  218. {
  219. SOT_BONE = 0,
  220. SOT_SPRITE,
  221. };
  222. // Spriter timeline key.
  223. struct SpriterTimelineKey2D
  224. {
  225. SpriterTimelineKey2D() :
  226. time_(0.0f),
  227. angle_(0.0f),
  228. spin_(1),
  229. scale_(1.0f, 1.0f),
  230. alpha_(1.0f),
  231. useHotSpot_(false)
  232. {
  233. }
  234. float time_;
  235. Vector2 position_;
  236. float angle_;
  237. int spin_;
  238. Vector2 scale_;
  239. SharedPtr<Sprite2D> sprite_;
  240. float alpha_;
  241. bool useHotSpot_;
  242. Vector2 hotSpot_;
  243. };
  244. // Spriter timeline.
  245. struct SpriterTimeline2D
  246. {
  247. SpriterTimeline2D() :
  248. parent_(-1),
  249. type_(SOT_BONE)
  250. {
  251. }
  252. String name_;
  253. int parent_;
  254. SpriterObjectType2D type_;
  255. Vector<SpriterTimelineKey2D> timelineKeys_;
  256. };
  257. // Spriter reference.
  258. struct SpriterReference2D
  259. {
  260. SpriterReference2D() :
  261. type_(SOT_BONE),
  262. timeline_(-1),
  263. key_(-1),
  264. zIndex_(0)
  265. {
  266. }
  267. SpriterObjectType2D type_;
  268. int timeline_;
  269. int key_;
  270. int zIndex_;
  271. };
  272. // Spriter mainline Key.
  273. struct SpriterMainlineKey2D
  274. {
  275. float time_;
  276. PODVector<SpriterReference2D> references_;
  277. };
  278. bool AnimationSet2D::LoadSpriterAnimation(const XMLElement& animationElem)
  279. {
  280. String name = animationElem.GetAttribute("name");
  281. float length = animationElem.GetFloat("length") * 0.001f;
  282. bool looped = true;
  283. if (animationElem.HasAttribute("looping"))
  284. looped = animationElem.GetBool("looping");
  285. float highestKeyTime = 0.0f;
  286. // Load timelines
  287. Vector<SpriterTimeline2D> timelines;
  288. for (XMLElement timelineElem = animationElem.GetChild("timeline"); timelineElem;
  289. timelineElem = timelineElem.GetNext("timeline"))
  290. {
  291. SpriterTimeline2D timeline;
  292. timeline.name_ = timelineElem.GetAttribute("name");
  293. if (timelineElem.GetAttribute("object_type") == "bone")
  294. timeline.type_ = SOT_BONE;
  295. else
  296. timeline.type_ = SOT_SPRITE;
  297. for (XMLElement keyElem = timelineElem.GetChild("key"); keyElem; keyElem = keyElem.GetNext("key"))
  298. {
  299. SpriterTimelineKey2D key;
  300. key.time_ = keyElem.GetFloat("time") * 0.001f;
  301. if (keyElem.HasAttribute("spin"))
  302. key.spin_ = keyElem.GetInt("spin");
  303. XMLElement childElem = keyElem.GetChild();
  304. key.position_.x_ = childElem.GetFloat("x");
  305. key.position_.y_ = childElem.GetFloat("y");
  306. key.angle_ = childElem.GetFloat("angle");
  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((unsigned)((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. animation->SetName(name);
  372. animation->SetLength(length);
  373. animation->SetLooped(looped);
  374. Vector<AnimationTrack2D>& tracks = animation->GetAllTracks();
  375. tracks.Resize(numTimelines);
  376. // Setup animation track key frames
  377. for (unsigned i = 0; i < numTimelines; ++i)
  378. {
  379. SpriterTimeline2D& timeline = timelines[i];
  380. AnimationTrack2D& track = tracks[i];
  381. track.name_ = timeline.name_;
  382. track.hasSprite_ = timeline.type_ == SOT_SPRITE;
  383. unsigned numTimelineKeys = timeline.timelineKeys_.Size();
  384. tracks[i].keyFrames_.Resize(numTimelineKeys);
  385. for (unsigned j = 0; j < numTimelineKeys; ++j)
  386. {
  387. SpriterTimelineKey2D& timelineKey = timeline.timelineKeys_[j];
  388. AnimationKeyFrame2D& keyFrame = track.keyFrames_[j];
  389. keyFrame.time_ = timelineKey.time_;
  390. highestKeyTime = Max(highestKeyTime, keyFrame.time_);
  391. // Set disabled
  392. keyFrame.enabled_ = false;
  393. keyFrame.parent_ = timeline.parent_;
  394. keyFrame.transform_ = Transform2D(timelineKey.position_, timelineKey.angle_, timelineKey.scale_);
  395. keyFrame.spin_ = timelineKey.spin_;
  396. if (track.hasSprite_)
  397. {
  398. keyFrame.sprite_ = timelineKey.sprite_;
  399. keyFrame.alpha_ = timelineKey.alpha_;
  400. keyFrame.useHotSpot_ = timelineKey.useHotSpot_;
  401. if (timelineKey.useHotSpot_)
  402. keyFrame.hotSpot_ = timelineKey.hotSpot_;
  403. }
  404. }
  405. }
  406. // Set animation key frame enabled and set draw order
  407. for (unsigned i = 0; i < numMainlineKeys; ++i)
  408. {
  409. SpriterMainlineKey2D& mainlineKey = mainlineKeys[i];
  410. PODVector<SpriterReference2D>& references = mainlineKey.references_;
  411. for (unsigned j = 0; j < references.Size(); ++j)
  412. {
  413. SpriterReference2D& ref = references[j];
  414. AnimationKeyFrame2D& keyFrame = tracks[ref.timeline_].keyFrames_[ref.key_];
  415. // Set enabled
  416. keyFrame.enabled_ = true;
  417. // Set draw order
  418. keyFrame.zIndex_ = ref.zIndex_;
  419. }
  420. }
  421. // Fix looped animation
  422. if (looped)
  423. {
  424. for (unsigned i = 0; i < numTimelines; ++i)
  425. {
  426. Vector<AnimationKeyFrame2D>& keyFrames = tracks[i].keyFrames_;
  427. if (keyFrames.Front().time_ < 0.01f && !Equals(keyFrames.Back().time_, length))
  428. {
  429. AnimationKeyFrame2D keyFrame = keyFrames.Front();
  430. keyFrame.time_ = length;
  431. keyFrames.Push(keyFrame);
  432. }
  433. }
  434. }
  435. else
  436. {
  437. // Crop non-looped animation length if longer than the last keyframe
  438. if (length > highestKeyTime)
  439. animation->SetLength(highestKeyTime);
  440. }
  441. animations_.Push(animation);
  442. return true;
  443. }
  444. }