MaterialHotReloadTestComponent.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608
  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 <MaterialHotReloadTestComponent.h>
  9. #include <SampleComponentManager.h>
  10. #include <SampleComponentConfig.h>
  11. #include <Automation/ScriptableImGui.h>
  12. #include <Automation/ScriptRunnerBus.h>
  13. #include <Atom/RPI.Reflect/Model/ModelAsset.h>
  14. #include <Atom/RPI.Reflect/Material/MaterialAsset.h>
  15. #include <Atom/RPI.Reflect/Asset/AssetUtils.h>
  16. #include <Atom/Feature/Material/MaterialAssignment.h>
  17. #include <AzCore/IO/Path/Path.h>
  18. #include <AzFramework/IO/LocalFileIO.h>
  19. #include <AzFramework/Asset/AssetSystemBus.h>
  20. #include <AzFramework/Asset/AssetProcessorMessages.h>
  21. #include <Atom/RPI.Public/RPISystemInterface.h>
  22. #include <AzCore/Utils/Utils.h>
  23. #include <RHI/BasicRHIComponent.h>
  24. namespace AtomSampleViewer
  25. {
  26. using namespace AZ;
  27. using namespace RPI;
  28. namespace
  29. {
  30. namespace Sources
  31. {
  32. static constexpr const char MaterialFileName[] = "HotReloadTest.material";
  33. static constexpr const char MaterialTypeFileName[] = "HotReloadTest.materialtype";
  34. static constexpr const char ShaderFileName[] = "HotReloadTest.shader";
  35. static constexpr const char AzslFileName[] = "HotReloadTest.azsl";
  36. static constexpr const char ShaderVariantListFileName[] = "HotReloadTest.shadervariantlist";
  37. }
  38. namespace Products
  39. {
  40. static constexpr const char ModelFilePath[] = "objects/plane.azmodel";
  41. static constexpr const char MaterialFilePath[] = "materials/hotreloadtest/temp/hotreloadtest.azmaterial";
  42. static constexpr const char MaterialTypeFilePath[] = "materials/hotreloadtest/temp/hotreloadtest.azmaterialtype";
  43. static constexpr const char ShaderFilePath[] = "materials/hotreloadtest/temp/hotreloadtest.azshader";
  44. static constexpr const char ShaderVariantTreeFileName[] = "hotreloadtest.azshadervarianttree";
  45. static AZStd::string GetShaderVariantFileName(RPI::ShaderVariantStableId stableId)
  46. {
  47. RPISystemInterface* rpiSystem = RPISystemInterface::Get();
  48. Name apiName = rpiSystem->GetRenderApiName();
  49. AZStd::string filePath = AZStd::string::format("hotreloadtest_%s_%u.azshadervariant", apiName.GetCStr(), stableId.GetIndex());
  50. return filePath;
  51. }
  52. // These materials are used to communicate which shader variant is being used, for screenshot comparison tests.
  53. static constexpr const char RootVariantIndicatorMaterialFilePath[] = "materials/hotreloadtest/testdata/variantselection_root.azmaterial";
  54. static constexpr const char FullyBakedVariantIndicatorMaterialFilePath[] = "materials/hotreloadtest/testdata/variantselection_fullybaked.azmaterial";
  55. }
  56. namespace TestData
  57. {
  58. namespace MaterialFileNames
  59. {
  60. static constexpr const char Default[] = "Material_UseDefaults.txt";
  61. static constexpr const char ChangePrimaryToRed[] = "Material_ChangePrimaryToRed.txt";
  62. static constexpr const char ChangePrimaryToBlue[] = "Material_ChangePrimaryToBlue.txt";
  63. }
  64. namespace MaterialTypeFileNames
  65. {
  66. static constexpr const char StraightLines[] = "MaterialType_StraightLines.txt";
  67. static constexpr const char WavyLines[] = "MaterialType_WavyLines.txt";
  68. }
  69. namespace ShaderFileNames
  70. {
  71. static constexpr const char BlendingOff[] = "Shader_BlendingOff.txt";
  72. static constexpr const char BlendingOn[] = "Shader_BlendingOn.txt";
  73. }
  74. namespace AzslFileNames
  75. {
  76. static constexpr const char HorizontalPattern[] = "AZSL_HorizontalPattern.txt";
  77. static constexpr const char VerticalPattern[] = "AZSL_VerticalPattern.txt";
  78. }
  79. namespace ShaderVariantListFileNames
  80. {
  81. static constexpr const char All[] = "ShaderVariantList_All.txt";
  82. static constexpr const char OnlyStraightLines[] = "ShaderVariantList_OnlyStraightLines.txt";
  83. static constexpr const char OnlyWavyLines[] = "ShaderVariantList_OnlyWavyLines.txt";
  84. }
  85. }
  86. }
  87. void MaterialHotReloadTestComponent::Reflect(ReflectContext* context)
  88. {
  89. if (SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context))
  90. {
  91. serializeContext->Class<MaterialHotReloadTestComponent, CommonSampleComponentBase>()
  92. ->Version(0)
  93. ;
  94. }
  95. }
  96. MaterialHotReloadTestComponent::MaterialHotReloadTestComponent()
  97. : m_imguiSidebar{"@user@/MaterialHotReloadTestComponent/sidebar.xml"}
  98. {
  99. }
  100. void MaterialHotReloadTestComponent::InitTestDataFolders()
  101. {
  102. auto io = AZ::IO::LocalFileIO::GetInstance();
  103. auto projectPath = AZ::Utils::GetProjectPath();
  104. AZStd::string mainTestFolder;
  105. AzFramework::StringFunc::Path::Join(projectPath.c_str(), "Materials/HotReloadTest/", mainTestFolder);
  106. AzFramework::StringFunc::Path::Join(mainTestFolder.c_str(), "TestData/", m_testDataFolder);
  107. if (!io->Exists(m_testDataFolder.c_str()))
  108. {
  109. AZ_Error("MaterialHotReloadTestComponent", false, "Could not find source folder '%s'. This sample can only be used on dev platforms.", m_testDataFolder.c_str());
  110. m_testDataFolder.clear();
  111. return;
  112. }
  113. AzFramework::StringFunc::Path::Join(mainTestFolder.c_str(), "Temp/", m_tempSourceFolder);
  114. if (!io->CreatePath(m_tempSourceFolder.c_str()))
  115. {
  116. AZ_Error("MaterialHotReloadTestComponent", false, "Could not create temp folder '%s'.", m_tempSourceFolder.c_str());
  117. }
  118. }
  119. void MaterialHotReloadTestComponent::Activate()
  120. {
  121. m_initStatus = InitStatus::None;
  122. TickBus::Handler::BusConnect();
  123. m_imguiSidebar.Activate();
  124. InitTestDataFolders();
  125. // Delete any existing temp files and wait for the assets to disappear, to ensure we have a clean slate.
  126. // (If we were to just replace existing temp source files with the default ones without fully
  127. // removing them first, it would be tricky to figure out whether the assets loaded assets are the new
  128. // ones or stale ones left over from a prior instance of this sample).
  129. DeleteTestFile(Sources::MaterialFileName);
  130. DeleteTestFile(Sources::MaterialTypeFileName);
  131. DeleteTestFile(Sources::ShaderFileName);
  132. DeleteTestFile(Sources::AzslFileName);
  133. DeleteTestFile(Sources::ShaderVariantListFileName);
  134. m_initStatus = InitStatus::ClearingTestAssets;
  135. m_clearAssetsTimeout = 5.0f;
  136. // Wait until the test material is fully initialized. Use a long timeout because it can take a while for the shaders to compile.
  137. ScriptRunnerRequestBus::Broadcast(&ScriptRunnerRequests::PauseScriptWithTimeout, LongTimeout);
  138. Data::AssetId modelAssetId;
  139. Data::AssetCatalogRequestBus::BroadcastResult(modelAssetId, &Data::AssetCatalogRequestBus::Events::GetAssetIdByPath, Products::ModelFilePath, AZ::Uuid::CreateNull(), false);
  140. AZ_Assert(modelAssetId.IsValid(), "Failed to get model asset id: %s", Products::ModelFilePath);
  141. m_modelAsset.Create(modelAssetId);
  142. const Transform cameraTransform = Transform::CreateFromQuaternionAndTranslation(
  143. Quaternion::CreateFromAxisAngle(Vector3::CreateAxisZ(), AZ::Constants::Pi),
  144. Vector3{0.0f, 2.0f, 0.0f}
  145. );
  146. AZ::TransformBus::Event(GetCameraEntityId(), &AZ::TransformBus::Events::SetWorldTM, cameraTransform);
  147. m_meshFeatureProcessor = Scene::GetFeatureProcessorForEntityContextId<Render::MeshFeatureProcessorInterface>(GetEntityContextId());
  148. // The shader variant indicator banner will appear right below the main test mesh
  149. m_shaderVariantIndicatorMeshTransform = Transform::CreateIdentity();
  150. m_shaderVariantIndicatorMeshTransform.SetTranslation(Vector3{0.0f, 0.0f, -0.6f});
  151. m_shaderVariantIndicatorMeshNonUniformScale = Vector3(1.0f, 0.125f, 1.0f);
  152. m_shaderVariantIndicatorMeshTransform.SetRotation(Quaternion::CreateFromAxisAngle(Vector3::CreateAxisX(), -AZ::Constants::HalfPi));
  153. // Load materials that will be used to indicate which shader variant is active...
  154. Data::Asset<MaterialAsset> indicatorMaterialAsset;
  155. indicatorMaterialAsset = AssetUtils::LoadAssetByProductPath<MaterialAsset>(Products::RootVariantIndicatorMaterialFilePath, AssetUtils::TraceLevel::Assert);
  156. m_shaderVariantIndicatorMaterial_root = Material::FindOrCreate(indicatorMaterialAsset);
  157. indicatorMaterialAsset = AssetUtils::LoadAssetByProductPath<MaterialAsset>(Products::FullyBakedVariantIndicatorMaterialFilePath, AssetUtils::TraceLevel::Assert);
  158. m_shaderVariantIndicatorMaterial_fullyBaked = Material::FindOrCreate(indicatorMaterialAsset);
  159. }
  160. void MaterialHotReloadTestComponent::Deactivate()
  161. {
  162. m_meshFeatureProcessor->ReleaseMesh(m_meshHandle);
  163. m_meshFeatureProcessor->ReleaseMesh(m_shaderVariantIndicatorMeshHandle);
  164. m_shaderVariantIndicatorMaterial_root.reset();
  165. m_shaderVariantIndicatorMaterial_fullyBaked.reset();
  166. m_shaderVariantIndicatorMaterial_current.reset();
  167. Data::AssetBus::Handler::BusDisconnect();
  168. TickBus::Handler::BusDisconnect();
  169. m_imguiSidebar.Deactivate();
  170. m_initStatus = InitStatus::None;
  171. }
  172. void MaterialHotReloadTestComponent::DeleteTestFile(const char* tempSourceFile)
  173. {
  174. AZ::IO::Path deletePath = AZ::IO::Path(m_tempSourceFolder).Append(tempSourceFile);
  175. if (AZ::IO::LocalFileIO::GetInstance()->Exists(deletePath.c_str()))
  176. {
  177. if (!AZ::IO::LocalFileIO::GetInstance()->Remove(deletePath.c_str()))
  178. {
  179. AZ_Error("MaterialHotReloadTestComponent", false, "Failed to delete '%s'.", deletePath.c_str());
  180. }
  181. }
  182. }
  183. void MaterialHotReloadTestComponent::CopyTestFile(const AZStd::string& testDataFile, const AZStd::string& tempSourceFile)
  184. {
  185. // Instead of copying the file using AZ::IO::LocalFileIO, we load the file and write out a new file over top
  186. // the destination. This is necessary to make the AP reliably detect the changes (if we just copy the file,
  187. // sometimes it recognizes the OS level copy as an updated file and sometimes not).
  188. AZ::IO::Path copyFrom = AZ::IO::Path(m_testDataFolder).Append(testDataFile);
  189. AZ::IO::Path copyTo = AZ::IO::Path(m_tempSourceFolder).Append(tempSourceFile);
  190. auto readResult = AZ::Utils::ReadFile(copyFrom.c_str());
  191. if (!readResult.IsSuccess())
  192. {
  193. AZ_Error("MaterialHotReloadTestComponent", false, "%s", readResult.GetError().c_str());
  194. return;
  195. }
  196. auto writeResult = AZ::Utils::WriteFile(readResult.GetValue(), copyTo.c_str());
  197. if (!writeResult.IsSuccess())
  198. {
  199. AZ_Error("MaterialHotReloadTestComponent", false, "%s", writeResult.GetError().c_str());
  200. return;
  201. }
  202. }
  203. const char* ToString(AzFramework::AssetSystem::AssetStatus status)
  204. {
  205. switch (status)
  206. {
  207. case AzFramework::AssetSystem::AssetStatus_Missing: return "Missing";
  208. case AzFramework::AssetSystem::AssetStatus_Queued: return "Queued...";
  209. case AzFramework::AssetSystem::AssetStatus_Compiling: return "Compiling...";
  210. case AzFramework::AssetSystem::AssetStatus_Compiled: return "Compiled";
  211. case AzFramework::AssetSystem::AssetStatus_Failed: return "Failed";
  212. default: return "Unknown";
  213. }
  214. }
  215. AzFramework::AssetSystem::AssetStatus MaterialHotReloadTestComponent::GetTestAssetStatus(const char* tempSourceFile) const
  216. {
  217. AZStd::string filePath = AZStd::string("Materials/HotReloadTest/Temp/") + tempSourceFile;
  218. AzFramework::AssetSystem::AssetStatus status = AzFramework::AssetSystem::AssetStatus::AssetStatus_Unknown;
  219. AzFramework::AssetSystemRequestBus::BroadcastResult(status, &AzFramework::AssetSystem::AssetSystemRequests::GetAssetStatusSearchType,
  220. filePath.c_str(), AzFramework::AssetSystem::RequestAssetStatus::SearchType::Exact);
  221. return status;
  222. }
  223. void MaterialHotReloadTestComponent::DrawAssetStatus(const char* tempSourceFile, bool includeFileName)
  224. {
  225. AzFramework::AssetSystem::AssetStatus status = GetTestAssetStatus(tempSourceFile);
  226. if (includeFileName)
  227. {
  228. ImGui::Text("%s", tempSourceFile);
  229. ImGui::Indent();
  230. }
  231. ImGui::Text("Status:");
  232. ImGui::SameLine();
  233. ImGui::Text("%s", ToString(status));
  234. if (includeFileName)
  235. {
  236. ImGui::Unindent();
  237. }
  238. }
  239. Data::AssetId MaterialHotReloadTestComponent::GetAssetId(const char* productFilePath)
  240. {
  241. Data::AssetId assetId;
  242. Data::AssetCatalogRequestBus::BroadcastResult(assetId, &Data::AssetCatalogRequestBus::Events::GetAssetIdByPath, productFilePath, AZ::Uuid::CreateNull(), false);
  243. return assetId;
  244. }
  245. void MaterialHotReloadTestComponent::OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset)
  246. {
  247. if (m_initStatus == InitStatus::WaitingForDefaultMaterialToLoad && asset.GetId() == m_materialAsset.GetId())
  248. {
  249. m_materialAsset = asset;
  250. Data::AssetBus::Handler::BusDisconnect(asset.GetId());
  251. m_initStatus = InitStatus::Ready;
  252. // Now that we finally have the material asset, we can add the model to the scene...
  253. m_material = Material::Create(m_materialAsset);
  254. Transform meshTransform = Transform::CreateFromQuaternion(Quaternion::CreateFromAxisAngle(Vector3::CreateAxisX(), -AZ::Constants::HalfPi));
  255. m_meshHandle = m_meshFeatureProcessor->AcquireMesh(AZ::Render::MeshHandleDescriptor{ m_modelAsset }, m_material);
  256. m_meshFeatureProcessor->SetTransform(m_meshHandle, meshTransform);
  257. Data::Instance<RPI::Model> model = GetMeshFeatureProcessor()->GetModel(m_meshHandle);
  258. if (model)
  259. {
  260. // Both the model and material are ready so scripts can continue
  261. ScriptRunnerRequestBus::Broadcast(&ScriptRunnerRequests::ResumeScript);
  262. }
  263. else
  264. {
  265. // The material is ready but the model isn't ready yet; wait until it's ready before allowing scripts to continue
  266. m_meshChangedHandler = AZ::Render::MeshFeatureProcessorInterface::ModelChangedEvent::Handler
  267. {
  268. [](AZ::Data::Instance<AZ::RPI::Model> model) { ScriptRunnerRequestBus::Broadcast(&ScriptRunnerRequests::ResumeScript); }
  269. };
  270. GetMeshFeatureProcessor()->ConnectModelChangeEventHandler(m_meshHandle, m_meshChangedHandler);
  271. }
  272. }
  273. }
  274. MaterialHotReloadTestComponent::ShaderVariantStatus MaterialHotReloadTestComponent::GetShaderVariantStatus() const
  275. {
  276. ShaderVariantStatus shaderVariantStatus = ShaderVariantStatus::None;
  277. if (m_material)
  278. {
  279. const AZ::RPI::MeshDrawPacketLods& drawPackets = GetMeshFeatureProcessor()->GetDrawPackets(m_meshHandle);
  280. if (!drawPackets.empty())
  281. {
  282. AZ_Assert(drawPackets.size() == 1, "Expected exactly 1 LOD");
  283. AZ_Assert(drawPackets[0].size() == 1, "Expected exactly 1 mesh");
  284. AZ_Assert(drawPackets[0][0].GetMaterial() == m_material, "MeshDrawPacket didn't have the expected material.");
  285. const RPI::MeshDrawPacket::ShaderList& activeShaderList = drawPackets[0][0].GetActiveShaderList();
  286. AZ_Assert(activeShaderList.size() == 1, "Expected exactly 1 active shader");
  287. const ShaderVariantId activeVariantId = activeShaderList[0].m_activeShaderVariantId;
  288. ShaderOptionGroup activeShaderOptions{activeShaderList[0].m_shader->GetAsset()->GetShaderOptionGroupLayout(), activeVariantId};
  289. if (activeShaderOptions.IsFullySpecified())
  290. {
  291. shaderVariantStatus = ShaderVariantStatus::FullyBaked;
  292. }
  293. else if (activeVariantId.IsEmpty())
  294. {
  295. shaderVariantStatus = ShaderVariantStatus::Root;
  296. }
  297. else
  298. {
  299. shaderVariantStatus = ShaderVariantStatus::PartiallyBaked;
  300. }
  301. }
  302. }
  303. return shaderVariantStatus;
  304. }
  305. void MaterialHotReloadTestComponent::OnTick([[maybe_unused]] float deltaTime, ScriptTimePoint /*scriptTime*/)
  306. {
  307. if (m_initStatus == InitStatus::ClearingTestAssets)
  308. {
  309. m_clearAssetsTimeout -= deltaTime;
  310. Data::AssetId materialAssetId = GetAssetId(Products::MaterialFilePath);
  311. Data::AssetId materialTypeAssetId = GetAssetId(Products::MaterialTypeFilePath);
  312. Data::AssetId shaderAssetId = GetAssetId(Products::ShaderFilePath);
  313. bool proceed = !materialAssetId.IsValid() && !materialTypeAssetId.IsValid() && !shaderAssetId.IsValid();
  314. if (!proceed && m_clearAssetsTimeout < 0)
  315. {
  316. // There was a specific bug in the Asset Processor where deleting a source file does not always remove the corresponding product
  317. // from the cache and from the asset database.
  318. AZ_Error("MaterialHotReloadTestComponent", false, "Timed out while waiting for test assets to be removed.");
  319. proceed = true;
  320. }
  321. if (proceed)
  322. {
  323. // [GFX TODO] [ATOM-5899] Once this ticket is addressed, This block can call all required CopyTestFile() at once,
  324. // and the states InitStatus::CopyingDefault***TestFile won't be needed.
  325. // Any stale assets have been cleared, now we can create the new ones.
  326. // Copy a default set files into the temp folder.
  327. CopyTestFile(TestData::AzslFileNames::HorizontalPattern, Sources::AzslFileName);
  328. CopyTestFile(TestData::ShaderFileNames::BlendingOff, Sources::ShaderFileName);
  329. m_initStatus = InitStatus::CopyingDefaultShaderTestFile;
  330. }
  331. }
  332. if (m_initStatus == InitStatus::CopyingDefaultShaderTestFile)
  333. {
  334. AzFramework::AssetSystem::AssetStatus status = GetTestAssetStatus(Sources::ShaderFileName);
  335. if (status == AzFramework::AssetSystem::AssetStatus::AssetStatus_Compiled)
  336. {
  337. CopyTestFile(TestData::MaterialTypeFileNames::StraightLines, Sources::MaterialTypeFileName);
  338. m_initStatus = InitStatus::CopyingDefaultMaterialTypeTestFile;
  339. }
  340. }
  341. else if (m_initStatus == InitStatus::CopyingDefaultMaterialTypeTestFile)
  342. {
  343. AzFramework::AssetSystem::AssetStatus status = GetTestAssetStatus(Sources::MaterialTypeFileName);
  344. if (status == AzFramework::AssetSystem::AssetStatus::AssetStatus_Compiled)
  345. {
  346. CopyTestFile(TestData::MaterialFileNames::Default, Sources::MaterialFileName);
  347. m_initStatus = InitStatus::WaitingForDefaultMaterialToRegister;
  348. }
  349. }
  350. if (m_initStatus == InitStatus::WaitingForDefaultMaterialToRegister)
  351. {
  352. Data::AssetId materialAssetId = GetAssetId(Products::MaterialFilePath);
  353. if (materialAssetId.IsValid())
  354. {
  355. m_initStatus = InitStatus::WaitingForDefaultMaterialToLoad;
  356. Data::AssetBus::Handler::BusConnect(materialAssetId);
  357. m_materialAsset.Create(materialAssetId, true);
  358. }
  359. }
  360. auto shaderVariantStatus = GetShaderVariantStatus();
  361. if (shaderVariantStatus == ShaderVariantStatus::None)
  362. {
  363. m_meshFeatureProcessor->ReleaseMesh(m_shaderVariantIndicatorMeshHandle);
  364. m_shaderVariantIndicatorMaterial_current.reset();
  365. }
  366. else
  367. {
  368. bool updateIndicatorMesh = false;
  369. if (shaderVariantStatus == ShaderVariantStatus::Root)
  370. {
  371. if (m_shaderVariantIndicatorMaterial_current != m_shaderVariantIndicatorMaterial_root)
  372. {
  373. m_shaderVariantIndicatorMaterial_current = m_shaderVariantIndicatorMaterial_root;
  374. updateIndicatorMesh = true;
  375. }
  376. }
  377. else if(shaderVariantStatus == ShaderVariantStatus::FullyBaked)
  378. {
  379. if (m_shaderVariantIndicatorMaterial_current != m_shaderVariantIndicatorMaterial_fullyBaked)
  380. {
  381. m_shaderVariantIndicatorMaterial_current = m_shaderVariantIndicatorMaterial_fullyBaked;
  382. updateIndicatorMesh = true;
  383. }
  384. }
  385. else
  386. {
  387. AZ_Assert(false, "Unsupported ShaderVariantStatus");
  388. }
  389. if (updateIndicatorMesh)
  390. {
  391. m_meshFeatureProcessor->ReleaseMesh(m_shaderVariantIndicatorMeshHandle);
  392. m_shaderVariantIndicatorMeshHandle = m_meshFeatureProcessor->AcquireMesh(Render::MeshHandleDescriptor{ m_modelAsset }, m_shaderVariantIndicatorMaterial_current);
  393. m_meshFeatureProcessor->SetTransform(m_shaderVariantIndicatorMeshHandle, m_shaderVariantIndicatorMeshTransform,
  394. m_shaderVariantIndicatorMeshNonUniformScale);
  395. }
  396. }
  397. if (m_imguiSidebar.Begin())
  398. {
  399. if (m_initStatus != InitStatus::Ready)
  400. {
  401. ImGui::Text("Waiting for assets...");
  402. ImGui::Separator();
  403. }
  404. ImGui::Text(Sources::MaterialFileName);
  405. ImGui::Indent();
  406. {
  407. DrawAssetStatus(Sources::MaterialFileName);
  408. if (m_initStatus == InitStatus::Ready)
  409. {
  410. if (ScriptableImGui::Button("Default Colors"))
  411. {
  412. CopyTestFile(TestData::MaterialFileNames::Default, Sources::MaterialFileName);
  413. }
  414. if (ScriptableImGui::Button("ColorA = Red"))
  415. {
  416. CopyTestFile(TestData::MaterialFileNames::ChangePrimaryToRed, Sources::MaterialFileName);
  417. }
  418. if (ScriptableImGui::Button("ColorA = Blue"))
  419. {
  420. CopyTestFile(TestData::MaterialFileNames::ChangePrimaryToBlue, Sources::MaterialFileName);
  421. }
  422. }
  423. }
  424. ImGui::Unindent();
  425. ImGui::Text(Sources::MaterialTypeFileName);
  426. ImGui::Indent();
  427. {
  428. DrawAssetStatus(Sources::MaterialTypeFileName);
  429. if (m_initStatus == InitStatus::Ready)
  430. {
  431. if (ScriptableImGui::Button("Straight Lines"))
  432. {
  433. CopyTestFile(TestData::MaterialTypeFileNames::StraightLines, Sources::MaterialTypeFileName);
  434. }
  435. if (ScriptableImGui::Button("Wavy Lines"))
  436. {
  437. CopyTestFile(TestData::MaterialTypeFileNames::WavyLines, Sources::MaterialTypeFileName);
  438. }
  439. }
  440. }
  441. ImGui::Unindent();
  442. ImGui::Text(Sources::ShaderFileName);
  443. ImGui::Indent();
  444. {
  445. DrawAssetStatus(Sources::ShaderFileName);
  446. if (m_initStatus == InitStatus::Ready)
  447. {
  448. if (ScriptableImGui::Button("Blending Off"))
  449. {
  450. CopyTestFile(TestData::ShaderFileNames::BlendingOff, Sources::ShaderFileName);
  451. }
  452. if (ScriptableImGui::Button("Blending On"))
  453. {
  454. CopyTestFile(TestData::ShaderFileNames::BlendingOn, Sources::ShaderFileName);
  455. }
  456. }
  457. }
  458. ImGui::Unindent();
  459. ImGui::Text(Sources::AzslFileName);
  460. ImGui::Indent();
  461. {
  462. // The AZSL file is a source dependency of the .shader file, so display the same asset status
  463. DrawAssetStatus(Sources::AzslFileName);
  464. if (m_initStatus == InitStatus::Ready)
  465. {
  466. if (ScriptableImGui::Button("Horizontal Pattern"))
  467. {
  468. CopyTestFile(TestData::AzslFileNames::HorizontalPattern, Sources::AzslFileName);
  469. }
  470. if (ScriptableImGui::Button("Vertical Pattern"))
  471. {
  472. CopyTestFile(TestData::AzslFileNames::VerticalPattern, Sources::AzslFileName);
  473. }
  474. }
  475. }
  476. ImGui::Unindent();
  477. ImGui::Text(Sources::ShaderVariantListFileName);
  478. ImGui::Indent();
  479. {
  480. // The AZSL file is a source dependency of the .shader file, so display the same asset status
  481. DrawAssetStatus(Sources::ShaderVariantListFileName, false);
  482. DrawAssetStatus(Products::ShaderVariantTreeFileName, true);
  483. DrawAssetStatus(Products::GetShaderVariantFileName(ShaderVariantStableId{0}).c_str(), true);
  484. DrawAssetStatus(Products::GetShaderVariantFileName(ShaderVariantStableId{1}).c_str(), true);
  485. DrawAssetStatus(Products::GetShaderVariantFileName(ShaderVariantStableId{2}).c_str(), true);
  486. if (m_initStatus == InitStatus::Ready)
  487. {
  488. ScriptableImGui::PushNameContext("ShaderVariantList");
  489. if (ScriptableImGui::Button("None"))
  490. {
  491. DeleteTestFile(Sources::ShaderVariantListFileName);
  492. }
  493. if (ScriptableImGui::Button("All"))
  494. {
  495. CopyTestFile(TestData::ShaderVariantListFileNames::All, Sources::ShaderVariantListFileName);
  496. }
  497. if (ScriptableImGui::Button("Only Wavy Lines"))
  498. {
  499. CopyTestFile(TestData::ShaderVariantListFileNames::OnlyWavyLines, Sources::ShaderVariantListFileName);
  500. }
  501. if (ScriptableImGui::Button("Only Straight Lines"))
  502. {
  503. CopyTestFile(TestData::ShaderVariantListFileNames::OnlyStraightLines, Sources::ShaderVariantListFileName);
  504. }
  505. ScriptableImGui::PopNameContext();
  506. }
  507. }
  508. ImGui::Unindent();
  509. m_imguiSidebar.End();
  510. }
  511. }
  512. } // namespace AtomSampleViewer