ContainerEntitySystemComponent.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  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 <AzToolsFramework/ContainerEntity/ContainerEntitySystemComponent.h>
  9. #include <AzCore/Component/TransformBus.h>
  10. #include <AzToolsFramework/API/ToolsApplicationAPI.h>
  11. #include <AzToolsFramework/API/ViewportEditorModeTrackerInterface.h>
  12. #include <AzToolsFramework/ContainerEntity/ContainerEntityNotificationBus.h>
  13. #include <AzToolsFramework/Prefab/PrefabEditorPreferences.h>
  14. #include <AzToolsFramework/Prefab/PrefabFocusPublicInterface.h>
  15. namespace AzToolsFramework
  16. {
  17. void ContainerEntitySystemComponent::Activate()
  18. {
  19. AZ::Interface<ContainerEntityInterface>::Register(this);
  20. EditorEntityContextNotificationBus::Handler::BusConnect();
  21. }
  22. void ContainerEntitySystemComponent::Deactivate()
  23. {
  24. EditorEntityContextNotificationBus::Handler::BusDisconnect();
  25. AZ::Interface<ContainerEntityInterface>::Unregister(this);
  26. }
  27. void ContainerEntitySystemComponent::Reflect(AZ::ReflectContext* context)
  28. {
  29. if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  30. {
  31. serializeContext->Class<ContainerEntitySystemComponent, AZ::Component>()->Version(1);
  32. }
  33. }
  34. void ContainerEntitySystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
  35. {
  36. provided.push_back(AZ_CRC_CE("ContainerEntityService"));
  37. }
  38. ContainerEntityOperationResult ContainerEntitySystemComponent::RegisterEntityAsContainer(AZ::EntityId entityId)
  39. {
  40. if (IsContainer(entityId))
  41. {
  42. return AZ::Failure(AZStd::string(
  43. "ContainerEntitySystemComponent error - trying to register entity as container twice."));
  44. }
  45. m_containers.insert(entityId);
  46. return AZ::Success();
  47. }
  48. ContainerEntityOperationResult ContainerEntitySystemComponent::UnregisterEntityAsContainer(AZ::EntityId entityId)
  49. {
  50. if (!IsContainer(entityId))
  51. {
  52. return AZ::Failure(AZStd::string(
  53. "ContainerEntitySystemComponent error - trying to unregister entity that is not a container."));
  54. }
  55. m_containers.erase(entityId);
  56. return AZ::Success();
  57. }
  58. bool ContainerEntitySystemComponent::IsContainer(AZ::EntityId entityId) const
  59. {
  60. return m_containers.contains(entityId);
  61. }
  62. ContainerEntityOperationResult ContainerEntitySystemComponent::SetContainerOpen(AZ::EntityId entityId, bool open)
  63. {
  64. if (!IsContainer(entityId))
  65. {
  66. return AZ::Failure(AZStd::string(
  67. "ContainerEntitySystemComponent error - cannot set open state of entity that was not registered as container."));
  68. }
  69. if(open)
  70. {
  71. m_openContainers.insert(entityId);
  72. }
  73. else
  74. {
  75. m_openContainers.erase(entityId);
  76. }
  77. ContainerEntityNotificationBus::Broadcast(&ContainerEntityNotificationBus::Events::OnContainerEntityStatusChanged, entityId, open);
  78. return AZ::Success();
  79. }
  80. bool ContainerEntitySystemComponent::IsContainerOpen(AZ::EntityId entityId) const
  81. {
  82. // Non-container entities behave the same as open containers. This saves the caller an additional check.
  83. if(!m_containers.contains(entityId))
  84. {
  85. return true;
  86. }
  87. // If the entity is a container, return its state.
  88. return m_openContainers.contains(entityId);
  89. }
  90. AZ::EntityId ContainerEntitySystemComponent::FindHighestSelectableEntity(AZ::EntityId entityId) const
  91. {
  92. if (!entityId.IsValid())
  93. {
  94. return entityId;
  95. }
  96. // is entity pick mode enabled?
  97. bool isEntityPickModeEnabled = false;
  98. if (auto viewportEditorModeTracker = AZ::Interface<AzToolsFramework::ViewportEditorModeTrackerInterface>::Get())
  99. {
  100. auto entityContextId = AzFramework::EntityContextId::CreateNull();
  101. EditorEntityContextRequestBus::BroadcastResult(entityContextId, &EditorEntityContextRequests::GetEditorEntityContextId);
  102. isEntityPickModeEnabled = viewportEditorModeTracker->GetViewportEditorModes({ entityContextId })
  103. ->IsModeActive(AzToolsFramework::ViewportEditorMode::Pick);
  104. }
  105. auto editorEntityContextId = AzFramework::EntityContextId::CreateNull();
  106. EditorEntityContextRequestBus::BroadcastResult(editorEntityContextId, &EditorEntityContextRequests::GetEditorEntityContextId);
  107. if (Prefab::IsOutlinerOverrideManagementEnabled())
  108. {
  109. // Get currently selected entities
  110. EntityIdList selectedEntities;
  111. ToolsApplicationRequestBus::BroadcastResult(selectedEntities, &ToolsApplicationRequests::GetSelectedEntities);
  112. if (AZStd::find(selectedEntities.begin(), selectedEntities.end(), entityId) != selectedEntities.end())
  113. {
  114. // Entity is already selected, keep the same selection
  115. return entityId;
  116. }
  117. // Return the highest unselected ancestor container of the entity, or the entity if none is found.
  118. AZ::EntityId highestUnselectedAncestorContainer = entityId;
  119. // Skip the queried entity, as we only want to check its ancestors.
  120. AZ::TransformBus::EventResult(entityId, entityId, &AZ::TransformBus::Events::GetParentId);
  121. // Go up the hierarchy until you hit the root or a selected container
  122. while (entityId.IsValid())
  123. {
  124. if (IsContainer(entityId))
  125. {
  126. if (AZStd::find(selectedEntities.begin(), selectedEntities.end(), entityId) != selectedEntities.end())
  127. {
  128. if (!IsContainerOpen(entityId))
  129. {
  130. // If the selected container is closed, keep selecting it.
  131. highestUnselectedAncestorContainer = entityId;
  132. }
  133. break;
  134. }
  135. else
  136. {
  137. if (!isEntityPickModeEnabled || !IsContainerOpen(entityId))
  138. {
  139. highestUnselectedAncestorContainer = entityId;
  140. }
  141. }
  142. }
  143. AZ::EntityId parentId; // start with an invalid id, in case the parent is invalid.
  144. AZ::TransformBus::EventResult(parentId, entityId, &AZ::TransformBus::Events::GetParentId);
  145. if (entityId == parentId)
  146. {
  147. break; // avoid situation where a GetParentId returns the same id leading to an infinite loop
  148. }
  149. entityId = parentId;
  150. }
  151. return highestUnselectedAncestorContainer;
  152. }
  153. // Return the highest closed container, or the entity if none is found.
  154. AZ::EntityId highestSelectableEntityId = entityId;
  155. // Skip the queried entity, as we only want to check its ancestors.
  156. AZ::TransformBus::EventResult(entityId, entityId, &AZ::TransformBus::Events::GetParentId);
  157. // Go up the hierarchy until you hit the root
  158. while (entityId.IsValid())
  159. {
  160. if (!IsContainerOpen(entityId))
  161. {
  162. // If one of the ancestors is a container and it's closed, keep track of its id.
  163. // We only return of the higher closed container in the hierarchy.
  164. highestSelectableEntityId = entityId;
  165. }
  166. AZ::EntityId parentId; // start with an invalid id, in case the parent is invalid.
  167. AZ::TransformBus::EventResult(parentId, entityId, &AZ::TransformBus::Events::GetParentId);
  168. if (entityId == parentId)
  169. {
  170. break; // avoid an infinite loop in the case where the same entity is returned.
  171. }
  172. entityId = parentId;
  173. }
  174. return highestSelectableEntityId;
  175. }
  176. void ContainerEntitySystemComponent::OnEntityStreamLoadSuccess()
  177. {
  178. // We don't yet support multiple entity contexts, so just use the default.
  179. auto editorEntityContextId = AzFramework::EntityContextId::CreateNull();
  180. EditorEntityContextRequestBus::BroadcastResult(editorEntityContextId, &EditorEntityContextRequests::GetEditorEntityContextId);
  181. Clear(editorEntityContextId);
  182. }
  183. void ContainerEntitySystemComponent::RefreshAllContainerEntities([[maybe_unused]] AzFramework::EntityContextId entityContextId) const
  184. {
  185. for (AZ::EntityId containerEntityId : m_containers)
  186. {
  187. ContainerEntityNotificationBus::Broadcast(
  188. &ContainerEntityNotificationBus::Events::OnContainerEntityStatusChanged, containerEntityId, m_openContainers.contains(containerEntityId));
  189. }
  190. }
  191. ContainerEntityOperationResult ContainerEntitySystemComponent::Clear(AzFramework::EntityContextId entityContextId)
  192. {
  193. // We don't yet support multiple entity contexts, so only clear the default.
  194. auto editorEntityContextId = AzFramework::EntityContextId::CreateNull();
  195. EditorEntityContextRequestBus::BroadcastResult(editorEntityContextId, &EditorEntityContextRequests::GetEditorEntityContextId);
  196. if (entityContextId != editorEntityContextId)
  197. {
  198. return AZ::Failure(AZStd::string(
  199. "Error in ContainerEntitySystemComponent::Clear - cannot clear non-default Entity Context!"));
  200. }
  201. if (!m_containers.empty())
  202. {
  203. return AZ::Failure(AZStd::string(
  204. "Error in ContainerEntitySystemComponent::Clear - cannot clear container states if entities are still registered!"));
  205. }
  206. m_openContainers.clear();
  207. return AZ::Success();
  208. }
  209. bool ContainerEntitySystemComponent::IsUnderClosedContainerEntity(AZ::EntityId entityId) const
  210. {
  211. if (!entityId.IsValid())
  212. {
  213. return false;
  214. }
  215. // Skip the queried entity, as we only want to check its ancestors.
  216. AZ::TransformBus::EventResult(entityId, entityId, &AZ::TransformBus::Events::GetParentId);
  217. // Go up the hierarchy until you hit the root.
  218. while (entityId.IsValid())
  219. {
  220. if (!IsContainerOpen(entityId))
  221. {
  222. // One of the ancestors is a container and it's closed.
  223. return true;
  224. }
  225. AZ::EntityId parentId;
  226. AZ::TransformBus::EventResult(parentId, entityId, &AZ::TransformBus::Events::GetParentId);
  227. if (parentId == entityId)
  228. {
  229. // In some circumstances, querying a root level entity with GetParentId will return
  230. // the entity itself instead of an invalid entityId.
  231. break;
  232. }
  233. entityId = parentId;
  234. }
  235. // All ancestors are either regular entities or open containers.
  236. return false;
  237. }
  238. } // namespace AzToolsFramework