MaterialHotReloadTestComponent.cpp 26 KB

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