SceneManifest.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  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 <SceneAPI/SceneCore/Containers/SceneManifest.h>
  9. #include <AzCore/Casting/numeric_cast.h>
  10. #include <AzCore/Component/ComponentApplicationBus.h>
  11. #include <AzCore/Debug/Trace.h>
  12. #include <AzCore/IO/GenericStreams.h>
  13. #include <AzCore/IO/Path/Path.h>
  14. #include <AzCore/IO/SystemFile.h>
  15. #include <AzCore/JSON/rapidjson.h>
  16. #include <AzCore/JSON/document.h>
  17. #include <AzCore/JSON/prettywriter.h>
  18. #include <AzCore/Memory/SystemAllocator.h>
  19. #include <AzCore/RTTI/BehaviorContext.h>
  20. #include <AzCore/Serialization/Json/RegistrationContext.h>
  21. #include <AzCore/Serialization/Json/JsonSerialization.h>
  22. #include <AzCore/Serialization/Json/JsonSerializationResult.h>
  23. #include <AzCore/Serialization/Json/JsonUtils.h>
  24. #include <AzCore/Serialization/Utils.h>
  25. #include <AzCore/std/algorithm.h>
  26. #include <AzCore/Utils/Utils.h>
  27. #include <AzFramework/StringFunc/StringFunc.h>
  28. #include <AzToolsFramework/Debug/TraceContext.h>
  29. #include <SceneAPI/SceneCore/Utilities/Reporting.h>
  30. namespace AZ::SceneAPI::DataTypes
  31. {
  32. AZ_TYPE_INFO_SPECIALIZE_WITH_NAME_IMPL(IManifestObject, "IManifestObject", "{3B839407-1884-4FF4-ABEA-CA9D347E83F7}");
  33. AZ_RTTI_NO_TYPE_INFO_IMPL(IManifestObject)
  34. // Implement IManifestObject reflection in a cpp file
  35. void IManifestObject::Reflect(AZ::ReflectContext* context)
  36. {
  37. if(AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  38. {
  39. serializeContext->Class<IManifestObject>()
  40. ->Version(0);
  41. }
  42. }
  43. }
  44. namespace AZ
  45. {
  46. namespace SceneAPI
  47. {
  48. namespace Containers
  49. {
  50. const char ErrorWindowName[] = "SceneManifest";
  51. AZ_CLASS_ALLOCATOR_IMPL(SceneManifest, AZ::SystemAllocator)
  52. SceneManifest::~SceneManifest()
  53. {
  54. }
  55. void SceneManifest::Clear()
  56. {
  57. m_storageLookup.clear();
  58. m_values.clear();
  59. }
  60. bool SceneManifest::AddEntry(AZStd::shared_ptr<DataTypes::IManifestObject>&& value)
  61. {
  62. auto itValue = m_storageLookup.find(value.get());
  63. if (itValue != m_storageLookup.end())
  64. {
  65. AZ_TracePrintf(Utilities::WarningWindow, "Manifest Object has already been registered with the manifest.");
  66. return false;
  67. }
  68. Index index = aznumeric_caster(m_values.size());
  69. m_storageLookup[value.get()] = index;
  70. m_values.push_back(AZStd::move(value));
  71. AZ_Assert(m_values.size() == m_storageLookup.size(),
  72. "SceneManifest values and storage-lookup tables have gone out of lockstep (%i vs %i)",
  73. m_values.size(), m_storageLookup.size());
  74. return true;
  75. }
  76. bool SceneManifest::RemoveEntry(const DataTypes::IManifestObject* const value)
  77. {
  78. auto storageLookupIt = m_storageLookup.find(value);
  79. if (storageLookupIt == m_storageLookup.end())
  80. {
  81. AZ_Assert(false, "Value not registered in SceneManifest.");
  82. return false;
  83. }
  84. size_t index = storageLookupIt->second;
  85. m_values.erase(m_values.begin() + index);
  86. m_storageLookup.erase(storageLookupIt);
  87. for (auto& entry : m_storageLookup)
  88. {
  89. if (entry.second > index)
  90. {
  91. entry.second--;
  92. }
  93. }
  94. return true;
  95. }
  96. SceneManifest::Index SceneManifest::FindIndex(const DataTypes::IManifestObject* const value) const
  97. {
  98. auto it = m_storageLookup.find(value);
  99. return it != m_storageLookup.end() ? (*it).second : s_invalidIndex;
  100. }
  101. bool SceneManifest::LoadFromFile(const AZStd::string& absoluteFilePath, SerializeContext* context)
  102. {
  103. if (absoluteFilePath.empty())
  104. {
  105. AZ_Error(ErrorWindowName, false, "Unable to load Scene Manifest: no file path was provided.");
  106. return false;
  107. }
  108. // Utils::ReadFile fails if the file doesn't exist.
  109. // Check if it exists first, it's not an error if there is no scene manifest.
  110. if (!AZ::IO::SystemFile::Exists(absoluteFilePath.c_str()))
  111. {
  112. return false;
  113. }
  114. auto readFileOutcome = Utils::ReadFile(absoluteFilePath, MaxSceneManifestFileSizeInBytes);
  115. if (!readFileOutcome.IsSuccess())
  116. {
  117. AZ_Error(ErrorWindowName, false, readFileOutcome.GetError().c_str());
  118. return false;
  119. }
  120. AZStd::string fileContents(readFileOutcome.TakeValue());
  121. // Attempt to read the file as JSON
  122. auto loadJsonOutcome = LoadFromString(fileContents, context);
  123. if (loadJsonOutcome.IsSuccess())
  124. {
  125. return true;
  126. }
  127. // If JSON parsing failed, try to deserialize with XML
  128. auto loadXmlOutcome = LoadFromString(fileContents, context, nullptr, true);
  129. AZStd::string fileName;
  130. AzFramework::StringFunc::Path::GetFileName(absoluteFilePath.c_str(), fileName);
  131. if (loadXmlOutcome.IsSuccess())
  132. {
  133. AZ_TracePrintf(ErrorWindowName, "Scene Manifest ( %s ) is using the deprecated XML file format. It will be upgraded to JSON the next time it is modified.\n", fileName.c_str());
  134. return true;
  135. }
  136. // If both failed, throw an error
  137. AZ_Error(ErrorWindowName, false,
  138. "Unable to deserialize ( %s ) using JSON or XML. \nJSON reported error: %s\nXML reported error: %s",
  139. fileName.c_str(), loadJsonOutcome.GetError().c_str(), loadXmlOutcome.GetError().c_str());
  140. return false;
  141. }
  142. bool SceneManifest::SaveToFile(const AZStd::string& absoluteFilePath, SerializeContext* context)
  143. {
  144. AZ_TraceContext(ErrorWindowName, absoluteFilePath);
  145. if (absoluteFilePath.empty())
  146. {
  147. AZ_Error(ErrorWindowName, false, "Unable to save Scene Manifest: no file path was provided.");
  148. return false;
  149. }
  150. AZStd::string errorMsg = AZStd::string::format("Unable to save Scene Manifest to ( %s ):\n", absoluteFilePath.c_str());
  151. AZ::Outcome<rapidjson::Document, AZStd::string> saveToJsonOutcome = SaveToJsonDocument(context);
  152. if (!saveToJsonOutcome.IsSuccess())
  153. {
  154. AZ_Error(ErrorWindowName, false, "%s%s", errorMsg.c_str(), saveToJsonOutcome.GetError().c_str());
  155. return false;
  156. }
  157. auto saveToFileOutcome = AZ::JsonSerializationUtils::WriteJsonFile(saveToJsonOutcome.GetValue(), absoluteFilePath);
  158. if (!saveToFileOutcome.IsSuccess())
  159. {
  160. AZ_Error(ErrorWindowName, false, "%s%s", errorMsg.c_str(), saveToFileOutcome.GetError().c_str());
  161. return false;
  162. }
  163. return true;
  164. }
  165. AZStd::shared_ptr<const DataTypes::IManifestObject> SceneManifest::SceneManifestConstDataConverter(
  166. const AZStd::shared_ptr<DataTypes::IManifestObject>& value)
  167. {
  168. return AZStd::shared_ptr<const DataTypes::IManifestObject>(value);
  169. }
  170. void SceneManifest::Reflect(ReflectContext* context)
  171. {
  172. SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context);
  173. if (serializeContext)
  174. {
  175. serializeContext->Class<SceneManifest>()
  176. ->Version(1, &SceneManifest::VersionConverter)
  177. ->Field("values", &SceneManifest::m_values);
  178. }
  179. BehaviorContext* behaviorContext = azrtti_cast<BehaviorContext*>(context);
  180. if (behaviorContext)
  181. {
  182. behaviorContext->Class<SceneManifest>()
  183. ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common)
  184. ->Attribute(AZ::Script::Attributes::Module, "scene")
  185. ->Method("ImportFromJson", [](SceneManifest& self, AZStd::string_view jsonBuffer) -> bool
  186. {
  187. auto outcome = self.LoadFromString(jsonBuffer);
  188. if (outcome.IsSuccess())
  189. {
  190. return true;
  191. }
  192. AZ_Warning(ErrorWindowName, false, "LoadFromString outcome failure (%s)", outcome.GetError().c_str());
  193. return true;
  194. })
  195. ->Method("ExportToJson", [](SceneManifest& self) -> AZStd::string
  196. {
  197. auto outcome = self.SaveToJsonDocument();
  198. if (outcome.IsSuccess())
  199. {
  200. // write the manifest to a UTF-8 string buffer and move return the string
  201. rapidjson::StringBuffer sb;
  202. rapidjson::Writer<rapidjson::StringBuffer, rapidjson::UTF8<>> writer(sb);
  203. rapidjson::Document& document = outcome.GetValue();
  204. document.Accept(writer);
  205. return AZStd::move(AZStd::string(sb.GetString()));
  206. }
  207. AZ_Warning(ErrorWindowName, false, "SaveToJsonDocument outcome failure (%s)", outcome.GetError().c_str());
  208. return {};
  209. });
  210. }
  211. }
  212. bool SceneManifest::VersionConverter(SerializeContext& context, SerializeContext::DataElementNode& node)
  213. {
  214. if (node.GetVersion() != 0)
  215. {
  216. AZ_TracePrintf(ErrorWindowName, "Unable to upgrade SceneManifest from version %i.", node.GetVersion());
  217. return false;
  218. }
  219. // Copy out the original values.
  220. AZStd::vector<SerializeContext::DataElementNode> values;
  221. values.reserve(node.GetNumSubElements());
  222. for (int i = 0; i < node.GetNumSubElements(); ++i)
  223. {
  224. // The old format stored AZStd::pair<AZStd::string, AZStd::shared_ptr<IManifestObjets>>. All this
  225. // data is still used, but needs to be move to the new location. The shared ptr needs to be
  226. // moved into the new container, while the name needs to be moved to the group name.
  227. SerializeContext::DataElementNode& pairNode = node.GetSubElement(i);
  228. // This is the original content of the shared ptr. Using the shared pointer directly caused
  229. // registration issues so it's extracting the data the shared ptr was storing instead.
  230. SerializeContext::DataElementNode& elementNode = pairNode.GetSubElement(1).GetSubElement(0);
  231. SerializeContext::DataElementNode& nameNode = pairNode.GetSubElement(0);
  232. AZStd::string name;
  233. if (nameNode.GetData(name))
  234. {
  235. elementNode.AddElementWithData<AZStd::string>(context, "name", name);
  236. }
  237. // It's better not to set a default name here as the default behaviors will take care of that
  238. // will have more information to work with.
  239. values.push_back(elementNode);
  240. }
  241. // Delete old values
  242. for (int i = 0; i < node.GetNumSubElements(); ++i)
  243. {
  244. node.RemoveElement(i);
  245. }
  246. // Put stored values back
  247. int vectorIndex = node.AddElement<ValueStorage>(context, "values");
  248. SerializeContext::DataElementNode& vectorNode = node.GetSubElement(vectorIndex);
  249. for (SerializeContext::DataElementNode& value : values)
  250. {
  251. value.SetName("element");
  252. // Put in a blank shared ptr to be filled with a value stored from "values".
  253. int valueIndex = vectorNode.AddElement<ValueStorageType>(context, "element");
  254. SerializeContext::DataElementNode& pointerNode = vectorNode.GetSubElement(valueIndex);
  255. // Type doesn't matter as it will be overwritten by the stored value.
  256. pointerNode.AddElement<int>(context, "element");
  257. pointerNode.GetSubElement(0) = value;
  258. }
  259. AZ_TracePrintf(Utilities::WarningWindow,
  260. "The SceneManifest has been updated from version %i. It's recommended to save the updated file.", node.GetVersion());
  261. return true;
  262. }
  263. AZ::Outcome<void, AZStd::string> SceneManifest::LoadFromString(const AZStd::string& fileContents, SerializeContext* context, JsonRegistrationContext* registrationContext, bool loadXml)
  264. {
  265. Clear();
  266. AZStd::string failureMessage;
  267. if (loadXml)
  268. {
  269. // Attempt to read the stream as XML (old format)
  270. // Gems can be removed, causing the setting for manifest objects in the the Gem to not be registered. Instead of failing
  271. // to load the entire manifest, just ignore those values.
  272. ObjectStream::FilterDescriptor loadFilter(&AZ::Data::AssetFilterNoAssetLoading, ObjectStream::FILTERFLAG_IGNORE_UNKNOWN_CLASSES);
  273. if (Utils::LoadObjectFromBufferInPlace<SceneManifest>(fileContents.data(), fileContents.size(), *this, context, loadFilter))
  274. {
  275. Init();
  276. return AZ::Success();
  277. }
  278. failureMessage = "Unable to load Scene Manifest as XML";
  279. }
  280. else
  281. {
  282. // Attempt to read the stream as JSON
  283. auto readJsonOutcome = AZ::JsonSerializationUtils::ReadJsonString(fileContents);
  284. AZStd::string errorMsg;
  285. if (!readJsonOutcome.IsSuccess())
  286. {
  287. return AZ::Failure(readJsonOutcome.TakeError());
  288. }
  289. rapidjson::Document document = readJsonOutcome.TakeValue();
  290. AZ::JsonDeserializerSettings settings;
  291. settings.m_serializeContext = context;
  292. settings.m_registrationContext = registrationContext;
  293. AZ::JsonSerializationResult::ResultCode jsonResult = AZ::JsonSerialization::Load(*this, document, settings);
  294. if (jsonResult.GetProcessing() != AZ::JsonSerializationResult::Processing::Halted)
  295. {
  296. Init();
  297. return AZ::Success();
  298. }
  299. failureMessage = jsonResult.ToString("");
  300. }
  301. return AZ::Failure(failureMessage);
  302. }
  303. AZ::Outcome<rapidjson::Document, AZStd::string> SceneManifest::SaveToJsonDocument(SerializeContext* context, JsonRegistrationContext* registrationContext)
  304. {
  305. AZ::JsonSerializerSettings settings;
  306. settings.m_serializeContext = context;
  307. settings.m_registrationContext = registrationContext;
  308. rapidjson::Document jsonDocument;
  309. auto jsonResult = JsonSerialization::Store(jsonDocument, jsonDocument.GetAllocator(), *this, settings);
  310. if (jsonResult.GetProcessing() == AZ::JsonSerializationResult::Processing::Halted)
  311. {
  312. return AZ::Failure(AZStd::string::format("JSON serialization failed: %s", jsonResult.ToString("").c_str()));
  313. }
  314. return AZ::Success(AZStd::move(jsonDocument));
  315. }
  316. void SceneManifest::Init()
  317. {
  318. auto end = AZStd::remove_if(m_values.begin(), m_values.end(),
  319. [](const ValueStorageType& entry) -> bool
  320. {
  321. return !entry;
  322. });
  323. m_values.erase(end, m_values.end());
  324. for (size_t i = 0; i < m_values.size(); ++i)
  325. {
  326. Index index = aznumeric_caster(i);
  327. m_storageLookup[m_values[i].get()] = index;
  328. }
  329. }
  330. } // Containers
  331. } // SceneAPI
  332. } // AZ