SceneManifest.cpp 17 KB

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