SurfaceSlopeGradientComponent.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417
  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/SurfaceSlopeGradientComponent.h>
  9. #include <AzCore/Component/Entity.h>
  10. #include <AzCore/Math/MathUtils.h>
  11. #include <AzCore/RTTI/BehaviorContext.h>
  12. #include <AzCore/Serialization/EditContext.h>
  13. #include <AzCore/Serialization/SerializeContext.h>
  14. #include <SurfaceData/SurfaceDataSystemRequestBus.h>
  15. #include <GradientSignal/Util.h>
  16. #include <LmbrCentral/Dependency/DependencyMonitor.h>
  17. namespace GradientSignal
  18. {
  19. void SurfaceSlopeGradientConfig::Reflect(AZ::ReflectContext* context)
  20. {
  21. AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
  22. if (serialize)
  23. {
  24. serialize->Class<SurfaceSlopeGradientConfig, AZ::ComponentConfig>()
  25. ->Version(1)
  26. ->Field("SurfaceTagsToSample", &SurfaceSlopeGradientConfig::m_surfaceTagsToSample)
  27. ->Field("SlopeMin", &SurfaceSlopeGradientConfig::m_slopeMin)
  28. ->Field("SlopeMax", &SurfaceSlopeGradientConfig::m_slopeMax)
  29. ->Field("RampType", &SurfaceSlopeGradientConfig::m_rampType)
  30. ->Field("SmoothStep", &SurfaceSlopeGradientConfig::m_smoothStep)
  31. ;
  32. AZ::EditContext* edit = serialize->GetEditContext();
  33. if (edit)
  34. {
  35. edit->Class<SurfaceSlopeGradientConfig>(
  36. "Slope Gradient", "")
  37. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  38. ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
  39. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  40. ->DataElement(0, &SurfaceSlopeGradientConfig::m_surfaceTagsToSample, "Surface Tags to track", "")
  41. ->DataElement(AZ::Edit::UIHandlers::Slider, &SurfaceSlopeGradientConfig::m_slopeMin, "Slope Min", "Minimum surface slope angle in degrees.")
  42. ->Attribute(AZ::Edit::Attributes::Min, 0.0f)
  43. ->Attribute(AZ::Edit::Attributes::Max, 90.0f)
  44. ->DataElement(AZ::Edit::UIHandlers::Slider, &SurfaceSlopeGradientConfig::m_slopeMax, "Slope Max", "Maximum surface slope angle in degrees.")
  45. ->Attribute(AZ::Edit::Attributes::Min, 0.0f)
  46. ->Attribute(AZ::Edit::Attributes::Max, 90.0f)
  47. ->DataElement(AZ::Edit::UIHandlers::ComboBox, &SurfaceSlopeGradientConfig::m_rampType, "Ramp Type", "Type of ramp to apply to the slope.")
  48. ->EnumAttribute(SurfaceSlopeGradientConfig::RampType::LINEAR_RAMP_DOWN, "Linear Ramp Down")
  49. ->EnumAttribute(SurfaceSlopeGradientConfig::RampType::LINEAR_RAMP_UP, "Linear Ramp Up")
  50. ->EnumAttribute(SurfaceSlopeGradientConfig::RampType::SMOOTH_STEP, "Smooth Step")
  51. // Note: ReadOnly doesn't currently propagate to children, so instead we hide/show smooth step parameters when
  52. // we change the ramp type. If ReadOnly is ever changed to propagate downwards, we should change the next line
  53. // to PropertyRefreshLevels::AttributesAndLevels and change the Visibility line below on m_smoothStep
  54. // to AZ::Edit::PropertyVisibility::Show.
  55. ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree)
  56. ->DataElement(AZ::Edit::UIHandlers::Default, &SurfaceSlopeGradientConfig::m_smoothStep, "Smooth Step Settings", "Parameters for controlling the smooth-step curve.")
  57. ->Attribute(AZ::Edit::Attributes::Visibility, &SurfaceSlopeGradientConfig::GetSmoothStepParameterVisibility)
  58. ->Attribute(AZ::Edit::Attributes::ReadOnly, &SurfaceSlopeGradientConfig::IsSmoothStepReadOnly)
  59. ->Attribute(AZ::Edit::Attributes::AutoExpand, false)
  60. ;
  61. }
  62. }
  63. if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
  64. {
  65. behaviorContext->Class<SurfaceSlopeGradientConfig>()
  66. ->Attribute(AZ::Script::Attributes::Category, "Vegetation")
  67. ->Constructor()
  68. ->Property("slopeMin", BehaviorValueProperty(&SurfaceSlopeGradientConfig::m_slopeMin))
  69. ->Property("slopeMax", BehaviorValueProperty(&SurfaceSlopeGradientConfig::m_slopeMax))
  70. ->Property("rampType",
  71. [](SurfaceSlopeGradientConfig* config) { return reinterpret_cast<AZ::u8&>(config->m_rampType); },
  72. [](SurfaceSlopeGradientConfig* config, const AZ::u8& i) { config->m_rampType = static_cast<SurfaceSlopeGradientConfig::RampType>(i); })
  73. ->Property("smoothStep", BehaviorValueProperty(&SurfaceSlopeGradientConfig::m_smoothStep))
  74. ->Method("GetNumTags", &SurfaceSlopeGradientConfig::GetNumTags)
  75. ->Method("GetTag", &SurfaceSlopeGradientConfig::GetTag)
  76. ->Method("RemoveTag", &SurfaceSlopeGradientConfig::RemoveTag)
  77. ->Method("AddTag", &SurfaceSlopeGradientConfig::AddTag)
  78. ;
  79. }
  80. }
  81. size_t SurfaceSlopeGradientConfig::GetNumTags() const
  82. {
  83. return m_surfaceTagsToSample.size();
  84. }
  85. AZ::Crc32 SurfaceSlopeGradientConfig::GetTag(int tagIndex) const
  86. {
  87. if (tagIndex < m_surfaceTagsToSample.size())
  88. {
  89. return m_surfaceTagsToSample[tagIndex];
  90. }
  91. return AZ::Crc32();
  92. }
  93. void SurfaceSlopeGradientConfig::RemoveTag(int tagIndex)
  94. {
  95. if (tagIndex < m_surfaceTagsToSample.size())
  96. {
  97. m_surfaceTagsToSample.erase(m_surfaceTagsToSample.begin() + tagIndex);
  98. }
  99. }
  100. void SurfaceSlopeGradientConfig::AddTag(AZStd::string tag)
  101. {
  102. m_surfaceTagsToSample.push_back(SurfaceData::SurfaceTag(tag));
  103. }
  104. void SurfaceSlopeGradientComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services)
  105. {
  106. services.push_back(AZ_CRC_CE("GradientService"));
  107. }
  108. void SurfaceSlopeGradientComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services)
  109. {
  110. services.push_back(AZ_CRC_CE("GradientService"));
  111. services.push_back(AZ_CRC_CE("GradientTransformService"));
  112. }
  113. void SurfaceSlopeGradientComponent::GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& services)
  114. {
  115. }
  116. void SurfaceSlopeGradientComponent::Reflect(AZ::ReflectContext* context)
  117. {
  118. SurfaceSlopeGradientConfig::Reflect(context);
  119. AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
  120. if (serialize)
  121. {
  122. serialize->Class<SurfaceSlopeGradientComponent, AZ::Component>()
  123. ->Version(0)
  124. ->Field("Configuration", &SurfaceSlopeGradientComponent::m_configuration)
  125. ;
  126. }
  127. if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
  128. {
  129. behaviorContext->Constant("SurfaceSlopeGradientComponentTypeId", BehaviorConstant(SurfaceSlopeGradientComponentTypeId));
  130. behaviorContext->Class<SurfaceSlopeGradientComponent>()->RequestBus("SurfaceSlopeGradientRequestBus");
  131. behaviorContext->EBus<SurfaceSlopeGradientRequestBus>("SurfaceSlopeGradientRequestBus")
  132. ->Attribute(AZ::Script::Attributes::Category, "Vegetation")
  133. ->Event("GetSlopeMin", &SurfaceSlopeGradientRequestBus::Events::GetSlopeMin)
  134. ->Event("SetSlopeMin", &SurfaceSlopeGradientRequestBus::Events::SetSlopeMin)
  135. ->VirtualProperty("SlopeMin", "GetSlopeMin", "SetSlopeMin")
  136. ->Event("GetSlopeMax", &SurfaceSlopeGradientRequestBus::Events::GetSlopeMax)
  137. ->Event("SetSlopeMax", &SurfaceSlopeGradientRequestBus::Events::SetSlopeMax)
  138. ->VirtualProperty("SlopeMax", "GetSlopeMax", "SetSlopeMax")
  139. ->Event("GetNumTags", &SurfaceSlopeGradientRequestBus::Events::GetNumTags)
  140. ->Event("GetTag", &SurfaceSlopeGradientRequestBus::Events::GetTag)
  141. ->Event("RemoveTag", &SurfaceSlopeGradientRequestBus::Events::RemoveTag)
  142. ->Event("AddTag", &SurfaceSlopeGradientRequestBus::Events::AddTag)
  143. ->Event("GetRampType", &SurfaceSlopeGradientRequestBus::Events::GetRampType)
  144. ->Event("SetRampType", &SurfaceSlopeGradientRequestBus::Events::SetRampType)
  145. ->VirtualProperty("RampType", "GetRampType", "SetRampType")
  146. ;
  147. }
  148. }
  149. SurfaceSlopeGradientComponent::SurfaceSlopeGradientComponent(const SurfaceSlopeGradientConfig& configuration)
  150. : m_configuration(configuration)
  151. {
  152. }
  153. void SurfaceSlopeGradientComponent::Activate()
  154. {
  155. SurfaceSlopeGradientRequestBus::Handler::BusConnect(GetEntityId());
  156. SmoothStepRequestBus::Handler::BusConnect(GetEntityId());
  157. // Connect to GradientRequestBus last so that everything is initialized before listening for gradient queries.
  158. GradientRequestBus::Handler::BusConnect(GetEntityId());
  159. }
  160. void SurfaceSlopeGradientComponent::Deactivate()
  161. {
  162. // Disconnect from GradientRequestBus first to ensure no queries are in process when deactivating.
  163. GradientRequestBus::Handler::BusDisconnect();
  164. SurfaceSlopeGradientRequestBus::Handler::BusDisconnect();
  165. SmoothStepRequestBus::Handler::BusDisconnect();
  166. }
  167. bool SurfaceSlopeGradientComponent::ReadInConfig(const AZ::ComponentConfig* baseConfig)
  168. {
  169. if (auto config = azrtti_cast<const SurfaceSlopeGradientConfig*>(baseConfig))
  170. {
  171. m_configuration = *config;
  172. return true;
  173. }
  174. return false;
  175. }
  176. bool SurfaceSlopeGradientComponent::WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const
  177. {
  178. if (auto config = azrtti_cast<SurfaceSlopeGradientConfig*>(outBaseConfig))
  179. {
  180. *config = m_configuration;
  181. return true;
  182. }
  183. return false;
  184. }
  185. float SurfaceSlopeGradientComponent::GetValue(const GradientSampleParams& sampleParams) const
  186. {
  187. float result = 0.0f;
  188. GetValues(AZStd::span<const AZ::Vector3>(&sampleParams.m_position, 1), AZStd::span<float>(&result, 1));
  189. return result;
  190. }
  191. void SurfaceSlopeGradientComponent::GetValues(AZStd::span<const AZ::Vector3> positions, AZStd::span<float> outValues) const
  192. {
  193. if (positions.size() != outValues.size())
  194. {
  195. AZ_Assert(false, "input and output lists are different sizes (%zu vs %zu).", positions.size(), outValues.size());
  196. return;
  197. }
  198. if (GradientRequestBus::HasReentrantEBusUseThisThread())
  199. {
  200. AZ_ErrorOnce("GradientSignal", false, "Detected cyclic dependencies with surface tag references on entity '%s' (%s)",
  201. GetEntity()->GetName().c_str(), GetEntityId().ToString().c_str());
  202. return;
  203. }
  204. AZStd::shared_lock lock(m_queryMutex);
  205. SurfaceData::SurfacePointList points;
  206. AZ::Interface<SurfaceData::SurfaceDataSystem>::Get()->GetSurfacePointsFromList(
  207. positions, m_configuration.m_surfaceTagsToSample, points);
  208. const float angleMin = AZ::DegToRad(AZ::GetClamp(m_configuration.m_slopeMin, 0.0f, 90.0f));
  209. const float angleMax = AZ::DegToRad(AZ::GetClamp(m_configuration.m_slopeMax, 0.0f, 90.0f));
  210. for (size_t index = 0; index < positions.size(); index++)
  211. {
  212. if (points.IsEmpty(index))
  213. {
  214. outValues[index] = 0.0f;
  215. }
  216. else
  217. {
  218. // Assuming our surface normal vector is actually normalized, we can get the slope
  219. // by just grabbing the Z value. It's the same thing as normal.Dot(AZ::Vector3::CreateAxisZ()).
  220. auto highestSurfacePoint = points.GetHighestSurfacePoint(index);
  221. AZ_Assert(
  222. highestSurfacePoint.m_normal.GetNormalized().IsClose(highestSurfacePoint.m_normal),
  223. "Surface normals are expected to be normalized");
  224. const float slope = highestSurfacePoint.m_normal.GetZ();
  225. // Convert slope back to an angle so that we can lerp in "angular space", not "slope value space".
  226. // (We want our 0-1 range to be linear across the range of angles)
  227. const float slopeAngle = acosf(slope);
  228. switch (m_configuration.m_rampType)
  229. {
  230. case SurfaceSlopeGradientConfig::RampType::SMOOTH_STEP:
  231. outValues[index] = m_configuration.m_smoothStep.GetSmoothedValue(GetRatio(angleMin, angleMax, slopeAngle));
  232. break;
  233. case SurfaceSlopeGradientConfig::RampType::LINEAR_RAMP_UP:
  234. // For ramp up, linearly interpolate from min to max.
  235. outValues[index] = GetRatio(angleMin, angleMax, slopeAngle);
  236. break;
  237. case SurfaceSlopeGradientConfig::RampType::LINEAR_RAMP_DOWN:
  238. default:
  239. // For ramp down, linearly interpolate from max to min.
  240. outValues[index] = GetRatio(angleMax, angleMin, slopeAngle);
  241. break;
  242. }
  243. }
  244. }
  245. }
  246. float SurfaceSlopeGradientComponent::GetSlopeMin() const
  247. {
  248. return m_configuration.m_slopeMin;
  249. }
  250. void SurfaceSlopeGradientComponent::SetSlopeMin(float slopeMin)
  251. {
  252. // Only hold the lock while we're changing the data. Don't hold onto it during the OnCompositionChanged call, because that can
  253. // execute an arbitrary amount of logic, including calls back to this component.
  254. {
  255. AZStd::unique_lock lock(m_queryMutex);
  256. m_configuration.m_slopeMin = slopeMin;
  257. }
  258. LmbrCentral::DependencyNotificationBus::Event(GetEntityId(), &LmbrCentral::DependencyNotificationBus::Events::OnCompositionChanged);
  259. }
  260. float SurfaceSlopeGradientComponent::GetSlopeMax() const
  261. {
  262. return m_configuration.m_slopeMax;
  263. }
  264. void SurfaceSlopeGradientComponent::SetSlopeMax(float slopeMax)
  265. {
  266. // Only hold the lock while we're changing the data. Don't hold onto it during the OnCompositionChanged call, because that can
  267. // execute an arbitrary amount of logic, including calls back to this component.
  268. {
  269. AZStd::unique_lock lock(m_queryMutex);
  270. m_configuration.m_slopeMax = slopeMax;
  271. }
  272. LmbrCentral::DependencyNotificationBus::Event(GetEntityId(), &LmbrCentral::DependencyNotificationBus::Events::OnCompositionChanged);
  273. }
  274. size_t SurfaceSlopeGradientComponent::GetNumTags() const
  275. {
  276. return m_configuration.GetNumTags();
  277. }
  278. AZ::Crc32 SurfaceSlopeGradientComponent::GetTag(int tagIndex) const
  279. {
  280. return m_configuration.GetTag(tagIndex);
  281. }
  282. void SurfaceSlopeGradientComponent::RemoveTag(int tagIndex)
  283. {
  284. // Only hold the lock while we're changing the data. Don't hold onto it during the OnCompositionChanged call, because that can
  285. // execute an arbitrary amount of logic, including calls back to this component.
  286. {
  287. AZStd::unique_lock lock(m_queryMutex);
  288. m_configuration.RemoveTag(tagIndex);
  289. }
  290. LmbrCentral::DependencyNotificationBus::Event(GetEntityId(), &LmbrCentral::DependencyNotificationBus::Events::OnCompositionChanged);
  291. }
  292. void SurfaceSlopeGradientComponent::AddTag(AZStd::string tag)
  293. {
  294. // Only hold the lock while we're changing the data. Don't hold onto it during the OnCompositionChanged call, because that can
  295. // execute an arbitrary amount of logic, including calls back to this component.
  296. {
  297. AZStd::unique_lock lock(m_queryMutex);
  298. m_configuration.AddTag(tag);
  299. }
  300. LmbrCentral::DependencyNotificationBus::Event(GetEntityId(), &LmbrCentral::DependencyNotificationBus::Events::OnCompositionChanged);
  301. }
  302. AZ::u8 SurfaceSlopeGradientComponent::GetRampType() const
  303. {
  304. return static_cast<AZ::u8>(m_configuration.m_rampType);
  305. }
  306. void SurfaceSlopeGradientComponent::SetRampType(AZ::u8 type)
  307. {
  308. // Only hold the lock while we're changing the data. Don't hold onto it during the OnCompositionChanged call, because that can
  309. // execute an arbitrary amount of logic, including calls back to this component.
  310. {
  311. AZStd::unique_lock lock(m_queryMutex);
  312. m_configuration.m_rampType = static_cast<SurfaceSlopeGradientConfig::RampType>(type);
  313. }
  314. LmbrCentral::DependencyNotificationBus::Event(GetEntityId(), &LmbrCentral::DependencyNotificationBus::Events::OnCompositionChanged);
  315. }
  316. float SurfaceSlopeGradientComponent::GetFallOffRange() const
  317. {
  318. return m_configuration.m_smoothStep.m_falloffRange;
  319. }
  320. void SurfaceSlopeGradientComponent::SetFallOffRange(float range)
  321. {
  322. // Only hold the lock while we're changing the data. Don't hold onto it during the OnCompositionChanged call, because that can
  323. // execute an arbitrary amount of logic, including calls back to this component.
  324. {
  325. AZStd::unique_lock lock(m_queryMutex);
  326. m_configuration.m_smoothStep.m_falloffRange = range;
  327. }
  328. LmbrCentral::DependencyNotificationBus::Event(GetEntityId(), &LmbrCentral::DependencyNotificationBus::Events::OnCompositionChanged);
  329. }
  330. float SurfaceSlopeGradientComponent::GetFallOffStrength() const
  331. {
  332. return m_configuration.m_smoothStep.m_falloffStrength;
  333. }
  334. void SurfaceSlopeGradientComponent::SetFallOffStrength(float strength)
  335. {
  336. // Only hold the lock while we're changing the data. Don't hold onto it during the OnCompositionChanged call, because that can
  337. // execute an arbitrary amount of logic, including calls back to this component.
  338. {
  339. AZStd::unique_lock lock(m_queryMutex);
  340. m_configuration.m_smoothStep.m_falloffStrength = strength;
  341. }
  342. LmbrCentral::DependencyNotificationBus::Event(GetEntityId(), &LmbrCentral::DependencyNotificationBus::Events::OnCompositionChanged);
  343. }
  344. float SurfaceSlopeGradientComponent::GetFallOffMidpoint() const
  345. {
  346. return m_configuration.m_smoothStep.m_falloffMidpoint;
  347. }
  348. void SurfaceSlopeGradientComponent::SetFallOffMidpoint(float midpoint)
  349. {
  350. // Only hold the lock while we're changing the data. Don't hold onto it during the OnCompositionChanged call, because that can
  351. // execute an arbitrary amount of logic, including calls back to this component.
  352. {
  353. AZStd::unique_lock lock(m_queryMutex);
  354. m_configuration.m_smoothStep.m_falloffMidpoint = midpoint;
  355. }
  356. LmbrCentral::DependencyNotificationBus::Event(GetEntityId(), &LmbrCentral::DependencyNotificationBus::Events::OnCompositionChanged);
  357. }
  358. }