DebugOutput.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  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 "DebugOutput.h"
  9. #include <AzCore/std/optional.h>
  10. #include <AzCore/IO/SystemFile.h>
  11. #include <AzCore/Serialization/Json/JsonUtils.h>
  12. #include <AzCore/Serialization/Utils.h>
  13. #include <AzCore/Settings/SettingsRegistry.h>
  14. #include <AzFramework/StringFunc/StringFunc.h>
  15. #include <SceneAPI/SceneCore/Containers/Scene.h>
  16. #include <SceneAPI/SceneCore/Containers/Views/PairIterator.h>
  17. #include <SceneAPI/SceneCore/Containers/Views/SceneGraphDownwardsIterator.h>
  18. #include <SceneAPI/SceneCore/DataTypes/IGraphObject.h>
  19. #include <SceneAPI/SceneCore/Events/ExportProductList.h>
  20. #include <SceneAPI/SceneCore/Utilities/Reporting.h>
  21. namespace AZ::SceneAPI::Utilities
  22. {
  23. bool IsDebugEnabled()
  24. {
  25. bool resultValue = false;
  26. if (auto* registry = AZ::SettingsRegistry::Get())
  27. {
  28. registry->Get(resultValue, AZ::SceneAPI::Utilities::Key_AssetProcessorInDebugOutput);
  29. }
  30. return resultValue;
  31. }
  32. bool SaveToJson(const AZStd::string& fileName, const DebugSceneGraph& graph);
  33. void DebugNode::Reflect(AZ::ReflectContext* context)
  34. {
  35. AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
  36. if (serialize)
  37. {
  38. serialize->Class<DebugNode>()
  39. ->Field("Name", &DebugNode::m_name)
  40. ->Field("Path", &DebugNode::m_path)
  41. ->Field("Type", &DebugNode::m_type)
  42. ->Field("Data", &DebugNode::m_data);
  43. }
  44. }
  45. void DebugSceneGraph::Reflect(AZ::ReflectContext* context)
  46. {
  47. DebugNode::Reflect(context);
  48. AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
  49. if (serialize)
  50. {
  51. serialize->Class<DebugSceneGraph>()
  52. ->Field("Version", &DebugSceneGraph::m_version)
  53. ->Field("ProductName", &DebugSceneGraph::m_productName)
  54. ->Field("SceneName", &DebugSceneGraph::m_sceneName)
  55. ->Field("Nodes", &DebugSceneGraph::m_nodes);
  56. }
  57. }
  58. void DebugOutput::Write(const char* name, const char* data)
  59. {
  60. m_output += AZStd::string::format("\t%s: %s\n", name, data);
  61. }
  62. void DebugOutput::WriteArray(const char* name, const unsigned int* data, int size)
  63. {
  64. m_output += AZStd::string::format("\t%s: ", name);
  65. for (int index = 0; index < size; ++index)
  66. {
  67. m_output += AZStd::string::format("%d, ", data[index]);
  68. }
  69. m_output += AZStd::string::format("\n");
  70. }
  71. void DebugOutput::Write(const char* name, const AZStd::string& data)
  72. {
  73. Write(name, data.c_str());
  74. AddToNode(name, data);
  75. }
  76. void DebugOutput::Write(const char* name, double data)
  77. {
  78. m_output += AZStd::string::format("\t%s: %f\n", name, data);
  79. AddToNode(name, data);
  80. }
  81. void DebugOutput::Write(const char* name, uint64_t data)
  82. {
  83. AZ::u64 multiplatformSafeData(static_cast<AZ::u64>(data));
  84. m_output += AZStd::string::format("\t%s: %llu\n", name, multiplatformSafeData);
  85. AddToNode(name, multiplatformSafeData);
  86. }
  87. void DebugOutput::Write(const char* name, int64_t data)
  88. {
  89. AZ::s64 multiplatformSafeData(static_cast<AZ::s64>(data));
  90. m_output += AZStd::string::format("\t%s: %lld\n", name, multiplatformSafeData);
  91. AddToNode(name, multiplatformSafeData);
  92. }
  93. void DebugOutput::Write(const char* name, const DataTypes::MatrixType& data)
  94. {
  95. AZ::Vector3 basisX{};
  96. AZ::Vector3 basisY{};
  97. AZ::Vector3 basisZ{};
  98. AZ::Vector3 translation{};
  99. data.GetBasisAndTranslation(&basisX, &basisY, &basisZ, &translation);
  100. m_pauseNodeData = true;
  101. m_output += AZStd::string::format("\t%s:\n", name);
  102. m_output += "\t";
  103. Write("BasisX", basisX);
  104. m_output += "\t";
  105. Write("BasisY", basisY);
  106. m_output += "\t";
  107. Write("BasisZ", basisZ);
  108. m_output += "\t";
  109. Write("Transl", translation);
  110. m_pauseNodeData = false;
  111. AddToNode(name, data);
  112. }
  113. void DebugOutput::Write(const char* name, bool data)
  114. {
  115. m_output += AZStd::string::format("\t%s: %s\n", name, data ? "true" : "false");
  116. AddToNode(name, data);
  117. }
  118. void DebugOutput::Write(const char* name, Vector3 data)
  119. {
  120. m_output += AZStd::string::format("\t%s: <% f, % f, % f>\n", name, data.GetX(), data.GetY(), data.GetZ());
  121. AddToNode(name, data);
  122. }
  123. void DebugOutput::Write(const char* name, AZStd::optional<bool> data)
  124. {
  125. if (data.has_value())
  126. {
  127. Write(name, data.value());
  128. }
  129. else
  130. {
  131. Write(name, "Not set");
  132. }
  133. }
  134. void DebugOutput::Write(const char* name, AZStd::optional<float> data)
  135. {
  136. if (data.has_value())
  137. {
  138. Write(name, data.value());
  139. }
  140. else
  141. {
  142. Write(name, "Not set");
  143. }
  144. }
  145. void DebugOutput::Write(const char* name, AZStd::optional<AZ::Vector3> data)
  146. {
  147. if (data.has_value())
  148. {
  149. Write(name, data.value());
  150. }
  151. else
  152. {
  153. Write(name, "Not set");
  154. }
  155. }
  156. const AZStd::string& DebugOutput::GetOutput() const
  157. {
  158. return m_output;
  159. }
  160. DebugNode DebugOutput::GetDebugNode() const
  161. {
  162. return m_currentNode;
  163. }
  164. void WriteAndLog(AZ::IO::SystemFile& dbgFile, const char* strToWrite)
  165. {
  166. AZ_TracePrintf(AZ::SceneAPI::Utilities::LogWindow, "%s", strToWrite);
  167. dbgFile.Write(strToWrite, strlen(strToWrite));
  168. dbgFile.Write("\n", strlen("\n"));
  169. }
  170. void DebugOutput::BuildDebugSceneGraph(const char* outputFolder, AZ::SceneAPI::Events::ExportProductList& productList, const AZStd::shared_ptr<AZ::SceneAPI::Containers::Scene>& scene, AZStd::string productName)
  171. {
  172. AZStd::string debugSceneFile;
  173. AzFramework::StringFunc::Path::ConstructFull(outputFolder, productName.c_str(), debugSceneFile);
  174. AZ_TracePrintf(AZ::SceneAPI::Utilities::LogWindow, "outputFolder %s, name %s.\n", outputFolder, productName.c_str());
  175. AZ::IO::SystemFile dbgFile;
  176. if (dbgFile.Open(debugSceneFile.c_str(), AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY))
  177. {
  178. WriteAndLog(dbgFile, AZStd::string::format("ProductName: %s", productName.c_str()).c_str());
  179. WriteAndLog(dbgFile, AZStd::string::format("debugSceneGraphVersion: %d", SceneGraphVersion).c_str());
  180. WriteAndLog(dbgFile, scene->GetName().c_str());
  181. const AZ::SceneAPI::Containers::SceneGraph& sceneGraph = scene->GetGraph();
  182. auto names = sceneGraph.GetNameStorage();
  183. auto content = sceneGraph.GetContentStorage();
  184. auto pairView = AZ::SceneAPI::Containers::Views::MakePairView(names, content);
  185. auto view = AZ::SceneAPI::Containers::Views::MakeSceneGraphDownwardsView<
  186. AZ::SceneAPI::Containers::Views::BreadthFirst>(
  187. sceneGraph, sceneGraph.GetRoot(), pairView.cbegin(), true);
  188. DebugSceneGraph debugSceneGraph;
  189. debugSceneGraph.m_version = SceneGraphVersion;
  190. debugSceneGraph.m_productName = productName;
  191. debugSceneGraph.m_sceneName = scene->GetName().c_str();
  192. for (auto&& viewIt : view)
  193. {
  194. if (viewIt.second == nullptr)
  195. {
  196. continue;
  197. }
  198. AZ::SceneAPI::DataTypes::IGraphObject* graphObject = const_cast<AZ::SceneAPI::DataTypes::IGraphObject*>(viewIt.second.get());
  199. WriteAndLog(dbgFile, AZStd::string::format("Node Name: %s", viewIt.first.GetName()).c_str());
  200. WriteAndLog(dbgFile, AZStd::string::format("Node Path: %s", viewIt.first.GetPath()).c_str());
  201. WriteAndLog(dbgFile, AZStd::string::format("Node Type: %s", graphObject->RTTI_GetTypeName()).c_str());
  202. AZ::SceneAPI::Utilities::DebugOutput debugOutput(
  203. DebugNode(viewIt.first.GetName(), viewIt.first.GetPath(), graphObject->RTTI_GetTypeName()));
  204. viewIt.second->GetDebugOutput(debugOutput);
  205. if (!debugOutput.GetOutput().empty())
  206. {
  207. WriteAndLog(dbgFile, debugOutput.GetOutput().c_str());
  208. }
  209. debugSceneGraph.m_nodes.push_back(debugOutput.GetDebugNode());
  210. }
  211. dbgFile.Close();
  212. // XML is useful because it stores more information than JSON with the serializer, so some automation is better suited to use XML.
  213. Utils::SaveObjectToFile((debugSceneFile + ".xml").c_str(), DataStream::StreamType::ST_XML, &debugSceneGraph);
  214. // JSON is useful because it can be quicker and easier to parse than XML, and more structured than the human readable dbgsg file.
  215. AZStd::string jsonFileName(debugSceneFile + ".json");
  216. SaveToJson(jsonFileName, debugSceneGraph);
  217. static const AZ::Data::AssetType dbgSceneGraphAssetType("{07F289D1-4DC7-4C40-94B4-0A53BBCB9F0B}");
  218. productList.AddProduct(productName, AZ::Uuid::CreateName(productName.c_str()), dbgSceneGraphAssetType,
  219. AZStd::nullopt, AZStd::nullopt);
  220. static const AZ::Data::AssetType dbgSceneGraphXmlAssetType("{51F37614-0D77-4F36-9AC6-7ED70A0AC868}");
  221. productList.AddProduct(
  222. (productName + ".xml"), AZ::Uuid::CreateName((productName + ".xml").c_str()), dbgSceneGraphXmlAssetType,
  223. AZStd::nullopt, AZStd::nullopt);
  224. static const AZ::Data::AssetType dbgSceneGraphJsonAssetType("{4342B27E-0E14-49C3-B3B9-BCDB9A5FCA23}");
  225. productList.AddProduct(
  226. jsonFileName, AZ::Uuid::CreateName((productName + ".json").c_str()), dbgSceneGraphJsonAssetType,
  227. AZStd::nullopt, AZStd::nullopt);
  228. // save out debug text for the Scene Manifest
  229. AZStd::string productNameDebugManifest { debugSceneFile };
  230. AzFramework::StringFunc::Path::ReplaceExtension(productNameDebugManifest, "assetinfo.dbg");
  231. scene->GetManifest().SaveToFile(productNameDebugManifest.c_str());
  232. static const AZ::Data::AssetType dbgSceneManifestAssetType("{48A78BE7-B3F2-44B8-8AA6-F0607E9A75A5}");
  233. productList.AddProduct(
  234. productNameDebugManifest,
  235. AZ::Uuid::CreateName((productName + ".assetinfo.dbg").c_str()),
  236. dbgSceneManifestAssetType,
  237. AZStd::nullopt,
  238. AZStd::nullopt);
  239. }
  240. }
  241. bool SaveToJson(const AZStd::string& fileName, const DebugSceneGraph& graph)
  242. {
  243. AZ::JsonSerializerSettings settings;
  244. rapidjson::Document jsonDocument;
  245. auto jsonResult = JsonSerialization::Store(jsonDocument, jsonDocument.GetAllocator(), graph, settings);
  246. if (jsonResult.GetProcessing() == AZ::JsonSerializationResult::Processing::Halted)
  247. {
  248. AZ_Error("Scene Debug Output", false,
  249. AZStd::string::format(
  250. "JSON serialization of file %.*s failed: %.*s", AZ_STRING_ARG(fileName), AZ_STRING_ARG(jsonResult.ToString(""))).c_str());
  251. return false;
  252. }
  253. auto jsonSaveResult = AZ::JsonSerializationUtils::WriteJsonFile(jsonDocument, fileName);
  254. AZ_Error(
  255. "Scene Debug Output",
  256. jsonSaveResult.IsSuccess(),
  257. AZStd::string::format("Saving JSON to file %.*s failed: %.*s", AZ_STRING_ARG(fileName), AZ_STRING_ARG(jsonSaveResult.GetError())).c_str());
  258. return jsonSaveResult.IsSuccess();
  259. }
  260. }