EditorSequenceComponent.cpp 23 KB

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