Animatable.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554
  1. // Copyright (c) 2008-2023 the Urho3D project
  2. // License: MIT
  3. #include "../Precompiled.h"
  4. #include "../Core/Context.h"
  5. #include "../IO/Log.h"
  6. #include "../Resource/ResourceCache.h"
  7. #include "../Resource/JSONValue.h"
  8. #include "../Resource/XMLElement.h"
  9. #include "../Scene/Animatable.h"
  10. #include "../Scene/ObjectAnimation.h"
  11. #include "../Scene/SceneEvents.h"
  12. #include "../Scene/ValueAnimation.h"
  13. #include "../DebugNew.h"
  14. namespace Urho3D
  15. {
  16. extern const char* wrapModeNames[];
  17. AttributeAnimationInfo::AttributeAnimationInfo(Animatable* animatable, const AttributeInfo& attributeInfo,
  18. ValueAnimation* attributeAnimation, WrapMode wrapMode, float speed) :
  19. ValueAnimationInfo(animatable, attributeAnimation, wrapMode, speed),
  20. attributeInfo_(attributeInfo)
  21. {
  22. }
  23. AttributeAnimationInfo::AttributeAnimationInfo(const AttributeAnimationInfo& other) = default;
  24. AttributeAnimationInfo::~AttributeAnimationInfo() = default;
  25. void AttributeAnimationInfo::ApplyValue(const Variant& newValue)
  26. {
  27. auto* animatable = static_cast<Animatable*>(target_.Get());
  28. if (animatable)
  29. {
  30. animatable->OnSetAttribute(attributeInfo_, newValue);
  31. animatable->ApplyAttributes();
  32. }
  33. }
  34. Animatable::Animatable(Context* context) :
  35. Serializable(context),
  36. animationEnabled_(true)
  37. {
  38. }
  39. Animatable::~Animatable() = default;
  40. void Animatable::RegisterObject(Context* context)
  41. {
  42. URHO3D_ACCESSOR_ATTRIBUTE("Object Animation", GetObjectAnimationAttr, SetObjectAnimationAttr,
  43. ResourceRef(ObjectAnimation::GetTypeStatic()), AM_DEFAULT);
  44. }
  45. bool Animatable::LoadXML(const XMLElement& source)
  46. {
  47. if (!Serializable::LoadXML(source))
  48. return false;
  49. SetObjectAnimation(nullptr);
  50. attributeAnimationInfos_.Clear();
  51. XMLElement elem = source.GetChild("objectanimation");
  52. if (elem)
  53. {
  54. SharedPtr<ObjectAnimation> objectAnimation(new ObjectAnimation(context_));
  55. if (!objectAnimation->LoadXML(elem))
  56. return false;
  57. SetObjectAnimation(objectAnimation);
  58. }
  59. elem = source.GetChild("attributeanimation");
  60. while (elem)
  61. {
  62. String name = elem.GetAttribute("name");
  63. SharedPtr<ValueAnimation> attributeAnimation(new ValueAnimation(context_));
  64. if (!attributeAnimation->LoadXML(elem))
  65. return false;
  66. String wrapModeString = source.GetAttribute("wrapmode");
  67. WrapMode wrapMode = WM_LOOP;
  68. for (int i = 0; i <= WM_CLAMP; ++i)
  69. {
  70. if (wrapModeString == wrapModeNames[i])
  71. {
  72. wrapMode = (WrapMode)i;
  73. break;
  74. }
  75. }
  76. float speed = elem.GetFloat("speed");
  77. SetAttributeAnimation(name, attributeAnimation, wrapMode, speed);
  78. elem = elem.GetNext("attributeanimation");
  79. }
  80. return true;
  81. }
  82. bool Animatable::LoadJSON(const JSONValue& source)
  83. {
  84. if (!Serializable::LoadJSON(source))
  85. return false;
  86. SetObjectAnimation(nullptr);
  87. attributeAnimationInfos_.Clear();
  88. JSONValue value = source.Get("objectanimation");
  89. if (!value.IsNull())
  90. {
  91. SharedPtr<ObjectAnimation> objectAnimation(new ObjectAnimation(context_));
  92. if (!objectAnimation->LoadJSON(value))
  93. return false;
  94. SetObjectAnimation(objectAnimation);
  95. }
  96. JSONValue attributeAnimationValue = source.Get("attributeanimation");
  97. if (attributeAnimationValue.IsNull())
  98. return true;
  99. if (!attributeAnimationValue.IsObject())
  100. {
  101. URHO3D_LOGWARNING("'attributeanimation' value is present in JSON data, but is not a JSON object; skipping it");
  102. return true;
  103. }
  104. const JSONObject& attributeAnimationObject = attributeAnimationValue.GetObject();
  105. for (JSONObject::ConstIterator it = attributeAnimationObject.Begin(); it != attributeAnimationObject.End(); it++)
  106. {
  107. String name = it->first_;
  108. JSONValue value = it->second_;
  109. SharedPtr<ValueAnimation> attributeAnimation(new ValueAnimation(context_));
  110. if (!attributeAnimation->LoadJSON(it->second_))
  111. return false;
  112. String wrapModeString = value.Get("wrapmode").GetString();
  113. WrapMode wrapMode = WM_LOOP;
  114. for (int i = 0; i <= WM_CLAMP; ++i)
  115. {
  116. if (wrapModeString == wrapModeNames[i])
  117. {
  118. wrapMode = (WrapMode)i;
  119. break;
  120. }
  121. }
  122. float speed = value.Get("speed").GetFloat();
  123. SetAttributeAnimation(name, attributeAnimation, wrapMode, speed);
  124. }
  125. return true;
  126. }
  127. bool Animatable::SaveXML(XMLElement& dest) const
  128. {
  129. if (!Serializable::SaveXML(dest))
  130. return false;
  131. // Object animation without name
  132. if (objectAnimation_ && objectAnimation_->GetName().Empty())
  133. {
  134. XMLElement elem = dest.CreateChild("objectanimation");
  135. if (!objectAnimation_->SaveXML(elem))
  136. return false;
  137. }
  138. for (HashMap<String, SharedPtr<AttributeAnimationInfo>>::ConstIterator i = attributeAnimationInfos_.Begin();
  139. i != attributeAnimationInfos_.End(); ++i)
  140. {
  141. ValueAnimation* attributeAnimation = i->second_->GetAnimation();
  142. if (attributeAnimation->GetOwner())
  143. continue;
  144. const AttributeInfo& attr = i->second_->GetAttributeInfo();
  145. XMLElement elem = dest.CreateChild("attributeanimation");
  146. elem.SetAttribute("name", attr.name_);
  147. if (!attributeAnimation->SaveXML(elem))
  148. return false;
  149. elem.SetAttribute("wrapmode", wrapModeNames[i->second_->GetWrapMode()]);
  150. elem.SetFloat("speed", i->second_->GetSpeed());
  151. }
  152. return true;
  153. }
  154. bool Animatable::SaveJSON(JSONValue& dest) const
  155. {
  156. if (!Serializable::SaveJSON(dest))
  157. return false;
  158. // Object animation without name
  159. if (objectAnimation_ && objectAnimation_->GetName().Empty())
  160. {
  161. JSONValue objectAnimationValue;
  162. if (!objectAnimation_->SaveJSON(objectAnimationValue))
  163. return false;
  164. dest.Set("objectanimation", objectAnimationValue);
  165. }
  166. JSONValue attributeAnimationValue;
  167. for (HashMap<String, SharedPtr<AttributeAnimationInfo>>::ConstIterator i = attributeAnimationInfos_.Begin();
  168. i != attributeAnimationInfos_.End(); ++i)
  169. {
  170. ValueAnimation* attributeAnimation = i->second_->GetAnimation();
  171. if (attributeAnimation->GetOwner())
  172. continue;
  173. const AttributeInfo& attr = i->second_->GetAttributeInfo();
  174. JSONValue attributeValue;
  175. attributeValue.Set("name", attr.name_);
  176. if (!attributeAnimation->SaveJSON(attributeValue))
  177. return false;
  178. attributeValue.Set("wrapmode", wrapModeNames[i->second_->GetWrapMode()]);
  179. attributeValue.Set("speed", (float) i->second_->GetSpeed());
  180. attributeAnimationValue.Set(attr.name_, attributeValue);
  181. }
  182. if (!attributeAnimationValue.IsNull())
  183. dest.Set("attributeanimation", attributeAnimationValue);
  184. return true;
  185. }
  186. void Animatable::SetAnimationEnabled(bool enable)
  187. {
  188. if (objectAnimation_)
  189. {
  190. // In object animation there may be targets in hierarchy. Set same enable/disable state in all
  191. HashSet<Animatable*> targets;
  192. const HashMap<String, SharedPtr<ValueAnimationInfo>>& infos = objectAnimation_->GetAttributeAnimationInfos();
  193. for (HashMap<String, SharedPtr<ValueAnimationInfo>>::ConstIterator i = infos.Begin(); i != infos.End(); ++i)
  194. {
  195. String outName;
  196. Animatable* target = FindAttributeAnimationTarget(i->first_, outName);
  197. if (target && target != this)
  198. targets.Insert(target);
  199. }
  200. for (HashSet<Animatable*>::Iterator i = targets.Begin(); i != targets.End(); ++i)
  201. (*i)->animationEnabled_ = enable;
  202. }
  203. animationEnabled_ = enable;
  204. }
  205. void Animatable::SetAnimationTime(float time)
  206. {
  207. if (objectAnimation_)
  208. {
  209. // In object animation there may be targets in hierarchy. Set same time in all
  210. const HashMap<String, SharedPtr<ValueAnimationInfo>>& infos = objectAnimation_->GetAttributeAnimationInfos();
  211. for (HashMap<String, SharedPtr<ValueAnimationInfo>>::ConstIterator i = infos.Begin(); i != infos.End(); ++i)
  212. {
  213. String outName;
  214. Animatable* target = FindAttributeAnimationTarget(i->first_, outName);
  215. if (target)
  216. target->SetAttributeAnimationTime(outName, time);
  217. }
  218. }
  219. else
  220. {
  221. for (HashMap<String, SharedPtr<AttributeAnimationInfo>>::ConstIterator i = attributeAnimationInfos_.Begin();
  222. i != attributeAnimationInfos_.End(); ++i)
  223. i->second_->SetTime(time);
  224. }
  225. }
  226. void Animatable::SetObjectAnimation(ObjectAnimation* objectAnimation)
  227. {
  228. if (objectAnimation == objectAnimation_)
  229. return;
  230. if (objectAnimation_)
  231. {
  232. OnObjectAnimationRemoved(objectAnimation_);
  233. UnsubscribeFromEvent(objectAnimation_, E_ATTRIBUTEANIMATIONADDED);
  234. UnsubscribeFromEvent(objectAnimation_, E_ATTRIBUTEANIMATIONREMOVED);
  235. }
  236. objectAnimation_ = objectAnimation;
  237. if (objectAnimation_)
  238. {
  239. OnObjectAnimationAdded(objectAnimation_);
  240. SubscribeToEvent(objectAnimation_, E_ATTRIBUTEANIMATIONADDED, URHO3D_HANDLER(Animatable, HandleAttributeAnimationAdded));
  241. SubscribeToEvent(objectAnimation_, E_ATTRIBUTEANIMATIONREMOVED, URHO3D_HANDLER(Animatable, HandleAttributeAnimationRemoved));
  242. }
  243. }
  244. void Animatable::SetAttributeAnimation(const String& name, ValueAnimation* attributeAnimation, WrapMode wrapMode, float speed)
  245. {
  246. AttributeAnimationInfo* info = GetAttributeAnimationInfo(name);
  247. if (attributeAnimation)
  248. {
  249. if (info && attributeAnimation == info->GetAnimation())
  250. {
  251. info->SetWrapMode(wrapMode);
  252. info->SetSpeed(speed);
  253. return;
  254. }
  255. // Get attribute info
  256. const AttributeInfo* attributeInfo = nullptr;
  257. if (info)
  258. attributeInfo = &info->GetAttributeInfo();
  259. else
  260. {
  261. const Vector<AttributeInfo>* attributes = GetAttributes();
  262. if (!attributes)
  263. {
  264. URHO3D_LOGERROR(GetTypeName() + " has no attributes");
  265. return;
  266. }
  267. for (Vector<AttributeInfo>::ConstIterator i = attributes->Begin(); i != attributes->End(); ++i)
  268. {
  269. if (name == (*i).name_)
  270. {
  271. attributeInfo = &(*i);
  272. break;
  273. }
  274. }
  275. }
  276. if (!attributeInfo)
  277. {
  278. URHO3D_LOGERROR("Invalid name: " + name);
  279. return;
  280. }
  281. // Check value type is same with attribute type
  282. if (attributeAnimation->GetValueType() != attributeInfo->type_)
  283. {
  284. URHO3D_LOGERROR("Invalid value type");
  285. return;
  286. }
  287. // Add network attribute to set
  288. if (attributeInfo->mode_ & AM_NET)
  289. animatedNetworkAttributes_.Insert(attributeInfo);
  290. attributeAnimationInfos_[name] = new AttributeAnimationInfo(this, *attributeInfo, attributeAnimation, wrapMode, speed);
  291. if (!info)
  292. OnAttributeAnimationAdded();
  293. }
  294. else
  295. {
  296. if (!info)
  297. return;
  298. // Remove network attribute from set
  299. if (info->GetAttributeInfo().mode_ & AM_NET)
  300. animatedNetworkAttributes_.Erase(&info->GetAttributeInfo());
  301. attributeAnimationInfos_.Erase(name);
  302. OnAttributeAnimationRemoved();
  303. }
  304. }
  305. void Animatable::SetAttributeAnimationWrapMode(const String& name, WrapMode wrapMode)
  306. {
  307. AttributeAnimationInfo* info = GetAttributeAnimationInfo(name);
  308. if (info)
  309. info->SetWrapMode(wrapMode);
  310. }
  311. void Animatable::SetAttributeAnimationSpeed(const String& name, float speed)
  312. {
  313. AttributeAnimationInfo* info = GetAttributeAnimationInfo(name);
  314. if (info)
  315. info->SetSpeed(speed);
  316. }
  317. void Animatable::SetAttributeAnimationTime(const String& name, float time)
  318. {
  319. AttributeAnimationInfo* info = GetAttributeAnimationInfo(name);
  320. if (info)
  321. info->SetTime(time);
  322. }
  323. void Animatable::RemoveObjectAnimation()
  324. {
  325. SetObjectAnimation(nullptr);
  326. }
  327. void Animatable::RemoveAttributeAnimation(const String& name)
  328. {
  329. SetAttributeAnimation(name, nullptr);
  330. }
  331. ObjectAnimation* Animatable::GetObjectAnimation() const
  332. {
  333. return objectAnimation_;
  334. }
  335. ValueAnimation* Animatable::GetAttributeAnimation(const String& name) const
  336. {
  337. const AttributeAnimationInfo* info = GetAttributeAnimationInfo(name);
  338. return info ? info->GetAnimation() : nullptr;
  339. }
  340. WrapMode Animatable::GetAttributeAnimationWrapMode(const String& name) const
  341. {
  342. const AttributeAnimationInfo* info = GetAttributeAnimationInfo(name);
  343. return info ? info->GetWrapMode() : WM_LOOP;
  344. }
  345. float Animatable::GetAttributeAnimationSpeed(const String& name) const
  346. {
  347. const AttributeAnimationInfo* info = GetAttributeAnimationInfo(name);
  348. return info ? info->GetSpeed() : 1.0f;
  349. }
  350. float Animatable::GetAttributeAnimationTime(const String& name) const
  351. {
  352. const AttributeAnimationInfo* info = GetAttributeAnimationInfo(name);
  353. return info ? info->GetTime() : 0.0f;
  354. }
  355. void Animatable::SetObjectAnimationAttr(const ResourceRef& value)
  356. {
  357. if (!value.name_.Empty())
  358. {
  359. auto* cache = GetSubsystem<ResourceCache>();
  360. SetObjectAnimation(cache->GetResource<ObjectAnimation>(value.name_));
  361. }
  362. }
  363. ResourceRef Animatable::GetObjectAnimationAttr() const
  364. {
  365. return GetResourceRef(objectAnimation_, ObjectAnimation::GetTypeStatic());
  366. }
  367. Animatable* Animatable::FindAttributeAnimationTarget(const String& name, String& outName)
  368. {
  369. // Base implementation only handles self
  370. outName = name;
  371. return this;
  372. }
  373. void Animatable::SetObjectAttributeAnimation(const String& name, ValueAnimation* attributeAnimation, WrapMode wrapMode, float speed)
  374. {
  375. String outName;
  376. Animatable* target = FindAttributeAnimationTarget(name, outName);
  377. if (target)
  378. target->SetAttributeAnimation(outName, attributeAnimation, wrapMode, speed);
  379. }
  380. void Animatable::OnObjectAnimationAdded(ObjectAnimation* objectAnimation)
  381. {
  382. if (!objectAnimation)
  383. return;
  384. // Set all attribute animations from the object animation
  385. const HashMap<String, SharedPtr<ValueAnimationInfo>>& attributeAnimationInfos = objectAnimation->GetAttributeAnimationInfos();
  386. for (HashMap<String, SharedPtr<ValueAnimationInfo>>::ConstIterator i = attributeAnimationInfos.Begin();
  387. i != attributeAnimationInfos.End(); ++i)
  388. {
  389. const String& name = i->first_;
  390. ValueAnimationInfo* info = i->second_;
  391. SetObjectAttributeAnimation(name, info->GetAnimation(), info->GetWrapMode(), info->GetSpeed());
  392. }
  393. }
  394. void Animatable::OnObjectAnimationRemoved(ObjectAnimation* objectAnimation)
  395. {
  396. if (!objectAnimation)
  397. return;
  398. // Just remove all attribute animations listed by the object animation
  399. const HashMap<String, SharedPtr<ValueAnimationInfo>>& infos = objectAnimation->GetAttributeAnimationInfos();
  400. for (HashMap<String, SharedPtr<ValueAnimationInfo>>::ConstIterator i = infos.Begin(); i != infos.End(); ++i)
  401. SetObjectAttributeAnimation(i->first_, nullptr, WM_LOOP, 1.0f);
  402. }
  403. void Animatable::UpdateAttributeAnimations(float timeStep)
  404. {
  405. if (!animationEnabled_)
  406. return;
  407. // Keep weak pointer to self to check for destruction caused by event handling
  408. WeakPtr<Animatable> self(this);
  409. Vector<String> finishedNames;
  410. for (HashMap<String, SharedPtr<AttributeAnimationInfo>>::ConstIterator i = attributeAnimationInfos_.Begin();
  411. i != attributeAnimationInfos_.End(); ++i)
  412. {
  413. bool finished = i->second_->Update(timeStep);
  414. // If self deleted as a result of an event sent during animation playback, nothing more to do
  415. if (self.Expired())
  416. return;
  417. if (finished)
  418. finishedNames.Push(i->second_->GetAttributeInfo().name_);
  419. }
  420. for (const String& finishedName : finishedNames)
  421. SetAttributeAnimation(finishedName, nullptr);
  422. }
  423. bool Animatable::IsAnimatedNetworkAttribute(const AttributeInfo& attrInfo) const
  424. {
  425. return animatedNetworkAttributes_.Find(&attrInfo) != animatedNetworkAttributes_.End();
  426. }
  427. AttributeAnimationInfo* Animatable::GetAttributeAnimationInfo(const String& name) const
  428. {
  429. HashMap<String, SharedPtr<AttributeAnimationInfo>>::ConstIterator i = attributeAnimationInfos_.Find(name);
  430. if (i != attributeAnimationInfos_.End())
  431. return i->second_;
  432. return nullptr;
  433. }
  434. void Animatable::HandleAttributeAnimationAdded(StringHash eventType, VariantMap& eventData)
  435. {
  436. if (!objectAnimation_)
  437. return;
  438. using namespace AttributeAnimationAdded;
  439. const String& name = eventData[P_ATTRIBUTEANIMATIONNAME].GetString();
  440. ValueAnimationInfo* info = objectAnimation_->GetAttributeAnimationInfo(name);
  441. if (!info)
  442. return;
  443. SetObjectAttributeAnimation(name, info->GetAnimation(), info->GetWrapMode(), info->GetSpeed());
  444. }
  445. void Animatable::HandleAttributeAnimationRemoved(StringHash eventType, VariantMap& eventData)
  446. {
  447. if (!objectAnimation_)
  448. return;
  449. using namespace AttributeAnimationRemoved;
  450. const String& name = eventData[P_ATTRIBUTEANIMATIONNAME].GetString();
  451. SetObjectAttributeAnimation(name, nullptr, WM_LOOP, 1.0f);
  452. }
  453. }