3
0

AreaComponentBase.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  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 <Vegetation/AreaComponentBase.h>
  9. #include <AzCore/RTTI/BehaviorContext.h>
  10. #include <AzCore/Serialization/EditContext.h>
  11. #include <AzCore/Serialization/SerializeContext.h>
  12. #include <Vegetation/Ebuses/AreaSystemRequestBus.h>
  13. #include <AzCore/Debug/Profiler.h>
  14. #include <VegetationProfiler.h>
  15. namespace Vegetation
  16. {
  17. namespace AreaUtil
  18. {
  19. static bool UpdateVersion(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement)
  20. {
  21. if (classElement.GetVersion() < 1)
  22. {
  23. AZ::u8 areaType = 0; //default to cluster
  24. if (classElement.GetChildData(AZ_CRC("AreaType", 0x5365f66f), areaType))
  25. {
  26. classElement.RemoveElementByName(AZ_CRC("AreaType", 0x5365f66f));
  27. switch (areaType)
  28. {
  29. default:
  30. case 0: //cluster
  31. classElement.AddElementWithData(context, "Layer", AreaConstants::s_foregroundLayer);
  32. break;
  33. case 1: //coverage
  34. classElement.AddElementWithData(context, "Layer", AreaConstants::s_backgroundLayer);
  35. break;
  36. }
  37. }
  38. int priority = 1;
  39. if (classElement.GetChildData(AZ_CRC("Priority", 0x62a6dc27), priority))
  40. {
  41. classElement.RemoveElementByName(AZ_CRC("Priority", 0x62a6dc27));
  42. classElement.AddElementWithData(context, "Priority", (float)(priority - 1) / (float)std::numeric_limits<int>::max());
  43. }
  44. }
  45. if (classElement.GetVersion() < 2)
  46. {
  47. float priority = 0.0f;
  48. if (classElement.GetChildData(AZ_CRC("Priority", 0x62a6dc27), priority))
  49. {
  50. priority = AZ::GetClamp(priority, 0.0f, 1.0f);
  51. const AZ::u32 convertedPriority = (AZ::u32)(priority * AreaConstants::s_prioritySoftMax); //using soft max accommodate slider range and int/float conversion
  52. classElement.RemoveElementByName(AZ_CRC("Priority", 0x62a6dc27));
  53. classElement.AddElementWithData(context, "Priority", convertedPriority);
  54. }
  55. }
  56. return true;
  57. }
  58. }
  59. void AreaConfig::Reflect(AZ::ReflectContext* context)
  60. {
  61. AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
  62. if (serialize)
  63. {
  64. serialize->Class<AreaConfig, AZ::ComponentConfig>()
  65. ->Version(2, &AreaUtil::UpdateVersion)
  66. ->Field("Layer", &AreaConfig::m_layer)
  67. ->Field("Priority", &AreaConfig::m_priority)
  68. ;
  69. AZ::EditContext* edit = serialize->GetEditContext();
  70. if (edit)
  71. {
  72. edit->Class<AreaConfig>(
  73. "Vegetation Area", "")
  74. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  75. ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
  76. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  77. ->DataElement(AZ::Edit::UIHandlers::ComboBox, &AreaConfig::m_layer, "Layer Priority", "Defines a high level order vegetation areas are applied")
  78. ->Attribute(AZ::Edit::Attributes::EnumValues, &AreaConfig::GetSelectableLayers)
  79. ->DataElement(AZ::Edit::UIHandlers::Slider, &AreaConfig::m_priority, "Sub Priority", "Defines order vegetation areas are applied within a layer. Larger numbers = higher priority")
  80. ->Attribute(AZ::Edit::Attributes::Min, AreaConstants::s_priorityMin)
  81. ->Attribute(AZ::Edit::Attributes::Max, AreaConstants::s_priorityMax)
  82. ->Attribute(AZ::Edit::Attributes::SoftMin, AreaConstants::s_priorityMin)
  83. ->Attribute(AZ::Edit::Attributes::SoftMax, AreaConstants::s_prioritySoftMax)
  84. ;
  85. }
  86. }
  87. if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
  88. {
  89. behaviorContext->Class<AreaConfig>()
  90. ->Attribute(AZ::Script::Attributes::Category, "Vegetation")
  91. ->Constructor()
  92. ->Property("areaPriority", BehaviorValueProperty(&AreaConfig::m_priority))
  93. ->Property("areaLayer",
  94. [](AreaConfig* config) { return config->m_layer; },
  95. [](AreaConfig* config, const AZ::u32& i) { config->m_layer = i; })
  96. ;
  97. }
  98. }
  99. AZStd::vector<AZStd::pair<AZ::u32, AZStd::string>> AreaConfig::GetSelectableLayers() const
  100. {
  101. AZStd::vector<AZStd::pair<AZ::u32, AZStd::string>> selectableLayers;
  102. selectableLayers.push_back({ AreaConstants::s_backgroundLayer, AZStd::string("Background") });
  103. selectableLayers.push_back({ AreaConstants::s_foregroundLayer, AZStd::string("Foreground") });
  104. return selectableLayers;
  105. }
  106. void AreaComponentBase::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services)
  107. {
  108. services.push_back(AZ_CRC("VegetationAreaService", 0x6a859504));
  109. }
  110. void AreaComponentBase::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services)
  111. {
  112. services.push_back(AZ_CRC("VegetationAreaService", 0x6a859504));
  113. services.push_back(AZ_CRC("GradientService", 0x21c18d23));
  114. services.push_back(AZ_CRC("GradientTransformService", 0x8c8c5ecc));
  115. }
  116. void AreaComponentBase::GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& services)
  117. {
  118. }
  119. void AreaComponentBase::Reflect(AZ::ReflectContext* context)
  120. {
  121. AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
  122. if (serialize)
  123. {
  124. serialize->Class<AreaComponentBase, AZ::Component>()
  125. ->Version(0)
  126. ->Field("Configuration", &AreaComponentBase::m_configuration)
  127. ;
  128. }
  129. }
  130. AreaComponentBase::AreaComponentBase(const AreaConfig& configuration)
  131. : m_configuration(configuration)
  132. {
  133. }
  134. void AreaComponentBase::Activate()
  135. {
  136. m_areaRegistered = false;
  137. LmbrCentral::ShapeComponentNotificationsBus::Handler::BusConnect(GetEntityId());
  138. AZ::TransformNotificationBus::Handler::BusConnect(GetEntityId());
  139. AreaNotificationBus::Handler::BusConnect(GetEntityId());
  140. AreaInfoBus::Handler::BusConnect(GetEntityId());
  141. LmbrCentral::DependencyNotificationBus::Handler::BusConnect(GetEntityId());
  142. UpdateRegistration();
  143. }
  144. void AreaComponentBase::Deactivate()
  145. {
  146. // Disconnect from the busses *before* unregistering to ensure that unregistration can't trigger any
  147. // messages back into this component while it is deactivating.
  148. // Specifically, unregistering the area first previously caused a bug in the SpawnerComponent in which OnUnregisterArea
  149. // cleared out Descriptor pointers, and if any of them went to a refcount of 0, they could trigger an
  150. // OnCompositionChanged event which ended up looping back into this component.
  151. AreaNotificationBus::Handler::BusDisconnect();
  152. AreaInfoBus::Handler::BusDisconnect();
  153. AreaRequestBus::Handler::BusDisconnect();
  154. LmbrCentral::DependencyNotificationBus::Handler::BusDisconnect();
  155. LmbrCentral::ShapeComponentNotificationsBus::Handler::BusDisconnect();
  156. AZ::TransformNotificationBus::Handler::BusDisconnect();
  157. if (m_areaRegistered)
  158. {
  159. m_areaRegistered = false;
  160. AreaSystemRequestBus::Broadcast(&AreaSystemRequestBus::Events::UnregisterArea, GetEntityId());
  161. // Let area subclasses know that we've just unregistered the area
  162. OnUnregisterArea();
  163. }
  164. }
  165. bool AreaComponentBase::ReadInConfig(const AZ::ComponentConfig* baseConfig)
  166. {
  167. if (auto config = azrtti_cast<const AreaConfig*>(baseConfig))
  168. {
  169. m_configuration = *config;
  170. return true;
  171. }
  172. return false;
  173. }
  174. bool AreaComponentBase::WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const
  175. {
  176. if (auto config = azrtti_cast<AreaConfig*>(outBaseConfig))
  177. {
  178. *config = m_configuration;
  179. return true;
  180. }
  181. return false;
  182. }
  183. AZ::u32 AreaComponentBase::GetLayer() const
  184. {
  185. return m_configuration.m_layer;
  186. }
  187. AZ::u32 AreaComponentBase::GetPriority() const
  188. {
  189. return m_configuration.m_priority;
  190. }
  191. AZ::u32 AreaComponentBase::GetChangeIndex() const
  192. {
  193. return m_changeIndex;
  194. }
  195. void AreaComponentBase::UpdateRegistration()
  196. {
  197. // Area "valid" lifetimes can be shorter than the time in which the area components are active.
  198. // This can occur due to the chain of entity dependencies, or dependencies on asset loading, etc.
  199. // This method ensures that we update our registration status so that the area is only registered with
  200. // the vegetation system once the area is completely valid, and the area is unregistered the moment
  201. // it becomes invalid. Right now, we're defining "completely valid" as "has a well-defined valid AABB",
  202. // since that's the minimum requirement for a vegetation area.
  203. AZ::u32 layer = GetLayer();
  204. AZ::u32 priority = GetPriority();
  205. AZ::Aabb bounds = GetEncompassingAabb();
  206. bool areaIsValid = bounds.IsValid();
  207. if (m_areaRegistered && areaIsValid)
  208. {
  209. // Area is already registered, we're just updating information, so Refresh the area
  210. AreaSystemRequestBus::Broadcast(&AreaSystemRequestBus::Events::RefreshArea, GetEntityId(), layer, priority, bounds);
  211. }
  212. else if (!m_areaRegistered && areaIsValid)
  213. {
  214. // We've gone from an invalid to valid state, so Register the area
  215. m_areaRegistered = true;
  216. AreaSystemRequestBus::Broadcast(&AreaSystemRequestBus::Events::RegisterArea, GetEntityId(), layer, priority, bounds);
  217. // Let area subclasses know that we've just registered the area
  218. OnRegisterArea();
  219. }
  220. else if (m_areaRegistered && !areaIsValid)
  221. {
  222. // We've gone from a valid to invalid state, so Unregister the area
  223. m_areaRegistered = false;
  224. AreaSystemRequestBus::Broadcast(&AreaSystemRequestBus::Events::UnregisterArea, GetEntityId());
  225. // Let area subclasses know that we've just unregistered the area
  226. OnUnregisterArea();
  227. }
  228. else
  229. {
  230. // Our state before and after were both invalid, so do nothing.
  231. }
  232. }
  233. void AreaComponentBase::OnCompositionChanged()
  234. {
  235. UpdateRegistration();
  236. ++m_changeIndex;
  237. }
  238. void AreaComponentBase::OnAreaConnect()
  239. {
  240. AreaRequestBus::Handler::BusConnect(GetEntityId());
  241. }
  242. void AreaComponentBase::OnAreaDisconnect()
  243. {
  244. AreaRequestBus::Handler::BusDisconnect();
  245. }
  246. void AreaComponentBase::OnAreaRefreshed()
  247. {
  248. }
  249. void AreaComponentBase::OnTransformChanged(const AZ::Transform& /*local*/, const AZ::Transform& /*world*/)
  250. {
  251. AZ_PROFILE_FUNCTION(Vegetation);
  252. OnCompositionChanged();
  253. }
  254. void AreaComponentBase::OnShapeChanged([[maybe_unused]] ShapeComponentNotifications::ShapeChangeReasons reasons)
  255. {
  256. AZ_PROFILE_FUNCTION(Vegetation);
  257. OnCompositionChanged();
  258. }
  259. }