SurfaceAltitudeGradientComponent.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425
  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 <GradientSignal/Components/SurfaceAltitudeGradientComponent.h>
  9. #include <AzCore/Component/Entity.h>
  10. #include <AzCore/RTTI/BehaviorContext.h>
  11. #include <AzCore/Serialization/EditContext.h>
  12. #include <AzCore/Serialization/SerializeContext.h>
  13. #include <LmbrCentral/Shape/ShapeComponentBus.h>
  14. #include <SurfaceData/SurfaceDataSystemRequestBus.h>
  15. #include <GradientSignal/Util.h>
  16. #include <LmbrCentral/Dependency/DependencyMonitor.h>
  17. namespace GradientSignal
  18. {
  19. void SurfaceAltitudeGradientConfig::Reflect(AZ::ReflectContext* context)
  20. {
  21. AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
  22. if (serialize)
  23. {
  24. serialize->Class<SurfaceAltitudeGradientConfig, AZ::ComponentConfig>()
  25. ->Version(0)
  26. ->Field("ShapeEntityId", &SurfaceAltitudeGradientConfig::m_shapeEntityId)
  27. ->Field("AltitudeMin", &SurfaceAltitudeGradientConfig::m_altitudeMin)
  28. ->Field("AltitudeMax", &SurfaceAltitudeGradientConfig::m_altitudeMax)
  29. ->Field("SurfaceTagsToSample", &SurfaceAltitudeGradientConfig::m_surfaceTagsToSample)
  30. ;
  31. AZ::EditContext* edit = serialize->GetEditContext();
  32. if (edit)
  33. {
  34. edit->Class<SurfaceAltitudeGradientConfig>(
  35. "Altitude Gradient", "altitude Gradient")
  36. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  37. ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
  38. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  39. ->DataElement(0, &SurfaceAltitudeGradientConfig::m_shapeEntityId, "Pin To Shape Entity Id", "Shape bounds override min/max altitude if specified.")
  40. ->Attribute(AZ::Edit::Attributes::RequiredService, AZ_CRC_CE("ShapeService"))
  41. ->DataElement(0, &SurfaceAltitudeGradientConfig::m_altitudeMin, "Altitude Min", "Minimum acceptable surface altitude.")
  42. ->Attribute(AZ::Edit::Attributes::ReadOnly, &SurfaceAltitudeGradientConfig::IsShapeValid)
  43. ->DataElement(0, &SurfaceAltitudeGradientConfig::m_altitudeMax, "Altitude Max", "Maximum acceptable surface altitude.")
  44. ->Attribute(AZ::Edit::Attributes::ReadOnly, &SurfaceAltitudeGradientConfig::IsShapeValid)
  45. ->DataElement(0, &SurfaceAltitudeGradientConfig::m_surfaceTagsToSample, "Surface Tags to track", "")
  46. ;
  47. }
  48. }
  49. if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
  50. {
  51. behaviorContext->Class<SurfaceAltitudeGradientConfig>()
  52. ->Attribute(AZ::Script::Attributes::Category, "Vegetation")
  53. ->Constructor()
  54. ->Property("shapeEntityId", BehaviorValueProperty(&SurfaceAltitudeGradientConfig::m_shapeEntityId))
  55. ->Property("altitudeMin", BehaviorValueProperty(&SurfaceAltitudeGradientConfig::m_altitudeMin))
  56. ->Property("altitudeMax", BehaviorValueProperty(&SurfaceAltitudeGradientConfig::m_altitudeMax))
  57. ->Method("GetNumTags", &SurfaceAltitudeGradientConfig::GetNumTags)
  58. ->Method("GetTag", &SurfaceAltitudeGradientConfig::GetTag)
  59. ->Method("RemoveTag", &SurfaceAltitudeGradientConfig::RemoveTag)
  60. ->Method("AddTag", &SurfaceAltitudeGradientConfig::AddTag)
  61. ;
  62. }
  63. }
  64. bool SurfaceAltitudeGradientConfig::IsShapeValid() const
  65. {
  66. return m_shapeEntityId.IsValid();
  67. }
  68. size_t SurfaceAltitudeGradientConfig::GetNumTags() const
  69. {
  70. return m_surfaceTagsToSample.size();
  71. }
  72. AZ::Crc32 SurfaceAltitudeGradientConfig::GetTag(int tagIndex) const
  73. {
  74. if (tagIndex < m_surfaceTagsToSample.size() && tagIndex >= 0)
  75. {
  76. return m_surfaceTagsToSample[tagIndex];
  77. }
  78. return AZ::Crc32();
  79. }
  80. void SurfaceAltitudeGradientConfig::RemoveTag(int tagIndex)
  81. {
  82. if (tagIndex < m_surfaceTagsToSample.size() && tagIndex >= 0)
  83. {
  84. m_surfaceTagsToSample.erase(m_surfaceTagsToSample.begin() + tagIndex);
  85. }
  86. }
  87. void SurfaceAltitudeGradientConfig::AddTag(AZStd::string tag)
  88. {
  89. m_surfaceTagsToSample.push_back(SurfaceData::SurfaceTag(tag));
  90. }
  91. void SurfaceAltitudeGradientComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services)
  92. {
  93. services.push_back(AZ_CRC_CE("GradientService"));
  94. }
  95. void SurfaceAltitudeGradientComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services)
  96. {
  97. services.push_back(AZ_CRC_CE("GradientService"));
  98. services.push_back(AZ_CRC_CE("GradientTransformService"));
  99. }
  100. void SurfaceAltitudeGradientComponent::GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& services)
  101. {
  102. }
  103. void SurfaceAltitudeGradientComponent::Reflect(AZ::ReflectContext* context)
  104. {
  105. SurfaceAltitudeGradientConfig::Reflect(context);
  106. AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
  107. if (serialize)
  108. {
  109. serialize->Class<SurfaceAltitudeGradientComponent, AZ::Component>()
  110. ->Version(0)
  111. ->Field("Configuration", &SurfaceAltitudeGradientComponent::m_configuration)
  112. ;
  113. }
  114. if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
  115. {
  116. behaviorContext->Constant("SurfaceAltitudeGradientComponentTypeId", BehaviorConstant(SurfaceAltitudeGradientComponentTypeId));
  117. behaviorContext->Class<SurfaceAltitudeGradientComponent>()->RequestBus("SurfaceAltitudeGradientRequestBus");
  118. behaviorContext->EBus<SurfaceAltitudeGradientRequestBus>("SurfaceAltitudeGradientRequestBus")
  119. ->Attribute(AZ::Script::Attributes::Category, "Vegetation")
  120. ->Event("GetShapeEntityId", &SurfaceAltitudeGradientRequestBus::Events::GetShapeEntityId)
  121. ->Event("SetShapeEntityId", &SurfaceAltitudeGradientRequestBus::Events::SetShapeEntityId)
  122. ->VirtualProperty("ShapeEntityId", "GetShapeEntityId", "SetShapeEntityId")
  123. ->Event("GetAltitudeMin", &SurfaceAltitudeGradientRequestBus::Events::GetAltitudeMin)
  124. ->Event("SetAltitudeMin", &SurfaceAltitudeGradientRequestBus::Events::SetAltitudeMin)
  125. ->VirtualProperty("AltitudeMin", "GetAltitudeMin", "SetAltitudeMin")
  126. ->Event("GetAltitudeMax", &SurfaceAltitudeGradientRequestBus::Events::GetAltitudeMax)
  127. ->Event("SetAltitudeMax", &SurfaceAltitudeGradientRequestBus::Events::SetAltitudeMax)
  128. ->VirtualProperty("AltitudeMax", "GetAltitudeMax", "SetAltitudeMax")
  129. ->Event("GetNumTags", &SurfaceAltitudeGradientRequestBus::Events::GetNumTags)
  130. ->Event("GetTag", &SurfaceAltitudeGradientRequestBus::Events::GetTag)
  131. ->Event("RemoveTag", &SurfaceAltitudeGradientRequestBus::Events::RemoveTag)
  132. ->Event("AddTag", &SurfaceAltitudeGradientRequestBus::Events::AddTag)
  133. ;
  134. }
  135. }
  136. SurfaceAltitudeGradientComponent::SurfaceAltitudeGradientComponent(const SurfaceAltitudeGradientConfig& configuration)
  137. : m_configuration(configuration)
  138. {
  139. }
  140. void SurfaceAltitudeGradientComponent::Activate()
  141. {
  142. m_dependencyMonitor.Reset();
  143. m_dependencyMonitor.ConnectOwner(GetEntityId());
  144. m_dependencyMonitor.ConnectDependency(m_configuration.m_shapeEntityId);
  145. LmbrCentral::DependencyNotificationBus::Handler::BusConnect(GetEntityId());
  146. AZ::TickBus::Handler::BusConnect();
  147. SurfaceAltitudeGradientRequestBus::Handler::BusConnect(GetEntityId());
  148. SurfaceData::SurfaceDataSystemNotificationBus::Handler::BusConnect();
  149. UpdateFromShape();
  150. m_dirty = false;
  151. // Connect to GradientRequestBus last so that everything is initialized before listening for gradient queries.
  152. GradientRequestBus::Handler::BusConnect(GetEntityId());
  153. }
  154. void SurfaceAltitudeGradientComponent::Deactivate()
  155. {
  156. // Disconnect from GradientRequestBus first to ensure no queries are in process when deactivating.
  157. GradientRequestBus::Handler::BusDisconnect();
  158. m_dependencyMonitor.Reset();
  159. SurfaceData::SurfaceDataSystemNotificationBus::Handler::BusDisconnect();
  160. LmbrCentral::DependencyNotificationBus::Handler::BusDisconnect();
  161. AZ::TickBus::Handler::BusDisconnect();
  162. SurfaceAltitudeGradientRequestBus::Handler::BusDisconnect();
  163. m_dirty = false;
  164. }
  165. bool SurfaceAltitudeGradientComponent::ReadInConfig(const AZ::ComponentConfig* baseConfig)
  166. {
  167. if (auto config = azrtti_cast<const SurfaceAltitudeGradientConfig*>(baseConfig))
  168. {
  169. m_configuration = *config;
  170. return true;
  171. }
  172. return false;
  173. }
  174. bool SurfaceAltitudeGradientComponent::WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const
  175. {
  176. if (auto config = azrtti_cast<SurfaceAltitudeGradientConfig*>(outBaseConfig))
  177. {
  178. *config = m_configuration;
  179. return true;
  180. }
  181. return false;
  182. }
  183. float SurfaceAltitudeGradientComponent::GetValue(const GradientSampleParams& sampleParams) const
  184. {
  185. float result = 0.0f;
  186. GetValues(AZStd::span<const AZ::Vector3>(&sampleParams.m_position, 1), AZStd::span<float>(&result, 1));
  187. return result;
  188. }
  189. void SurfaceAltitudeGradientComponent::GetValues(AZStd::span<const AZ::Vector3> positions, AZStd::span<float> outValues) const
  190. {
  191. if (positions.size() != outValues.size())
  192. {
  193. AZ_Assert(false, "input and output lists are different sizes (%zu vs %zu).", positions.size(), outValues.size());
  194. return;
  195. }
  196. if (GradientRequestBus::HasReentrantEBusUseThisThread())
  197. {
  198. AZ_ErrorOnce("GradientSignal", false, "Detected cyclic dependencies with surface tag references on entity '%s' (%s)",
  199. GetEntity()->GetName().c_str(), GetEntityId().ToString().c_str());
  200. return;
  201. }
  202. AZStd::shared_lock lock(m_queryMutex);
  203. SurfaceData::SurfacePointList points;
  204. AZ::Interface<SurfaceData::SurfaceDataSystem>::Get()->GetSurfacePointsFromList(
  205. positions, m_configuration.m_surfaceTagsToSample, points);
  206. // For each position, turn the height into a 0-1 value based on our min/max altitudes.
  207. for (size_t index = 0; index < positions.size(); index++)
  208. {
  209. if (!points.IsEmpty(index))
  210. {
  211. // Get the point with the highest Z value and use that for the altitude.
  212. const float highestAltitude = points.GetHighestSurfacePoint(index).m_position.GetZ();
  213. // Turn the absolute altitude value into a 0-1 value by returning the % of the given altitude range that it falls at.
  214. outValues[index] = GetRatio(m_configuration.m_altitudeMin, m_configuration.m_altitudeMax, highestAltitude);
  215. }
  216. else
  217. {
  218. outValues[index] = 0.0f;
  219. }
  220. }
  221. }
  222. void SurfaceAltitudeGradientComponent::OnCompositionChanged()
  223. {
  224. m_dirty = true;
  225. }
  226. void SurfaceAltitudeGradientComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
  227. {
  228. if (m_dirty)
  229. {
  230. const auto altitudeMinOld = m_configuration.m_altitudeMin;
  231. const auto altitudeMaxOld = m_configuration.m_altitudeMax;
  232. //updating on tick to query shape bus on main thread
  233. UpdateFromShape();
  234. //notify observers if content has changed
  235. if (altitudeMinOld != m_configuration.m_altitudeMin ||
  236. altitudeMaxOld != m_configuration.m_altitudeMax ||
  237. m_surfaceDirty)
  238. {
  239. LmbrCentral::DependencyNotificationBus::Event(GetEntityId(), &LmbrCentral::DependencyNotificationBus::Events::OnCompositionChanged);
  240. }
  241. m_dirty = false;
  242. m_surfaceDirty = false;
  243. }
  244. }
  245. void SurfaceAltitudeGradientComponent::OnSurfaceChanged(
  246. [[maybe_unused]] const AZ::EntityId& entityId,
  247. [[maybe_unused]] const AZ::Aabb& oldBounds,
  248. [[maybe_unused]] const AZ::Aabb& newBounds,
  249. [[maybe_unused]] const SurfaceData::SurfaceTagSet& changedSurfaceTags)
  250. {
  251. /* The following logic is currently disabled until we can find a safer way to do this.
  252. The intent of the logic is to make the SurfaceAltitudeGradient refresh its data if the surface(s) that it depends on changes.
  253. However, it's currently possible to get into a refresh feedback loop if a surface provider (like terrain) uses one of these
  254. gradients. The loop looks like this:
  255. - Surface that the gradient depends on changes, which triggers this OnSurfaceChanged message
  256. - Gradient marks itself as dirty, which triggers an OnCompositionChanged message to anything depending on the gradient
  257. - Terrain receives message and triggers an OnSurfaceChanged message
  258. - OnSurfaceChanged message makes it back to this gradient. Even if this gradient doesn't depend on that specific surface,
  259. it doesn't have enough information here to know that, so if the AABB overlaps, it will mark itself as dirty again, even
  260. though the actual surfaces we're listening to in that AABB didn't change.
  261. We can't just query the surface provider itself to see what surfaces it provides, because if there are any surface modifiers,
  262. it's *possible* for them to modify the points of the surface provider to add the surface types we're listening for.
  263. By disabling this code, we end up with stale data on the gradient, but enabling it can cause refreshes on every frame which
  264. destroys the framerate.
  265. */
  266. /*
  267. // Create a box that's infinite in the XY direction, but contains our altitude range, so that we can compare against the dirty
  268. // surface region.
  269. const AZ::Aabb altitudeBox = AZ::Aabb::CreateFromMinMaxValues(
  270. AZStd::numeric_limits<float>::lowest(), AZStd::numeric_limits<float>::lowest(), m_configuration.m_altitudeMin,
  271. AZStd::numeric_limits<float>::max(), AZStd::numeric_limits<float>::max(), m_configuration.m_altitudeMax);
  272. if (oldBounds.Overlaps(altitudeBox) || newBounds.Overlaps(altitudeBox))
  273. {
  274. m_dirty = true;
  275. m_surfaceDirty = true;
  276. }
  277. */
  278. }
  279. void SurfaceAltitudeGradientComponent::UpdateFromShape()
  280. {
  281. AZStd::unique_lock lock(m_queryMutex);
  282. if (m_configuration.m_shapeEntityId.IsValid())
  283. {
  284. AZ::Aabb bounds = AZ::Aabb::CreateFromMinMax(
  285. AZ::Vector3(-AZ::Constants::FloatMax, -AZ::Constants::FloatMax, AZ::GetMin(m_configuration.m_altitudeMin, m_configuration.m_altitudeMax)),
  286. AZ::Vector3(AZ::Constants::FloatMax, AZ::Constants::FloatMax, AZ::GetMax(m_configuration.m_altitudeMin, m_configuration.m_altitudeMax)));
  287. LmbrCentral::ShapeComponentRequestsBus::EventResult(bounds, m_configuration.m_shapeEntityId, &LmbrCentral::ShapeComponentRequestsBus::Events::GetEncompassingAabb);
  288. if (bounds.IsValid())
  289. {
  290. m_configuration.m_altitudeMin = bounds.GetMin().GetZ();
  291. m_configuration.m_altitudeMax = bounds.GetMax().GetZ();
  292. }
  293. }
  294. }
  295. AZ::EntityId SurfaceAltitudeGradientComponent::GetShapeEntityId() const
  296. {
  297. return m_configuration.m_shapeEntityId;
  298. }
  299. void SurfaceAltitudeGradientComponent::SetShapeEntityId(AZ::EntityId entityId)
  300. {
  301. // Only hold the lock while we're changing the data. Don't hold onto it during the OnCompositionChanged call, because that can
  302. // execute an arbitrary amount of logic, including calls back to this component.
  303. {
  304. AZStd::unique_lock lock(m_queryMutex);
  305. m_configuration.m_shapeEntityId = entityId;
  306. }
  307. LmbrCentral::DependencyNotificationBus::Event(GetEntityId(), &LmbrCentral::DependencyNotificationBus::Events::OnCompositionChanged);
  308. }
  309. float SurfaceAltitudeGradientComponent::GetAltitudeMin() const
  310. {
  311. return m_configuration.m_altitudeMin;
  312. }
  313. void SurfaceAltitudeGradientComponent::SetAltitudeMin(float altitudeMin)
  314. {
  315. // Only hold the lock while we're changing the data. Don't hold onto it during the OnCompositionChanged call, because that can
  316. // execute an arbitrary amount of logic, including calls back to this component.
  317. {
  318. AZStd::unique_lock lock(m_queryMutex);
  319. m_configuration.m_altitudeMin = altitudeMin;
  320. }
  321. LmbrCentral::DependencyNotificationBus::Event(GetEntityId(), &LmbrCentral::DependencyNotificationBus::Events::OnCompositionChanged);
  322. }
  323. float SurfaceAltitudeGradientComponent::GetAltitudeMax() const
  324. {
  325. return m_configuration.m_altitudeMax;
  326. }
  327. void SurfaceAltitudeGradientComponent::SetAltitudeMax(float altitudeMax)
  328. {
  329. // Only hold the lock while we're changing the data. Don't hold onto it during the OnCompositionChanged call, because that can
  330. // execute an arbitrary amount of logic, including calls back to this component.
  331. {
  332. AZStd::unique_lock lock(m_queryMutex);
  333. m_configuration.m_altitudeMax = altitudeMax;
  334. }
  335. LmbrCentral::DependencyNotificationBus::Event(GetEntityId(), &LmbrCentral::DependencyNotificationBus::Events::OnCompositionChanged);
  336. }
  337. size_t SurfaceAltitudeGradientComponent::GetNumTags() const
  338. {
  339. return m_configuration.GetNumTags();
  340. }
  341. AZ::Crc32 SurfaceAltitudeGradientComponent::GetTag(int tagIndex) const
  342. {
  343. return m_configuration.GetTag(tagIndex);
  344. }
  345. void SurfaceAltitudeGradientComponent::RemoveTag(int tagIndex)
  346. {
  347. // Only hold the lock while we're changing the data. Don't hold onto it during the OnCompositionChanged call, because that can
  348. // execute an arbitrary amount of logic, including calls back to this component.
  349. {
  350. AZStd::unique_lock lock(m_queryMutex);
  351. m_configuration.RemoveTag(tagIndex);
  352. }
  353. LmbrCentral::DependencyNotificationBus::Event(GetEntityId(), &LmbrCentral::DependencyNotificationBus::Events::OnCompositionChanged);
  354. }
  355. void SurfaceAltitudeGradientComponent::AddTag(AZStd::string tag)
  356. {
  357. // Only hold the lock while we're changing the data. Don't hold onto it during the OnCompositionChanged call, because that can
  358. // execute an arbitrary amount of logic, including calls back to this component.
  359. {
  360. AZStd::unique_lock lock(m_queryMutex);
  361. m_configuration.AddTag(tag);
  362. }
  363. LmbrCentral::DependencyNotificationBus::Event(GetEntityId(), &LmbrCentral::DependencyNotificationBus::Events::OnCompositionChanged);
  364. }
  365. }