PrefabGroupBehavior.cpp 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  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 <PrefabGroup/PrefabGroupBehavior.h>
  9. #include <PrefabGroup/PrefabGroup.h>
  10. #include <PrefabGroup/ProceduralAssetHandler.h>
  11. #include <AzCore/Asset/AssetManagerBus.h>
  12. #include <AzCore/IO/FileIO.h>
  13. #include <AzCore/IO/Path/Path.h>
  14. #include <AzCore/JSON/document.h>
  15. #include <AzCore/JSON/error/en.h>
  16. #include <AzCore/JSON/error/error.h>
  17. #include <AzCore/JSON/prettywriter.h>
  18. #include <AzCore/JSON/stringbuffer.h>
  19. #include <AzCore/Serialization/SerializeContext.h>
  20. #include <AzCore/std/smart_ptr/make_shared.h>
  21. #include <AzToolsFramework/API/EditorAssetSystemAPI.h>
  22. #include <AzToolsFramework/Prefab/PrefabLoaderInterface.h>
  23. #include <AzToolsFramework/Prefab/PrefabSystemComponentInterface.h>
  24. #include <AzToolsFramework/Prefab/Instance/InstanceToTemplateInterface.h>
  25. #include <AzToolsFramework/Prefab/Procedural/ProceduralPrefabAsset.h>
  26. #include <SceneAPI/SceneCore/Components/ExportingComponent.h>
  27. #include <SceneAPI/SceneCore/Containers/Scene.h>
  28. #include <SceneAPI/SceneCore/Containers/SceneManifest.h>
  29. #include <SceneAPI/SceneCore/Events/ExportEventContext.h>
  30. #include <SceneAPI/SceneCore/Events/ExportProductList.h>
  31. #include <SceneAPI/SceneCore/Utilities/FileUtilities.h>
  32. namespace AZ::SceneAPI::Behaviors
  33. {
  34. //
  35. // ExportEventHandler
  36. //
  37. struct PrefabGroupBehavior::ExportEventHandler final
  38. : public AZ::SceneAPI::SceneCore::ExportingComponent
  39. {
  40. using PreExportEventContextFunction = AZStd::function<Events::ProcessingResult(Events::PreExportEventContext&)>;
  41. PreExportEventContextFunction m_preExportEventContextFunction;
  42. AZ::Prefab::PrefabGroupAssetHandler m_prefabGroupAssetHandler;
  43. ExportEventHandler() = delete;
  44. ExportEventHandler(PreExportEventContextFunction function)
  45. : m_preExportEventContextFunction(AZStd::move(function))
  46. {
  47. BindToCall(&ExportEventHandler::PrepareForExport);
  48. AZ::SceneAPI::SceneCore::ExportingComponent::Activate();
  49. }
  50. ~ExportEventHandler()
  51. {
  52. AZ::SceneAPI::SceneCore::ExportingComponent::Deactivate();
  53. }
  54. Events::ProcessingResult PrepareForExport(Events::PreExportEventContext& context)
  55. {
  56. return m_preExportEventContextFunction(context);
  57. }
  58. };
  59. //
  60. // PrefabGroupBehavior
  61. //
  62. void PrefabGroupBehavior::Activate()
  63. {
  64. m_exportEventHandler = AZStd::make_shared<ExportEventHandler>([this](auto& context)
  65. {
  66. return this->OnPrepareForExport(context);
  67. });
  68. }
  69. void PrefabGroupBehavior::Deactivate()
  70. {
  71. m_exportEventHandler.reset();
  72. }
  73. AZStd::unique_ptr<rapidjson::Document> PrefabGroupBehavior::CreateProductAssetData(const SceneData::PrefabGroup* prefabGroup) const
  74. {
  75. using namespace AzToolsFramework::Prefab;
  76. auto* prefabLoaderInterface = AZ::Interface<PrefabLoaderInterface>::Get();
  77. if (!prefabLoaderInterface)
  78. {
  79. AZ_Error("prefab", false, "Could not get PrefabLoaderInterface");
  80. return {};
  81. }
  82. // write to a UTF-8 string buffer
  83. auto prefabDomRef = prefabGroup->GetPrefabDomRef();
  84. if (!prefabDomRef)
  85. {
  86. AZ_Error("prefab", false, "PrefabGroup(%s) missing PrefabDom", prefabGroup->GetName().c_str());
  87. return {};
  88. }
  89. const AzToolsFramework::Prefab::PrefabDom& prefabDom = prefabDomRef.value();
  90. rapidjson::StringBuffer sb;
  91. rapidjson::Writer<rapidjson::StringBuffer, rapidjson::UTF8<>> writer(sb);
  92. if (prefabDom.Accept(writer) == false)
  93. {
  94. AZ_Error("prefab", false, "Could not write PrefabGroup(%s) to JSON", prefabGroup->GetName().c_str());
  95. return {};
  96. }
  97. // validate the PrefabDom will make a valid Prefab template instance
  98. auto templateId = prefabLoaderInterface->LoadTemplateFromString(sb.GetString(), prefabGroup->GetName().c_str());
  99. if (templateId == InvalidTemplateId)
  100. {
  101. AZ_Error("prefab", false, "PrefabGroup(%s) Could not write load template", prefabGroup->GetName().c_str());
  102. return {};
  103. }
  104. auto* prefabSystemComponentInterface = AZ::Interface<PrefabSystemComponentInterface>::Get();
  105. if (!prefabSystemComponentInterface)
  106. {
  107. AZ_Error("prefab", false, "Could not get PrefabSystemComponentInterface");
  108. return {};
  109. }
  110. // create instance to update the asset hints
  111. auto instance = prefabSystemComponentInterface->InstantiatePrefab(templateId);
  112. if (!instance)
  113. {
  114. AZ_Error("prefab", false, "PrefabGroup(%s) Could not instantiate prefab", prefabGroup->GetName().c_str());
  115. return {};
  116. }
  117. auto* instanceToTemplateInterface = AZ::Interface<InstanceToTemplateInterface>::Get();
  118. if (!instanceToTemplateInterface)
  119. {
  120. AZ_Error("prefab", false, "Could not get InstanceToTemplateInterface");
  121. return {};
  122. }
  123. // fill out a JSON DOM
  124. auto proceduralPrefab = AZStd::make_unique<rapidjson::Document>(rapidjson::kObjectType);
  125. instanceToTemplateInterface->GenerateDomForInstance(*proceduralPrefab.get(), *instance.get());
  126. return proceduralPrefab;
  127. }
  128. bool PrefabGroupBehavior::WriteOutProductAsset(
  129. Events::PreExportEventContext& context,
  130. const SceneData::PrefabGroup* prefabGroup,
  131. const rapidjson::Document& doc) const
  132. {
  133. // Retrieve source asset info so we can get a string with the relative path to the asset
  134. bool assetInfoResult;
  135. Data::AssetInfo info;
  136. AZStd::string watchFolder;
  137. AzToolsFramework::AssetSystemRequestBus::BroadcastResult(
  138. assetInfoResult,
  139. &AzToolsFramework::AssetSystemRequestBus::Events::GetSourceInfoBySourcePath,
  140. context.GetScene().GetSourceFilename().c_str(),
  141. info,
  142. watchFolder);
  143. AZ::IO::FixedMaxPath assetPath(info.m_relativePath);
  144. assetPath.ReplaceFilename(prefabGroup->GetName().c_str());
  145. AZStd::string filePath = AZ::SceneAPI::Utilities::FileUtilities::CreateOutputFileName(
  146. assetPath.c_str(),
  147. context.GetOutputDirectory(),
  148. AZ::Prefab::PrefabGroupAssetHandler::s_Extension);
  149. AZ::IO::FileIOStream fileStream(filePath.c_str(), AZ::IO::OpenMode::ModeWrite);
  150. if (fileStream.IsOpen() == false)
  151. {
  152. AZ_Error("prefab", false, "File path(%s) could not open for write", filePath.c_str());
  153. return false;
  154. }
  155. // write to a UTF-8 string buffer
  156. rapidjson::StringBuffer sb;
  157. rapidjson::Writer<rapidjson::StringBuffer, rapidjson::UTF8<>> writer(sb);
  158. if (doc.Accept(writer) == false)
  159. {
  160. AZ_Error("prefab", false, "PrefabGroup(%s) Could not buffer JSON", prefabGroup->GetName().c_str());
  161. return false;
  162. }
  163. const auto bytesWritten = fileStream.Write(sb.GetSize(), sb.GetString());
  164. if (bytesWritten > 1)
  165. {
  166. AZ::u32 subId = AZ::Crc32(filePath.c_str());
  167. context.GetProductList().AddProduct(
  168. filePath,
  169. context.GetScene().GetSourceGuid(),
  170. azrtti_typeid<Prefab::ProceduralPrefabAsset>(),
  171. {},
  172. AZStd::make_optional(subId));
  173. return true;
  174. }
  175. return false;
  176. }
  177. Events::ProcessingResult PrefabGroupBehavior::OnPrepareForExport(Events::PreExportEventContext& context) const
  178. {
  179. AZStd::vector<const SceneData::PrefabGroup*> prefabGroupCollection;
  180. const Containers::SceneManifest& manifest = context.GetScene().GetManifest();
  181. for (size_t i = 0; i < manifest.GetEntryCount(); ++i)
  182. {
  183. const auto* group = azrtti_cast<const SceneData::PrefabGroup*>(manifest.GetValue(i).get());
  184. if (group)
  185. {
  186. prefabGroupCollection.push_back(group);
  187. }
  188. }
  189. if (prefabGroupCollection.empty())
  190. {
  191. return AZ::SceneAPI::Events::ProcessingResult::Ignored;
  192. }
  193. for (const auto* prefabGroup : prefabGroupCollection)
  194. {
  195. auto result = CreateProductAssetData(prefabGroup);
  196. if (!result)
  197. {
  198. return Events::ProcessingResult::Failure;
  199. }
  200. if (WriteOutProductAsset(context, prefabGroup, *result.get()) == false)
  201. {
  202. return Events::ProcessingResult::Failure;
  203. }
  204. }
  205. return Events::ProcessingResult::Success;
  206. }
  207. void PrefabGroupBehavior::Reflect(ReflectContext* context)
  208. {
  209. AZ::SceneAPI::SceneData::PrefabGroup::Reflect(context);
  210. Prefab::ProceduralPrefabAsset::Reflect(context);
  211. SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context);
  212. if (serializeContext)
  213. {
  214. serializeContext->Class<PrefabGroupBehavior, BehaviorComponent>()->Version(1);
  215. }
  216. }
  217. }