AssetBlendTrack.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <AzCore/Serialization/SerializeContext.h>
  9. #include <AzFramework/StringFunc/StringFunc.h>
  10. #include <Maestro/Types/AssetBlends.h>
  11. #include <AnimKey.h>
  12. #include <Maestro/Types/AssetBlendKey.h>
  13. #include "AssetBlendTrack.h"
  14. #include "Maestro/Types/AnimValueType.h"
  15. #define LOOP_TRANSITION_TIME 1.0f
  16. //////////////////////////////////////////////////////////////////////////
  17. /// @deprecated Serialization for Sequence data in Component Entity Sequences now occurs through AZ::SerializeContext and the Sequence Component
  18. bool CAssetBlendTrack::Serialize(XmlNodeRef& xmlNode, bool bLoading, bool bLoadEmptyTracks)
  19. {
  20. return TAnimTrack<AZ::IAssetBlendKey>::Serialize(xmlNode, bLoading, bLoadEmptyTracks);
  21. }
  22. void CAssetBlendTrack::SerializeKey(AZ::IAssetBlendKey& key, XmlNodeRef& keyNode, bool bLoading)
  23. {
  24. if (bLoading)
  25. {
  26. // Read the AssetId made up of the guid and the sub id.
  27. key.m_assetId.SetInvalid();
  28. const char* assetIdGuidStr = keyNode->getAttr("assetIdGuid");
  29. if (assetIdGuidStr != nullptr && assetIdGuidStr[0] != 0)
  30. {
  31. AZ::Uuid guid(assetIdGuidStr, strlen(assetIdGuidStr));
  32. AZ::u32 assetIdSubId = 0;
  33. keyNode->getAttr("assetIdSubId", assetIdSubId);
  34. key.m_assetId = AZ::Data::AssetId(guid, assetIdSubId);
  35. }
  36. const char* descriptionIdStr = keyNode->getAttr("description");
  37. if (descriptionIdStr != nullptr)
  38. {
  39. key.m_description = descriptionIdStr;
  40. }
  41. key.m_duration = 0;
  42. key.m_endTime = 0;
  43. key.m_startTime = 0;
  44. key.m_bLoop = false;
  45. key.m_speed = 1;
  46. keyNode->getAttr("length", key.m_duration);
  47. keyNode->getAttr("end", key.m_endTime);
  48. keyNode->getAttr("speed", key.m_speed);
  49. keyNode->getAttr("loop", key.m_bLoop);
  50. keyNode->getAttr("start", key.m_startTime);
  51. keyNode->getAttr("blendInTime", key.m_blendInTime);
  52. keyNode->getAttr("blendOutTime", key.m_blendOutTime);
  53. }
  54. else
  55. {
  56. if (key.m_assetId.IsValid())
  57. {
  58. XmlString temp = key.m_assetId.m_guid.ToString<AZStd::string>().c_str();
  59. keyNode->setAttr("assetIdGuid", temp);
  60. keyNode->setAttr("assetIdSubId", key.m_assetId.m_subId);
  61. }
  62. if (!key.m_description.empty())
  63. {
  64. XmlString temp = key.m_description.c_str();
  65. keyNode->setAttr("description", temp);
  66. }
  67. if (key.m_duration > 0)
  68. {
  69. keyNode->setAttr("length", key.m_duration);
  70. }
  71. if (key.m_endTime > 0)
  72. {
  73. keyNode->setAttr("end", key.m_endTime);
  74. }
  75. if (key.m_speed != 1)
  76. {
  77. keyNode->setAttr("speed", key.m_speed);
  78. }
  79. if (key.m_bLoop)
  80. {
  81. keyNode->setAttr("loop", key.m_bLoop);
  82. }
  83. if (key.m_startTime != 0)
  84. {
  85. keyNode->setAttr("start", key.m_startTime);
  86. }
  87. if (key.m_blendInTime != 0)
  88. {
  89. keyNode->setAttr("blendInTime", key.m_blendInTime);
  90. }
  91. if (key.m_blendOutTime != 0)
  92. {
  93. keyNode->setAttr("blendOutTime", key.m_blendOutTime);
  94. }
  95. }
  96. }
  97. void CAssetBlendTrack::GetKeyInfo(int key, const char*& description, float& duration)
  98. {
  99. assert(key >= 0 && key < (int)m_keys.size());
  100. CheckValid();
  101. description = 0;
  102. duration = 0;
  103. if (m_keys[key].m_assetId.IsValid())
  104. {
  105. description = m_keys[key].m_description.c_str();
  106. if (m_keys[key].m_bLoop)
  107. {
  108. float lastTime = m_timeRange.end;
  109. if (key + 1 < (int)m_keys.size())
  110. {
  111. lastTime = m_keys[key + 1].time;
  112. }
  113. // duration is unlimited but cannot last past end of track or time of next key on track.
  114. duration = lastTime - m_keys[key].time;
  115. }
  116. else
  117. {
  118. if (m_keys[key].m_speed == 0)
  119. {
  120. m_keys[key].m_speed = 1.0f;
  121. }
  122. duration = m_keys[key].GetActualDuration();
  123. }
  124. }
  125. }
  126. float CAssetBlendTrack::GetKeyDuration(int key) const
  127. {
  128. assert(key >= 0 && key < (int)m_keys.size());
  129. const float EPSILON = 0.001f;
  130. if (m_keys[key].m_bLoop)
  131. {
  132. float lastTime = m_timeRange.end;
  133. if (key + 1 < (int)m_keys.size())
  134. {
  135. // EPSILON is required to ensure the correct ordering when getting nearest keys.
  136. lastTime = m_keys[key + 1].time + std::min(LOOP_TRANSITION_TIME,
  137. GetKeyDuration(key + 1) - EPSILON);
  138. }
  139. // duration is unlimited but cannot last past end of track or time of next key on track.
  140. return std::max(lastTime - m_keys[key].time, 0.0f);
  141. }
  142. else
  143. {
  144. return m_keys[key].GetActualDuration();
  145. }
  146. }
  147. //////////////////////////////////////////////////////////////////////////
  148. static bool AssetBlendTrackVersionConverter(
  149. AZ::SerializeContext& serializeContext,
  150. AZ::SerializeContext::DataElementNode& rootElement)
  151. {
  152. if (rootElement.GetVersion() < 3)
  153. {
  154. rootElement.AddElement(serializeContext, "BaseClass1", azrtti_typeid<IAnimTrack>());
  155. }
  156. return true;
  157. }
  158. template<>
  159. inline void TAnimTrack<AZ::IAssetBlendKey>::Reflect(AZ::ReflectContext* context)
  160. {
  161. if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  162. {
  163. serializeContext->Class<TAnimTrack<AZ::IAssetBlendKey>, IAnimTrack>()
  164. ->Version(3, &AssetBlendTrackVersionConverter)
  165. ->Field("Flags", &TAnimTrack<AZ::IAssetBlendKey>::m_flags)
  166. ->Field("Range", &TAnimTrack<AZ::IAssetBlendKey>::m_timeRange)
  167. ->Field("ParamType", &TAnimTrack<AZ::IAssetBlendKey>::m_nParamType)
  168. ->Field("Keys", &TAnimTrack<AZ::IAssetBlendKey>::m_keys)
  169. ->Field("Id", &TAnimTrack<AZ::IAssetBlendKey>::m_id);
  170. }
  171. }
  172. //////////////////////////////////////////////////////////////////////////
  173. AnimValueType CAssetBlendTrack::GetValueType()
  174. {
  175. return AnimValueType::AssetBlend;
  176. }
  177. //////////////////////////////////////////////////////////////////////////
  178. void CAssetBlendTrack::GetValue(float time, Maestro::AssetBlends<AZ::Data::AssetData>& value)
  179. {
  180. // Start by clearing all the assets.
  181. m_assetBlend.m_assetBlends.clear();
  182. // Keep track of the nearest keys to be used if not key is found at the input time.
  183. bool foundPreviousKey = false;
  184. bool foundNextKey = false;
  185. float previousKeyTimeDistance = FLT_MAX;
  186. float nextKeyTimeDistance = FLT_MAX;
  187. AZ::IAssetBlendKey previousKey;
  188. AZ::IAssetBlendKey nextKey;
  189. // Check each key to see if it has an asset that is in time range right now.
  190. for (auto key : m_keys)
  191. {
  192. float localTime = time - key.time;
  193. float segmentLength = key.GetValidEndTime() - key.m_startTime;
  194. if (key.IsInRange(time) && key.m_assetId.IsValid())
  195. {
  196. float segmentPercent = localTime / (segmentLength / key.GetValidSpeed());
  197. m_assetBlend.m_assetBlends.push_back(Maestro::AssetBlend(key.m_assetId, key.m_startTime + (segmentLength * segmentPercent), key.m_blendInTime, key.m_blendOutTime));
  198. }
  199. // Find the nearest previous key
  200. if (key.time < time)
  201. {
  202. if (abs(time - key.time) < previousKeyTimeDistance)
  203. {
  204. previousKeyTimeDistance = abs(time - key.time);
  205. previousKey = key;
  206. foundPreviousKey = true;
  207. }
  208. }
  209. // Find the nearest next key
  210. if (key.time > time)
  211. {
  212. if (abs(time - key.time) < nextKeyTimeDistance)
  213. {
  214. nextKeyTimeDistance = abs(time - key.time);
  215. nextKey = key;
  216. foundNextKey = true;
  217. }
  218. }
  219. }
  220. // If no asset blends have been added, and there is a key somewhere in the time line,
  221. // add the first or last frame of the key.
  222. if (m_assetBlend.m_assetBlends.empty() && !m_keys.empty())
  223. {
  224. // Check for looping the anim on the last key
  225. if (foundPreviousKey && previousKey.m_bLoop)
  226. {
  227. float localTime = time - previousKey.time;
  228. float segmentLength = previousKey.GetValidEndTime() - previousKey.m_startTime;
  229. float segmentPercent = static_cast<float>(fmod(localTime / (segmentLength / previousKey.GetValidSpeed()), 1.0));
  230. m_assetBlend.m_assetBlends.push_back(Maestro::AssetBlend(previousKey.m_assetId, previousKey.m_startTime + (segmentLength * segmentPercent), previousKey.m_blendInTime, previousKey.m_blendOutTime));
  231. }
  232. else
  233. {
  234. // Nothing set, just freeze frame on the first or last frame of the nearest animation
  235. if (!foundPreviousKey && foundNextKey)
  236. {
  237. m_assetBlend.m_assetBlends.push_back(Maestro::AssetBlend(nextKey.m_assetId, nextKey.m_startTime, nextKey.m_blendInTime, nextKey.m_blendOutTime));
  238. }
  239. else if (foundPreviousKey)
  240. {
  241. // Add a small fudge factor to the end time so the animation will be frozen on the last frame if we play off the end
  242. // of an animation and there is no other animation set on the tack.
  243. m_assetBlend.m_assetBlends.push_back(Maestro::AssetBlend(previousKey.m_assetId, previousKey.GetValidEndTime() - 0.001f, previousKey.m_blendInTime, previousKey.m_blendOutTime));
  244. }
  245. }
  246. }
  247. // Return the updated assetBlend
  248. value = m_assetBlend;
  249. }
  250. //////////////////////////////////////////////////////////////////////////
  251. void CAssetBlendTrack::SetDefaultValue(const Maestro::AssetBlends<AZ::Data::AssetData>& defaultValue)
  252. {
  253. m_defaultValue = defaultValue;
  254. Invalidate();
  255. }
  256. //////////////////////////////////////////////////////////////////////////
  257. float CAssetBlendTrack::GetEndTime() const
  258. {
  259. return m_timeRange.end;
  260. }
  261. //////////////////////////////////////////////////////////////////////////
  262. void CAssetBlendTrack::Reflect(AZ::ReflectContext* context)
  263. {
  264. TAnimTrack<AZ::IAssetBlendKey>::Reflect(context);
  265. if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  266. {
  267. serializeContext->Class<CAssetBlendTrack, TAnimTrack<AZ::IAssetBlendKey>>()
  268. ->Version(1);
  269. }
  270. }