3
0

EditorSequenceComponent.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  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 "EditorSequenceComponent.h"
  9. #include "EditorSequenceAgentComponent.h"
  10. #include "Objects/EntityObject.h"
  11. #include "TrackView/TrackViewSequenceManager.h"
  12. #include <Maestro/Types/AnimValueType.h>
  13. #include <Maestro/Types/SequenceType.h>
  14. #include <Maestro/Types/AnimNodeType.h>
  15. #include <AzCore/Math/Uuid.h>
  16. #include <AzToolsFramework/API/ToolsApplicationAPI.h>
  17. #include <AzCore/Serialization/SerializeContext.h>
  18. #include <AzCore/Serialization/EditContext.h>
  19. #include <AzCore/RTTI/BehaviorContext.h>
  20. #include <AzCore/Component/ComponentApplicationBus.h>
  21. #include <AzToolsFramework/API/ComponentEntityObjectBus.h>
  22. #include <Maestro/Bus/SequenceAgentComponentBus.h>
  23. #include <AzToolsFramework/API/EntityCompositionRequestBus.h>
  24. #include <AzToolsFramework/Entity/EditorEntityHelpers.h>
  25. namespace Maestro
  26. {
  27. /*static*/ AZ::ScriptTimePoint EditorSequenceComponent::s_lastPropertyRefreshTime;
  28. /*static*/ const double EditorSequenceComponent::s_refreshPeriodMilliseconds = 200.0; // 5 Hz refresh rate
  29. /*static*/ const uint32 EditorSequenceComponent::s_invalidSequenceId = std::numeric_limits<uint32>::max();
  30. namespace ClassConverters
  31. {
  32. static bool UpVersionAnimationData(AZ::SerializeContext&, AZ::SerializeContext::DataElementNode&);
  33. } // namespace ClassConverters
  34. ///////////////////////////////////////////////////////////////////////////////////////////////////////
  35. EditorSequenceComponent::EditorSequenceComponent()
  36. : m_sequenceId(s_invalidSequenceId)
  37. {
  38. }
  39. ///////////////////////////////////////////////////////////////////////////////////////////////////////
  40. EditorSequenceComponent::~EditorSequenceComponent()
  41. {
  42. bool isDuringUndo = false;
  43. AzToolsFramework::ToolsApplicationRequests::Bus::BroadcastResult(isDuringUndo, &AzToolsFramework::ToolsApplicationRequests::Bus::Events::IsDuringUndoRedo);
  44. // Don't RemoveEntityToAnimate if we are in the middle of an Undo event.
  45. // Doing so will create will mark this entity dirty and break the undo system.
  46. if (!isDuringUndo && m_sequence)
  47. {
  48. for (int i = m_sequence->GetNodeCount(); --i >= 0;)
  49. {
  50. IAnimNode* animNode = m_sequence->GetNode(i);
  51. if (animNode->GetType() == AnimNodeType::AzEntity)
  52. {
  53. RemoveEntityToAnimate(animNode->GetAzEntityId());
  54. }
  55. }
  56. }
  57. IEditor* editor = nullptr;
  58. AzToolsFramework::EditorRequests::Bus::BroadcastResult(editor, &AzToolsFramework::EditorRequests::Bus::Events::GetEditor);
  59. if (editor)
  60. {
  61. IAnimSequence* sequence = editor->GetMovieSystem()->FindSequenceById(m_sequenceId);
  62. ITrackViewSequenceManager* pSequenceManager = editor->GetSequenceManager();
  63. if (sequence && pSequenceManager && pSequenceManager->GetSequenceByEntityId(sequence->GetSequenceEntityId()))
  64. {
  65. pSequenceManager->OnDeleteSequenceEntity(sequence->GetSequenceEntityId());
  66. }
  67. }
  68. if (m_sequence)
  69. {
  70. m_sequence = nullptr;
  71. m_sequenceId = s_invalidSequenceId;
  72. }
  73. }
  74. ///////////////////////////////////////////////////////////////////////////////////////////////////////
  75. /*static*/ void EditorSequenceComponent::Reflect(AZ::ReflectContext* context)
  76. {
  77. AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
  78. if (serializeContext)
  79. {
  80. serializeContext->Class<AnimSerialize::AnimationData>()
  81. ->Field("SerializedString", &AnimSerialize::AnimationData::m_serializedData)
  82. ->Version(1, &ClassConverters::UpVersionAnimationData);
  83. serializeContext->Class<EditorSequenceComponent, EditorComponentBase>()
  84. ->Field("Sequence", &EditorSequenceComponent::m_sequence)
  85. ->Version(4);
  86. AZ::EditContext* editContext = serializeContext->GetEditContext();
  87. if (editContext)
  88. {
  89. editContext->Class<EditorSequenceComponent>(
  90. "Sequence", "Plays Cinematic Animations")
  91. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  92. ->Attribute(AZ::Edit::Attributes::Category, "Cinematics")
  93. ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/Sequence.png")
  94. ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Sequence.png")
  95. //->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game"))
  96. ->Attribute(AZ::Edit::Attributes::AddableByUser, false) // SequenceAgents are only added by TrackView
  97. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  98. ;
  99. }
  100. }
  101. AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context);
  102. if (behaviorContext)
  103. {
  104. behaviorContext->Class<EditorSequenceComponent>()->RequestBus("SequenceComponentRequestBus");
  105. }
  106. }
  107. ///////////////////////////////////////////////////////////////////////////////////////////////////////
  108. void EditorSequenceComponent::Init()
  109. {
  110. EditorComponentBase::Init();
  111. m_sequenceId = s_invalidSequenceId;
  112. IEditor* editor = nullptr;
  113. AzToolsFramework::EditorRequests::Bus::BroadcastResult(editor, &AzToolsFramework::EditorRequests::Bus::Events::GetEditor);
  114. if (editor)
  115. {
  116. bool sequenceWasDeserialized = false;
  117. if (m_sequence)
  118. {
  119. // m_sequence is already filled if the component it was deserialized - register it with Track View
  120. sequenceWasDeserialized = true;
  121. editor->GetSequenceManager()->OnCreateSequenceComponent(m_sequence);
  122. }
  123. else
  124. {
  125. // if m_sequence is NULL, we're creating a new sequence - request the creation from the Track view
  126. m_sequence = static_cast<CAnimSequence*>(editor->GetSequenceManager()->OnCreateSequenceObject(m_entity->GetName().c_str(), false, GetEntityId()));
  127. }
  128. if (m_sequence)
  129. {
  130. m_sequenceId = m_sequence->GetId();
  131. }
  132. if (sequenceWasDeserialized)
  133. {
  134. // Notify Trackview of the load
  135. ITrackViewSequence* trackViewSequence = editor->GetSequenceManager()->GetSequenceByEntityId(GetEntityId());
  136. if (trackViewSequence)
  137. {
  138. trackViewSequence->Load();
  139. }
  140. }
  141. }
  142. }
  143. ///////////////////////////////////////////////////////////////////////////////////////////////////////
  144. void EditorSequenceComponent::Activate()
  145. {
  146. EditorComponentBase::Activate();
  147. Maestro::EditorSequenceComponentRequestBus::Handler::BusConnect(GetEntityId());
  148. Maestro::SequenceComponentRequestBus::Handler::BusConnect(GetEntityId());
  149. IEditor* editor = nullptr;
  150. AzToolsFramework::EditorRequests::Bus::BroadcastResult(editor, &AzToolsFramework::EditorRequests::Bus::Events::GetEditor);
  151. if (editor)
  152. {
  153. editor->GetSequenceManager()->OnSequenceActivated(GetEntityId());
  154. }
  155. }
  156. ///////////////////////////////////////////////////////////////////////////////////////////////////////
  157. void EditorSequenceComponent::Deactivate()
  158. {
  159. Maestro::EditorSequenceComponentRequestBus::Handler::BusDisconnect();
  160. Maestro::SequenceComponentRequestBus::Handler::BusDisconnect();
  161. // disconnect from TickBus if we're connected (which would only happen if we deactivated during a pending property refresh)
  162. AZ::TickBus::Handler::BusDisconnect();
  163. EditorComponentBase::Deactivate();
  164. }
  165. ///////////////////////////////////////////////////////////////////////////////////////////////////////
  166. void EditorSequenceComponent::AddEntityToAnimate(AZ::EntityId entityToAnimate)
  167. {
  168. Maestro::EditorSequenceAgentComponent* agentComponent = nullptr;
  169. auto component = AzToolsFramework::FindComponent<EditorSequenceAgentComponent>::OnEntity(entityToAnimate);
  170. if (component)
  171. {
  172. agentComponent = static_cast<Maestro::EditorSequenceAgentComponent*>(component);
  173. }
  174. else
  175. {
  176. // #TODO LY-21846: Use "SequenceAgentComponentService" to find component, rather than specific component-type.
  177. auto addComponentResult = AzToolsFramework::AddComponents<EditorSequenceAgentComponent>::ToEntities(entityToAnimate);
  178. if (addComponentResult.IsSuccess())
  179. {
  180. if (!addComponentResult.GetValue()[entityToAnimate].m_componentsAdded.empty())
  181. {
  182. // We need to register our Entity and Component Ids with the SequenceAgentComponent so we can communicate over EBuses
  183. // with it. We can't do this registration over an EBus because we haven't registered with it yet - do it with pointers?
  184. // Is this safe?
  185. agentComponent = static_cast<Maestro::EditorSequenceAgentComponent*>(
  186. addComponentResult.GetValue()[entityToAnimate].m_componentsAdded[0]);
  187. }
  188. else
  189. {
  190. AZ_Assert(
  191. !addComponentResult.GetValue()[entityToAnimate].m_componentsAdded.empty(),
  192. "Add component result was successful, but the EditorSequenceAgentComponent wasn't added. "
  193. "This can happen if the entity id isn't found for some reason: entity id = %s",
  194. entityToAnimate.ToString().c_str());
  195. }
  196. }
  197. }
  198. AZ_Assert(agentComponent, "EditorSequenceComponent::AddEntityToAnimate unable to create or find sequenceAgentComponent.");
  199. // Notify the SequenceAgentComponent that we're connected to it - after this call, all communication with the Agent is over an EBus
  200. if (agentComponent)
  201. {
  202. agentComponent->ConnectSequence(GetEntityId());
  203. }
  204. }
  205. ///////////////////////////////////////////////////////////////////////////////////////////////////////
  206. void EditorSequenceComponent::RemoveEntityToAnimate(AZ::EntityId removedEntityId)
  207. {
  208. const Maestro::SequenceAgentEventBusId ebusId(GetEntityId(), removedEntityId);
  209. // Notify the SequenceAgentComponent that we're disconnecting from it
  210. Maestro::SequenceAgentComponentRequestBus::Event(ebusId, &Maestro::SequenceAgentComponentRequestBus::Events::DisconnectSequence);
  211. }
  212. ///////////////////////////////////////////////////////////////////////////////////////////////////////
  213. void EditorSequenceComponent::GetAllAnimatablePropertiesForComponent(IAnimNode::AnimParamInfos& properties, AZ::EntityId animatedEntityId, AZ::ComponentId componentId)
  214. {
  215. const Maestro::SequenceAgentEventBusId ebusId(GetEntityId(), animatedEntityId);
  216. Maestro::EditorSequenceAgentComponentRequestBus::Event(ebusId, &Maestro::EditorSequenceAgentComponentRequestBus::Events::GetAllAnimatableProperties, properties, componentId);
  217. }
  218. ///////////////////////////////////////////////////////////////////////////////////////////////////////
  219. void EditorSequenceComponent::GetAnimatableComponents(AZStd::vector<AZ::ComponentId>& componentIds, AZ::EntityId animatedEntityId)
  220. {
  221. const Maestro::SequenceAgentEventBusId ebusId(GetEntityId(), animatedEntityId);
  222. Maestro::EditorSequenceAgentComponentRequestBus::Event(
  223. ebusId, &Maestro::EditorSequenceAgentComponentRequestBus::Events::GetAnimatableComponents, componentIds);
  224. }
  225. ///////////////////////////////////////////////////////////////////////////////////////////////////////
  226. AZ::Uuid EditorSequenceComponent::GetAnimatedAddressTypeId(const AZ::EntityId& animatedEntityId, const Maestro::SequenceComponentRequests::AnimatablePropertyAddress& animatableAddress)
  227. {
  228. AZ::Uuid typeId = AZ::Uuid::CreateNull();
  229. const Maestro::SequenceAgentEventBusId ebusId(GetEntityId(), animatedEntityId);
  230. Maestro::SequenceAgentComponentRequestBus::EventResult(typeId, ebusId, &Maestro::SequenceAgentComponentRequestBus::Events::GetAnimatedAddressTypeId, animatableAddress);
  231. return typeId;
  232. }
  233. ///////////////////////////////////////////////////////////////////////////////////////////////////////
  234. void EditorSequenceComponent::GetAssetDuration(AnimatedValue& returnValue, const AZ::EntityId& animatedEntityId, AZ::ComponentId componentId, const AZ::Data::AssetId& assetId)
  235. {
  236. const Maestro::SequenceAgentEventBusId ebusId(GetEntityId(), animatedEntityId);
  237. Maestro::SequenceAgentComponentRequestBus::Event(
  238. ebusId, &Maestro::SequenceAgentComponentRequestBus::Events::GetAssetDuration, returnValue, componentId, assetId);
  239. }
  240. ///////////////////////////////////////////////////////////////////////////////////////////////////////
  241. void EditorSequenceComponent::BuildGameEntity(AZ::Entity* gameEntity)
  242. {
  243. SequenceComponent *gameSequenceComponent = gameEntity->CreateComponent<SequenceComponent>();
  244. gameSequenceComponent->m_sequence = m_sequence;
  245. }
  246. ///////////////////////////////////////////////////////////////////////////////////////////////////////
  247. AnimValueType EditorSequenceComponent::GetValueType([[maybe_unused]] const AZStd::string& animatableAddress)
  248. {
  249. // TODO: look up type from BehaviorContext Property
  250. return AnimValueType::Float;
  251. }
  252. ///////////////////////////////////////////////////////////////////////////////////////////////////////
  253. bool EditorSequenceComponent::SetAnimatedPropertyValue(const AZ::EntityId& animatedEntityId, const Maestro::SequenceComponentRequests::AnimatablePropertyAddress& animatableAddress, const AnimatedValue& value)
  254. {
  255. const Maestro::SequenceAgentEventBusId ebusId(GetEntityId(), animatedEntityId);
  256. bool changed = false;
  257. bool animatedEntityIsSelected = false;
  258. // put this component on the TickBus to refresh propertyGrids if it is Selected (and hence it's values will be shown in the EntityInspector)
  259. AzToolsFramework::ToolsApplicationRequests::Bus::BroadcastResult(animatedEntityIsSelected, &AzToolsFramework::ToolsApplicationRequests::Bus::Events::IsSelected, animatedEntityId);
  260. if (animatedEntityIsSelected && !AZ::TickBus::Handler::BusIsConnected())
  261. {
  262. AZ::TickBus::Handler::BusConnect();
  263. }
  264. Maestro::SequenceAgentComponentRequestBus::EventResult(
  265. changed, ebusId, &Maestro::SequenceAgentComponentRequestBus::Events::SetAnimatedPropertyValue, animatableAddress, value);
  266. return changed;
  267. }
  268. void EditorSequenceComponent::OnTick([[maybe_unused]] float deltaTime, AZ::ScriptTimePoint time)
  269. {
  270. // refresh the property displays at a lower refresh rate
  271. if ((time.GetMilliseconds() - s_lastPropertyRefreshTime.GetMilliseconds()) > s_refreshPeriodMilliseconds)
  272. {
  273. s_lastPropertyRefreshTime = time;
  274. // refresh
  275. AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast(&AzToolsFramework::ToolsApplicationEvents::Bus::Events::InvalidatePropertyDisplay, AzToolsFramework::Refresh_Values);
  276. // disconnect from tick bus now that we've refreshed
  277. AZ::TickBus::Handler::BusDisconnect();
  278. }
  279. }
  280. ///////////////////////////////////////////////////////////////////////////////////////////////////////
  281. void EditorSequenceComponent::GetAnimatedPropertyValue(AnimatedValue& returnValue, const AZ::EntityId& animatedEntityId, const Maestro::SequenceComponentRequests::AnimatablePropertyAddress& animatableAddress)
  282. {
  283. const Maestro::SequenceAgentEventBusId ebusId(GetEntityId(), animatedEntityId);
  284. Maestro::SequenceAgentComponentRequestBus::Event(
  285. ebusId, &Maestro::SequenceAgentComponentRequestBus::Events::GetAnimatedPropertyValue, returnValue, animatableAddress);
  286. }
  287. ///////////////////////////////////////////////////////////////////////////////////////////////////////
  288. bool EditorSequenceComponent::MarkEntityAsDirty() const
  289. {
  290. bool retSuccess = false;
  291. AZ::Entity* entity = nullptr;
  292. AZ::ComponentApplicationBus::BroadcastResult(entity, &AZ::ComponentApplicationBus::Events::FindEntity, GetEntityId());
  293. if (entity)
  294. {
  295. CEntityObject* entityObject = nullptr;
  296. AzToolsFramework::ComponentEntityEditorRequestBus::EventResult(
  297. entityObject, GetEntityId(), &AzToolsFramework::ComponentEntityEditorRequestBus::Events::GetSandboxObject);
  298. if (entityObject)
  299. {
  300. entityObject->SetModified(false);
  301. retSuccess = true;
  302. }
  303. }
  304. return retSuccess;
  305. }
  306. //=========================================================================
  307. namespace ClassConverters
  308. {
  309. // recursively traverses XML tree rooted at node converting transform nodes. Returns true if any node was converted.
  310. static bool convertTransformXMLNodes(XmlNodeRef node)
  311. {
  312. bool nodeConverted = false;
  313. // recurse through children
  314. for (int i = node->getChildCount(); --i >= 0;)
  315. {
  316. if (convertTransformXMLNodes(node->getChild(i)))
  317. {
  318. nodeConverted = true;
  319. }
  320. }
  321. XmlString nodeType;
  322. if (node->isTag("Node") && node->getAttr("Type", nodeType) && nodeType == "Component")
  323. {
  324. XmlString componentTypeId;
  325. if (node->getAttr("ComponentTypeId", componentTypeId) && componentTypeId == "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0}") // Type Uuid AZ::EditorTransformComponentTypeId
  326. {
  327. static const char* paramTypeName = "paramType";
  328. static const char* paramUserValueName = "paramUserValue";
  329. static const char* virtualPropertyName = "virtualPropertyName";
  330. // go through child nodes. Convert previous Position, Rotation or Scale tracks ByString to enumerated param types
  331. for (const XmlNodeRef& childNode : node)
  332. {
  333. XmlString paramType;
  334. if (childNode->isTag("Track") && childNode->getAttr(paramTypeName, paramType) && paramType == "ByString")
  335. {
  336. XmlString paramUserValue;
  337. if (childNode->getAttr(paramUserValueName, paramUserValue) && paramUserValue == "Position")
  338. {
  339. childNode->setAttr(paramTypeName, "Position");
  340. childNode->setAttr(virtualPropertyName, "Position");
  341. childNode->delAttr(paramUserValueName);
  342. nodeConverted = true;
  343. }
  344. else if (childNode->getAttr(paramUserValueName, paramUserValue) && paramUserValue == "Rotation")
  345. {
  346. childNode->setAttr(paramTypeName, "Rotation");
  347. childNode->setAttr(virtualPropertyName, "Rotation");
  348. childNode->delAttr(paramUserValueName);
  349. nodeConverted = true;
  350. }
  351. else if (childNode->getAttr(paramUserValueName, paramUserValue) && paramUserValue == "Scale")
  352. {
  353. childNode->setAttr(paramTypeName, "Scale");
  354. childNode->setAttr(virtualPropertyName, "Scale");
  355. childNode->delAttr(paramUserValueName);
  356. nodeConverted = true;
  357. }
  358. }
  359. }
  360. }
  361. }
  362. return nodeConverted;
  363. }
  364. static bool UpVersionAnimationData(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement)
  365. {
  366. if (classElement.GetVersion() == 0)
  367. {
  368. // upgrade V0 to V1 - change "Position", "Rotation", "Scale" anim params in Transform Component Nodes from AnimParamType::ByString to
  369. // AnimParamType::Position, AnimParamType::Rotation, AnimParamType::Scale respectively
  370. int serializedAnimStringIdx = classElement.FindElement(AZ::Crc32("SerializedString"));
  371. if (serializedAnimStringIdx == -1)
  372. {
  373. AZ_Error("Serialization", false, "Failed to find 'SerializedString' element.");
  374. return false;
  375. }
  376. AZStd::string serializedAnimString;
  377. classElement.GetSubElement(serializedAnimStringIdx).GetData(serializedAnimString);
  378. const char* buffer = serializedAnimString.c_str();
  379. size_t size = serializedAnimString.length();
  380. if (size > 0)
  381. {
  382. XmlNodeRef xmlArchive = gEnv->pSystem->LoadXmlFromBuffer(buffer, size);
  383. // recursively traverse and convert through all nodes
  384. if (convertTransformXMLNodes(xmlArchive))
  385. {
  386. // if a node was converted, replace the classElement Data with the converted XML
  387. serializedAnimString = xmlArchive->getXML();
  388. classElement.GetSubElement(serializedAnimStringIdx).SetData(context, serializedAnimString);
  389. }
  390. }
  391. }
  392. return true;
  393. }
  394. } // namespace ClassConverters
  395. } // namespace Maestro