RandomGradientComponent.cpp 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  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/RandomGradientComponent.h>
  9. #include <AzCore/RTTI/BehaviorContext.h>
  10. #include <AzCore/Serialization/EditContext.h>
  11. #include <AzCore/Serialization/SerializeContext.h>
  12. #include <AzCore/Debug/Profiler.h>
  13. #include <LmbrCentral/Dependency/DependencyNotificationBus.h>
  14. #include <GradientSignal/Ebuses/GradientTransformRequestBus.h>
  15. namespace GradientSignal
  16. {
  17. void RandomGradientConfig::Reflect(AZ::ReflectContext* context)
  18. {
  19. AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
  20. if (serialize)
  21. {
  22. serialize->Class<RandomGradientConfig, AZ::ComponentConfig>()
  23. ->Version(0)
  24. ->Field("RandomSeed", &RandomGradientConfig::m_randomSeed)
  25. ;
  26. AZ::EditContext* edit = serialize->GetEditContext();
  27. if (edit)
  28. {
  29. edit->Class<RandomGradientConfig>(
  30. "Random Gradient", "")
  31. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  32. ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
  33. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  34. ->DataElement(AZ::Edit::UIHandlers::Slider, &RandomGradientConfig::m_randomSeed, "Random Seed", "Seed value for the Random Noise Generator.")
  35. ->Attribute(AZ::Edit::Attributes::Min, 1)
  36. ->Attribute(AZ::Edit::Attributes::Max, std::numeric_limits<int>::max())
  37. ->Attribute(AZ::Edit::Attributes::SoftMin, 1)
  38. ->Attribute(AZ::Edit::Attributes::SoftMax, 100)
  39. ->Attribute(AZ::Edit::Attributes::Step, 10)
  40. ;
  41. }
  42. }
  43. if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
  44. {
  45. behaviorContext->Class<RandomGradientConfig>()
  46. ->Constructor()
  47. ->Attribute(AZ::Script::Attributes::Category, "Vegetation")
  48. ->Property("randomSeed", BehaviorValueProperty(&RandomGradientConfig::m_randomSeed))
  49. ;
  50. }
  51. }
  52. void RandomGradientComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services)
  53. {
  54. services.push_back(AZ_CRC_CE("GradientService"));
  55. }
  56. void RandomGradientComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services)
  57. {
  58. services.push_back(AZ_CRC_CE("GradientService"));
  59. }
  60. void RandomGradientComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& services)
  61. {
  62. services.push_back(AZ_CRC_CE("GradientTransformService"));
  63. }
  64. void RandomGradientComponent::Reflect(AZ::ReflectContext* context)
  65. {
  66. RandomGradientConfig::Reflect(context);
  67. AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
  68. if (serialize)
  69. {
  70. serialize->Class<RandomGradientComponent, AZ::Component>()
  71. ->Version(0)
  72. ->Field("Configuration", &RandomGradientComponent::m_configuration)
  73. ;
  74. }
  75. if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
  76. {
  77. behaviorContext->Constant("RandomGradientComponentTypeId", BehaviorConstant(RandomGradientComponentTypeId));
  78. behaviorContext->Class<RandomGradientComponent>()->RequestBus("RandomGradientRequestBus");
  79. behaviorContext->EBus<RandomGradientRequestBus>("RandomGradientRequestBus")
  80. ->Attribute(AZ::Script::Attributes::Category, "Vegetation")
  81. ->Event("GetRandomSeed", &RandomGradientRequestBus::Events::GetRandomSeed)
  82. ->Event("SetRandomSeed", &RandomGradientRequestBus::Events::SetRandomSeed)
  83. ->VirtualProperty("RandomSeed", "GetRandomSeed", "SetRandomSeed")
  84. ;
  85. }
  86. }
  87. RandomGradientComponent::RandomGradientComponent(const RandomGradientConfig& configuration)
  88. : m_configuration(configuration)
  89. {
  90. }
  91. void RandomGradientComponent::Activate()
  92. {
  93. // This will immediately call OnGradientTransformChanged and initialize m_gradientTransform.
  94. GradientTransformNotificationBus::Handler::BusConnect(GetEntityId());
  95. RandomGradientRequestBus::Handler::BusConnect(GetEntityId());
  96. // Connect to GradientRequestBus last so that everything is initialized before listening for gradient queries.
  97. GradientRequestBus::Handler::BusConnect(GetEntityId());
  98. }
  99. void RandomGradientComponent::Deactivate()
  100. {
  101. // Disconnect from GradientRequestBus first to ensure no queries are in process when deactivating.
  102. GradientRequestBus::Handler::BusDisconnect();
  103. RandomGradientRequestBus::Handler::BusDisconnect();
  104. GradientTransformNotificationBus::Handler::BusDisconnect();
  105. }
  106. bool RandomGradientComponent::ReadInConfig(const AZ::ComponentConfig* baseConfig)
  107. {
  108. if (auto config = azrtti_cast<const RandomGradientConfig*>(baseConfig))
  109. {
  110. m_configuration = *config;
  111. return true;
  112. }
  113. return false;
  114. }
  115. bool RandomGradientComponent::WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const
  116. {
  117. if (auto config = azrtti_cast<RandomGradientConfig*>(outBaseConfig))
  118. {
  119. *config = m_configuration;
  120. return true;
  121. }
  122. return false;
  123. }
  124. void RandomGradientComponent::OnGradientTransformChanged(const GradientTransform& newTransform)
  125. {
  126. AZStd::unique_lock lock(m_queryMutex);
  127. m_gradientTransform = newTransform;
  128. }
  129. float RandomGradientComponent::GetRandomValue(const AZ::Vector3& position, AZStd::size_t seed) const
  130. {
  131. // generating stable pseudo-random noise from a position based hash
  132. float x = position.GetX();
  133. float y = position.GetY();
  134. AZStd::size_t result = 0;
  135. AZStd::hash_combine<float>(result, x * seed + y);
  136. AZStd::hash_combine<float>(result, y * seed + x);
  137. AZStd::hash_combine<float>(result, x * y * seed);
  138. // always returns [0.0,1.0]
  139. return static_cast<float>(result % std::numeric_limits<AZ::u8>::max()) / static_cast<float>(std::numeric_limits<AZ::u8>::max());
  140. }
  141. float RandomGradientComponent::GetValue(const GradientSampleParams& sampleParams) const
  142. {
  143. AZStd::shared_lock lock(m_queryMutex);
  144. AZ::Vector3 uvw = sampleParams.m_position;
  145. bool wasPointRejected = false;
  146. m_gradientTransform.TransformPositionToUVW(sampleParams.m_position, uvw, wasPointRejected);
  147. if (!wasPointRejected)
  148. {
  149. const AZStd::size_t seed = m_configuration.m_randomSeed +
  150. AZStd::size_t(2); // Add 2 to avoid seeds 0 and 1, which can create strange patterns with this particular algorithm
  151. return GetRandomValue(uvw, seed);
  152. }
  153. return 0.0f;
  154. }
  155. void RandomGradientComponent::GetValues(AZStd::span<const AZ::Vector3> positions, AZStd::span<float> outValues) const
  156. {
  157. if (positions.size() != outValues.size())
  158. {
  159. AZ_Assert(false, "input and output lists are different sizes (%zu vs %zu).", positions.size(), outValues.size());
  160. return;
  161. }
  162. AZStd::shared_lock lock(m_queryMutex);
  163. AZ::Vector3 uvw;
  164. bool wasPointRejected = false;
  165. const AZStd::size_t seed = m_configuration.m_randomSeed +
  166. AZStd::size_t(2); // Add 2 to avoid seeds 0 and 1, which can create strange patterns with this particular algorithm
  167. for (size_t index = 0; index < positions.size(); index++)
  168. {
  169. m_gradientTransform.TransformPositionToUVW(positions[index], uvw, wasPointRejected);
  170. if (!wasPointRejected)
  171. {
  172. outValues[index] = GetRandomValue(uvw, seed);
  173. }
  174. else
  175. {
  176. outValues[index] = 0.0f;
  177. }
  178. }
  179. }
  180. int RandomGradientComponent::GetRandomSeed() const
  181. {
  182. return m_configuration.m_randomSeed;
  183. }
  184. void RandomGradientComponent::SetRandomSeed(int seed)
  185. {
  186. // Only hold the lock while we're changing the data. Don't hold onto it during the OnCompositionChanged call, because that can
  187. // execute an arbitrary amount of logic, including calls back to this component.
  188. {
  189. AZStd::unique_lock lock(m_queryMutex);
  190. m_configuration.m_randomSeed = seed;
  191. }
  192. LmbrCentral::DependencyNotificationBus::Event(GetEntityId(), &LmbrCentral::DependencyNotificationBus::Events::OnCompositionChanged);
  193. }
  194. }