SurfaceMaskGradientComponent.cpp 11 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 <GradientSignal/Components/SurfaceMaskGradientComponent.h>
  9. #include <AzCore/RTTI/BehaviorContext.h>
  10. #include <AzCore/Serialization/EditContext.h>
  11. #include <AzCore/Serialization/SerializeContext.h>
  12. #include <LmbrCentral/Shape/ShapeComponentBus.h>
  13. #include <AzCore/Debug/Profiler.h>
  14. namespace GradientSignal
  15. {
  16. void SurfaceMaskGradientConfig::Reflect(AZ::ReflectContext* context)
  17. {
  18. AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
  19. if (serialize)
  20. {
  21. serialize->Class<SurfaceMaskGradientConfig, AZ::ComponentConfig>()
  22. ->Version(0)
  23. ->Field("SurfaceTagList", &SurfaceMaskGradientConfig::m_surfaceTagList)
  24. ;
  25. AZ::EditContext* edit = serialize->GetEditContext();
  26. if (edit)
  27. {
  28. edit->Class<SurfaceMaskGradientConfig>(
  29. "Surface Mask Gradient", "")
  30. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  31. ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
  32. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  33. ->DataElement(0, &SurfaceMaskGradientConfig::m_surfaceTagList, "Surface Tag List", "Identifiers used to compare against underlying surfaces.")
  34. ;
  35. }
  36. }
  37. if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
  38. {
  39. behaviorContext->Class<SurfaceMaskGradientConfig>()
  40. ->Attribute(AZ::Script::Attributes::Category, "Vegetation")
  41. ->Constructor()
  42. ->Method("GetNumTags", &SurfaceMaskGradientConfig::GetNumTags)
  43. ->Method("GetTag", &SurfaceMaskGradientConfig::GetTag)
  44. ->Method("RemoveTag", &SurfaceMaskGradientConfig::RemoveTag)
  45. ->Method("AddTag", &SurfaceMaskGradientConfig::AddTag)
  46. ;
  47. }
  48. }
  49. size_t SurfaceMaskGradientConfig::GetNumTags() const
  50. {
  51. return m_surfaceTagList.size();
  52. }
  53. AZ::Crc32 SurfaceMaskGradientConfig::GetTag(int tagIndex) const
  54. {
  55. if (tagIndex < m_surfaceTagList.size())
  56. {
  57. return m_surfaceTagList[tagIndex];
  58. }
  59. return AZ::Crc32();
  60. }
  61. void SurfaceMaskGradientConfig::RemoveTag(int tagIndex)
  62. {
  63. if (tagIndex < m_surfaceTagList.size())
  64. {
  65. m_surfaceTagList.erase(m_surfaceTagList.begin() + tagIndex);
  66. }
  67. }
  68. void SurfaceMaskGradientConfig::AddTag(AZStd::string tag)
  69. {
  70. m_surfaceTagList.push_back(SurfaceData::SurfaceTag(tag));
  71. }
  72. void SurfaceMaskGradientComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services)
  73. {
  74. services.push_back(AZ_CRC_CE("GradientService"));
  75. }
  76. void SurfaceMaskGradientComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services)
  77. {
  78. services.push_back(AZ_CRC_CE("GradientService"));
  79. services.push_back(AZ_CRC_CE("GradientTransformService"));
  80. }
  81. void SurfaceMaskGradientComponent::Reflect(AZ::ReflectContext* context)
  82. {
  83. SurfaceMaskGradientConfig::Reflect(context);
  84. AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
  85. if (serialize)
  86. {
  87. serialize->Class<SurfaceMaskGradientComponent, AZ::Component>()
  88. ->Version(0)
  89. ->Field("Configuration", &SurfaceMaskGradientComponent::m_configuration)
  90. ;
  91. }
  92. if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
  93. {
  94. behaviorContext->Constant("SurfaceMaskGradientComponentTypeId", BehaviorConstant(SurfaceMaskGradientComponentTypeId));
  95. behaviorContext->Class<SurfaceMaskGradientComponent>()->RequestBus("SurfaceMaskGradientRequestBus");
  96. behaviorContext->EBus<SurfaceMaskGradientRequestBus>("SurfaceMaskGradientRequestBus")
  97. ->Attribute(AZ::Script::Attributes::Category, "Vegetation")
  98. ->Event("GetNumTags", &SurfaceMaskGradientRequestBus::Events::GetNumTags)
  99. ->Event("GetTag", &SurfaceMaskGradientRequestBus::Events::GetTag)
  100. ->Event("RemoveTag", &SurfaceMaskGradientRequestBus::Events::RemoveTag)
  101. ->Event("AddTag", &SurfaceMaskGradientRequestBus::Events::AddTag)
  102. ;
  103. }
  104. }
  105. SurfaceMaskGradientComponent::SurfaceMaskGradientComponent(const SurfaceMaskGradientConfig& configuration)
  106. : m_configuration(configuration)
  107. {
  108. }
  109. void SurfaceMaskGradientComponent::Activate()
  110. {
  111. m_dependencyMonitor.Reset();
  112. m_dependencyMonitor.SetRegionChangedEntityNotificationFunction();
  113. m_dependencyMonitor.ConnectOwner(GetEntityId());
  114. SurfaceMaskGradientRequestBus::Handler::BusConnect(GetEntityId());
  115. SurfaceData::SurfaceDataSystemNotificationBus::Handler::BusConnect();
  116. // Connect to GradientRequestBus last so that everything is initialized before listening for gradient queries.
  117. GradientRequestBus::Handler::BusConnect(GetEntityId());
  118. }
  119. void SurfaceMaskGradientComponent::Deactivate()
  120. {
  121. // Disconnect from GradientRequestBus first to ensure no queries are in process when deactivating.
  122. GradientRequestBus::Handler::BusDisconnect();
  123. SurfaceData::SurfaceDataSystemNotificationBus::Handler::BusDisconnect();
  124. m_dependencyMonitor.Reset();
  125. SurfaceMaskGradientRequestBus::Handler::BusDisconnect();
  126. }
  127. bool SurfaceMaskGradientComponent::ReadInConfig(const AZ::ComponentConfig* baseConfig)
  128. {
  129. if (auto config = azrtti_cast<const SurfaceMaskGradientConfig*>(baseConfig))
  130. {
  131. m_configuration = *config;
  132. return true;
  133. }
  134. return false;
  135. }
  136. bool SurfaceMaskGradientComponent::WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const
  137. {
  138. if (auto config = azrtti_cast<SurfaceMaskGradientConfig*>(outBaseConfig))
  139. {
  140. *config = m_configuration;
  141. return true;
  142. }
  143. return false;
  144. }
  145. float SurfaceMaskGradientComponent::GetValue(const GradientSampleParams& params) const
  146. {
  147. float result = 0.0f;
  148. GetValues(AZStd::span<const AZ::Vector3>(&params.m_position, 1), AZStd::span<float>(&result, 1));
  149. return result;
  150. }
  151. void SurfaceMaskGradientComponent::GetValues(AZStd::span<const AZ::Vector3> positions, AZStd::span<float> outValues) const
  152. {
  153. if (positions.size() != outValues.size())
  154. {
  155. AZ_Assert(false, "input and output lists are different sizes (%zu vs %zu).", positions.size(), outValues.size());
  156. return;
  157. }
  158. if (GradientRequestBus::HasReentrantEBusUseThisThread())
  159. {
  160. AZ_ErrorOnce("GradientSignal", false, "Detected cyclic dependencies with surface tag references on entity '%s' (%s)",
  161. GetEntity()->GetName().c_str(), GetEntityId().ToString().c_str());
  162. return;
  163. }
  164. AZStd::shared_lock lock(m_queryMutex);
  165. // Initialize all our output values to 0.
  166. AZStd::fill(outValues.begin(), outValues.end(), 0.0f);
  167. if (!m_configuration.m_surfaceTagList.empty())
  168. {
  169. SurfaceData::SurfacePointList points;
  170. AZ::Interface<SurfaceData::SurfaceDataSystem>::Get()->GetSurfacePointsFromList(
  171. positions, m_configuration.m_surfaceTagList, points);
  172. // For each position, get the max surface weight that matches our filter and that appears at that position.
  173. points.EnumeratePoints(
  174. [this, &outValues](
  175. size_t inPositionIndex, [[maybe_unused]] const AZ::Vector3& position, [[maybe_unused]] const AZ::Vector3& normal,
  176. const SurfaceData::SurfaceTagWeights& masks) -> bool
  177. {
  178. masks.EnumerateWeights(
  179. [this, inPositionIndex, &outValues]([[maybe_unused]] AZ::Crc32 surfaceType, float weight) -> bool
  180. {
  181. if (AZStd::find(
  182. m_configuration.m_surfaceTagList.begin(), m_configuration.m_surfaceTagList.end(), surfaceType) !=
  183. m_configuration.m_surfaceTagList.end())
  184. {
  185. outValues[inPositionIndex] = AZ::GetMax(AZ::GetClamp(weight, 0.0f, 1.0f), outValues[inPositionIndex]);
  186. }
  187. return true;
  188. });
  189. return true;
  190. });
  191. }
  192. }
  193. size_t SurfaceMaskGradientComponent::GetNumTags() const
  194. {
  195. return m_configuration.GetNumTags();
  196. }
  197. AZ::Crc32 SurfaceMaskGradientComponent::GetTag(int tagIndex) const
  198. {
  199. return m_configuration.GetTag(tagIndex);
  200. }
  201. void SurfaceMaskGradientComponent::RemoveTag(int tagIndex)
  202. {
  203. // Only hold the lock while we're changing the data. Don't hold onto it during the OnCompositionChanged call, because that can
  204. // execute an arbitrary amount of logic, including calls back to this component.
  205. {
  206. AZStd::unique_lock lock(m_queryMutex);
  207. m_configuration.RemoveTag(tagIndex);
  208. }
  209. LmbrCentral::DependencyNotificationBus::Event(GetEntityId(), &LmbrCentral::DependencyNotificationBus::Events::OnCompositionChanged);
  210. }
  211. void SurfaceMaskGradientComponent::AddTag(AZStd::string tag)
  212. {
  213. // Only hold the lock while we're changing the data. Don't hold onto it during the OnCompositionChanged call, because that can
  214. // execute an arbitrary amount of logic, including calls back to this component.
  215. {
  216. AZStd::unique_lock lock(m_queryMutex);
  217. m_configuration.AddTag(tag);
  218. }
  219. LmbrCentral::DependencyNotificationBus::Event(GetEntityId(), &LmbrCentral::DependencyNotificationBus::Events::OnCompositionChanged);
  220. }
  221. void SurfaceMaskGradientComponent::OnSurfaceChanged(
  222. [[maybe_unused]] const AZ::EntityId& entityId,
  223. [[maybe_unused]] const AZ::Aabb& oldBounds,
  224. [[maybe_unused]] const AZ::Aabb& newBounds,
  225. const SurfaceData::SurfaceTagSet& changedSurfaceTags)
  226. {
  227. bool changedTagAffectsGradient = false;
  228. // Only hold the lock while we're comparing the surface tags. Don't hold onto it during the OnCompositionChanged call,
  229. // because that can execute an arbitrary amount of logic, including calls back to this component.
  230. {
  231. AZStd::shared_lock lock(m_queryMutex);
  232. for (auto& tag : m_configuration.m_surfaceTagList)
  233. {
  234. if (changedSurfaceTags.contains(tag))
  235. {
  236. changedTagAffectsGradient = true;
  237. break;
  238. }
  239. }
  240. }
  241. if (changedTagAffectsGradient)
  242. {
  243. AZ::Aabb expandedBounds(oldBounds);
  244. expandedBounds.AddAabb(newBounds);
  245. LmbrCentral::DependencyNotificationBus::Event(
  246. GetEntityId(), &LmbrCentral::DependencyNotificationBus::Events::OnCompositionRegionChanged, expandedBounds);
  247. }
  248. }
  249. }