EditorImageGradientComponent.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  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 <Editor/EditorImageGradientComponent.h>
  9. #include <AzToolsFramework/API/EntityCompositionNotificationBus.h>
  10. #include <GradientSignal/Editor/EditorGradientImageCreatorUtils.h>
  11. namespace GradientSignal
  12. {
  13. // Implements EditorImageGradientComponent RTTI functions
  14. AZ_RTTI_NO_TYPE_INFO_IMPL(EditorImageGradientComponent, AzToolsFramework::Components::EditorComponentBase);
  15. void EditorImageGradientComponent::Reflect(AZ::ReflectContext* context)
  16. {
  17. EditorImageGradientComponentMode::Reflect(context);
  18. if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  19. {
  20. serializeContext->Class<EditorImageGradientComponent, AzToolsFramework::Components::EditorComponentBase>()
  21. ->Version(4)
  22. ->Field("Previewer", &EditorImageGradientComponent::m_previewer)
  23. ->Field("Configuration", &EditorImageGradientComponent::m_configuration)
  24. ->Field("PaintableImageAssetHelper", &EditorImageGradientComponent::m_paintableImageAssetHelper)
  25. ;
  26. if (auto editContext = serializeContext->GetEditContext())
  27. {
  28. editContext->Class<ImageGradientConfig>("Image Gradient", "")
  29. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  30. ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
  31. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  32. ->DataElement(AZ::Edit::UIHandlers::Default, &ImageGradientConfig::m_imageAsset,
  33. "Image Asset", "Image asset whose values will be mapped as gradient output.")
  34. ->Attribute(AZ::Edit::Attributes::Handler, AZ_CRC_CE("GradientSignalStreamingImageAsset"))
  35. ->Attribute(AZ::Edit::Attributes::NameLabelOverride, &ImageGradientConfig::GetImageAssetPropertyName)
  36. ->Attribute(AZ::Edit::Attributes::ReadOnly, &ImageGradientConfig::IsImageAssetReadOnly)
  37. // Refresh the attributes because some fields will switch between read-only and writeable when
  38. // the image asset is changed.
  39. ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::AttributesAndValues)
  40. ->DataElement(AZ::Edit::UIHandlers::ComboBox, &ImageGradientConfig::m_samplingType,
  41. "Sampling Type", "Sampling type to use for the image data.")
  42. ->EnumAttribute(SamplingType::Point, "Point")
  43. ->EnumAttribute(SamplingType::Bilinear, "Bilinear")
  44. ->EnumAttribute(SamplingType::Bicubic, "Bicubic")
  45. ->Attribute(AZ::Edit::Attributes::ReadOnly, &ImageGradientConfig::AreImageOptionsReadOnly)
  46. ->DataElement(AZ::Edit::UIHandlers::Vector2, &ImageGradientConfig::m_tiling,
  47. "Tiling", "Number of times to tile horizontally/vertically.")
  48. ->Attribute(AZ::Edit::Attributes::Min, 0.01f)
  49. ->Attribute(AZ::Edit::Attributes::SoftMin, 1.0f)
  50. ->Attribute(AZ::Edit::Attributes::Max, std::numeric_limits<float>::max())
  51. ->Attribute(AZ::Edit::Attributes::SoftMax, 1024.0f)
  52. ->Attribute(AZ::Edit::Attributes::Step, 0.25f)
  53. ->Attribute(AZ::Edit::Attributes::ReadOnly, &ImageGradientConfig::AreImageOptionsReadOnly)
  54. ->DataElement(AZ::Edit::UIHandlers::ComboBox, &ImageGradientConfig::m_channelToUse,
  55. "Channel To Use", "The channel to use from the image.")
  56. ->EnumAttribute(ChannelToUse::Red, "Red")
  57. ->EnumAttribute(ChannelToUse::Green, "Green")
  58. ->EnumAttribute(ChannelToUse::Blue, "Blue")
  59. ->EnumAttribute(ChannelToUse::Alpha, "Alpha")
  60. ->EnumAttribute(ChannelToUse::Terrarium, "Terrarium")
  61. ->Attribute(AZ::Edit::Attributes::ReadOnly, &ImageGradientConfig::AreImageOptionsReadOnly)
  62. ->DataElement(
  63. AZ::Edit::UIHandlers::Slider, &ImageGradientConfig::m_mipIndex, "Mip Index", "Mip index to sample from.")
  64. ->Attribute(AZ::Edit::Attributes::Min, 0)
  65. ->Attribute(AZ::Edit::Attributes::Max, AZ::RHI::Limits::Image::MipCountMax)
  66. ->Attribute(AZ::Edit::Attributes::ReadOnly, &ImageGradientConfig::AreImageOptionsReadOnly)
  67. ->DataElement(AZ::Edit::UIHandlers::ComboBox, &ImageGradientConfig::m_customScaleType,
  68. "Custom Scale", "Choose a type of scaling to be applied to the image data.")
  69. ->EnumAttribute(CustomScaleType::None, "None")
  70. ->EnumAttribute(CustomScaleType::Auto, "Auto")
  71. ->EnumAttribute(CustomScaleType::Manual, "Manual")
  72. // Refresh the entire tree on scaling changes, because it will show/hide the scale ranges for Manual scaling.
  73. ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree)
  74. ->Attribute(AZ::Edit::Attributes::ReadOnly, &ImageGradientConfig::AreImageOptionsReadOnly)
  75. ->DataElement(AZ::Edit::UIHandlers::Default, &ImageGradientConfig::m_scaleRangeMin,
  76. "Range Minimum", "The minimum range each value from the image data is scaled against.")
  77. ->Attribute(AZ::Edit::Attributes::Visibility, &ImageGradientConfig::GetManualScaleVisibility)
  78. ->Attribute(AZ::Edit::Attributes::ReadOnly, &ImageGradientConfig::AreImageOptionsReadOnly)
  79. ->DataElement(AZ::Edit::UIHandlers::Default, &ImageGradientConfig::m_scaleRangeMax,
  80. "Range Maximum", "The maximum range each value from the image data is scaled against.")
  81. ->Attribute(AZ::Edit::Attributes::Visibility, &ImageGradientConfig::GetManualScaleVisibility)
  82. ->Attribute(AZ::Edit::Attributes::ReadOnly, &ImageGradientConfig::AreImageOptionsReadOnly)
  83. ;
  84. editContext
  85. ->Class<EditorImageGradientComponent>(
  86. EditorImageGradientComponent::s_componentName, EditorImageGradientComponent::s_componentDescription)
  87. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  88. ->Attribute(AZ::Edit::Attributes::Icon, EditorImageGradientComponent::s_icon)
  89. ->Attribute(AZ::Edit::Attributes::ViewportIcon, EditorImageGradientComponent::s_viewportIcon)
  90. ->Attribute(AZ::Edit::Attributes::HelpPageURL, EditorImageGradientComponent::s_helpUrl)
  91. ->Attribute(AZ::Edit::Attributes::Category, EditorImageGradientComponent::s_categoryName)
  92. ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game"))
  93. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  94. ->DataElement(
  95. AZ::Edit::UIHandlers::Default, &EditorImageGradientComponent::m_previewer, "Previewer", "Gradient Previewer")
  96. // Configuration for the Image Gradient control itself
  97. ->DataElement(AZ::Edit::UIHandlers::Default, &EditorImageGradientComponent::m_configuration, "Configuration", "")
  98. ->Attribute(AZ::Edit::Attributes::ReadOnly, &EditorImageGradientComponent::GetImageOptionsReadOnly)
  99. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorImageGradientComponent::ConfigurationChanged)
  100. // Paint controls for editing the image
  101. ->DataElement(AZ::Edit::UIHandlers::Default, &EditorImageGradientComponent::m_paintableImageAssetHelper,
  102. "Paint Image", "Paint into an image asset")
  103. ->Attribute(AZ::Edit::Attributes::ButtonText, "Paint")
  104. ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
  105. ;
  106. }
  107. }
  108. }
  109. // The following methods pass through to the runtime component so that the Editor component shares the same requirements.
  110. void EditorImageGradientComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& services)
  111. {
  112. ImageGradientComponent::GetRequiredServices(services);
  113. }
  114. void EditorImageGradientComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services)
  115. {
  116. ImageGradientComponent::GetIncompatibleServices(services);
  117. }
  118. void EditorImageGradientComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services)
  119. {
  120. ImageGradientComponent::GetProvidedServices(services);
  121. }
  122. void EditorImageGradientComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& services)
  123. {
  124. ImageGradientComponent::GetDependentServices(services);
  125. }
  126. void EditorImageGradientComponent::BuildGameEntity(AZ::Entity* gameEntity)
  127. {
  128. // When building the game entity, use the copy of the runtime configuration on the Editor component to create
  129. // a new runtime component that's configured correctly.
  130. gameEntity->AddComponent(aznew ImageGradientComponent(m_configuration));
  131. }
  132. void EditorImageGradientComponent::Init()
  133. {
  134. AzToolsFramework::Components::EditorComponentBase::Init();
  135. // Initialize the copy of the runtime component.
  136. m_runtimeComponentActive = false;
  137. m_component.ReadInConfig(&m_configuration);
  138. m_component.Init();
  139. }
  140. void EditorImageGradientComponent::Activate()
  141. {
  142. // This block of code is aligned with EditorWrappedComponentBase
  143. {
  144. AzToolsFramework::Components::EditorComponentBase::Activate();
  145. // Use the visibility bus to control whether or not the runtime gradient is active and processing in the Editor.
  146. AzToolsFramework::EditorVisibilityNotificationBus::Handler::BusConnect(GetEntityId());
  147. AzToolsFramework::EditorEntityInfoRequestBus::EventResult(
  148. m_visible, GetEntityId(), &AzToolsFramework::EditorEntityInfoRequestBus::Events::IsVisible);
  149. // Synchronize the runtime component with the Editor component.
  150. m_component.ReadInConfig(&m_configuration);
  151. m_component.SetEntity(GetEntity());
  152. if (m_visible)
  153. {
  154. m_component.Activate();
  155. m_runtimeComponentActive = true;
  156. }
  157. }
  158. LmbrCentral::DependencyNotificationBus::Handler::BusConnect(GetEntityId());
  159. AzFramework::PaintBrushNotificationBus::Handler::BusConnect({ GetEntityId(), GetId() });
  160. m_previewer.Activate(GetEntityId());
  161. // Initialize the paintable image asset helper.
  162. m_paintableImageAssetHelper.Activate(
  163. AZ::EntityComponentIdPair(GetEntityId(), GetId()),
  164. OutputFormat::R32,
  165. "Image Asset",
  166. [this]()
  167. {
  168. // Get a default image filename and path that either uses the source asset filename (if the source asset exists)
  169. // or creates a new name by taking the entity name and adding "_gsi.tif".
  170. return AZ::IO::Path(ImageCreatorUtils::GetDefaultImageSourcePath(
  171. m_configuration.m_imageAsset.GetId(), GetEntity()->GetName() + AZStd::string("_gsi.tif")));
  172. },
  173. [this](AZ::Data::Asset<AZ::Data::AssetData> createdAsset)
  174. {
  175. // Set the active image to the created one.
  176. m_component.SetImageAsset(createdAsset);
  177. OnCompositionChanged();
  178. });
  179. AZStd::string assetLabel =
  180. m_paintableImageAssetHelper.Refresh(m_configuration.m_imageAsset);
  181. m_configuration.SetImageAssetPropertyName(assetLabel);
  182. }
  183. void EditorImageGradientComponent::Deactivate()
  184. {
  185. m_paintableImageAssetHelper.Deactivate();
  186. m_previewer.Deactivate();
  187. AzFramework::PaintBrushNotificationBus::Handler::BusDisconnect();
  188. LmbrCentral::DependencyNotificationBus::Handler::BusDisconnect();
  189. // This block of code is aligned with EditorWrappedComponentBase
  190. {
  191. AzToolsFramework::EditorVisibilityNotificationBus::Handler::BusDisconnect();
  192. AzToolsFramework::Components::EditorComponentBase::Deactivate();
  193. m_runtimeComponentActive = false;
  194. m_component.Deactivate();
  195. // remove the entity association, in case the parent component is being removed, otherwise the component will be reactivated
  196. m_component.SetEntity(nullptr);
  197. }
  198. }
  199. void EditorImageGradientComponent::OnEntityVisibilityChanged(bool visibility)
  200. {
  201. if (m_visible != visibility)
  202. {
  203. m_visible = visibility;
  204. ConfigurationChanged();
  205. }
  206. }
  207. void EditorImageGradientComponent::OnCompositionRegionChanged([[maybe_unused]] const AZ::Aabb& dirtyRegion)
  208. {
  209. // If only a region of the image gradient changed, then we only need to refresh the preview.
  210. m_previewer.RefreshPreview();
  211. }
  212. void EditorImageGradientComponent::OnCompositionChanged()
  213. {
  214. // Keep track of what our previous label was, so that we know to refresh if it changes.
  215. // We need to grab this before calling WriteOutConfig() because that will overwrite the label with
  216. // the empty label that's stored with the runtime component.
  217. auto previousImageAssetPropertyName = m_configuration.GetImageAssetPropertyName();
  218. m_previewer.RefreshPreview();
  219. m_component.WriteOutConfig(&m_configuration);
  220. SetDirty();
  221. AZStd::string assetLabel = m_paintableImageAssetHelper.Refresh(m_configuration.m_imageAsset);
  222. m_configuration.SetImageAssetPropertyName(assetLabel);
  223. if (m_configuration.GetImageAssetPropertyName() != previousImageAssetPropertyName)
  224. {
  225. // If the asset status changed and the image asset property is visible, refresh the entire tree so
  226. // that the label change is picked up.
  227. AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast(
  228. &AzToolsFramework::ToolsApplicationEvents::InvalidatePropertyDisplay, AzToolsFramework::Refresh_EntireTree);
  229. }
  230. else
  231. {
  232. AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast(
  233. &AzToolsFramework::ToolsApplicationEvents::InvalidatePropertyDisplay, AzToolsFramework::Refresh_AttributesAndValues);
  234. }
  235. }
  236. AZ::u32 EditorImageGradientComponent::ConfigurationChanged()
  237. {
  238. // Cancel any pending preview refreshes before locking, to help ensure the preview itself isn't holding the lock
  239. auto entityIds = m_previewer.CancelPreviewRendering();
  240. // This block of code aligns with EditorWrappedComponentBase
  241. {
  242. if (m_runtimeComponentActive)
  243. {
  244. m_runtimeComponentActive = false;
  245. m_component.Deactivate();
  246. }
  247. m_component.ReadInConfig(&m_configuration);
  248. if (m_visible && !m_runtimeComponentActive)
  249. {
  250. m_component.Activate();
  251. m_runtimeComponentActive = true;
  252. }
  253. }
  254. // Refresh any of the previews that we canceled that were still in progress so they can be completed
  255. m_previewer.RefreshPreviews(entityIds);
  256. // This OnCompositionChanged notification will refresh our own preview so we don't need to call RefreshPreview explicitly
  257. LmbrCentral::DependencyNotificationBus::Event(GetEntityId(), &LmbrCentral::DependencyNotificationBus::Events::OnCompositionChanged);
  258. return AZ::Edit::PropertyRefreshLevels::None;
  259. }
  260. bool EditorImageGradientComponent::GetImageOptionsReadOnly() const
  261. {
  262. return (m_component.ModificationBufferIsActive());
  263. }
  264. void EditorImageGradientComponent::OnPaintModeBegin()
  265. {
  266. m_configuration.m_numImageModificationsActive++;
  267. // Forward the paint brush notification to the runtime component.
  268. AzFramework::PaintBrushNotificationBus::Event(
  269. { m_component.GetEntityId(), m_component.GetId() }, &AzFramework::PaintBrushNotificationBus::Events::OnPaintModeBegin);
  270. // While we're editing, we need to set all the configuration properties to read-only and refresh them.
  271. // Otherwise, the property changes could conflict with the current painted modifications.
  272. AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast(
  273. &AzToolsFramework::ToolsApplicationEvents::InvalidatePropertyDisplay, AzToolsFramework::Refresh_AttributesAndValues);
  274. }
  275. void EditorImageGradientComponent::OnPaintModeEnd()
  276. {
  277. // Forward the paint brush notification to the runtime component.
  278. AzFramework::PaintBrushNotificationBus::Event(
  279. { m_component.GetEntityId(), m_component.GetId() }, &AzFramework::PaintBrushNotificationBus::Events::OnPaintModeEnd);
  280. m_configuration.m_numImageModificationsActive--;
  281. // We're done editing, so set all the configuration properties back to writeable and refresh them.
  282. AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast(
  283. &AzToolsFramework::ToolsApplicationEvents::InvalidatePropertyDisplay, AzToolsFramework::Refresh_AttributesAndValues);
  284. // It's possible that we're leaving component mode as the result of an "undo" action.
  285. // If that's the case, don't prompt the user to save the changes.
  286. if (!AzToolsFramework::UndoRedoOperationInProgress() && m_component.ImageIsModified())
  287. {
  288. SavePaintedData();
  289. }
  290. }
  291. void EditorImageGradientComponent::OnBrushStrokeBegin(const AZ::Color& color)
  292. {
  293. // Forward the paint brush notification to the runtime component.
  294. AzFramework::PaintBrushNotificationBus::Event(
  295. { m_component.GetEntityId(), m_component.GetId() },
  296. &AzFramework::PaintBrushNotificationBus::Events::OnBrushStrokeBegin,
  297. color);
  298. }
  299. void EditorImageGradientComponent::OnBrushStrokeEnd()
  300. {
  301. // Forward the paint brush notification to the runtime component.
  302. AzFramework::PaintBrushNotificationBus::Event(
  303. { m_component.GetEntityId(), m_component.GetId() }, &AzFramework::PaintBrushNotificationBus::Events::OnBrushStrokeEnd);
  304. }
  305. void EditorImageGradientComponent::OnPaint(
  306. const AZ::Color& color, const AZ::Aabb& dirtyArea, ValueLookupFn& valueLookupFn, BlendFn& blendFn)
  307. {
  308. // Forward the paint brush notification to the runtime component.
  309. AzFramework::PaintBrushNotificationBus::Event(
  310. { m_component.GetEntityId(), m_component.GetId() }, &AzFramework::PaintBrushNotificationBus::Events::OnPaint,
  311. color, dirtyArea, valueLookupFn, blendFn);
  312. }
  313. void EditorImageGradientComponent::OnSmooth(
  314. const AZ::Color& color,
  315. const AZ::Aabb& dirtyArea,
  316. ValueLookupFn& valueLookupFn,
  317. AZStd::span<const AZ::Vector3> valuePointOffsets,
  318. SmoothFn& smoothFn)
  319. {
  320. // Forward the paint brush notification to the runtime component.
  321. AzFramework::PaintBrushNotificationBus::Event(
  322. { m_component.GetEntityId(), m_component.GetId() }, &AzFramework::PaintBrushNotificationBus::Events::OnSmooth,
  323. color, dirtyArea, valueLookupFn, valuePointOffsets, smoothFn);
  324. }
  325. AZ::Color EditorImageGradientComponent::OnGetColor(const AZ::Vector3& brushCenter) const
  326. {
  327. AZ::Color result;
  328. // Forward the paint brush notification to the runtime component.
  329. AzFramework::PaintBrushNotificationBus::EventResult(result,
  330. { m_component.GetEntityId(), m_component.GetId() },
  331. &AzFramework::PaintBrushNotificationBus::Events::OnGetColor,
  332. brushCenter);
  333. return result;
  334. }
  335. bool EditorImageGradientComponent::SavePaintedData()
  336. {
  337. // Get the resolution of our modified image.
  338. const int imageResolutionX = aznumeric_cast<int>(m_component.GetImageWidth());
  339. const int imageResolutionY = aznumeric_cast<int>(m_component.GetImageHeight());
  340. // Get the image modification buffer
  341. auto pixelBuffer = m_component.GetImageModificationBuffer();
  342. const OutputFormat format = OutputFormat::R32;
  343. auto createdAsset = m_paintableImageAssetHelper.SaveImage(
  344. imageResolutionX, imageResolutionY, format,
  345. AZStd::span<const uint8_t>(reinterpret_cast<uint8_t*>(pixelBuffer->data()), pixelBuffer->size() * sizeof(float)));
  346. if (createdAsset)
  347. {
  348. // Set the active image to the created one.
  349. m_component.SetImageAsset(createdAsset.value());
  350. OnCompositionChanged();
  351. }
  352. return createdAsset.has_value();
  353. }
  354. }