EditorReflectionProbeComponent.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  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 <ReflectionProbe/EditorReflectionProbeComponent.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 <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI.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 <AzCore/Asset/AssetSerializer.h>
  18. #include <AzCore/Component/Entity.h>
  19. AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT
  20. #include <QApplication>
  21. #include <QMessageBox>
  22. #include <QProgressDialog>
  23. AZ_POP_DISABLE_WARNING
  24. namespace AZ
  25. {
  26. namespace Render
  27. {
  28. void EditorReflectionProbeComponent::Reflect(AZ::ReflectContext* context)
  29. {
  30. BaseClass::Reflect(context);
  31. if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  32. {
  33. serializeContext->Class<EditorReflectionProbeComponent, BaseClass>()
  34. ->Version(2, ConvertToEditorRenderComponentAdapter<1>)
  35. ->Field("useBakedCubemap", &EditorReflectionProbeComponent::m_useBakedCubemap)
  36. ->Field("bakedCubeMapQualityLevel", &EditorReflectionProbeComponent::m_bakedCubeMapQualityLevel)
  37. ->Field("bakedCubeMapRelativePath", &EditorReflectionProbeComponent::m_bakedCubeMapRelativePath)
  38. ->Field("authoredCubeMapAsset", &EditorReflectionProbeComponent::m_authoredCubeMapAsset)
  39. ->Field("bakeExposure", &EditorReflectionProbeComponent::m_bakeExposure)
  40. ;
  41. if (AZ::EditContext* editContext = serializeContext->GetEditContext())
  42. {
  43. editContext->Class<EditorReflectionProbeComponent>(
  44. "Reflection Probe", "The ReflectionProbe component captures an IBL specular reflection at a specific position in the level")
  45. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  46. ->Attribute(AZ::Edit::Attributes::Category, "Graphics/Lighting")
  47. ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/Component_Placeholder.svg")
  48. ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Component_Placeholder.svg")
  49. ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game"))
  50. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  51. ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
  52. ->Attribute(Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/atom/reflection-probe/")
  53. ->Attribute(AZ::Edit::Attributes::PrimaryAssetType, AZ::AzTypeInfo<RPI::ModelAsset>::Uuid())
  54. ->ClassElement(AZ::Edit::ClassElements::Group, "Cubemap Bake")
  55. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  56. ->UIElement(AZ::Edit::UIHandlers::Button, "Bake Reflection Probe", "Bake Reflection Probe")
  57. ->Attribute(AZ::Edit::Attributes::NameLabelOverride, "")
  58. ->Attribute(AZ::Edit::Attributes::ButtonText, "Bake Reflection Probe")
  59. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorReflectionProbeComponent::BakeReflectionProbe)
  60. ->Attribute(AZ::Edit::Attributes::Visibility, &EditorReflectionProbeComponent::GetBakedCubemapVisibilitySetting)
  61. ->DataElement(AZ::Edit::UIHandlers::Slider, &EditorReflectionProbeComponent::m_bakeExposure, "Bake Exposure", "Exposure to use when baking the cubemap")
  62. ->Attribute(AZ::Edit::Attributes::SoftMin, -16.0f)
  63. ->Attribute(AZ::Edit::Attributes::SoftMax, 16.0f)
  64. ->Attribute(AZ::Edit::Attributes::Min, -20.0f)
  65. ->Attribute(AZ::Edit::Attributes::Max, 20.0f)
  66. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorReflectionProbeComponent::OnBakeExposureChanged)
  67. ->Attribute(AZ::Edit::Attributes::Visibility, &EditorReflectionProbeComponent::GetBakedCubemapVisibilitySetting)
  68. ->ClassElement(AZ::Edit::ClassElements::Group, "Cubemap")
  69. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  70. ->DataElement(AZ::Edit::UIHandlers::Default, &EditorReflectionProbeComponent::m_useBakedCubemap, "Use Baked Cubemap", "Selects between a cubemap that captures the environment at location in the scene or a preauthored cubemap")
  71. ->Attribute(AZ::Edit::Attributes::ChangeValidate, &EditorReflectionProbeComponent::OnUseBakedCubemapValidate)
  72. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorReflectionProbeComponent::OnUseBakedCubemapChanged)
  73. ->DataElement(AZ::Edit::UIHandlers::ComboBox, &EditorReflectionProbeComponent::m_bakedCubeMapQualityLevel, "Baked Cubemap Quality", "Resolution of the baked cubemap")
  74. ->Attribute(AZ::Edit::Attributes::Visibility, &EditorReflectionProbeComponent::GetBakedCubemapVisibilitySetting)
  75. ->EnumAttribute(CubeMapSpecularQualityLevel::VeryLow, "Very Low")
  76. ->EnumAttribute(CubeMapSpecularQualityLevel::Low, "Low")
  77. ->EnumAttribute(CubeMapSpecularQualityLevel::Medium, "Medium")
  78. ->EnumAttribute(CubeMapSpecularQualityLevel::High, "High")
  79. ->EnumAttribute(CubeMapSpecularQualityLevel::VeryHigh, "Very High")
  80. ->DataElement(AZ::Edit::UIHandlers::MultiLineEdit, &EditorReflectionProbeComponent::m_bakedCubeMapRelativePath, "Baked Cubemap Path", "Baked Cubemap Path")
  81. ->Attribute(AZ::Edit::Attributes::ReadOnly, true)
  82. ->Attribute(AZ::Edit::Attributes::Visibility, &EditorReflectionProbeComponent::GetBakedCubemapVisibilitySetting)
  83. ->DataElement(AZ::Edit::UIHandlers::Default, &EditorReflectionProbeComponent::m_authoredCubeMapAsset, "Cubemap file", "Authored Cubemap file")
  84. ->Attribute(AZ::Edit::Attributes::Visibility, &EditorReflectionProbeComponent::GetAuthoredCubemapVisibilitySetting)
  85. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorReflectionProbeComponent::OnAuthoredCubemapChanged)
  86. ;
  87. editContext->Class<ReflectionProbeComponentController>(
  88. "ReflectionProbeComponentController", "")
  89. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  90. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  91. ->DataElement(AZ::Edit::UIHandlers::Default, &ReflectionProbeComponentController::m_configuration, "Configuration", "")
  92. ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
  93. ;
  94. editContext->Class<ReflectionProbeComponentConfig>(
  95. "ReflectionProbeComponentConfig", "")
  96. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  97. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  98. ->ClassElement(AZ::Edit::ClassElements::Group, "Inner Extents")
  99. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  100. ->DataElement(AZ::Edit::UIHandlers::Default, &ReflectionProbeComponentConfig::m_innerHeight, "Height", "Height of the reflection probe inner volume")
  101. ->Attribute(AZ::Edit::Attributes::Min, 0.f)
  102. ->Attribute(AZ::Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly)
  103. ->DataElement(AZ::Edit::UIHandlers::Default, &ReflectionProbeComponentConfig::m_innerLength, "Length", "Length of the reflection probe inner volume")
  104. ->Attribute(AZ::Edit::Attributes::Min, 0.f)
  105. ->Attribute(AZ::Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly)
  106. ->DataElement(AZ::Edit::UIHandlers::Default, &ReflectionProbeComponentConfig::m_innerWidth, "Width", "Width of the reflection probe inner volume")
  107. ->Attribute(AZ::Edit::Attributes::Min, 0.f)
  108. ->Attribute(AZ::Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly)
  109. ->ClassElement(AZ::Edit::ClassElements::Group, "Settings")
  110. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  111. ->DataElement(AZ::Edit::UIHandlers::CheckBox, &ReflectionProbeComponentConfig::m_useParallaxCorrection, "Parallax Correction", "Correct the reflection to adjust for the offset from the capture position")
  112. ->Attribute(AZ::Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly)
  113. ->DataElement(AZ::Edit::UIHandlers::CheckBox, &ReflectionProbeComponentConfig::m_showVisualization, "Show Visualization", "Show the reflection probe visualization sphere")
  114. ->Attribute(AZ::Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly)
  115. ->DataElement(AZ::Edit::UIHandlers::Slider, &ReflectionProbeComponentConfig::m_renderExposure, "Exposure", "Exposure to use when rendering meshes with the cubemap")
  116. ->Attribute(AZ::Edit::Attributes::SoftMin, -5.0f)
  117. ->Attribute(AZ::Edit::Attributes::SoftMax, 5.0f)
  118. ->Attribute(AZ::Edit::Attributes::Min, -20.0f)
  119. ->Attribute(AZ::Edit::Attributes::Max, 20.0f)
  120. ;
  121. }
  122. }
  123. if (auto behaviorContext = azrtti_cast<BehaviorContext*>(context))
  124. {
  125. behaviorContext->EBus<EditorReflectionProbeBus>("EditorReflectionProbeBus")
  126. ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
  127. ->Attribute(AZ::Script::Attributes::Module, "render")
  128. ->Event("BakeReflectionProbe", &EditorReflectionProbeInterface::BakeReflectionProbe)
  129. ;
  130. behaviorContext->ConstantProperty("EditorReflectionProbeComponentTypeId", BehaviorConstant(Uuid(EditorReflectionProbeComponentTypeId)))
  131. ->Attribute(AZ::Script::Attributes::Module, "render")
  132. ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation);
  133. }
  134. }
  135. EditorReflectionProbeComponent::EditorReflectionProbeComponent(const ReflectionProbeComponentConfig& config)
  136. : BaseClass(config)
  137. {
  138. }
  139. void EditorReflectionProbeComponent::Activate()
  140. {
  141. BaseClass::Activate();
  142. AzFramework::EntityDebugDisplayEventBus::Handler::BusConnect(GetEntityId());
  143. AzToolsFramework::EditorComponentSelectionRequestsBus::Handler::BusConnect(GetEntityId());
  144. EditorReflectionProbeBus::Handler::BusConnect(GetEntityId());
  145. AZ::TickBus::Handler::BusConnect();
  146. ReflectionProbeComponentConfig& configuration = m_controller.m_configuration;
  147. // update UI cubemap path display
  148. m_bakedCubeMapRelativePath = configuration.m_bakedCubeMapRelativePath;
  149. AZ::u64 entityId = (AZ::u64)GetEntityId();
  150. configuration.m_entityId = entityId;
  151. AZ::EntityComponentIdPair entityComponentId = AZ::EntityComponentIdPair(GetEntityId(), GetId());
  152. m_innerExtentsChangedHandler = AZ::Event<bool>::Handler([entityComponentId]([[maybe_unused]] bool value)
  153. {
  154. AzToolsFramework::ToolsApplicationEvents::Bus::Broadcast(
  155. &AzToolsFramework::ToolsApplicationEvents::InvalidatePropertyDisplayForComponent,
  156. entityComponentId,
  157. AzToolsFramework::Refresh_Values);
  158. });
  159. m_controller.RegisterInnerExtentsChangedHandler(m_innerExtentsChangedHandler);
  160. }
  161. void EditorReflectionProbeComponent::Deactivate()
  162. {
  163. m_innerExtentsChangedHandler.Disconnect();
  164. EditorReflectionProbeBus::Handler::BusDisconnect(GetEntityId());
  165. AzToolsFramework::EditorComponentSelectionRequestsBus::Handler::BusDisconnect();
  166. AzFramework::EntityDebugDisplayEventBus::Handler::BusDisconnect();
  167. AZ::TickBus::Handler::BusDisconnect();
  168. BaseClass::Deactivate();
  169. }
  170. void EditorReflectionProbeComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
  171. {
  172. if (!m_controller.m_featureProcessor)
  173. {
  174. return;
  175. }
  176. if (m_controller.m_configuration.m_useBakedCubemap)
  177. {
  178. AZStd::string cubeMapRelativePath = m_controller.m_configuration.m_bakedCubeMapRelativePath + ".streamingimage";
  179. Data::Asset<RPI::StreamingImageAsset> cubeMapAsset;
  180. CubeMapAssetNotificationType notificationType = CubeMapAssetNotificationType::None;
  181. if (m_controller.m_featureProcessor->CheckCubeMapAssetNotification(cubeMapRelativePath, cubeMapAsset, notificationType))
  182. {
  183. // a cubemap bake is in progress for this entity component
  184. if (notificationType == CubeMapAssetNotificationType::Ready)
  185. {
  186. // bake is complete, update configuration with the new baked cubemap asset
  187. AzToolsFramework::ScopedUndoBatch undoBatch("ReflectionProbe Bake");
  188. m_controller.m_configuration.m_bakedCubeMapAsset = cubeMapAsset;
  189. SetDirty();
  190. // refresh the currently rendered cubemap
  191. m_controller.UpdateCubeMap();
  192. // update the UI
  193. AzToolsFramework::PropertyEditorGUIMessages::Bus::Broadcast(&AzToolsFramework::PropertyEditorGUIMessages::RequestRefresh, AzToolsFramework::PropertyModificationRefreshLevel::Refresh_AttributesAndValues);
  194. }
  195. else if (notificationType == CubeMapAssetNotificationType::Error)
  196. {
  197. // cubemap bake failed
  198. QMessageBox::information(
  199. QApplication::activeWindow(),
  200. "Reflection Probe",
  201. "Reflection Probe cubemap failed to bake, please check the Asset Processor for more information.",
  202. QMessageBox::Ok);
  203. // clear relative path, this will allow the user to retry
  204. m_controller.m_configuration.m_bakedCubeMapRelativePath.clear();
  205. // update the UI
  206. AzToolsFramework::PropertyEditorGUIMessages::Bus::Broadcast(&AzToolsFramework::PropertyEditorGUIMessages::RequestRefresh, AzToolsFramework::PropertyModificationRefreshLevel::Refresh_AttributesAndValues);
  207. }
  208. }
  209. }
  210. }
  211. void EditorReflectionProbeComponent::DisplayEntityViewport([[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay)
  212. {
  213. // only draw the bounds if selected
  214. if (!IsSelected())
  215. {
  216. return;
  217. }
  218. const AZ::Vector3 translationOffset =
  219. m_controller.m_shapeBus ? m_controller.m_shapeBus->GetTranslationOffset() : AZ::Vector3::CreateZero();
  220. AZ::Transform worldTransform = AZ::Transform::CreateIdentity();
  221. AZ::TransformBus::EventResult(worldTransform, GetEntityId(), &AZ::TransformBus::Events::GetWorldTM);
  222. if (m_controller.m_boxShapeInterface && m_controller.m_boxShapeInterface->IsTypeAxisAligned())
  223. {
  224. worldTransform.SetRotation(AZ::Quaternion::CreateIdentity());
  225. }
  226. AZ::Quaternion rotationQuaternion = worldTransform.GetRotation();
  227. AZ::Matrix3x3 rotationMatrix = AZ::Matrix3x3::CreateFromQuaternion(rotationQuaternion);
  228. const AZ::Vector3 position = worldTransform.TransformPoint(translationOffset);
  229. float scale = worldTransform.GetUniformScale();
  230. // draw AABB at probe position using the inner dimensions
  231. Color color(0.0f, 0.0f, 1.0f, 1.0f);
  232. debugDisplay.SetColor(color);
  233. ReflectionProbeComponentConfig& configuration = m_controller.m_configuration;
  234. AZ::Vector3 innerExtents(configuration.m_innerWidth, configuration.m_innerLength, configuration.m_innerHeight);
  235. innerExtents *= scale;
  236. debugDisplay.DrawWireOBB(position, rotationMatrix.GetBasisX(), rotationMatrix.GetBasisY(), rotationMatrix.GetBasisZ(), innerExtents / 2.0f);
  237. }
  238. AZ::Aabb EditorReflectionProbeComponent::GetEditorSelectionBoundsViewport([[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo)
  239. {
  240. return m_controller.GetWorldBounds();
  241. }
  242. bool EditorReflectionProbeComponent::SupportsEditorRayIntersect()
  243. {
  244. return false;
  245. }
  246. AZ::Outcome<void, AZStd::string> EditorReflectionProbeComponent::OnUseBakedCubemapValidate([[maybe_unused]] void* newValue, [[maybe_unused]] const AZ::Uuid& valueType)
  247. {
  248. if (!m_controller.m_featureProcessor)
  249. {
  250. return AZ::Failure(AZStd::string("This Reflection Probe entity is hidden, it must be visible in order to change the cubemap type."));
  251. }
  252. return AZ::Success();
  253. }
  254. AZ::u32 EditorReflectionProbeComponent::OnUseBakedCubemapChanged()
  255. {
  256. // save setting to the configuration
  257. m_controller.m_configuration.m_useBakedCubemap = m_useBakedCubemap;
  258. // refresh currently displayed cubemap
  259. m_controller.UpdateCubeMap();
  260. return AZ::Edit::PropertyRefreshLevels::EntireTree;
  261. }
  262. AZ::u32 EditorReflectionProbeComponent::OnAuthoredCubemapChanged()
  263. {
  264. // save the selected authored asset to the configuration
  265. m_controller.m_configuration.m_authoredCubeMapAsset = m_authoredCubeMapAsset;
  266. // refresh currently displayed cubemap
  267. m_controller.UpdateCubeMap();
  268. return AZ::Edit::PropertyRefreshLevels::None;
  269. }
  270. AZ::u32 EditorReflectionProbeComponent::OnBakeExposureChanged()
  271. {
  272. m_controller.SetBakeExposure(m_bakeExposure);
  273. return AZ::Edit::PropertyRefreshLevels::None;
  274. }
  275. AZ::u32 EditorReflectionProbeComponent::GetBakedCubemapVisibilitySetting()
  276. {
  277. // controls specific to baked cubemaps call this to determine their visibility
  278. // they are visible when the mode is set to baked, otherwise hidden
  279. return m_useBakedCubemap ? AZ::Edit::PropertyVisibility::Show : AZ::Edit::PropertyVisibility::Hide;
  280. }
  281. AZ::u32 EditorReflectionProbeComponent::GetAuthoredCubemapVisibilitySetting()
  282. {
  283. // controls specific to authored cubemaps call this to determine their visibility
  284. // they are hidden when the mode is set to baked, otherwise visible
  285. return m_useBakedCubemap ? AZ::Edit::PropertyVisibility::Hide : AZ::Edit::PropertyVisibility::Show;
  286. }
  287. AZ::u32 EditorReflectionProbeComponent::BakeReflectionProbe()
  288. {
  289. ReflectionProbeComponentConfig& configuration = m_controller.m_configuration;
  290. // if the quality level changed we need to generate a new filename
  291. if (configuration.m_bakedCubeMapQualityLevel != m_bakedCubeMapQualityLevel)
  292. {
  293. configuration.m_bakedCubeMapRelativePath.clear();
  294. }
  295. AzToolsFramework::ScopedUndoBatch undoBatch("ReflectionProbe Bake");
  296. AZ::u32 result = RenderCubeMap(
  297. [&](RenderCubeMapCallback callback, AZStd::string& relativePath) { m_controller.BakeReflectionProbe(callback, relativePath); },
  298. "Baking Reflection Probe...",
  299. GetEntity(),
  300. "ReflectionProbes",
  301. configuration.m_bakedCubeMapRelativePath,
  302. CubeMapCaptureType::Specular,
  303. m_bakedCubeMapQualityLevel);
  304. // update quality level
  305. m_controller.m_configuration.m_bakedCubeMapQualityLevel = m_bakedCubeMapQualityLevel;
  306. // update UI cubemap path display
  307. m_bakedCubeMapRelativePath = configuration.m_bakedCubeMapRelativePath;
  308. SetDirty();
  309. return result;
  310. }
  311. } // namespace Render
  312. } // namespace AZ