EditorDiffuseProbeGridComponent.cpp 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693
  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 <EditorComponents/EditorDiffuseProbeGridComponent.h>
  9. #include <AzFramework/StringFunc/StringFunc.h>
  10. #include <AzToolsFramework/API/ToolsApplicationAPI.h>
  11. #include <AzToolsFramework/Entity/EditorEntityInfoBus.h>
  12. #include <AzToolsFramework/API/EditorAssetSystemAPI.h>
  13. #include <AzCore/Component/Entity.h>
  14. #include <AzCore/IO/SystemFile.h>
  15. #include <Atom/RPI.Reflect/Image/StreamingImagePoolAsset.h>
  16. #include <Atom/RPI.Reflect/Model/ModelAsset.h>
  17. #include <Atom/Utils/DdsFile.h>
  18. AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT
  19. #include <QApplication>
  20. #include <QMessageBox>
  21. #include <QProgressDialog>
  22. AZ_POP_DISABLE_WARNING
  23. namespace AZ
  24. {
  25. namespace Render
  26. {
  27. void EditorDiffuseProbeGridComponent::Reflect(AZ::ReflectContext* context)
  28. {
  29. BaseClass::Reflect(context);
  30. if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  31. {
  32. serializeContext->Class<EditorDiffuseProbeGridComponent, BaseClass>()
  33. ->Version(3, ConvertToEditorRenderComponentAdapter<1>) // added emissive multiplier
  34. ->Field("probeSpacingX", &EditorDiffuseProbeGridComponent::m_probeSpacingX)
  35. ->Field("probeSpacingY", &EditorDiffuseProbeGridComponent::m_probeSpacingY)
  36. ->Field("probeSpacingZ", &EditorDiffuseProbeGridComponent::m_probeSpacingZ)
  37. ->Field("ambientMultiplier", &EditorDiffuseProbeGridComponent::m_ambientMultiplier)
  38. ->Field("viewBias", &EditorDiffuseProbeGridComponent::m_viewBias)
  39. ->Field("normalBias", &EditorDiffuseProbeGridComponent::m_normalBias)
  40. ->Field("numRaysPerProbe", &EditorDiffuseProbeGridComponent::m_numRaysPerProbe)
  41. ->Field("scrolling", &EditorDiffuseProbeGridComponent::m_scrolling)
  42. ->Field("edgeBlendIbl", &EditorDiffuseProbeGridComponent::m_edgeBlendIbl)
  43. ->Field("frameUpdateCount", &EditorDiffuseProbeGridComponent::m_frameUpdateCount)
  44. ->Field("transparencyMode", &EditorDiffuseProbeGridComponent::m_transparencyMode)
  45. ->Field("emissiveMultiplier", &EditorDiffuseProbeGridComponent::m_emissiveMultiplier)
  46. ->Field("editorMode", &EditorDiffuseProbeGridComponent::m_editorMode)
  47. ->Field("runtimeMode", &EditorDiffuseProbeGridComponent::m_runtimeMode)
  48. ->Field("showVisualization", &EditorDiffuseProbeGridComponent::m_showVisualization)
  49. ->Field("showInactiveProbes", &EditorDiffuseProbeGridComponent::m_showInactiveProbes)
  50. ->Field("visualizationSphereRadius", &EditorDiffuseProbeGridComponent::m_visualizationSphereRadius)
  51. ;
  52. if (AZ::EditContext* editContext = serializeContext->GetEditContext())
  53. {
  54. editContext->Class<EditorDiffuseProbeGridComponent>(
  55. "Diffuse Probe Grid", "The DiffuseProbeGrid component generates a grid of diffuse light probes for global illumination")
  56. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  57. ->Attribute(AZ::Edit::Attributes::Category, "Graphics/Lighting")
  58. ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/Component_Placeholder.svg")
  59. ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Component_Placeholder.svg")
  60. ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c))
  61. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  62. ->Attribute(Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/diffuse-probe-grid/")
  63. ->Attribute(AZ::Edit::Attributes::PrimaryAssetType, AZ::AzTypeInfo<RPI::ModelAsset>::Uuid())
  64. ->ClassElement(AZ::Edit::ClassElements::Group, "Probe Spacing")
  65. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  66. ->DataElement(AZ::Edit::UIHandlers::Default, &EditorDiffuseProbeGridComponent::m_probeSpacingX, "X-Axis", "Meters between probes on the X-axis")
  67. ->Attribute(AZ::Edit::Attributes::Min, 0.1f)
  68. ->Attribute(AZ::Edit::Attributes::Suffix, " meters")
  69. ->Attribute(AZ::Edit::Attributes::ChangeValidate, &EditorDiffuseProbeGridComponent::OnProbeSpacingValidateX)
  70. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorDiffuseProbeGridComponent::OnProbeSpacingChanged)
  71. ->DataElement(AZ::Edit::UIHandlers::Default, &EditorDiffuseProbeGridComponent::m_probeSpacingY, "Y-Axis", "Meters between probes on the Y-axis")
  72. ->Attribute(AZ::Edit::Attributes::Min, 0.1f)
  73. ->Attribute(AZ::Edit::Attributes::Suffix, " meters")
  74. ->Attribute(AZ::Edit::Attributes::ChangeValidate, &EditorDiffuseProbeGridComponent::OnProbeSpacingValidateY)
  75. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorDiffuseProbeGridComponent::OnProbeSpacingChanged)
  76. ->DataElement(AZ::Edit::UIHandlers::Default, &EditorDiffuseProbeGridComponent::m_probeSpacingZ, "Z-Axis", "Meters between probes on the Z-axis")
  77. ->Attribute(AZ::Edit::Attributes::Min, 0.1f)
  78. ->Attribute(AZ::Edit::Attributes::Suffix, " meters")
  79. ->Attribute(AZ::Edit::Attributes::ChangeValidate, &EditorDiffuseProbeGridComponent::OnProbeSpacingValidateZ)
  80. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorDiffuseProbeGridComponent::OnProbeSpacingChanged)
  81. ->ClassElement(AZ::Edit::ClassElements::Group, "Grid Settings")
  82. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  83. ->DataElement(AZ::Edit::UIHandlers::Slider, &EditorDiffuseProbeGridComponent::m_ambientMultiplier, "Ambient Multiplier", "Multiplier for the irradiance intensity")
  84. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorDiffuseProbeGridComponent::OnAmbientMultiplierChanged)
  85. ->Attribute(Edit::Attributes::Decimals, 1)
  86. ->Attribute(Edit::Attributes::Step, 0.1f)
  87. ->Attribute(Edit::Attributes::Min, 0.0f)
  88. ->Attribute(Edit::Attributes::Max, 10.0f)
  89. ->DataElement(AZ::Edit::UIHandlers::Slider, &EditorDiffuseProbeGridComponent::m_viewBias, "View Bias", "View bias adjustment")
  90. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorDiffuseProbeGridComponent::OnViewBiasChanged)
  91. ->Attribute(Edit::Attributes::Decimals, 2)
  92. ->Attribute(Edit::Attributes::Step, 0.1f)
  93. ->Attribute(Edit::Attributes::Min, 0.0f)
  94. ->Attribute(Edit::Attributes::Max, 1.0f)
  95. ->DataElement(AZ::Edit::UIHandlers::Slider, &EditorDiffuseProbeGridComponent::m_normalBias, "Normal Bias", "Normal bias adjustment")
  96. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorDiffuseProbeGridComponent::OnNormalBiasChanged)
  97. ->Attribute(Edit::Attributes::Decimals, 2)
  98. ->Attribute(Edit::Attributes::Step, 0.1f)
  99. ->Attribute(Edit::Attributes::Min, 0.0f)
  100. ->Attribute(Edit::Attributes::Max, 1.0f)
  101. ->DataElement(AZ::Edit::UIHandlers::ComboBox, &EditorDiffuseProbeGridComponent::m_numRaysPerProbe, "Number of Rays Per Probe", "Number of rays cast by each probe to detect lighting in its surroundings")
  102. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorDiffuseProbeGridComponent::OnNumRaysPerProbeChanged)
  103. ->Attribute(AZ::Edit::Attributes::EnumValues, &EditorDiffuseProbeGridComponent::GetNumRaysPerProbeEnumList)
  104. ->DataElement(AZ::Edit::UIHandlers::CheckBox, &EditorDiffuseProbeGridComponent::m_scrolling, "Scrolling", "Scrolling causes the grid to move probes on the edges of the volume when it is translated, instead of moving all of the probes. Use scrolling when the DiffuseProbeGrid is attached to a camera or moving entity.")
  105. ->Attribute(AZ::Edit::Attributes::ChangeValidate, &EditorDiffuseProbeGridComponent::OnScrollingChangeValidate)
  106. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorDiffuseProbeGridComponent::OnScrollingChanged)
  107. ->DataElement(AZ::Edit::UIHandlers::CheckBox, &EditorDiffuseProbeGridComponent::m_edgeBlendIbl, "Edge Blend IBL", "Blend the edges of the DiffuseProbeGrid with the Diffuse IBL cubemap.")
  108. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorDiffuseProbeGridComponent::OnEdgeBlendIblChanged)
  109. ->DataElement(AZ::Edit::UIHandlers::SpinBox, &EditorDiffuseProbeGridComponent::m_frameUpdateCount, "Number of Update Frames", "The number of frames to update the complete DiffuseProbeGrid, by updating a subset of the probes each frame. This will improve the performance of the Real-Time DiffuseProbeGrid update.")
  110. ->Attribute(Edit::Attributes::Min, 1)
  111. ->Attribute(Edit::Attributes::Max, 10)
  112. ->Attribute(AZ::Edit::Attributes::SoftMin, 1)
  113. ->Attribute(AZ::Edit::Attributes::SoftMax, 10)
  114. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorDiffuseProbeGridComponent::OnFrameUpdateCountChanged)
  115. ->DataElement(Edit::UIHandlers::ComboBox, &EditorDiffuseProbeGridComponent::m_transparencyMode, "Transparency Mode", "Controls how the DiffuseProbeGrid handles transparent geometry in the Real-Time update, and is a performance/quality tradeoff. 'Full' processes all transparencies found along the probe rays. 'Closest Only' processes only the closest transparency to the probe. 'None' disables transparency handling and treats all geometry as Opaque.")
  116. ->EnumAttribute(DiffuseProbeGridTransparencyMode::Full, "Full")
  117. ->EnumAttribute(DiffuseProbeGridTransparencyMode::ClosestOnly, "Closest Only")
  118. ->EnumAttribute(DiffuseProbeGridTransparencyMode::None, "None")
  119. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorDiffuseProbeGridComponent::OnTransparencyModeChanged)
  120. ->DataElement(AZ::Edit::UIHandlers::Slider, &EditorDiffuseProbeGridComponent::m_emissiveMultiplier, "Emissive Multiplier", "Multiplier for the emissive intensity")
  121. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorDiffuseProbeGridComponent::OnEmissiveMultiplierChanged)
  122. ->Attribute(Edit::Attributes::Decimals, 1)
  123. ->Attribute(Edit::Attributes::Step, 0.1f)
  124. ->Attribute(Edit::Attributes::Min, 0.0f)
  125. ->Attribute(Edit::Attributes::Max, 10.0f)
  126. ->ClassElement(AZ::Edit::ClassElements::Group, "Visualization")
  127. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  128. ->DataElement(AZ::Edit::UIHandlers::CheckBox, &EditorDiffuseProbeGridComponent::m_showVisualization, "Show Visualization", "Show the probe grid visualization")
  129. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorDiffuseProbeGridComponent::OnShowVisualizationChanged)
  130. ->DataElement(AZ::Edit::UIHandlers::CheckBox, &EditorDiffuseProbeGridComponent::m_showInactiveProbes, "Show Inactive Probes", "Show inactive probes in the probe grid visualization")
  131. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorDiffuseProbeGridComponent::OnShowInactiveProbesChanged)
  132. ->DataElement(AZ::Edit::UIHandlers::Slider, &EditorDiffuseProbeGridComponent::m_visualizationSphereRadius, "Visualization Sphere Radius", "Radius of the spheres in the probe grid visualization")
  133. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorDiffuseProbeGridComponent::OnVisualizationSphereRadiusChanged)
  134. ->Attribute(Edit::Attributes::Decimals, 2)
  135. ->Attribute(Edit::Attributes::Step, 0.25f)
  136. ->Attribute(Edit::Attributes::Min, 0.25f)
  137. ->Attribute(Edit::Attributes::Max, 2.0f)
  138. ->ClassElement(AZ::Edit::ClassElements::Group, "Grid mode")
  139. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  140. ->DataElement(Edit::UIHandlers::ComboBox, &EditorDiffuseProbeGridComponent::m_editorMode, "Editor Mode", "Controls whether the editor uses RealTime or Baked diffuse GI. RealTime requires a ray-tracing capable GPU. Auto-Select will fallback to Baked if ray-tracing is not available")
  141. ->Attribute(AZ::Edit::Attributes::ChangeValidate, &EditorDiffuseProbeGridComponent::OnModeChangeValidate)
  142. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorDiffuseProbeGridComponent::OnEditorModeChanged)
  143. ->EnumAttribute(DiffuseProbeGridMode::RealTime, "Real Time (Ray-Traced)")
  144. ->EnumAttribute(DiffuseProbeGridMode::Baked, "Baked")
  145. ->EnumAttribute(DiffuseProbeGridMode::AutoSelect, "Auto Select")
  146. ->DataElement(Edit::UIHandlers::ComboBox, &EditorDiffuseProbeGridComponent::m_runtimeMode, "Runtime Mode", "Controls whether the runtime uses RealTime or Baked diffuse GI. RealTime requires a ray-tracing capable GPU. Auto-Select will fallback to Baked if ray-tracing is not available")
  147. ->Attribute(AZ::Edit::Attributes::ChangeValidate, &EditorDiffuseProbeGridComponent::OnModeChangeValidate)
  148. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorDiffuseProbeGridComponent::OnRuntimeModeChanged)
  149. ->EnumAttribute(DiffuseProbeGridMode::RealTime, "Real Time (Ray-Traced)")
  150. ->EnumAttribute(DiffuseProbeGridMode::Baked, "Baked")
  151. ->EnumAttribute(DiffuseProbeGridMode::AutoSelect, "Auto Select")
  152. ->EndGroup()
  153. ->UIElement(AZ::Edit::UIHandlers::Button, "Bake Textures", "Bake the Diffuse Probe Grid textures to static assets that will be used when the mode is set to Baked")
  154. ->Attribute(AZ::Edit::Attributes::NameLabelOverride, "")
  155. ->Attribute(AZ::Edit::Attributes::ButtonText, "Bake Textures")
  156. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorDiffuseProbeGridComponent::BakeDiffuseProbeGrid)
  157. ->Attribute(AZ::Edit::Attributes::Visibility, &EditorDiffuseProbeGridComponent::GetBakeDiffuseProbeGridVisibilitySetting)
  158. ;
  159. editContext->Class<DiffuseProbeGridComponentController>(
  160. "DiffuseProbeGridComponentController", "")
  161. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  162. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  163. ->DataElement(AZ::Edit::UIHandlers::Default, &DiffuseProbeGridComponentController::m_configuration, "Configuration", "")
  164. ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
  165. ;
  166. }
  167. }
  168. if (auto behaviorContext = azrtti_cast<BehaviorContext*>(context))
  169. {
  170. behaviorContext->ConstantProperty("EditorDiffuseProbeGridComponentTypeId", BehaviorConstant(Uuid(EditorDiffuseProbeGridComponentTypeId)))
  171. ->Attribute(AZ::Script::Attributes::Module, "render")
  172. ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation);
  173. }
  174. }
  175. EditorDiffuseProbeGridComponent::EditorDiffuseProbeGridComponent()
  176. {
  177. }
  178. EditorDiffuseProbeGridComponent::EditorDiffuseProbeGridComponent(const DiffuseProbeGridComponentConfig& config)
  179. : BaseClass(config)
  180. {
  181. }
  182. void EditorDiffuseProbeGridComponent::Activate()
  183. {
  184. BaseClass::Activate();
  185. AzFramework::EntityDebugDisplayEventBus::Handler::BusConnect(GetEntityId());
  186. AzToolsFramework::EditorComponentSelectionRequestsBus::Handler::BusConnect(GetEntityId());
  187. AZ::TickBus::Handler::BusConnect();
  188. AzToolsFramework::EditorEntityInfoNotificationBus::Handler::BusConnect();
  189. m_boxChangedByGridHandler = AZ::Event<bool>::Handler([]([[maybe_unused]] bool value)
  190. {
  191. AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast(
  192. &AzToolsFramework::ToolsApplicationEvents::InvalidatePropertyDisplay,
  193. AzToolsFramework::Refresh_EntireTree);
  194. });
  195. m_controller.RegisterBoxChangedByGridHandler(m_boxChangedByGridHandler);
  196. AZ::u64 entityId = (AZ::u64)GetEntityId();
  197. m_controller.m_configuration.m_entityId = entityId;
  198. }
  199. void EditorDiffuseProbeGridComponent::Deactivate()
  200. {
  201. m_editorModeSet = false;
  202. m_boxChangedByGridHandler.Disconnect();
  203. AzToolsFramework::EditorEntityInfoNotificationBus::Handler::BusDisconnect();
  204. AZ::TickBus::Handler::BusDisconnect();
  205. AzToolsFramework::EditorComponentSelectionRequestsBus::Handler::BusDisconnect();
  206. AzFramework::EntityDebugDisplayEventBus::Handler::BusDisconnect();
  207. BaseClass::Deactivate();
  208. }
  209. void EditorDiffuseProbeGridComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
  210. {
  211. if (!m_controller.m_featureProcessor)
  212. {
  213. return;
  214. }
  215. DiffuseProbeGridComponentConfig& configuration = m_controller.m_configuration;
  216. // set the editor mode, which will override the runtime mode set by the controller
  217. if (!m_editorModeSet)
  218. {
  219. m_controller.m_featureProcessor->SetMode(m_controller.m_handle, configuration.m_editorMode);
  220. m_editorModeSet = true;
  221. }
  222. CheckTextureAssetNotification(configuration.m_bakedIrradianceTextureRelativePath, configuration.m_bakedIrradianceTextureAsset);
  223. CheckTextureAssetNotification(configuration.m_bakedDistanceTextureRelativePath, configuration.m_bakedDistanceTextureAsset);
  224. CheckTextureAssetNotification(configuration.m_bakedProbeDataTextureRelativePath, configuration.m_bakedProbeDataTextureAsset);
  225. }
  226. void EditorDiffuseProbeGridComponent::CheckTextureAssetNotification(const AZStd::string& relativePath, Data::Asset<RPI::StreamingImageAsset>& configurationAsset)
  227. {
  228. Data::Asset<RPI::StreamingImageAsset> textureAsset;
  229. DiffuseProbeGridTextureNotificationType notificationType = DiffuseProbeGridTextureNotificationType::None;
  230. if (m_controller.m_featureProcessor->CheckTextureAssetNotification(relativePath + ".streamingimage", textureAsset, notificationType))
  231. {
  232. if (notificationType == DiffuseProbeGridTextureNotificationType::Ready)
  233. {
  234. // bake is complete, update configuration with the new baked texture asset
  235. AzToolsFramework::ScopedUndoBatch undoBatch("DiffuseProbeGrid Texture Bake");
  236. configurationAsset = textureAsset;
  237. SetDirty();
  238. if (m_controller.m_configuration.m_bakedIrradianceTextureAsset.IsReady() &&
  239. m_controller.m_configuration.m_bakedDistanceTextureAsset.IsReady() &&
  240. m_controller.m_configuration.m_bakedProbeDataTextureAsset.IsReady())
  241. {
  242. m_controller.UpdateBakedTextures();
  243. }
  244. }
  245. else if (notificationType == DiffuseProbeGridTextureNotificationType::Error)
  246. {
  247. QMessageBox::information(
  248. QApplication::activeWindow(),
  249. "Diffuse Probe Grid",
  250. "Diffuse Probe Grid texture failed to bake, please check the Asset Processor for more information.",
  251. QMessageBox::Ok);
  252. }
  253. }
  254. }
  255. AZStd::vector<Edit::EnumConstant<DiffuseProbeGridNumRaysPerProbe>> EditorDiffuseProbeGridComponent::GetNumRaysPerProbeEnumList() const
  256. {
  257. AZStd::vector<Edit::EnumConstant<DiffuseProbeGridNumRaysPerProbe>> enumList;
  258. for (uint32_t index = 0; index < DiffuseProbeGridNumRaysPerProbeArraySize; ++index)
  259. {
  260. const DiffuseProbeGridNumRaysPerProbeEntry& entry = DiffuseProbeGridNumRaysPerProbeArray[index];
  261. enumList.push_back(Edit::EnumConstant<DiffuseProbeGridNumRaysPerProbe>(entry.m_enum, AZStd::to_string(entry.m_rayCount).c_str()));
  262. }
  263. return enumList;
  264. }
  265. AZ::Aabb EditorDiffuseProbeGridComponent::GetEditorSelectionBoundsViewport([[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo)
  266. {
  267. return m_controller.GetAabb();
  268. }
  269. bool EditorDiffuseProbeGridComponent::SupportsEditorRayIntersect()
  270. {
  271. return false;
  272. }
  273. void EditorDiffuseProbeGridComponent::OnEntityInfoUpdatedVisibility(AZ::EntityId entityId, bool visible)
  274. {
  275. if ((GetEntityId() == entityId) && !visible)
  276. {
  277. m_editorModeSet = false;
  278. }
  279. }
  280. AZ::Outcome<void, AZStd::string> EditorDiffuseProbeGridComponent::OnProbeSpacingValidateX(void* newValue, [[maybe_unused]] const AZ::Uuid& valueType)
  281. {
  282. if (!m_controller.m_featureProcessor)
  283. {
  284. return AZ::Failure(AZStd::string("This Diffuse Probe Grid entity is hidden, it must be visible in order to change the probe spacing."));
  285. }
  286. float newProbeSpacingX = *(reinterpret_cast<float*>(newValue));
  287. Vector3 newSpacing(newProbeSpacingX, m_probeSpacingY, m_probeSpacingZ);
  288. if (!m_controller.ValidateProbeSpacing(newSpacing))
  289. {
  290. return AZ::Failure(AZStd::string("Probe spacing exceeds max allowable grid size with current extents."));
  291. }
  292. return AZ::Success();
  293. }
  294. AZ::Outcome<void, AZStd::string> EditorDiffuseProbeGridComponent::OnProbeSpacingValidateY(void* newValue, [[maybe_unused]] const AZ::Uuid& valueType)
  295. {
  296. if (!m_controller.m_featureProcessor)
  297. {
  298. return AZ::Failure(AZStd::string("This Diffuse Probe Grid entity is hidden, it must be visible in order to change the probe spacing."));
  299. }
  300. float newProbeSpacingY = *(reinterpret_cast<float*>(newValue));
  301. Vector3 newSpacing(m_probeSpacingX, newProbeSpacingY, m_probeSpacingZ);
  302. if (!m_controller.ValidateProbeSpacing(newSpacing))
  303. {
  304. return AZ::Failure(AZStd::string("Probe spacing exceeds max allowable grid size with current extents."));
  305. }
  306. return AZ::Success();
  307. }
  308. AZ::Outcome<void, AZStd::string> EditorDiffuseProbeGridComponent::OnProbeSpacingValidateZ(void* newValue, [[maybe_unused]] const AZ::Uuid& valueType)
  309. {
  310. if (!m_controller.m_featureProcessor)
  311. {
  312. return AZ::Failure(AZStd::string("This Diffuse Probe Grid entity is hidden, it must be visible in order to change the probe spacing."));
  313. }
  314. float newProbeSpacingZ = *(reinterpret_cast<float*>(newValue));
  315. Vector3 newSpacing(m_probeSpacingX, m_probeSpacingY, newProbeSpacingZ);
  316. if (!m_controller.ValidateProbeSpacing(newSpacing))
  317. {
  318. return AZ::Failure(AZStd::string("Probe spacing exceeds max allowable grid size with current extents."));
  319. }
  320. return AZ::Success();
  321. }
  322. AZ::u32 EditorDiffuseProbeGridComponent::OnProbeSpacingChanged()
  323. {
  324. AZ::Vector3 probeSpacing(m_probeSpacingX, m_probeSpacingY, m_probeSpacingZ);
  325. m_controller.SetProbeSpacing(probeSpacing);
  326. return AZ::Edit::PropertyRefreshLevels::None;
  327. }
  328. AZ::u32 EditorDiffuseProbeGridComponent::OnAmbientMultiplierChanged()
  329. {
  330. m_controller.SetAmbientMultiplier(m_ambientMultiplier);
  331. return AZ::Edit::PropertyRefreshLevels::None;
  332. }
  333. AZ::u32 EditorDiffuseProbeGridComponent::OnViewBiasChanged()
  334. {
  335. m_controller.SetViewBias(m_viewBias);
  336. return AZ::Edit::PropertyRefreshLevels::None;
  337. }
  338. AZ::u32 EditorDiffuseProbeGridComponent::OnNormalBiasChanged()
  339. {
  340. m_controller.SetNormalBias(m_normalBias);
  341. return AZ::Edit::PropertyRefreshLevels::None;
  342. }
  343. AZ::u32 EditorDiffuseProbeGridComponent::OnNumRaysPerProbeChanged()
  344. {
  345. m_controller.SetNumRaysPerProbe(m_numRaysPerProbe);
  346. return AZ::Edit::PropertyRefreshLevels::None;
  347. }
  348. AZ::Outcome<void, AZStd::string> EditorDiffuseProbeGridComponent::OnScrollingChangeValidate([[maybe_unused]] void* newValue, [[maybe_unused]] const AZ::Uuid& valueType)
  349. {
  350. bool newScrolling = (*(reinterpret_cast<bool*>(newValue)));
  351. // scrolling requires Real-Time mode
  352. if (newScrolling && (m_editorMode == DiffuseProbeGridMode::Baked || m_runtimeMode == DiffuseProbeGridMode::Baked))
  353. {
  354. return AZ::Failure(AZStd::string("Scrolling requires that the Editor and Runtime modes are both set to Real-Time."));
  355. }
  356. return AZ::Success();
  357. }
  358. AZ::u32 EditorDiffuseProbeGridComponent::OnScrollingChanged()
  359. {
  360. m_controller.SetScrolling(m_scrolling);
  361. return AZ::Edit::PropertyRefreshLevels::None;
  362. }
  363. AZ::u32 EditorDiffuseProbeGridComponent::OnEdgeBlendIblChanged()
  364. {
  365. m_controller.SetEdgeBlendIbl(m_edgeBlendIbl);
  366. return AZ::Edit::PropertyRefreshLevels::None;
  367. }
  368. AZ::u32 EditorDiffuseProbeGridComponent::OnFrameUpdateCountChanged()
  369. {
  370. m_controller.SetFrameUpdateCount(m_frameUpdateCount);
  371. return AZ::Edit::PropertyRefreshLevels::None;
  372. }
  373. AZ::u32 EditorDiffuseProbeGridComponent::OnTransparencyModeChanged()
  374. {
  375. m_controller.SetTransparencyMode(m_transparencyMode);
  376. return AZ::Edit::PropertyRefreshLevels::None;
  377. }
  378. AZ::u32 EditorDiffuseProbeGridComponent::OnEmissiveMultiplierChanged()
  379. {
  380. m_controller.SetEmissiveMultiplier(m_emissiveMultiplier);
  381. return AZ::Edit::PropertyRefreshLevels::None;
  382. }
  383. AZ::u32 EditorDiffuseProbeGridComponent::OnEditorModeChanged()
  384. {
  385. // this will update the configuration and also change the DiffuseProbeGrid mode
  386. m_controller.SetEditorMode(m_editorMode);
  387. return AZ::Edit::PropertyRefreshLevels::EntireTree;
  388. }
  389. AZ::u32 EditorDiffuseProbeGridComponent::OnRuntimeModeChanged()
  390. {
  391. // this will only update the configuration
  392. m_controller.SetRuntimeMode(m_runtimeMode);
  393. return AZ::Edit::PropertyRefreshLevels::None;
  394. }
  395. AZ::u32 EditorDiffuseProbeGridComponent::OnShowVisualizationChanged()
  396. {
  397. m_controller.SetVisualizationEnabled(m_showVisualization);
  398. return AZ::Edit::PropertyRefreshLevels::None;
  399. }
  400. AZ::u32 EditorDiffuseProbeGridComponent::OnShowInactiveProbesChanged()
  401. {
  402. m_controller.SetVisualizationShowInactiveProbes(m_showInactiveProbes);
  403. return AZ::Edit::PropertyRefreshLevels::None;
  404. }
  405. AZ::u32 EditorDiffuseProbeGridComponent::OnVisualizationSphereRadiusChanged()
  406. {
  407. m_controller.SetVisualizationSphereRadius(m_visualizationSphereRadius);
  408. return AZ::Edit::PropertyRefreshLevels::None;
  409. }
  410. AZ::Outcome<void, AZStd::string> EditorDiffuseProbeGridComponent::OnModeChangeValidate([[maybe_unused]] void* newValue, [[maybe_unused]] const AZ::Uuid& valueType)
  411. {
  412. DiffuseProbeGridMode newMode = (*(reinterpret_cast<DiffuseProbeGridMode*>(newValue)));
  413. if (newMode == DiffuseProbeGridMode::Baked || newMode == DiffuseProbeGridMode::AutoSelect)
  414. {
  415. if (!m_controller.m_configuration.m_bakedIrradianceTextureAsset.GetId().IsValid() ||
  416. !m_controller.m_configuration.m_bakedDistanceTextureAsset.GetId().IsValid() ||
  417. !m_controller.m_configuration.m_bakedProbeDataTextureAsset.GetId().IsValid())
  418. {
  419. return AZ::Failure(AZStd::string("Please bake textures before changing the Diffuse Probe Grid to Baked or Auto-Select mode."));
  420. }
  421. // scrolling requires Real-Time mode
  422. if (m_scrolling)
  423. {
  424. return AZ::Failure(AZStd::string("Scrolling requires that the Editor and Runtime modes are both set to Real-Time."));
  425. }
  426. }
  427. return AZ::Success();
  428. }
  429. AZ::u32 EditorDiffuseProbeGridComponent::GetBakeDiffuseProbeGridVisibilitySetting()
  430. {
  431. // the Bake button is visible only when the editor mode is set to RealTime
  432. return m_editorMode == DiffuseProbeGridMode::RealTime ? AZ::Edit::PropertyVisibility::Show : AZ::Edit::PropertyVisibility::Hide;
  433. }
  434. AZ::u32 EditorDiffuseProbeGridComponent::BakeDiffuseProbeGrid()
  435. {
  436. if (m_bakeInProgress)
  437. {
  438. return AZ::Edit::PropertyRefreshLevels::None;
  439. }
  440. // retrieve entity visibility
  441. bool isHidden = false;
  442. AzToolsFramework::EditorEntityInfoRequestBus::EventResult(
  443. isHidden,
  444. GetEntityId(),
  445. &AzToolsFramework::EditorEntityInfoRequestBus::Events::IsHidden);
  446. // the entity must be visible in order to bake
  447. if (isHidden)
  448. {
  449. QMessageBox::information(
  450. QApplication::activeWindow(),
  451. "Diffuse Probe Grid",
  452. "This Diffuse Probe Grid entity is hidden, it must be visible in order to bake textures.",
  453. QMessageBox::Ok);
  454. return AZ::Edit::PropertyRefreshLevels::None;
  455. }
  456. if (!m_controller.CanBakeTextures())
  457. {
  458. QMessageBox::information(
  459. QApplication::activeWindow(),
  460. "Diffuse Probe Grid",
  461. "Can't bake the textures. Diffuse probe calculations require GPU raytracing support",
  462. QMessageBox::Ok);
  463. return AZ::Edit::PropertyRefreshLevels::None;
  464. }
  465. DiffuseProbeGridComponentConfig& configuration = m_controller.m_configuration;
  466. // retrieve the source image paths from the configuration
  467. // Note: we need to make sure to use the same source image for each bake
  468. AZStd::string irradianceTextureRelativePath = ValidateOrCreateNewTexturePath(configuration.m_bakedIrradianceTextureRelativePath, DiffuseProbeGridIrradianceFileName);
  469. AZStd::string distanceTextureRelativePath = ValidateOrCreateNewTexturePath(configuration.m_bakedDistanceTextureRelativePath, DiffuseProbeGridDistanceFileName);
  470. AZStd::string probeDataTextureRelativePath = ValidateOrCreateNewTexturePath(configuration.m_bakedProbeDataTextureRelativePath, DiffuseProbeGridProbeDataFileName);
  471. // create the full paths
  472. char projectPath[AZ_MAX_PATH_LEN];
  473. AZ::IO::FileIOBase::GetInstance()->ResolvePath("@projectroot@", projectPath, AZ_MAX_PATH_LEN);
  474. AZStd::string irradianceTextureFullPath;
  475. AzFramework::StringFunc::Path::Join(projectPath, irradianceTextureRelativePath.c_str(), irradianceTextureFullPath, true, true);
  476. AZStd::string distanceTextureFullPath;
  477. AzFramework::StringFunc::Path::Join(projectPath, distanceTextureRelativePath.c_str(), distanceTextureFullPath, true, true);
  478. AZStd::string probeDataTextureFullPath;
  479. AzFramework::StringFunc::Path::Join(projectPath, probeDataTextureRelativePath.c_str(), probeDataTextureFullPath, true, true);
  480. // make sure the folder is created
  481. AZStd::string diffuseProbeGridFolder;
  482. AzFramework::StringFunc::Path::GetFolderPath(irradianceTextureFullPath.data(), diffuseProbeGridFolder);
  483. AZ::IO::SystemFile::CreateDir(diffuseProbeGridFolder.c_str());
  484. // check out the files in source control
  485. CheckoutSourceTextureFile(irradianceTextureFullPath);
  486. CheckoutSourceTextureFile(distanceTextureFullPath);
  487. CheckoutSourceTextureFile(probeDataTextureFullPath);
  488. // update the configuration
  489. AzToolsFramework::ScopedUndoBatch undoBatch("DiffuseProbeGrid bake");
  490. configuration.m_bakedIrradianceTextureRelativePath = irradianceTextureRelativePath;
  491. configuration.m_bakedDistanceTextureRelativePath = distanceTextureRelativePath;
  492. configuration.m_bakedProbeDataTextureRelativePath = probeDataTextureRelativePath;
  493. SetDirty();
  494. // callback for the texture readback
  495. DiffuseProbeGridBakeTexturesCallback bakeTexturesCallback = [=](
  496. DiffuseProbeGridTexture irradianceTexture,
  497. DiffuseProbeGridTexture distanceTexture,
  498. DiffuseProbeGridTexture probeDataTexture)
  499. {
  500. // irradiance
  501. {
  502. AZ::DdsFile::DdsFileData fileData = { irradianceTexture.m_size, irradianceTexture.m_format, irradianceTexture.m_data.get() };
  503. [[maybe_unused]] const auto outcome = AZ::DdsFile::WriteFile(irradianceTextureFullPath, fileData);
  504. AZ_Assert(outcome.IsSuccess(), "Failed to write Irradiance texture .dds file [%s]", irradianceTextureFullPath.c_str());
  505. }
  506. // distance
  507. {
  508. AZ::DdsFile::DdsFileData fileData = { distanceTexture.m_size, distanceTexture.m_format, distanceTexture.m_data.get() };
  509. [[maybe_unused]] const auto outcome = AZ::DdsFile::WriteFile(distanceTextureFullPath, fileData);
  510. AZ_Assert(outcome.IsSuccess(), "Failed to write Distance texture .dds file [%s]", distanceTextureFullPath.c_str());
  511. }
  512. // probe data
  513. {
  514. AZ::DdsFile::DdsFileData fileData = { probeDataTexture.m_size, probeDataTexture.m_format, probeDataTexture.m_data.get() };
  515. [[maybe_unused]] const auto outcome = AZ::DdsFile::WriteFile(probeDataTextureFullPath, fileData);
  516. AZ_Assert(outcome.IsSuccess(), "Failed to write ProbeData texture .dds file [%s]", probeDataTextureFullPath.c_str());
  517. }
  518. m_bakeInProgress = false;
  519. };
  520. m_bakeInProgress = true;
  521. m_controller.BakeTextures(bakeTexturesCallback);
  522. QProgressDialog bakeDialog;
  523. bakeDialog.setWindowFlags(bakeDialog.windowFlags() & ~Qt::WindowCloseButtonHint);
  524. bakeDialog.setLabelText(QObject::tr("Baking Diffuse Probe Grid..."));
  525. bakeDialog.setWindowModality(Qt::WindowModal);
  526. bakeDialog.setMaximumSize(QSize(256, 96));
  527. bakeDialog.setMaximum(0);
  528. bakeDialog.setMinimumDuration(0);
  529. bakeDialog.setAutoClose(false);
  530. bakeDialog.setCancelButton(nullptr);
  531. bakeDialog.show();
  532. while (m_bakeInProgress)
  533. {
  534. if (bakeDialog.wasCanceled())
  535. {
  536. m_bakeInProgress = false;
  537. break;
  538. }
  539. QApplication::processEvents();
  540. AZStd::this_thread::sleep_for(AZStd::chrono::milliseconds(100));
  541. }
  542. QMessageBox::information(
  543. QApplication::activeWindow(),
  544. "Diffuse Probe Grid",
  545. "Successfully baked Diffuse Probe Grid textures.",
  546. QMessageBox::Ok);
  547. return AZ::Edit::PropertyRefreshLevels::None;
  548. }
  549. AZStd::string EditorDiffuseProbeGridComponent::ValidateOrCreateNewTexturePath(const AZStd::string& configurationRelativePath, const char* fileSuffix)
  550. {
  551. AZStd::string relativePath = configurationRelativePath;
  552. AZStd::string fullPath;
  553. char projectPath[AZ_MAX_PATH_LEN];
  554. AZ::IO::FileIOBase::GetInstance()->ResolvePath("@projectroot@", projectPath, AZ_MAX_PATH_LEN);
  555. if (!relativePath.empty())
  556. {
  557. // test to see if the texture file is actually there, if it was removed we need to
  558. // generate a new filename, otherwise it will cause an error in the asset system
  559. AzFramework::StringFunc::Path::Join(projectPath, configurationRelativePath.c_str(), fullPath, true, true);
  560. if (!AZ::IO::FileIOBase::GetInstance()->Exists(fullPath.c_str()))
  561. {
  562. // file does not exist, clear the relative path so we generate a new name
  563. relativePath.clear();
  564. }
  565. }
  566. // build a new image path if necessary
  567. if (relativePath.empty())
  568. {
  569. // the file name is a combination of the entity name, a UUID, and the filemask
  570. Entity* entity = GetEntity();
  571. AZ_Assert(entity, "DiffuseProbeGrid entity is null");
  572. AZ::Uuid uuid = AZ::Uuid::CreateRandom();
  573. AZStd::string uuidString;
  574. uuid.ToString(uuidString);
  575. relativePath = "DiffuseProbeGrids/" + entity->GetName() + uuidString + fileSuffix;
  576. // replace any invalid filename characters
  577. auto invalidCharacters = [](char letter)
  578. {
  579. return
  580. letter == ':' || letter == '"' || letter == '\'' ||
  581. letter == '{' || letter == '}' ||
  582. letter == '<' || letter == '>';
  583. };
  584. AZStd::replace_if(relativePath.begin(), relativePath.end(), invalidCharacters, '_');
  585. }
  586. return relativePath;
  587. }
  588. void EditorDiffuseProbeGridComponent::CheckoutSourceTextureFile(const AZStd::string& fullPath)
  589. {
  590. bool checkedOutSuccessfully = false;
  591. using ApplicationBus = AzToolsFramework::ToolsApplicationRequestBus;
  592. ApplicationBus::BroadcastResult(
  593. checkedOutSuccessfully,
  594. &ApplicationBus::Events::RequestEditForFileBlocking,
  595. fullPath.c_str(),
  596. "Checking out for edit...",
  597. ApplicationBus::Events::RequestEditProgressCallback());
  598. }
  599. } // namespace Render
  600. } // namespace AZ