ImGuiLYEntityOutliner.cpp 58 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227
  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 "ImGuiLYEntityOutliner.h"
  9. #ifdef IMGUI_ENABLED
  10. #include <AzCore/Component/TransformBus.h>
  11. #include <AzCore/Asset/AssetSerializer.h>
  12. #include <AzCore/std/sort.h>
  13. #include <AzCore/std/string/conversions.h>
  14. #include <AzFramework/Entity/EntityContext.h>
  15. #include <AzFramework/Entity/EntityContextBus.h>
  16. #include <AzFramework/Entity/GameEntityContextBus.h>
  17. #include "ImGuiColorDefines.h"
  18. namespace ImGui
  19. {
  20. // Text and Color Consts
  21. static const char* s_OnText = "On:";
  22. static const char* s_ColorText = "Color:";
  23. static const ImVec4 s_DisplayNameDefaultColor = ImColor(1.0f, 0.0f, 1.0f, 0.9f);
  24. static const ImVec4 s_DisplayChildCountDefaultColor = ImColor(0.32f, 0.38f, 0.16f);
  25. static const ImVec4 s_DisplayDescendantCountDefaultColor = ImColor(0.32f, 0.64f, 0.38f);
  26. static const ImVec4 s_DisplayEntityStateDefaultColor = ImColor(0.73f, 0.97f, 0.6f);
  27. static const ImVec4 s_DisplayParentInfoDefaultColor = ImColor(0.32f, 0.55f, 1.0f);
  28. static const ImVec4 s_DisplayLocalPosDefaultColor = ImColor(0.0f, 0.8f, 0.12f);
  29. static const ImVec4 s_DisplayLocalRotationDefaultColor = ImColor(0.0f, 0.8f, 0.12f, 0.55f);
  30. static const ImVec4 s_DisplayWorldPosDefaultColor = ImColor(1.0f, 0.8f, 0.12f);
  31. static const ImVec4 s_DisplayWorldRotationDefaultColor = ImColor(1.0f, 0.8f, 0.12f, 0.55f);
  32. static const ImVec4 s_ComponentParamColor_Type = ImColor(1.0f, 0.0f, 1.0f, 0.9f);
  33. static const ImVec4 s_ComponentParamColor_Name = ImColor(1.0f, 0.8f, 0.12f);
  34. static const ImVec4 s_ComponentParamColor_Value = ImColor(0.32f, 1.0f, 1.0f);
  35. ImGuiLYEntityOutliner::ImGuiLYEntityOutliner()
  36. : m_enabled(false)
  37. , m_displayName(true, s_DisplayNameDefaultColor)
  38. , m_displayChildCount(false, s_DisplayChildCountDefaultColor)
  39. , m_displayDescentdantCount(true, s_DisplayDescendantCountDefaultColor)
  40. , m_displayEntityState(false, s_DisplayEntityStateDefaultColor)
  41. , m_displayParentInfo(false, s_DisplayParentInfoDefaultColor)
  42. , m_displayLocalPos(false, s_DisplayLocalPosDefaultColor)
  43. , m_displayLocalRotation(false, s_DisplayLocalRotationDefaultColor)
  44. , m_displayWorldPos(true, s_DisplayWorldPosDefaultColor)
  45. , m_displayWorldRotation(true, s_DisplayWorldRotationDefaultColor)
  46. , m_hierarchyUpdateType(HierarchyUpdateType::Constant)
  47. , m_hierarchyUpdateTickTimeCurrent(0.0f)
  48. , m_hierarchyUpdateTickTimeTotal(1.0f)
  49. , m_rootEntityInfo(nullptr)
  50. , m_totalEntitiesFound(0)
  51. , m_drawTargetViewButton(false)
  52. {
  53. }
  54. ImGuiLYEntityOutliner::~ImGuiLYEntityOutliner()
  55. {
  56. }
  57. void ImGuiLYEntityOutliner::Initialize()
  58. {
  59. // Connect to Ebusses
  60. ImGuiEntityOutlinerRequestBus::Handler::BusConnect();
  61. }
  62. void ImGuiLYEntityOutliner::Shutdown()
  63. {
  64. // Disconnect Ebusses
  65. ImGuiEntityOutlinerRequestBus::Handler::BusDisconnect();
  66. }
  67. void ImGuiLYEntityOutliner::ImGuiUpdate_DrawViewOptions()
  68. {
  69. // Create a child to help better size the menu
  70. ImGui::BeginChild("EntityOutliner_ViewOptionsMenuChild", ImVec2(580.0f, 260.0f));
  71. // Options for view entity entries
  72. ImGui::Columns(3);
  73. ImGui::TextColored(m_displayName.m_color, "Display Name");
  74. ImGui::NextColumn();
  75. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "%s", s_OnText);
  76. ImGui::SameLine();
  77. ImGui::Checkbox("##DisplayNameCB", &m_displayName.m_enabled);
  78. ImGui::NextColumn();
  79. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "%s", s_ColorText);
  80. ImGui::SameLine();
  81. ImGui::ColorEdit4("##DisplayNameCol", reinterpret_cast<float*>(&m_displayName.m_color));
  82. ImGui::NextColumn();
  83. ImGui::TextColored(m_displayChildCount.m_color, "Display Child Count");
  84. ImGui::NextColumn();
  85. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "%s", s_OnText);
  86. ImGui::SameLine();
  87. ImGui::Checkbox("##DisplayChildCountCB", &m_displayChildCount.m_enabled);
  88. ImGui::NextColumn();
  89. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "%s", s_ColorText);
  90. ImGui::SameLine();
  91. ImGui::ColorEdit4("##DisplayChildCountCol", reinterpret_cast<float*>(&m_displayChildCount.m_color));
  92. ImGui::NextColumn();
  93. ImGui::TextColored(m_displayDescentdantCount.m_color, "Display Descendant Count");
  94. ImGui::NextColumn();
  95. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "%s", s_OnText);
  96. ImGui::SameLine();
  97. ImGui::Checkbox("##DisplayDescendantCountCB", &m_displayDescentdantCount.m_enabled);
  98. ImGui::NextColumn();
  99. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "%s", s_ColorText);
  100. ImGui::SameLine();
  101. ImGui::ColorEdit4("##DisplayDescendantCountCol", reinterpret_cast<float*>(&m_displayDescentdantCount.m_color));
  102. ImGui::NextColumn();
  103. ImGui::TextColored(m_displayEntityState.m_color, "Display Entity Status");
  104. ImGui::NextColumn();
  105. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "%s", s_OnText);
  106. ImGui::SameLine();
  107. ImGui::Checkbox("##DisplayEntityStateCB", &m_displayEntityState.m_enabled);
  108. ImGui::NextColumn();
  109. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "%s", s_ColorText);
  110. ImGui::SameLine();
  111. ImGui::ColorEdit4("##DisplayEntityStateCol", reinterpret_cast<float*>(&m_displayEntityState.m_color));
  112. ImGui::NextColumn();
  113. ImGui::TextColored(m_displayParentInfo.m_color, "Display Parent Info");
  114. ImGui::NextColumn();
  115. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "%s", s_OnText);
  116. ImGui::SameLine();
  117. ImGui::Checkbox("##DisplayParentInfoCB", &m_displayParentInfo.m_enabled);
  118. ImGui::NextColumn();
  119. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "%s", s_ColorText);
  120. ImGui::SameLine();
  121. ImGui::ColorEdit4("##DisplayParentInfoCol", reinterpret_cast<float*>(&m_displayParentInfo.m_color));
  122. ImGui::NextColumn();
  123. ImGui::TextColored(m_displayLocalPos.m_color, "Display Local Position");
  124. ImGui::NextColumn();
  125. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "%s", s_OnText);
  126. ImGui::SameLine();
  127. ImGui::Checkbox("##DisplayLocalPosCB", &m_displayLocalPos.m_enabled);
  128. ImGui::NextColumn();
  129. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "%s", s_ColorText);
  130. ImGui::SameLine();
  131. ImGui::ColorEdit4("##DisplayLocalPosCol", reinterpret_cast<float*>(&m_displayLocalPos.m_color));
  132. ImGui::NextColumn();
  133. ImGui::TextColored(m_displayLocalRotation.m_color, "Display Local Rotation");
  134. ImGui::NextColumn();
  135. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "%s", s_OnText);
  136. ImGui::SameLine();
  137. ImGui::Checkbox("##DisplayLocalRotationCB", &m_displayLocalRotation.m_enabled);
  138. ImGui::NextColumn();
  139. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "%s", s_ColorText);
  140. ImGui::SameLine();
  141. ImGui::ColorEdit4("##DisplayLocalRotationCol", reinterpret_cast<float*>(&m_displayLocalRotation.m_color));
  142. ImGui::NextColumn();
  143. ImGui::TextColored(m_displayWorldPos.m_color, "Display World Position");
  144. ImGui::NextColumn();
  145. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "%s", s_OnText);
  146. ImGui::SameLine();
  147. ImGui::Checkbox("##DisplayWorldPosCB", &m_displayWorldPos.m_enabled);
  148. ImGui::NextColumn();
  149. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "%s", s_ColorText);
  150. ImGui::SameLine();
  151. ImGui::ColorEdit4("##DisplayWorldPosCol", reinterpret_cast<float*>(&m_displayWorldPos.m_color));
  152. ImGui::NextColumn();
  153. ImGui::TextColored(m_displayWorldRotation.m_color, "Display World Rotation");
  154. ImGui::NextColumn();
  155. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "%s", s_OnText);
  156. ImGui::SameLine();
  157. ImGui::Checkbox("##DisplayWorldRotationCB", &m_displayWorldRotation.m_enabled);
  158. ImGui::NextColumn();
  159. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "%s", s_ColorText);
  160. ImGui::SameLine();
  161. ImGui::ColorEdit4("##DisplayWorldRotationCol", reinterpret_cast<float*>(&m_displayWorldRotation.m_color));
  162. // Set Column positions
  163. ImGui::SetColumnOffset(1, 200.0f);
  164. ImGui::SetColumnOffset(2, 270.0f);
  165. ImGui::Columns(1);
  166. // The 3rd parameter of this Combo box HAS to match the order of ImGuiLYEntityOutliner::HierarchyUpdateType
  167. ImGui::Combo("Hierarchy Update Type", reinterpret_cast<int*>(&m_hierarchyUpdateType), "Constant\0Update Tick");
  168. // Refresh the hierarchy / display further options, based on update type
  169. switch (m_hierarchyUpdateType)
  170. {
  171. default:
  172. break;
  173. case HierarchyUpdateType::UpdateTick:
  174. // allow a slider to determine tick time
  175. ImGui::SliderFloat("Update Tick Time", &m_hierarchyUpdateTickTimeTotal, 0.1f, 10.0f);
  176. ImGui::SameLine();
  177. ImGui::ProgressBar(m_hierarchyUpdateTickTimeCurrent / m_hierarchyUpdateTickTimeTotal);
  178. break;
  179. }
  180. ImGui::EndChild(); // "EntityOutliner_ViewOptionsMenuChild"
  181. }
  182. void ImGuiLYEntityOutliner::ImGuiUpdate_DrawComponentViewSubMenu()
  183. {
  184. AZ::SerializeContext *serializeContext = nullptr;
  185. AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
  186. if (serializeContext != nullptr)
  187. {
  188. ImGui::TextColored(ImGui::Colors::s_NiceLabelColor, "Open All Debug Component Views for Component:");
  189. for (const AZ::TypeId& comDebugInfoEntry : m_componentDebugSortedList)
  190. {
  191. AZStd::string componentName("**name_not_found**");
  192. const AZ::SerializeContext::ClassData* classData = serializeContext->FindClassData(comDebugInfoEntry);
  193. if (classData != nullptr)
  194. {
  195. componentName = classData->m_name;
  196. }
  197. // Component Name
  198. if (ImGui::MenuItem(componentName.c_str()))
  199. {
  200. RequestAllViewsForComponent(comDebugInfoEntry);
  201. }
  202. }
  203. }
  204. }
  205. void ImGuiLYEntityOutliner::ImGuiUpdate_DrawAutoEnableOptions()
  206. {
  207. // Display/Remove Search Strings
  208. if (ImGui::CollapsingHeader("Component Auto Enable Search Strings", ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_Framed))
  209. {
  210. ImGui::BeginChild("ComponentSearchStringList", ImVec2(400.0f, 100.0f));
  211. ImGui::Columns(2);
  212. AZStd::string stringToRemove = ""; // Record if we elect to remove a string in any frame. Don't do anything if it remains ""
  213. for (const AZStd::string& searchString : m_autoEnableComponentSearchStrings)
  214. {
  215. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "%s", searchString.c_str());
  216. ImGui::NextColumn();
  217. if (ImGui::Button(AZStd::string::format("Remove##%s", searchString.c_str()).c_str()))
  218. {
  219. stringToRemove = searchString;
  220. }
  221. ImGui::NextColumn();
  222. }
  223. if (stringToRemove != "")
  224. {
  225. m_autoEnableComponentSearchStrings.erase(stringToRemove);
  226. }
  227. ImGui::Columns(1);
  228. ImGui::EndChild();
  229. // Add Search String
  230. static char searchCharArray[128] = "";
  231. ImGui::InputText("", searchCharArray, sizeof(searchCharArray));
  232. ImGui::SameLine();
  233. if (ImGui::Button(AZStd::string::format("Add '%s'", searchCharArray).c_str()))
  234. {
  235. const AZStd::string& searchString = searchCharArray;
  236. // Don't add an empty string.
  237. if (searchString != "")
  238. {
  239. AddAutoEnableSearchString(searchString);
  240. }
  241. }
  242. }
  243. AZ::SerializeContext *serializeContext = nullptr;
  244. AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
  245. if (serializeContext != nullptr)
  246. {
  247. if (ImGui::CollapsingHeader("ImGui Registered Components", ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_Framed))
  248. {
  249. ImGui::BeginChild("ImGuiRegisteredComponents", ImVec2(800.0f, 200.0f));
  250. ImGui::Columns(4);
  251. // Column Headers
  252. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "Component Name");
  253. ImGui::NextColumn();
  254. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "Priority");
  255. ImGui::NextColumn();
  256. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "Auto Enable");
  257. ImGui::NextColumn();
  258. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "Open All Of Type");
  259. ImGui::NextColumn();
  260. for (const AZ::TypeId& comDebugInfoEntry : m_componentDebugSortedList)
  261. {
  262. AZStd::string componentName("**name_not_found**");
  263. const AZ::SerializeContext::ClassData* classData = serializeContext->FindClassData(comDebugInfoEntry);
  264. if (classData != nullptr)
  265. {
  266. componentName = classData->m_name;
  267. }
  268. // Component Name
  269. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "%s", componentName.c_str());
  270. ImGui::NextColumn();
  271. // Debug Priority
  272. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "%d", m_componentDebugInfoMap[comDebugInfoEntry].m_priority);
  273. ImGui::NextColumn();
  274. // Auto Enable
  275. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "Set:");
  276. ImGui::SameLine();
  277. ImGui::Checkbox(AZStd::string::format("##%s", componentName.c_str()).c_str(), &m_componentDebugInfoMap[comDebugInfoEntry].m_autoLaunchEnabled);
  278. ImGui::NextColumn();
  279. // Open All of Type Button
  280. if (ImGui::Button(AZStd::string::format("Open All %s", componentName.c_str()).c_str()))
  281. {
  282. RequestAllViewsForComponent(comDebugInfoEntry);
  283. }
  284. ImGui::NextColumn();
  285. }
  286. // Set the Column Offsets
  287. ImGui::SetColumnOffset(1, 290.0f);
  288. ImGui::SetColumnOffset(2, 360.0f);
  289. ImGui::SetColumnOffset(3, 455.0f);
  290. // Turn off Columns
  291. ImGui::Columns(1);
  292. ImGui::EndChild();
  293. }
  294. }
  295. }
  296. void ImGuiLYEntityOutliner::ImGuiUpdate()
  297. {
  298. if (m_enabled)
  299. {
  300. if (ImGui::Begin("Entity Outliner", &m_enabled, ImGuiWindowFlags_MenuBar|ImGuiWindowFlags_HorizontalScrollbar|ImGuiWindowFlags_NoSavedSettings))
  301. {
  302. if (ImGui::BeginMenuBar())
  303. {
  304. if (ImGui::BeginMenu("View Options##entityOutliner"))
  305. {
  306. ImGuiUpdate_DrawViewOptions();
  307. ImGui::EndMenu();
  308. }
  309. if (ImGui::BeginMenu("Auto-Open Options##entityOutliner"))
  310. {
  311. ImGuiUpdate_DrawAutoEnableOptions();
  312. ImGui::EndMenu();
  313. }
  314. ImGui::EndMenuBar();
  315. }
  316. // Refresh the Entity Hierarchy if we are going to
  317. // Refresh the hierarchy / display further options, based on update type
  318. switch (m_hierarchyUpdateType)
  319. {
  320. default:
  321. case HierarchyUpdateType::Constant:
  322. // constant: just refresh every frame!
  323. RefreshEntityHierarchy();
  324. break;
  325. case HierarchyUpdateType::UpdateTick:
  326. // increment the timer
  327. m_hierarchyUpdateTickTimeCurrent += ImGui::GetIO().DeltaTime;
  328. if (m_hierarchyUpdateTickTimeCurrent > m_hierarchyUpdateTickTimeTotal)
  329. {
  330. m_hierarchyUpdateTickTimeCurrent = fmod(m_hierarchyUpdateTickTimeCurrent, m_hierarchyUpdateTickTimeTotal);
  331. RefreshEntityHierarchy();
  332. }
  333. break;
  334. }
  335. // Draw the entity hierarchy
  336. ImGui::TextColored(ImGui::Colors::s_NiceLabelColor, "Entity Count: %d Hierarchy:", m_totalEntitiesFound);
  337. // Draw the root entity and all its decendants as a collapsable menu
  338. ImGuiUpdate_RecursivelyDisplayEntityInfoAndDecendants(m_rootEntityInfo, true);
  339. }
  340. ImGui::End();
  341. }
  342. // Loop through our unordered_set of Entities to draw entity views for, and draw them!
  343. for (auto itr = m_entitiesToView.begin(); itr != m_entitiesToView.end(); )
  344. {
  345. if (!ImGuiUpdate_DrawEntityView(*itr))
  346. {
  347. auto itrToErase = itr;
  348. itr++;
  349. // ImGuiUpdate_DrawEntityView will return false if we need to close the window, so lets remove the entry at this itr
  350. m_entitiesToView.erase(*itrToErase);
  351. }
  352. else
  353. {
  354. itr++;
  355. }
  356. }
  357. // Loop through our unordered_set of Component/Entity pairs to draw component views for, and draw them!
  358. for (auto itr = m_componentsToView.begin(); itr != m_componentsToView.end(); )
  359. {
  360. if (!ImGuiUpdate_DrawComponentView(*itr))
  361. {
  362. auto itrToErase = itr;
  363. itr++;
  364. // ImGuiUpdate_DrawComponentView will return false if we need to close the window, so lets remove the entry at this itr
  365. m_componentsToView.erase(*itrToErase);
  366. }
  367. else
  368. {
  369. itr++;
  370. }
  371. }
  372. }
  373. bool ImGuiLYEntityOutliner::ImGuiUpdate_DrawEntityView(const AZ::EntityId &ent)
  374. {
  375. // Check to make sure the entity is still valid..
  376. AZ::Entity* entity = nullptr;
  377. AZ::ComponentApplicationBus::BroadcastResult(entity, &AZ::ComponentApplicationBus::Events::FindEntity, ent);
  378. bool viewWindow = (entity != nullptr);
  379. if (viewWindow)
  380. {
  381. AZStd::string entityName;
  382. AZ::ComponentApplicationBus::BroadcastResult(entityName, &AZ::ComponentApplicationBus::Events::GetEntityName, ent);
  383. AZStd::string windowLabel = AZStd::string::format("Entity View %s%s", entityName.c_str(), ent.ToString().c_str());
  384. if (ImGui::Begin(windowLabel.c_str(), &viewWindow, ImGuiWindowFlags_HorizontalScrollbar|ImGuiWindowFlags_NoSavedSettings))
  385. {
  386. ImGui::TextColored(ImGui::Colors::s_NiceLabelColor, "%s%s", entityName.c_str(), ent.ToString().c_str());
  387. // Draw the same thing that is in the full hierarchy
  388. ImGuiUpdate_RecursivelyDisplayEntityInfoAndDecendants(m_entityIdToInfoNodePtrMap[ent], false, false, true, true, false, true);
  389. }
  390. ImGui::End();
  391. }
  392. return viewWindow;
  393. }
  394. bool ImGuiLYEntityOutliner::ImGuiUpdate_DrawComponentView(const ImGui::ImGuiEntComponentId &entCom)
  395. {
  396. // Check to make sure the entity is still valid..
  397. AZ::Entity* entity = nullptr;
  398. AZ::ComponentApplicationBus::BroadcastResult(entity, &AZ::ComponentApplicationBus::Events::FindEntity, entCom.first);
  399. bool viewWindow = (entity != nullptr);
  400. if (viewWindow)
  401. {
  402. AZStd::string componentName("**name_not_found**");
  403. AZ::SerializeContext *serializeContext = nullptr;
  404. AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
  405. if (serializeContext != nullptr)
  406. {
  407. const AZ::SerializeContext::ClassData* classData = serializeContext->FindClassData(entCom.second);
  408. if (classData != nullptr )
  409. {
  410. componentName = classData->m_name;
  411. }
  412. }
  413. ImGui::PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(250.0f, 200.0f));
  414. AZStd::string windowLabel = AZStd::string::format("Component View - %s - on Entity %s%s", componentName.c_str(), entity->GetName().c_str(), entCom.first.ToString().c_str());
  415. ImGuiWindowFlags flags = ImGuiWindowFlags_HorizontalScrollbar|ImGuiWindowFlags_NoSavedSettings;
  416. if (m_componentDebugInfoMap[entCom.second].m_menuBarEnabled)
  417. {
  418. flags |= ImGuiWindowFlags_MenuBar;
  419. }
  420. if (ImGui::Begin(windowLabel.c_str(), &viewWindow, flags))
  421. {
  422. // Attempt to draw any debug information for this component
  423. ImGuiUpdateDebugComponentListenerBus::Event(entCom, &ImGuiUpdateDebugComponentListenerBus::Events::OnImGuiDebugLYComponentUpdate);
  424. }
  425. ImGui::End();
  426. ImGui::PopStyleVar();
  427. }
  428. return viewWindow;
  429. }
  430. void ImGuiLYEntityOutliner::ImGuiUpdate_RecursivelyDisplayEntityInfoAndDecendants(EntityInfoNodePtr node, bool justDrawChildren /*= false*/, bool drawInspectButton /*= true*/,
  431. bool drawTargetButton /*= true*/, bool drawDebugButton /*= true*/, bool sameLine /*= true*/, bool drawComponents /*= false*/)
  432. {
  433. if (node != nullptr)
  434. {
  435. AZStd::string childTreeNodeStr = node->m_entityId.ToString();
  436. // Draw the stuff
  437. if (!node->m_children.empty())
  438. {
  439. if (justDrawChildren)
  440. {
  441. for (int i = 0; i < node->m_children.size(); i++)
  442. {
  443. ImGuiUpdate_RecursivelyDisplayEntityInfoAndDecendants(node->m_children[i]);
  444. }
  445. }
  446. else if (sameLine)
  447. {
  448. if (ImGui::TreeNode(childTreeNodeStr.c_str(), "%s", childTreeNodeStr.c_str()))
  449. {
  450. ImGuiUpdate_RecursivelyDisplayEntityInfoAndDecendants_DrawDisplayOptions(node, drawInspectButton, drawTargetButton, drawDebugButton, sameLine, drawComponents);
  451. for (int i = 0; i < node->m_children.size(); i++)
  452. {
  453. ImGuiUpdate_RecursivelyDisplayEntityInfoAndDecendants(node->m_children[i]);
  454. }
  455. ImGui::TreePop();
  456. }
  457. else
  458. {
  459. ImGuiUpdate_RecursivelyDisplayEntityInfoAndDecendants_DrawDisplayOptions(node, drawInspectButton, drawTargetButton, drawDebugButton, sameLine, drawComponents);
  460. }
  461. }
  462. else
  463. {
  464. ImGuiUpdate_RecursivelyDisplayEntityInfoAndDecendants_DrawDisplayOptions(node, drawInspectButton, drawTargetButton, drawDebugButton, sameLine, drawComponents);
  465. childTreeNodeStr = AZStd::string::format("Children ##%s", childTreeNodeStr.c_str());
  466. if (ImGui::TreeNode(childTreeNodeStr.c_str(), "%s", childTreeNodeStr.c_str()))
  467. {
  468. for (int i = 0; i < node->m_children.size(); i++)
  469. {
  470. ImGuiUpdate_RecursivelyDisplayEntityInfoAndDecendants(node->m_children[i]);
  471. }
  472. ImGui::TreePop();
  473. }
  474. }
  475. }
  476. else if (!justDrawChildren)
  477. {
  478. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "->"); ImGui::SameLine();
  479. ImGui::Text("%s", childTreeNodeStr.c_str());
  480. ImGuiUpdate_RecursivelyDisplayEntityInfoAndDecendants_DrawDisplayOptions(node, drawInspectButton, drawTargetButton, drawDebugButton, sameLine, drawComponents);
  481. }
  482. }
  483. }
  484. void ImGuiLYEntityOutliner::ImGuiUpdate_RecursivelyDisplayEntityInfoAndDecendants_DrawDisplayOptions(EntityInfoNodePtr node, bool drawInspectButton, bool drawTargetButton, bool drawDebugButton, bool sameLine, bool drawComponents)
  485. {
  486. if (node != nullptr)
  487. {
  488. // Entity Name
  489. if (m_displayName.m_enabled)
  490. {
  491. AZStd::string entityName;
  492. AZ::ComponentApplicationBus::BroadcastResult(entityName, &AZ::ComponentApplicationBus::Events::GetEntityName, node->m_entityId);
  493. if (sameLine)
  494. {
  495. ImGui::SameLine();
  496. }
  497. ImGui::TextColored(m_displayName.m_color, "%s", entityName.c_str());
  498. }
  499. // Draw EntityViewer Button
  500. if (drawInspectButton)
  501. {
  502. if (sameLine)
  503. {
  504. ImGui::SameLine();
  505. }
  506. const AZStd::string& inspectLabel = AZStd::string::format("Inspect##%s", node->m_entityId.ToString().c_str());
  507. if (ImGui::SmallButton(inspectLabel.c_str()))
  508. {
  509. // If we clicked the button, attempt to insert this entity into the set. It will only accept unique values and will limit to 1 entry per entityid
  510. RequestEntityView(node->m_entityId);
  511. }
  512. }
  513. // Target Button
  514. if (m_drawTargetViewButton && drawTargetButton)
  515. {
  516. if (sameLine)
  517. {
  518. ImGui::SameLine();
  519. }
  520. const AZStd::string& targetLabel = AZStd::string::format("View##%s", node->m_entityId.ToString().c_str());
  521. if (ImGui::SmallButton(targetLabel.c_str()))
  522. {
  523. // Send EBUS event out to Target an Entity. Up to game code to implement.
  524. ImGuiEntityOutlinerNotificationBus::Broadcast(&IImGuiEntityOutlinerNotifications::OnImGuiEntityOutlinerTarget, node->m_entityId);
  525. }
  526. }
  527. // Debug Button
  528. if (drawDebugButton && !node->m_highestPriorityComponentDebug.IsNull())
  529. {
  530. const AZStd::string& debugLabel = AZStd::string::format("Debug##%s", node->m_entityId.ToString().c_str());
  531. if (sameLine)
  532. {
  533. ImGui::SameLine();
  534. }
  535. if (ImGui::SmallButton(debugLabel.c_str()))
  536. {
  537. RequestComponentView(ImGuiEntComponentId(node->m_entityId, node->m_highestPriorityComponentDebug));
  538. }
  539. }
  540. // Child Entity Count
  541. if (m_displayChildCount.m_enabled)
  542. {
  543. if (sameLine)
  544. {
  545. ImGui::SameLine();
  546. }
  547. ImGui::TextColored(m_displayChildCount.m_color, "children: %zu", node->m_children.size());
  548. }
  549. // Descendant Entity Count
  550. if (m_displayDescentdantCount.m_enabled)
  551. {
  552. if (sameLine)
  553. {
  554. ImGui::SameLine();
  555. }
  556. ImGui::TextColored(m_displayDescentdantCount.m_color, "descendants: %d", node->m_descendantCount);
  557. }
  558. // Entity State
  559. if (m_displayEntityState.m_enabled)
  560. {
  561. if (sameLine)
  562. {
  563. ImGui::SameLine();
  564. }
  565. AZ::Entity* entity(nullptr);
  566. AZ::ComponentApplicationBus::BroadcastResult(entity, &AZ::ComponentApplicationBus::Events::FindEntity, node->m_entityId);
  567. AZStd::string stateString;
  568. if (entity == nullptr)
  569. {
  570. stateString = "*invalid_entity_found*";
  571. }
  572. else
  573. {
  574. switch (entity->GetState())
  575. {
  576. default:
  577. stateString = "*unhandled_entity_state_found*";
  578. break;
  579. case AZ::Entity::State::Activating:
  580. stateString = "ACTIVATING";
  581. break;
  582. case AZ::Entity::State::Active:
  583. stateString = "ACTIVE";
  584. break;
  585. case AZ::Entity::State::Constructed:
  586. stateString = "CONSTRUCTED";
  587. break;
  588. case AZ::Entity::State::Deactivating:
  589. stateString = "DEACTIVATING";
  590. break;
  591. case AZ::Entity::State::Init:
  592. stateString = "INIT";
  593. break;
  594. case AZ::Entity::State::Initializing:
  595. stateString = "INITIALIZING";
  596. break;
  597. }
  598. }
  599. ImGui::TextColored(m_displayEntityState.m_color, "EntityState: %s", stateString.c_str());
  600. }
  601. // Parent Entity Information
  602. if (m_displayParentInfo.m_enabled)
  603. {
  604. AZ::EntityId parentId(AZ::EntityId::InvalidEntityId);
  605. if (node->m_parent != nullptr)
  606. {
  607. parentId = node->m_parent->m_entityId;
  608. }
  609. AZStd::string parentName;
  610. AZ::ComponentApplicationBus::BroadcastResult(parentName, &AZ::ComponentApplicationBus::Events::GetEntityName, parentId);
  611. if (sameLine)
  612. {
  613. ImGui::SameLine();
  614. }
  615. ImGui::TextColored(m_displayParentInfo.m_color, "Parent: %s%s", parentName.c_str(), parentId.ToString().c_str());
  616. }
  617. // Local Position
  618. if (m_displayLocalPos.m_enabled)
  619. {
  620. AZ::Vector3 localPos = AZ::Vector3::CreateOne();
  621. AZ::TransformBus::EventResult(localPos, node->m_entityId, &AZ::TransformBus::Events::GetLocalTranslation);
  622. if (sameLine)
  623. {
  624. ImGui::SameLine();
  625. }
  626. ImGui::TextColored(m_displayLocalPos.m_color, "localPos: (%.02f, %.02f, %.02f)", (float)localPos.GetX(), (float)localPos.GetY(), (float)localPos.GetZ());
  627. }
  628. // Local Rotation
  629. if (m_displayLocalRotation.m_enabled)
  630. {
  631. AZ::Vector3 localRotation = AZ::Vector3::CreateOne();
  632. AZ::TransformBus::EventResult(localRotation, node->m_entityId, &AZ::TransformBus::Events::GetLocalRotation);
  633. if (sameLine)
  634. {
  635. ImGui::SameLine();
  636. }
  637. ImGui::TextColored(m_displayLocalRotation.m_color, "localRot: (%.02f, %.02f, %.02f)", (float)localRotation.GetX(), (float)localRotation.GetY(), (float)localRotation.GetZ());
  638. }
  639. // World Position
  640. if (m_displayWorldPos.m_enabled)
  641. {
  642. AZ::Vector3 worldPos = AZ::Vector3::CreateOne();
  643. AZ::TransformBus::EventResult(worldPos, node->m_entityId, &AZ::TransformBus::Events::GetWorldTranslation);
  644. if (sameLine)
  645. {
  646. ImGui::SameLine();
  647. }
  648. ImGui::TextColored(m_displayWorldPos.m_color, "WorldPos: (%.02f, %.02f, %.02f)", (float)worldPos.GetX(), (float)worldPos.GetY(), (float)worldPos.GetZ());
  649. }
  650. // World Rotation
  651. if (m_displayWorldRotation.m_enabled)
  652. {
  653. AZ::Vector3 worldRotation = AZ::Vector3::CreateOne();
  654. AZ::TransformBus::EventResult(worldRotation, node->m_entityId, &AZ::TransformBus::Events::GetWorldRotation);
  655. if (sameLine)
  656. {
  657. ImGui::SameLine();
  658. }
  659. ImGui::TextColored(m_displayWorldRotation.m_color, "WorldRot: (%.02f, %.02f, %.02f)", (float)worldRotation.GetX(), (float)worldRotation.GetY(), (float)worldRotation.GetZ());
  660. }
  661. // Components
  662. if (drawComponents)
  663. {
  664. AZ::Entity* entity = nullptr;
  665. AZ::ComponentApplicationBus::BroadcastResult(entity, &AZ::ComponentApplicationBus::Events::FindEntity, node->m_entityId);
  666. if (entity != nullptr)
  667. {
  668. // Draw collapsible menu for the components set
  669. AZStd::string uiLabel = AZStd::string::format("Components##%s", node->m_entityId.ToString().c_str());
  670. if (ImGui::TreeNode(uiLabel.c_str(), "%s", uiLabel.c_str()))
  671. {
  672. AZ::Entity::ComponentArrayType components = entity->GetComponents();
  673. // we should sort our array of components based on their names.
  674. static auto sortByComponentName = [](AZ::Component* com1, AZ::Component* com2)
  675. {
  676. AZStd::string name1 = com1->RTTI_GetTypeName();
  677. AZStd::string name2 = com2->RTTI_GetTypeName();
  678. AZStd::to_lower(name1.begin(), name1.end());
  679. AZStd::to_lower(name2.begin(), name2.end());
  680. return name1 < name2;
  681. };
  682. AZStd::sort(components.begin(), components.end(), sortByComponentName);
  683. for (auto component : components)
  684. {
  685. bool hasDebug = ComponentHasDebug(component->RTTI_GetType());
  686. // Draw a collapsible menu for each component
  687. uiLabel = AZStd::string::format("%s##%s", component->RTTI_GetTypeName(), node->m_entityId.ToString().c_str());
  688. if (ImGui::TreeNode(uiLabel.c_str(), "%s", uiLabel.c_str()))
  689. {
  690. if (hasDebug)
  691. {
  692. ImGui::SameLine();
  693. uiLabel = AZStd::string::format("Component Debug View##%s-%s", node->m_entityId.ToString().c_str(), component->RTTI_GetTypeName());
  694. if (ImGui::SmallButton(uiLabel.c_str()))
  695. {
  696. RequestComponentView(ImGuiEntComponentId(node->m_entityId, component->RTTI_GetType()));
  697. }
  698. }
  699. // Draw a collapsible menu for all Reflected Properties
  700. uiLabel = AZStd::string::format("Reflected Properties##%s", node->m_entityId.ToString().c_str());
  701. if (ImGui::TreeNode(uiLabel.c_str(), "%s", uiLabel.c_str()))
  702. {
  703. AZ::SerializeContext *serializeContext = nullptr;
  704. AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
  705. serializeContext->EnumerateObject(const_cast<AZ::Component *>(component),
  706. // beginElemCB
  707. [this](void *instance, const AZ::SerializeContext::ClassData *classData, const AZ::SerializeContext::ClassElement *classElement) -> bool
  708. {
  709. if (classElement != nullptr)
  710. {
  711. ImGuiUpdate_DrawComponent(instance, classData, classElement);
  712. }
  713. return true;
  714. },
  715. // endElemCB
  716. []() -> bool { return true; },
  717. AZ::SerializeContext::ENUM_ACCESS_FOR_READ, nullptr/* errorHandler */);
  718. ImGui::TreePop();
  719. }
  720. // Draw a collapsible menu for any potential component debuging stuff.
  721. if (hasDebug)
  722. {
  723. uiLabel = AZStd::string::format("Debug##%s", node->m_entityId.ToString().c_str());
  724. if (ImGui::TreeNode(uiLabel.c_str(), "%s", uiLabel.c_str()))
  725. {
  726. // Attempt to draw any debug information for this component
  727. ImGuiUpdateDebugComponentListenerBus::Event(ImGuiEntComponentId(node->m_entityId, component->RTTI_GetType())
  728. , &ImGuiUpdateDebugComponentListenerBus::Events::OnImGuiDebugLYComponentUpdate);
  729. ImGui::TreePop();
  730. }
  731. }
  732. ImGui::TreePop();
  733. }
  734. else if (hasDebug)
  735. {
  736. ImGui::SameLine();
  737. uiLabel = AZStd::string::format("Component Debug View##%s-%s", node->m_entityId.ToString().c_str(), component->RTTI_GetTypeName());
  738. if (ImGui::SmallButton(uiLabel.c_str()))
  739. {
  740. RequestComponentView(ImGuiEntComponentId(node->m_entityId, component->RTTI_GetType()));
  741. }
  742. }
  743. }
  744. ImGui::TreePop();
  745. }
  746. }
  747. }
  748. }
  749. }
  750. void ImGuiLYEntityOutliner::ImGuiUpdate_DrawComponent(void *instance, const AZ::SerializeContext::ClassData *classData, const AZ::SerializeContext::ClassElement *classElement)
  751. {
  752. const char *typeName = classData->m_name;
  753. AZStd::string value;
  754. if (classElement->m_typeId == AZ::SerializeGenericTypeInfo<char >::GetClassTypeId()) { value = AZStd::string::format("%d", *reinterpret_cast<const char *>(instance)); }
  755. else if (classElement->m_typeId == AZ::SerializeGenericTypeInfo<AZ::s8 >::GetClassTypeId()) { value = AZStd::string::format("%d", *reinterpret_cast<const AZ::s8 *>(instance)); }
  756. else if (classElement->m_typeId == AZ::SerializeGenericTypeInfo<short >::GetClassTypeId()) { value = AZStd::string::format("%d", *reinterpret_cast<const short *>(instance)); }
  757. else if (classElement->m_typeId == AZ::SerializeGenericTypeInfo<int >::GetClassTypeId()) { value = AZStd::string::format("%d", *reinterpret_cast<const int *>(instance)); }
  758. else if (classElement->m_typeId == AZ::SerializeGenericTypeInfo<long >::GetClassTypeId()) { value = AZStd::string::format("%ld", *reinterpret_cast<const long *>(instance)); }
  759. else if (classElement->m_typeId == AZ::SerializeGenericTypeInfo<AZ::s64 >::GetClassTypeId()) { value = AZStd::string::format("%lld", *reinterpret_cast<const AZ::s64 *>(instance)); }
  760. else if (classElement->m_typeId == AZ::SerializeGenericTypeInfo<unsigned char >::GetClassTypeId()) { value = AZStd::string::format("%u", *reinterpret_cast<const unsigned char *>(instance)); }
  761. else if (classElement->m_typeId == AZ::SerializeGenericTypeInfo<unsigned short>::GetClassTypeId()) { value = AZStd::string::format("%u", *reinterpret_cast<const unsigned short *>(instance)); }
  762. else if (classElement->m_typeId == AZ::SerializeGenericTypeInfo<unsigned int >::GetClassTypeId()) { value = AZStd::string::format("%u", *reinterpret_cast<const unsigned int *>(instance)); }
  763. else if (classElement->m_typeId == AZ::SerializeGenericTypeInfo<unsigned long >::GetClassTypeId()) { value = AZStd::string::format("%lu", *reinterpret_cast<const unsigned long *>(instance)); }
  764. else if (classElement->m_typeId == AZ::SerializeGenericTypeInfo<AZ::u64 >::GetClassTypeId()) { value = AZStd::string::format("%llu", *reinterpret_cast<const AZ::u64 *>(instance)); }
  765. else if (classElement->m_typeId == AZ::SerializeGenericTypeInfo<float >::GetClassTypeId()) { value = AZStd::string::format("%.*g", FLT_DIG, *reinterpret_cast<const float *>(instance)); }
  766. else if (classElement->m_typeId == AZ::SerializeGenericTypeInfo<double >::GetClassTypeId()) { value = AZStd::string::format("%.*g", DBL_DIG, *reinterpret_cast<const double *>(instance)); }
  767. else if (classElement->m_typeId == AZ::SerializeGenericTypeInfo<bool >::GetClassTypeId()) { value = AZStd::string::format(*reinterpret_cast<const bool *>(instance) ? "true" : "false"); }
  768. else if (classElement->m_typeId == AZ::SerializeGenericTypeInfo<AZStd::string >::GetClassTypeId()) { value = AZStd::string::format("\"%s\"", reinterpret_cast<const AZStd::string *>(instance)->c_str()); }
  769. else if (classElement->m_typeId == AZ::SerializeGenericTypeInfo<AZ::Vector3 >::GetClassTypeId())
  770. {
  771. const AZ::Vector3 *v = reinterpret_cast<const AZ::Vector3 *>(instance);
  772. value = AZStd::string::format("(%.*g %.*g %.*g)", FLT_DIG, (float)v->GetX(), FLT_DIG, (float)v->GetY(), FLT_DIG, (float)v->GetZ());
  773. }
  774. else if (classElement->m_typeId == AZ::SerializeGenericTypeInfo<AZ::Transform >::GetClassTypeId())
  775. {
  776. const AZ::Transform *t = reinterpret_cast<const AZ::Transform *>(instance);
  777. value = AZStd::string::format("pos(%.03f %.03f %.03f) x(%.03f %.03f %.03f) y(%.03f %.03f %.03f) z(%.03f %.03f %.03f)", (float)t->GetTranslation().GetX(), (float)t->GetTranslation().GetY(), (float)t->GetTranslation().GetZ(),
  778. (float)t->GetBasisX().GetX(), (float)t->GetBasisX().GetY(), (float)t->GetBasisX().GetZ(),
  779. (float)t->GetBasisY().GetX(), (float)t->GetBasisY().GetY(), (float)t->GetBasisY().GetZ(),
  780. (float)t->GetBasisZ().GetX(), (float)t->GetBasisZ().GetY(), (float)t->GetBasisZ().GetZ());
  781. }
  782. else if (const auto* genericInfo = classElement->m_genericClassInfo; genericInfo && genericInfo->GetGenericTypeId() == AZ::GetAssetClassId())
  783. {
  784. const AZ::Data::Asset<AZ::Data::AssetData> *a = reinterpret_cast<const AZ::Data::Asset<AZ::Data::AssetData> *>(instance);
  785. value = AZStd::string::format("\"%s\"", a->GetHint().c_str());
  786. }
  787. // FIXME - add other types
  788. else if ((classData->m_container == nullptr || classData->m_container->Size(instance) == 0) && classData->m_elements.size() == 0)
  789. {
  790. // not yet sure if/how this could be useful, but maybe to detect certain types
  791. #if 0
  792. if (classElement->m_genericClassInfo != nullptr)
  793. {
  794. AZ::SerializeContext::ClassData* cd = classElement->m_genericClassInfo->GetClassData();
  795. size_t numArgs = classElement->m_genericClassInfo->GetNumTemplatedArguments();
  796. const AZ::Uuid& sId = classElement->m_genericClassInfo->GetSpecializedTypeId();
  797. for (size_t i = 0; i < numArgs; i++)
  798. {
  799. const AZ::Uuid& tId = classElement->m_genericClassInfo->GetTemplatedTypeId(i);
  800. tId = tId;
  801. }
  802. }
  803. #endif
  804. // this is either a leaf type or a type which doesn't expose children types, so this 'value' will be placeholder to let someone know about types whose value parsing is unimplemented
  805. value = "<value parsing not implemented for this type>";
  806. }
  807. // Actually draw the data!
  808. ImGui::TextColored(s_ComponentParamColor_Type, " -> %s", typeName);
  809. ImGui::SameLine();
  810. ImGui::TextColored(s_ComponentParamColor_Name, "\"%s\"", classElement->m_name);
  811. ImGui::SameLine();
  812. ImGui::TextColored(ImGui::Colors::s_PlainLabelColor, "(0x%llX)", reinterpret_cast<AZ::u64>(instance));
  813. if (!value.empty())
  814. {
  815. ImGui::SameLine();
  816. ImGui::TextColored(s_ComponentParamColor_Value, "%s", value.c_str());
  817. }
  818. }
  819. void ImGuiLYEntityOutliner::RefreshEntityHierarchy()
  820. {
  821. // Retrieve Id map from game entity context (editor->runtime).
  822. AzFramework::EntityContextId gameContextId = AzFramework::EntityContextId::CreateNull();
  823. AzFramework::GameEntityContextRequestBus::BroadcastResult(gameContextId, &AzFramework::GameEntityContextRequests::GetGameEntityContextId);
  824. // Get the Root Slice Component
  825. AZ::SliceComponent* rootSliceComponent;
  826. AzFramework::SliceEntityOwnershipServiceRequestBus::EventResult(rootSliceComponent, gameContextId,
  827. &AzFramework::SliceEntityOwnershipServiceRequests::GetRootSlice);
  828. if (rootSliceComponent)
  829. {
  830. // Get an unordered_set of all EntityIds in the slice
  831. AZ::SliceComponent::EntityIdSet entityIds;
  832. rootSliceComponent->GetEntityIds(entityIds);
  833. // Save off our count for use later.
  834. m_totalEntitiesFound = static_cast<int>(entityIds.size());
  835. // Clear the entityId to InfoNodePtr Map.
  836. m_entityIdToInfoNodePtrMap.clear();
  837. // Delete the root Entity Info node and all children recursively
  838. DeleteEntityInfoAndDecendants(m_rootEntityInfo);
  839. m_rootEntityInfo.reset(); // Don't really have to do this, but will further ensure all nodes are deleted
  840. // Now, lets build the hierarchy! Not sure of the order of the entities, so it's a bit naieve. Will supply timers to control refresh rate
  841. // First, build the root Node, which is kind of a fake node
  842. AZ::EntityId invalidEntId = AZ::EntityId(AZ::EntityId::InvalidEntityId);
  843. m_rootEntityInfo = new EntityInfoNode(invalidEntId, nullptr);
  844. m_entityIdToInfoNodePtrMap[invalidEntId] = m_rootEntityInfo;
  845. // Lets remove entity Ids from this set as we find their place in the hierarchy.
  846. while (!entityIds.empty())
  847. {
  848. // Keep a flag to see if we found any parent entities this round. If not, we should probably bail ( else, loop forever! )
  849. bool anyParentFound = false;
  850. for (auto it = entityIds.begin(); it != entityIds.end(); )
  851. {
  852. AZ::EntityId childEntId = *it;
  853. AZ::EntityId entityParent;
  854. AZ::TransformBus::EventResult(entityParent, childEntId, &AZ::TransformBus::Events::GetParentId);
  855. EntityInfoNodePtr parentEntInfo = FindEntityInfoByEntityId(entityParent, m_rootEntityInfo);
  856. if (parentEntInfo != nullptr)
  857. {
  858. // We found our parent node! Lets create a node for ourselves and hang it off our parent
  859. EntityInfoNodePtr node = new EntityInfoNode(childEntId, parentEntInfo);
  860. parentEntInfo->m_children.push_back(node);
  861. m_entityIdToInfoNodePtrMap[childEntId] = node;
  862. // Delete this entity id from the unordered set, and get the next iterator
  863. it = entityIds.erase(it);
  864. // Flag that we have found any parent this round
  865. anyParentFound = true;
  866. }
  867. else
  868. {
  869. it++; // Advance the iterator
  870. }
  871. }
  872. // if we haven't found any new parents for remaining entities this round, we probably have rogue entities :(
  873. // break here in this case to avoid infinite loop
  874. if (!anyParentFound)
  875. {
  876. break;
  877. }
  878. }
  879. // with the hierarchy created, lets now traverse recursively and find every node's descendant count
  880. RefreshEntityHierarchy_FillCacheAndSort(m_rootEntityInfo);
  881. }
  882. }
  883. int ImGuiLYEntityOutliner::RefreshEntityHierarchy_FillCacheAndSort(EntityInfoNodePtr entityInfo)
  884. {
  885. int descendantCount = 0;
  886. for (int i = 0; i < entityInfo->m_children.size(); i++)
  887. {
  888. // Add one count for each of this entities children...
  889. descendantCount++;
  890. // .. and additional counts for their children's decendants!
  891. descendantCount += RefreshEntityHierarchy_FillCacheAndSort(entityInfo->m_children[i]);
  892. }
  893. // we should sort our array of children as well, based on their names.
  894. static auto sortByEntityName = [](const EntityInfoNodePtr& ent1, const EntityInfoNodePtr& ent2)
  895. {
  896. AZStd::string name1, name2;
  897. AZ::ComponentApplicationBus::BroadcastResult(name1, &AZ::ComponentApplicationBus::Events::GetEntityName, ent1->m_entityId);
  898. AZ::ComponentApplicationBus::BroadcastResult(name2, &AZ::ComponentApplicationBus::Events::GetEntityName, ent2->m_entityId);
  899. AZStd::to_lower(name1.begin(), name1.end());
  900. AZStd::to_lower(name2.begin(), name2.end());
  901. return name1 < name2;
  902. };
  903. AZStd::sort(entityInfo->m_children.begin(), entityInfo->m_children.end(), sortByEntityName);
  904. // Lets find this entities highest priority Debug Component.
  905. AZ::Entity* entity = nullptr;
  906. AZ::ComponentApplicationBus::BroadcastResult(entity, &AZ::ComponentApplicationBus::Events::FindEntity, entityInfo->m_entityId);
  907. if (entity != nullptr)
  908. {
  909. int highestPriority = -1;
  910. const AZ::Entity::ComponentArrayType& components = entity->GetComponents();
  911. for (auto c : components)
  912. {
  913. int pri = ComponentHasDebug(c->RTTI_GetType()) ? m_componentDebugInfoMap[c->RTTI_GetType()].m_priority : -1;
  914. if (pri > highestPriority)
  915. {
  916. highestPriority = pri;
  917. entityInfo->m_highestPriorityComponentDebug = c->RTTI_GetType();
  918. }
  919. }
  920. // if we didn't find any debug components, create a null TypeId for highest priority component
  921. if (highestPriority == -1)
  922. {
  923. entityInfo->m_highestPriorityComponentDebug = AZ::TypeId::CreateNull();
  924. }
  925. }
  926. return entityInfo->m_descendantCount = descendantCount;
  927. }
  928. ImGuiLYEntityOutliner::EntityInfoNodePtr ImGuiLYEntityOutliner::FindEntityInfoByEntityId(const AZ::EntityId &entityId, EntityInfoNodePtr searchNode)
  929. {
  930. if (searchNode != nullptr)
  931. {
  932. // if the provided node matches, return it!
  933. if (searchNode->m_entityId == entityId)
  934. {
  935. return searchNode;
  936. }
  937. // lets check our children
  938. for (int i = 0; i < searchNode->m_children.size(); i++)
  939. {
  940. // See if we find the info in all decendants
  941. EntityInfoNodePtr foundNode = FindEntityInfoByEntityId(entityId, searchNode->m_children[i]);
  942. if (foundNode != nullptr)
  943. {
  944. // return the Child Node that was found!
  945. return foundNode;
  946. }
  947. }
  948. }
  949. // we found nothing! Return a nullptr
  950. return nullptr;
  951. }
  952. void ImGuiLYEntityOutliner::DeleteEntityInfoAndDecendants(EntityInfoNodePtr entityInfo)
  953. {
  954. if (entityInfo != nullptr)
  955. {
  956. for (int i = 0; i < entityInfo->m_children.size(); i++)
  957. {
  958. // Recursively Delete Children
  959. DeleteEntityInfoAndDecendants(entityInfo->m_children[i]);
  960. }
  961. // We need to clear the array to remove any child smart pointers
  962. entityInfo->m_children.clear();
  963. // Now delete the node contents by reseting the smart pointer
  964. entityInfo.reset();
  965. }
  966. }
  967. void ImGuiLYEntityOutliner::RequestEntityView(AZ::EntityId entity)
  968. {
  969. m_entitiesToView.insert(entity);
  970. }
  971. void ImGuiLYEntityOutliner::RemoveEntityView(AZ::EntityId entity)
  972. {
  973. m_entitiesToView.erase(entity);
  974. }
  975. void ImGuiLYEntityOutliner::RequestComponentView(ImGuiEntComponentId component)
  976. {
  977. m_componentsToView.insert(component);
  978. }
  979. void ImGuiLYEntityOutliner::RemoveComponentView(ImGuiEntComponentId component)
  980. {
  981. m_componentsToView.erase(component);
  982. }
  983. void ImGuiLYEntityOutliner::RequestAllViewsForComponent(const AZ::TypeId& comType)
  984. {
  985. // To do this, we want to iterate through all component views connected to the bus
  986. ImGui::ImGuiUpdateDebugComponentListenerBus::EnumerateHandlers([&comType, this](ImGui::IImGuiUpdateDebugComponentListener* imGuiComListener)
  987. {
  988. if (AZ::Component* com = azrtti_cast<AZ::Component*>(imGuiComListener))
  989. {
  990. // If we found a Handler of this component type, open up the component view!
  991. if (azrtti_istypeof(comType, com))
  992. {
  993. ImGui::ImGuiEntComponentId id(com->GetEntityId(), comType);
  994. RequestComponentView(id);
  995. }
  996. }
  997. return true;
  998. });
  999. }
  1000. void ImGuiLYEntityOutliner::EnableTargetViewMode(bool enabled)
  1001. {
  1002. m_drawTargetViewButton = enabled;
  1003. }
  1004. void ImGuiLYEntityOutliner::SetEnabled(bool enabled)
  1005. {
  1006. m_enabled = enabled;
  1007. }
  1008. void ImGuiLYEntityOutliner::AddAutoEnableSearchString(const AZStd::string& searchString)
  1009. {
  1010. // Copy off the string and to_lower it
  1011. AZStd::string stringToAdd = searchString;
  1012. AZStd::to_lower(stringToAdd.begin(), stringToAdd.end());
  1013. // Insert the lower-cased string into our set
  1014. m_autoEnableComponentSearchStrings.insert(stringToAdd);
  1015. RefreshAutoEnableBasedOnSearchStrings();
  1016. }
  1017. void ImGuiLYEntityOutliner::RefreshAutoEnableBasedOnSearchStrings()
  1018. {
  1019. AZ::SerializeContext *serializeContext = nullptr;
  1020. AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
  1021. if (serializeContext != nullptr)
  1022. {
  1023. // Iterate through the auto Enable set and flick on Component debugs
  1024. for (AZ::TypeId& componentDebugInfoEntry : m_componentDebugSortedList)
  1025. {
  1026. // We are only really checking to add components, so if we are already added, then move on!
  1027. if (!m_componentDebugInfoMap[componentDebugInfoEntry].m_autoLaunchEnabled)
  1028. {
  1029. AZStd::string componentName("**name_not_found**");
  1030. const AZ::SerializeContext::ClassData* classData = serializeContext->FindClassData(componentDebugInfoEntry);
  1031. if (classData != nullptr)
  1032. {
  1033. componentName = classData->m_name;
  1034. AZStd::to_lower(componentName.begin(), componentName.end());
  1035. // Loop through the known Debugable components and see if we find our search string! If so, flick on autoLaunch
  1036. for (const AZStd::string& searchString : m_autoEnableComponentSearchStrings)
  1037. {
  1038. if (componentName.find(searchString) != AZStd::string::npos)
  1039. {
  1040. m_componentDebugInfoMap[componentDebugInfoEntry].m_autoLaunchEnabled = true;
  1041. }
  1042. }
  1043. }
  1044. }
  1045. }
  1046. }
  1047. }
  1048. bool ImGuiLYEntityOutliner::ComponentHasDebug(const AZ::TypeId& comType)
  1049. {
  1050. return m_componentDebugInfoMap.find(comType) != m_componentDebugInfoMap.end();
  1051. }
  1052. void ImGuiLYEntityOutliner::EnableComponentDebug(const AZ::TypeId& comType, int priority /*= 1*/, bool enableMenuBar /*= false*/)
  1053. {
  1054. // if not found, add to vector and sort on priorities!
  1055. if (!ComponentHasDebug(comType))
  1056. {
  1057. // Add to the vector
  1058. ComponentDebugInfo debugInfo(priority, enableMenuBar, false);
  1059. m_componentDebugSortedList.push_back(comType); // Add the entry to a Vector for 1) Constant Iteration, and 2) Ordering and Sorting
  1060. m_componentDebugInfoMap[comType] = debugInfo; // Add the entry to a Map for quick access if needed per frame
  1061. // Sort the list
  1062. static auto sortByComponentPriority = [this](const AZ::TypeId& type1, const AZ::TypeId& type2)
  1063. {
  1064. return m_componentDebugInfoMap[type1].m_priority > m_componentDebugInfoMap[type2].m_priority;
  1065. };
  1066. m_componentDebugSortedList.sort(sortByComponentPriority);
  1067. // Loop through the Search Strings and see if we should enable any components
  1068. RefreshAutoEnableBasedOnSearchStrings();
  1069. }
  1070. // regardless of if this is a new or existing component debug, this call signifies a new Connection has likely been made
  1071. // and thus, a new ImGui Component Debug Panel to display. Check here for the Debug Auto Enable Component flag for this component type
  1072. if (m_componentDebugInfoMap[comType].m_autoLaunchEnabled)
  1073. {
  1074. RequestAllViewsForComponent(comType);
  1075. }
  1076. }
  1077. } // namespace ImGui
  1078. #endif // IMGUI_ENABLED