AnimatedSprite2D.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520
  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 "../IO/Log.h"
  25. #include "../Resource/ResourceCache.h"
  26. #include "../Scene/Scene.h"
  27. #include "../Scene/SceneEvents.h"
  28. #include "../Urho2D/AnimatedSprite2D.h"
  29. #include "../Urho2D/AnimationSet2D.h"
  30. #include "../Urho2D/Sprite2D.h"
  31. #include "../Urho2D/SpriterInstance2D.h"
  32. #include "../DebugNew.h"
  33. #ifdef URHO3D_SPINE
  34. #include <spine/spine.h>
  35. #endif
  36. namespace Urho3D
  37. {
  38. extern const char* URHO2D_CATEGORY;
  39. extern const char* blendModeNames[];
  40. const char* loopModeNames[] =
  41. {
  42. "Default",
  43. "ForceLooped",
  44. "ForceClamped",
  45. nullptr
  46. };
  47. AnimatedSprite2D::AnimatedSprite2D(Context* context) :
  48. StaticSprite2D(context),
  49. #ifdef URHO3D_SPINE
  50. skeleton_(0),
  51. animationStateData_(0),
  52. animationState_(0),
  53. #endif
  54. speed_(1.0f),
  55. loopMode_(LM_DEFAULT)
  56. {
  57. }
  58. AnimatedSprite2D::~AnimatedSprite2D()
  59. {
  60. Dispose();
  61. }
  62. void AnimatedSprite2D::RegisterObject(Context* context)
  63. {
  64. context->RegisterFactory<AnimatedSprite2D>(URHO2D_CATEGORY);
  65. URHO3D_COPY_BASE_ATTRIBUTES(StaticSprite2D);
  66. URHO3D_REMOVE_ATTRIBUTE("Sprite");
  67. URHO3D_ACCESSOR_ATTRIBUTE("Speed", GetSpeed, SetSpeed, float, 1.0f, AM_DEFAULT);
  68. URHO3D_ACCESSOR_ATTRIBUTE("Entity", GetEntity, SetEntity, String, String::EMPTY, AM_DEFAULT);
  69. URHO3D_MIXED_ACCESSOR_ATTRIBUTE("Animation Set", GetAnimationSetAttr, SetAnimationSetAttr, ResourceRef,
  70. ResourceRef(AnimatedSprite2D::GetTypeStatic()), AM_DEFAULT);
  71. URHO3D_ACCESSOR_ATTRIBUTE("Animation", GetAnimation, SetAnimationAttr, String, String::EMPTY, AM_DEFAULT);
  72. URHO3D_ENUM_ACCESSOR_ATTRIBUTE("Loop Mode", GetLoopMode, SetLoopMode, LoopMode2D, loopModeNames, LM_DEFAULT, AM_DEFAULT);
  73. }
  74. void AnimatedSprite2D::OnSetEnabled()
  75. {
  76. StaticSprite2D::OnSetEnabled();
  77. bool enabled = IsEnabledEffective();
  78. Scene* scene = GetScene();
  79. if (scene)
  80. {
  81. if (enabled)
  82. SubscribeToEvent(scene, E_SCENEPOSTUPDATE, URHO3D_HANDLER(AnimatedSprite2D, HandleScenePostUpdate));
  83. else
  84. UnsubscribeFromEvent(scene, E_SCENEPOSTUPDATE);
  85. }
  86. }
  87. void AnimatedSprite2D::SetAnimationSet(AnimationSet2D* animationSet)
  88. {
  89. if (animationSet == animationSet_)
  90. return;
  91. Dispose();
  92. animationSet_ = animationSet;
  93. if (!animationSet_)
  94. return;
  95. SetSprite(animationSet_->GetSprite());
  96. #ifdef URHO3D_SPINE
  97. if (animationSet_->GetSkeletonData())
  98. {
  99. spSkeletonData* skeletonData = animationSet->GetSkeletonData();
  100. // Create skeleton
  101. skeleton_ = spSkeleton_create(skeletonData);
  102. skeleton_->scaleX = flipX_ ? -1 : 1;
  103. skeleton_->scaleY = flipY_ ? -1 : 1;
  104. if (skeleton_->data->skinsCount > 0)
  105. {
  106. // If entity is empty use first skin in spine
  107. if (entity_.Empty())
  108. entity_ = skeleton_->data->skins[0]->name;
  109. spSkeleton_setSkinByName(skeleton_, entity_.CString());
  110. }
  111. spSkeleton_updateWorldTransform(skeleton_);
  112. }
  113. #endif
  114. if (animationSet_->GetSpriterData())
  115. {
  116. spriterInstance_ = new Spriter::SpriterInstance(this, animationSet_->GetSpriterData());
  117. if (!animationSet_->GetSpriterData()->entities_.Empty())
  118. {
  119. // If entity is empty use first entity in spriter
  120. if (entity_.Empty())
  121. entity_ = animationSet_->GetSpriterData()->entities_[0]->name_;
  122. spriterInstance_->SetEntity(entity_);
  123. }
  124. }
  125. // Clear animation name
  126. animationName_.Clear();
  127. loopMode_ = LM_DEFAULT;
  128. }
  129. void AnimatedSprite2D::SetEntity(const String& entity)
  130. {
  131. if (entity == entity_)
  132. return;
  133. entity_ = entity;
  134. #ifdef URHO3D_SPINE
  135. if (skeleton_)
  136. spSkeleton_setSkinByName(skeleton_, entity_.CString());
  137. #endif
  138. if (spriterInstance_)
  139. spriterInstance_->SetEntity(entity_.CString());
  140. }
  141. void AnimatedSprite2D::SetAnimation(const String& name, LoopMode2D loopMode)
  142. {
  143. animationName_ = name;
  144. loopMode_ = loopMode;
  145. if (!animationSet_ || !animationSet_->HasAnimation(animationName_))
  146. return;
  147. #ifdef URHO3D_SPINE
  148. if (skeleton_)
  149. SetSpineAnimation();
  150. #endif
  151. if (spriterInstance_)
  152. SetSpriterAnimation();
  153. }
  154. void AnimatedSprite2D::SetLoopMode(LoopMode2D loopMode)
  155. {
  156. loopMode_ = loopMode;
  157. }
  158. void AnimatedSprite2D::SetSpeed(float speed)
  159. {
  160. speed_ = speed;
  161. MarkNetworkUpdate();
  162. }
  163. AnimationSet2D* AnimatedSprite2D::GetAnimationSet() const
  164. {
  165. return animationSet_;
  166. }
  167. void AnimatedSprite2D::SetAnimationSetAttr(const ResourceRef& value)
  168. {
  169. auto* cache = GetSubsystem<ResourceCache>();
  170. SetAnimationSet(cache->GetResource<AnimationSet2D>(value.name_));
  171. }
  172. ResourceRef AnimatedSprite2D::GetAnimationSetAttr() const
  173. {
  174. return GetResourceRef(animationSet_, AnimationSet2D::GetTypeStatic());
  175. }
  176. void AnimatedSprite2D::OnSceneSet(Scene* scene)
  177. {
  178. StaticSprite2D::OnSceneSet(scene);
  179. if (scene)
  180. {
  181. if (scene == node_)
  182. URHO3D_LOGWARNING(GetTypeName() + " should not be created to the root scene node");
  183. if (IsEnabledEffective())
  184. SubscribeToEvent(scene, E_SCENEPOSTUPDATE, URHO3D_HANDLER(AnimatedSprite2D, HandleScenePostUpdate));
  185. }
  186. else
  187. UnsubscribeFromEvent(E_SCENEPOSTUPDATE);
  188. }
  189. void AnimatedSprite2D::SetAnimationAttr(const String& name)
  190. {
  191. animationName_ = name;
  192. SetAnimation(animationName_, loopMode_);
  193. }
  194. void AnimatedSprite2D::UpdateSourceBatches()
  195. {
  196. #ifdef URHO3D_SPINE
  197. if (skeleton_ && animationState_)
  198. UpdateSourceBatchesSpine();
  199. #endif
  200. if (spriterInstance_ && spriterInstance_->GetAnimation())
  201. UpdateSourceBatchesSpriter();
  202. sourceBatchesDirty_ = false;
  203. }
  204. void AnimatedSprite2D::HandleScenePostUpdate(StringHash eventType, VariantMap& eventData)
  205. {
  206. using namespace ScenePostUpdate;
  207. float timeStep = eventData[P_TIMESTEP].GetFloat();
  208. UpdateAnimation(timeStep);
  209. }
  210. void AnimatedSprite2D::UpdateAnimation(float timeStep)
  211. {
  212. #ifdef URHO3D_SPINE
  213. if (skeleton_ && animationState_)
  214. UpdateSpineAnimation(timeStep);
  215. #endif
  216. if (spriterInstance_ && spriterInstance_->GetAnimation())
  217. UpdateSpriterAnimation(timeStep);
  218. }
  219. #ifdef URHO3D_SPINE
  220. void AnimatedSprite2D::SetSpineAnimation()
  221. {
  222. if (!animationStateData_)
  223. {
  224. animationStateData_ = spAnimationStateData_create(animationSet_->GetSkeletonData());
  225. if (!animationStateData_)
  226. {
  227. URHO3D_LOGERROR("Create animation state data failed");
  228. return;
  229. }
  230. }
  231. if (!animationState_)
  232. {
  233. animationState_ = spAnimationState_create(animationStateData_);
  234. if (!animationState_)
  235. {
  236. URHO3D_LOGERROR("Create animation state failed");
  237. return;
  238. }
  239. }
  240. // Reset slots to setup pose, fix issue #932
  241. spSkeleton_setSlotsToSetupPose(skeleton_);
  242. spAnimationState_setAnimationByName(animationState_, 0, animationName_.CString(), loopMode_ != LM_FORCE_CLAMPED ? true : false);
  243. UpdateAnimation(0.0f);
  244. MarkNetworkUpdate();
  245. }
  246. void AnimatedSprite2D::UpdateSpineAnimation(float timeStep)
  247. {
  248. timeStep *= speed_;
  249. skeleton_->scaleX = flipX_ ? -1 : 1;
  250. skeleton_->scaleY = flipY_ ? -1 : 1;
  251. spSkeleton_update(skeleton_, timeStep);
  252. spAnimationState_update(animationState_, timeStep);
  253. spAnimationState_apply(animationState_, skeleton_);
  254. spSkeleton_updateWorldTransform(skeleton_);
  255. sourceBatchesDirty_ = true;
  256. worldBoundingBoxDirty_ = true;
  257. }
  258. // This enum used to be defined in spine/RegionAttachment.h but it got moved inside RegionAttachment.c so it's no longer accessible.
  259. // It's required because AnimatedSprite2D::UpdateSourceBatchesSpine() references its values (SP_VERTEX_*)
  260. typedef enum {
  261. SP_VERTEX_X1 = 0, SP_VERTEX_Y1, SP_VERTEX_X2, SP_VERTEX_Y2, SP_VERTEX_X3, SP_VERTEX_Y3, SP_VERTEX_X4, SP_VERTEX_Y4
  262. } spVertexIndex;
  263. void AnimatedSprite2D::UpdateSourceBatchesSpine()
  264. {
  265. const Matrix3x4& worldTransform = GetNode()->GetWorldTransform();
  266. SourceBatch2D& sourceBatch = sourceBatches_[0];
  267. sourceBatches_[0].vertices_.Clear();
  268. const int SLOT_VERTEX_COUNT_MAX = 1024;
  269. float slotVertices[SLOT_VERTEX_COUNT_MAX];
  270. for (int i = 0; i < skeleton_->slotsCount; ++i)
  271. {
  272. spSlot* slot = skeleton_->drawOrder[i];
  273. spAttachment* attachment = slot->attachment;
  274. if (!attachment)
  275. continue;
  276. unsigned color = Color(color_.r_ * slot->color.r,
  277. color_.g_ * slot->color.g,
  278. color_.b_ * slot->color.b,
  279. color_.a_ * slot->color.a).ToUInt();
  280. if (attachment->type == SP_ATTACHMENT_REGION)
  281. {
  282. spRegionAttachment* region = (spRegionAttachment*)attachment;
  283. spRegionAttachment_computeWorldVertices(region, slot->bone, slotVertices, 0, 2);
  284. Vertex2D vertices[4];
  285. vertices[0].position_ = worldTransform * Vector3(slotVertices[SP_VERTEX_X1], slotVertices[SP_VERTEX_Y1]);
  286. vertices[1].position_ = worldTransform * Vector3(slotVertices[SP_VERTEX_X2], slotVertices[SP_VERTEX_Y2]);
  287. vertices[2].position_ = worldTransform * Vector3(slotVertices[SP_VERTEX_X3], slotVertices[SP_VERTEX_Y3]);
  288. vertices[3].position_ = worldTransform * Vector3(slotVertices[SP_VERTEX_X4], slotVertices[SP_VERTEX_Y4]);
  289. vertices[0].color_ = color;
  290. vertices[1].color_ = color;
  291. vertices[2].color_ = color;
  292. vertices[3].color_ = color;
  293. vertices[0].uv_ = Vector2(region->uvs[SP_VERTEX_X1], region->uvs[SP_VERTEX_Y1]);
  294. vertices[1].uv_ = Vector2(region->uvs[SP_VERTEX_X2], region->uvs[SP_VERTEX_Y2]);
  295. vertices[2].uv_ = Vector2(region->uvs[SP_VERTEX_X3], region->uvs[SP_VERTEX_Y3]);
  296. vertices[3].uv_ = Vector2(region->uvs[SP_VERTEX_X4], region->uvs[SP_VERTEX_Y4]);
  297. sourceBatches_[0].vertices_.Push(vertices[0]);
  298. sourceBatches_[0].vertices_.Push(vertices[1]);
  299. sourceBatches_[0].vertices_.Push(vertices[2]);
  300. sourceBatches_[0].vertices_.Push(vertices[3]);
  301. }
  302. else if (attachment->type == SP_ATTACHMENT_MESH || attachment->type == SP_ATTACHMENT_LINKED_MESH)
  303. {
  304. spMeshAttachment* mesh = (spMeshAttachment*)attachment;
  305. if (mesh->super.verticesCount > SLOT_VERTEX_COUNT_MAX)
  306. continue;
  307. spVertexAttachment_computeWorldVertices(&mesh->super, slot, 0, mesh->super.verticesCount, slotVertices, 0, 2);
  308. Vertex2D vertex;
  309. vertex.color_ = color;
  310. for (int j = 0; j < mesh->trianglesCount; ++j)
  311. {
  312. int index = mesh->triangles[j] << 1;
  313. vertex.position_ = worldTransform * Vector3(slotVertices[index], slotVertices[index + 1]);
  314. vertex.uv_ = Vector2(mesh->uvs[index], mesh->uvs[index + 1]);
  315. sourceBatches_[0].vertices_.Push(vertex);
  316. // Add padding vertex
  317. if (j % 3 == 2)
  318. sourceBatches_[0].vertices_.Push(vertex);
  319. }
  320. }
  321. }
  322. }
  323. #endif
  324. void AnimatedSprite2D::SetSpriterAnimation()
  325. {
  326. if (!spriterInstance_)
  327. spriterInstance_ = new Spriter::SpriterInstance(this, animationSet_->GetSpriterData());
  328. // Use entity is empty first entity
  329. if (entity_.Empty())
  330. entity_ = animationSet_->GetSpriterData()->entities_[0]->name_;
  331. if (!spriterInstance_->SetEntity(entity_.CString()))
  332. {
  333. URHO3D_LOGERROR("Set entity failed");
  334. return;
  335. }
  336. if (!spriterInstance_->SetAnimation(animationName_.CString(), (Spriter::LoopMode)loopMode_))
  337. {
  338. URHO3D_LOGERROR("Set animation failed");
  339. return;
  340. }
  341. UpdateAnimation(0.0f);
  342. MarkNetworkUpdate();
  343. }
  344. void AnimatedSprite2D::UpdateSpriterAnimation(float timeStep)
  345. {
  346. spriterInstance_->Update(timeStep * speed_);
  347. sourceBatchesDirty_ = true;
  348. worldBoundingBoxDirty_ = true;
  349. }
  350. void AnimatedSprite2D::UpdateSourceBatchesSpriter()
  351. {
  352. const Matrix3x4& nodeWorldTransform = GetNode()->GetWorldTransform();
  353. Vector<Vertex2D>& vertices = sourceBatches_[0].vertices_;
  354. vertices.Clear();
  355. Rect drawRect;
  356. Rect textureRect;
  357. unsigned color = color_.ToUInt();
  358. Vertex2D vertex0;
  359. Vertex2D vertex1;
  360. Vertex2D vertex2;
  361. Vertex2D vertex3;
  362. const PODVector<Spriter::SpatialTimelineKey*>& timelineKeys = spriterInstance_->GetTimelineKeys();
  363. for (unsigned i = 0; i < timelineKeys.Size(); ++i)
  364. {
  365. if (timelineKeys[i]->GetObjectType() != Spriter::SPRITE)
  366. continue;
  367. auto* timelineKey = (Spriter::SpriteTimelineKey*)timelineKeys[i];
  368. Spriter::SpatialInfo& info = timelineKey->info_;
  369. Vector3 position(info.x_, info.y_, 0.0f);
  370. if (flipX_)
  371. position.x_ = -position.x_;
  372. if (flipY_)
  373. position.y_ = -position.y_;
  374. float angle = info.angle_;
  375. if (flipX_ != flipY_)
  376. angle = -angle;
  377. Matrix3x4 localTransform(position * PIXEL_SIZE,
  378. Quaternion(angle),
  379. Vector3(info.scaleX_, info.scaleY_, 1.0f));
  380. Matrix3x4 worldTransform = nodeWorldTransform * localTransform;
  381. Sprite2D* sprite = animationSet_->GetSpriterFileSprite(timelineKey->folderId_, timelineKey->fileId_);
  382. if (!sprite)
  383. return;
  384. if (timelineKey->useDefaultPivot_)
  385. sprite->GetDrawRectangle(drawRect, flipX_, flipY_);
  386. else
  387. sprite->GetDrawRectangle(drawRect, Vector2(timelineKey->pivotX_, timelineKey->pivotY_), flipX_, flipY_);
  388. if (!sprite->GetTextureRectangle(textureRect, flipX_, flipY_))
  389. return;
  390. vertex0.position_ = worldTransform * Vector3(drawRect.min_.x_, drawRect.min_.y_, 0.0f);
  391. vertex1.position_ = worldTransform * Vector3(drawRect.min_.x_, drawRect.max_.y_, 0.0f);
  392. vertex2.position_ = worldTransform * Vector3(drawRect.max_.x_, drawRect.max_.y_, 0.0f);
  393. vertex3.position_ = worldTransform * Vector3(drawRect.max_.x_, drawRect.min_.y_, 0.0f);
  394. vertex0.uv_ = textureRect.min_;
  395. vertex1.uv_ = Vector2(textureRect.min_.x_, textureRect.max_.y_);
  396. vertex2.uv_ = textureRect.max_;
  397. vertex3.uv_ = Vector2(textureRect.max_.x_, textureRect.min_.y_);
  398. Color finalColor;
  399. finalColor.FromUInt(color);
  400. finalColor.a_ = info.alpha_;
  401. vertex0.color_ = vertex1.color_ = vertex2.color_ = vertex3.color_ = finalColor.ToUInt();
  402. vertices.Push(vertex0);
  403. vertices.Push(vertex1);
  404. vertices.Push(vertex2);
  405. vertices.Push(vertex3);
  406. }
  407. }
  408. void AnimatedSprite2D::Dispose()
  409. {
  410. #ifdef URHO3D_SPINE
  411. if (animationState_)
  412. {
  413. spAnimationState_dispose(animationState_);
  414. animationState_ = 0;
  415. }
  416. if (animationStateData_)
  417. {
  418. spAnimationStateData_dispose(animationStateData_);
  419. animationStateData_ = 0;
  420. }
  421. if (skeleton_)
  422. {
  423. spSkeleton_dispose(skeleton_);
  424. skeleton_ = 0;
  425. }
  426. #endif
  427. spriterInstance_.Reset();
  428. }
  429. }