EditorGradientSurfaceDataComponent.cpp 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  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 "EditorGradientSurfaceDataComponent.h"
  9. #include <AzCore/Serialization/Utils.h>
  10. #include <AzToolsFramework/API/ToolsApplicationAPI.h>
  11. #include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI.h>
  12. #include <LmbrCentral/Dependency/DependencyNotificationBus.h>
  13. #include <GradientSignal/Ebuses/GradientPreviewRequestBus.h>
  14. #include <SurfaceData/SurfacePointList.h>
  15. namespace GradientSignal
  16. {
  17. void EditorGradientSurfaceDataComponent::Reflect(AZ::ReflectContext* context)
  18. {
  19. BaseClassType::Reflect(context);
  20. AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
  21. if (serialize)
  22. {
  23. serialize->Class<EditorGradientSurfaceDataComponent, BaseClassType>()
  24. ->Version(1, &LmbrCentral::EditorWrappedComponentBaseVersionConverter<typename BaseClassType::WrappedComponentType, typename BaseClassType::WrappedConfigType,1>)
  25. ;
  26. AZ::EditContext* edit = serialize->GetEditContext();
  27. if (edit)
  28. {
  29. edit->Class<EditorGradientSurfaceDataComponent>(
  30. s_componentName, s_componentDescription)
  31. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  32. ->Attribute(AZ::Edit::Attributes::Icon, s_icon)
  33. ->Attribute(AZ::Edit::Attributes::ViewportIcon, s_viewportIcon)
  34. ->Attribute(AZ::Edit::Attributes::HelpPageURL, s_helpUrl)
  35. ->Attribute(AZ::Edit::Attributes::Category, s_categoryName)
  36. ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game"))
  37. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  38. ->ClassElement(AZ::Edit::ClassElements::Group, "Preview")
  39. ->Attribute(AZ::Edit::Attributes::AutoExpand, false)
  40. ->UIElement("GradientPreviewer", "Previewer")
  41. ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::Show)
  42. ->Attribute(AZ::Edit::Attributes::NameLabelOverride, "")
  43. ->Attribute(AZ_CRC_CE("GradientEntity"), &EditorGradientSurfaceDataComponent::GetGradientEntityId)
  44. ->Attribute(AZ_CRC_CE("GradientFilter"), &EditorGradientSurfaceDataComponent::GetFilterFunc)
  45. ->EndGroup()
  46. ;
  47. }
  48. }
  49. }
  50. void EditorGradientSurfaceDataComponent::Activate()
  51. {
  52. m_gradientEntityId = GetEntityId();
  53. BaseClassType::Activate();
  54. AzFramework::EntityDebugDisplayEventBus::Handler::BusConnect(GetEntityId());
  55. }
  56. void EditorGradientSurfaceDataComponent::Deactivate()
  57. {
  58. // Make sure any previews for this entity aren't currently trying to refresh. Otherwise, the preview job could call
  59. // back into our FilterFunc lambda below after the entity has already been destroyed.
  60. AZ::EntityId canceledEntity;
  61. GradientSignal::GradientPreviewRequestBus::EventResult(
  62. canceledEntity, GetEntityId(), &GradientSignal::GradientPreviewRequestBus::Events::CancelRefresh);
  63. // If the preview shouldn't be active, use an invalid entityId
  64. m_gradientEntityId = AZ::EntityId();
  65. AzFramework::EntityDebugDisplayEventBus::Handler::BusDisconnect();
  66. BaseClassType::Deactivate();
  67. }
  68. AZ::u32 EditorGradientSurfaceDataComponent::ConfigurationChanged()
  69. {
  70. auto result = BaseClassType::ConfigurationChanged();
  71. LmbrCentral::DependencyNotificationBus::Event(GetEntityId(), &LmbrCentral::DependencyNotificationBus::Events::OnCompositionChanged);
  72. return result;
  73. }
  74. AZ::EntityId EditorGradientSurfaceDataComponent::GetGradientEntityId() const
  75. {
  76. return m_gradientEntityId;
  77. }
  78. AZStd::function<float(float, const GradientSampleParams & params)> EditorGradientSurfaceDataComponent::GetFilterFunc()
  79. {
  80. // By default, our preview will show the gradient value that was queried from the gradient on this entity.
  81. // To show what this GradientSurfaceData component will produce, we use a custom FilterFunc to modify what
  82. // the preview will display. We create a SurfacePoint, call ModifySurfacePoints() on that point, and return
  83. // the max value from any tags returned, or 0 if no tags were added to the point.
  84. // This allows us to view the results of the GradientSurfaceData modifications, including threshold clamping and
  85. // constraining to shape bounds. Note that the primary gradient controls the "Pin Preview to Entity" preview setting
  86. // that affects *where* this preview is rendered. If the primary gradient is pinned to a different entity that doesn't
  87. // overlap this component's shape constraint entity, this preview can end up all black. To see a preview that lines up
  88. // with this shape constraint, the input gradient will need to "Pin Preview to Entity" to the same shape entity.
  89. return [this]([[maybe_unused]] float sampleValue, const GradientSampleParams& params)
  90. {
  91. // Create a fake surface point with the position we're sampling.
  92. AzFramework::SurfaceData::SurfacePoint point;
  93. point.m_position = params.m_position;
  94. point.m_normal = AZ::Vector3::CreateAxisZ();
  95. SurfaceData::SurfacePointList pointList;
  96. // Get the Surface Modifier handle for this component.
  97. SurfaceData::SurfaceDataRegistryHandle modifierHandle = SurfaceData::InvalidSurfaceDataRegistryHandle;
  98. modifierHandle = AZ::Interface<SurfaceData::SurfaceDataSystem>::Get()->GetSurfaceDataModifierHandle(m_component.GetEntityId());
  99. // Send the fake surface point into the component, see what emerges
  100. pointList.StartListConstruction(AZStd::span<const AzFramework::SurfaceData::SurfacePoint>(&point, 1));
  101. pointList.ModifySurfaceWeights(modifierHandle);
  102. pointList.EndListConstruction();
  103. // If the point was successfully modified, we should have one or more masks with a non-zero value.
  104. // Technically, they should all have the same value, but we'll grab the max from all of them in case
  105. // the underlying logic ever changes to allow separate ranges per tag.
  106. float result = 0.0f;
  107. pointList.EnumeratePoints([&result](
  108. [[maybe_unused]] size_t inPositionIndex, [[maybe_unused]] const AZ::Vector3& position,
  109. [[maybe_unused]] const AZ::Vector3& normal, const SurfaceData::SurfaceTagWeights& masks) -> bool
  110. {
  111. masks.EnumerateWeights(
  112. [&result]([[maybe_unused]] AZ::Crc32 surfaceType, float weight) -> bool
  113. {
  114. result = AZ::GetMax(result, weight);
  115. return true;
  116. });
  117. return true;
  118. });
  119. return result;
  120. };
  121. }
  122. }