PrefabConversionUtils.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  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 <AzCore/Asset/AssetManager.h>
  9. #include <AzFramework/Spawnable/Spawnable.h>
  10. #include <AzToolsFramework/API/EditorAssetSystemAPI.h>
  11. #include <AzToolsFramework/SourceControl/SourceControlAPI.h>
  12. #include <Editor/Source/Components/Conversion/PrefabConversionUtils.h>
  13. namespace PhysX::Utils
  14. {
  15. static AZStd::optional<AZStd::string> GetFullSourceAssetPathById(AZ::Data::AssetId assetId)
  16. {
  17. AZStd::string assetPath;
  18. AZ::Data::AssetCatalogRequestBus::BroadcastResult(assetPath, &AZ::Data::AssetCatalogRequests::GetAssetPathById, assetId);
  19. AZ_Assert(!assetPath.empty(), "Asset Catalog returned an invalid path from an enumerated asset.");
  20. if (assetPath.empty())
  21. {
  22. AZ_Warning(
  23. "PhysXPrefabUtils",
  24. false,
  25. "Not able to get asset path for asset with id %s.",
  26. assetId.ToString<AZStd::string>().c_str());
  27. return AZStd::nullopt;
  28. }
  29. AZStd::string assetFullPath;
  30. bool assetFullPathFound = false;
  31. AzToolsFramework::AssetSystemRequestBus::BroadcastResult(
  32. assetFullPathFound,
  33. &AzToolsFramework::AssetSystem::AssetSystemRequest::GetFullSourcePathFromRelativeProductPath,
  34. assetPath,
  35. assetFullPath);
  36. if (!assetFullPathFound)
  37. {
  38. AZ_Warning("PhysXPrefabUtils", false, "Source file of asset '%s' could not be found.", assetPath.c_str());
  39. return AZStd::nullopt;
  40. }
  41. return { AZStd::move(assetFullPath) };
  42. }
  43. AZStd::vector<PrefabInfo> CollectPrefabs()
  44. {
  45. AZStd::vector<PrefabInfo> prefabs;
  46. auto* prefabLoader = AZ::Interface<AzToolsFramework::Prefab::PrefabLoaderInterface>::Get();
  47. auto* prefabSystemComponent = AZ::Interface<AzToolsFramework::Prefab::PrefabSystemComponentInterface>::Get();
  48. AZ::Data::AssetCatalogRequests::AssetEnumerationCB assetEnumerationCB =
  49. [&prefabs, prefabLoader, prefabSystemComponent](
  50. const AZ::Data::AssetId assetId, const AZ::Data::AssetInfo& assetInfo)
  51. {
  52. if (assetInfo.m_assetType != AzFramework::Spawnable::RTTI_Type())
  53. {
  54. return;
  55. }
  56. AZStd::optional<AZStd::string> assetFullPath = GetFullSourceAssetPathById(assetId);
  57. if (!assetFullPath.has_value())
  58. {
  59. return;
  60. }
  61. if (auto templateId = prefabLoader->LoadTemplateFromFile(assetFullPath->c_str());
  62. templateId != AzToolsFramework::Prefab::InvalidTemplateId)
  63. {
  64. if (auto templateResult = prefabSystemComponent->FindTemplate(templateId); templateResult.has_value())
  65. {
  66. AzToolsFramework::Prefab::Template& templateRef = templateResult->get();
  67. prefabs.push_back({ templateId, &templateRef, AZStd::move(*assetFullPath) });
  68. }
  69. }
  70. };
  71. AZ::Data::AssetCatalogRequestBus::Broadcast(
  72. &AZ::Data::AssetCatalogRequestBus::Events::EnumerateAssets, /*beginCB*/nullptr, assetEnumerationCB, /*endCB*/nullptr);
  73. return prefabs;
  74. }
  75. void SavePrefab(PrefabInfo& prefabInfo)
  76. {
  77. auto* prefabSystemComponent = AZ::Interface<AzToolsFramework::Prefab::PrefabSystemComponentInterface>::Get();
  78. prefabInfo.m_template->MarkAsDirty(true);
  79. prefabSystemComponent->PropagateTemplateChanges(prefabInfo.m_templateId);
  80. // Request source control to edit prefab file
  81. AzToolsFramework::SourceControlCommandBus::Broadcast(
  82. &AzToolsFramework::SourceControlCommandBus::Events::RequestEdit,
  83. prefabInfo.m_prefabFullPath.c_str(),
  84. /*allowMultiCheckout*/true,
  85. [prefabInfo]([[maybe_unused]] bool success, const AzToolsFramework::SourceControlFileInfo& info)
  86. {
  87. // This is called from the main thread on the next frame from TickBus,
  88. // that is why 'prefabInfo' is captured as a copy.
  89. if (!info.IsReadOnly())
  90. {
  91. auto* prefabLoader = AZ::Interface<AzToolsFramework::Prefab::PrefabLoaderInterface>::Get();
  92. if (!prefabLoader->SaveTemplate(prefabInfo.m_templateId))
  93. {
  94. AZ_Warning("PhysXPrefabUtils", false, "Unable to save prefab '%s'", prefabInfo.m_prefabFullPath.c_str());
  95. }
  96. }
  97. else
  98. {
  99. AZ_Warning(
  100. "PhysXPrefabUtils",
  101. false,
  102. "Unable to check out asset '%s' in source control.",
  103. prefabInfo.m_prefabFullPath.c_str());
  104. }
  105. });
  106. }
  107. AZStd::vector<AzToolsFramework::Prefab::PrefabDomValue*> GetPrefabEntities(AzToolsFramework::Prefab::PrefabDom& prefab)
  108. {
  109. if (!prefab.IsObject())
  110. {
  111. return {};
  112. }
  113. AZStd::vector<AzToolsFramework::Prefab::PrefabDomValue*> entities;
  114. if (auto entitiesIter = prefab.FindMember(AzToolsFramework::Prefab::PrefabDomUtils::EntitiesName);
  115. entitiesIter != prefab.MemberEnd() && entitiesIter->value.IsObject())
  116. {
  117. entities.reserve(entitiesIter->value.MemberCount());
  118. for (auto entityIter = entitiesIter->value.MemberBegin(); entityIter != entitiesIter->value.MemberEnd(); ++entityIter)
  119. {
  120. if (entityIter->value.IsObject())
  121. {
  122. entities.push_back(&entityIter->value);
  123. }
  124. }
  125. }
  126. return entities;
  127. }
  128. AZStd::vector<AzToolsFramework::Prefab::PrefabDomValue*> GetEntityComponents(AzToolsFramework::Prefab::PrefabDomValue& entity)
  129. {
  130. AZStd::vector<AzToolsFramework::Prefab::PrefabDomValue*> components;
  131. if (auto componentsIter = entity.FindMember(AzToolsFramework::Prefab::PrefabDomUtils::ComponentsName);
  132. componentsIter != entity.MemberEnd() && componentsIter->value.IsObject())
  133. {
  134. components.reserve(componentsIter->value.MemberCount());
  135. for (auto componentIter = componentsIter->value.MemberBegin(); componentIter != componentsIter->value.MemberEnd();
  136. ++componentIter)
  137. {
  138. if (!componentIter->value.IsObject())
  139. {
  140. continue;
  141. }
  142. components.push_back(&componentIter->value);
  143. }
  144. }
  145. return components;
  146. }
  147. AZ::TypeId GetComponentTypeId(const AzToolsFramework::Prefab::PrefabDomValue& component)
  148. {
  149. const auto typeFieldIter = component.FindMember(AzToolsFramework::Prefab::PrefabDomUtils::TypeName);
  150. if (typeFieldIter == component.MemberEnd())
  151. {
  152. return AZ::TypeId::CreateNull();
  153. }
  154. AZ::TypeId typeId = AZ::TypeId::CreateNull();
  155. AZ::JsonSerialization::LoadTypeId(typeId, typeFieldIter->value);
  156. return typeId;
  157. }
  158. AZ::JsonSerializationResult::Result PrefabEntityIdMapper::MapJsonToId(
  159. AZ::EntityId& outputValue, const rapidjson::Value& inputValue, AZ::JsonDeserializerContext& context)
  160. {
  161. if (!inputValue.IsString())
  162. {
  163. return context.Report(
  164. AZ::JsonSerializationResult::Tasks::ReadField,
  165. AZ::JsonSerializationResult::Outcomes::TypeMismatch,
  166. "Unexpected json type for prefab id, expected a String type.");
  167. }
  168. const size_t entityIdHash = AZStd::hash<AZStd::string_view>()(inputValue.GetString());
  169. outputValue = AZ::EntityId(entityIdHash);
  170. m_entityIdMap[outputValue] = inputValue.GetString();
  171. return context.Report(
  172. AZ::JsonSerializationResult::Tasks::ReadField,
  173. AZ::JsonSerializationResult::Outcomes::Success,
  174. "Successfully mapped string id to entity id.");
  175. }
  176. AZ::JsonSerializationResult::Result PrefabEntityIdMapper::MapIdToJson(
  177. rapidjson::Value& outputValue, const AZ::EntityId& inputValue, AZ::JsonSerializerContext& context)
  178. {
  179. auto it = m_entityIdMap.find(inputValue);
  180. if (it == m_entityIdMap.end())
  181. {
  182. return context.Report(
  183. AZ::JsonSerializationResult::Tasks::WriteValue,
  184. AZ::JsonSerializationResult::Outcomes::Missing,
  185. "Missing entity id in the map.");
  186. }
  187. outputValue.SetString(it->second.c_str(), it->second.size(), context.GetJsonAllocator());
  188. return context.Report(
  189. AZ::JsonSerializationResult::Tasks::WriteValue,
  190. AZ::JsonSerializationResult::Outcomes::Success,
  191. "Successfully mapped entity id to string id.");
  192. }
  193. bool LoadPrefabEntity(
  194. PrefabEntityIdMapper& prefabEntityIdMapper, const AzToolsFramework::Prefab::PrefabDomValue& prefabEntity, AZ::Entity& entity)
  195. {
  196. AZ::JsonDeserializerSettings settings;
  197. settings.m_metadata.Add(static_cast<AZ::JsonEntityIdSerializer::JsonEntityIdMapper*>(&prefabEntityIdMapper));
  198. auto result = AZ::JsonSerialization::Load(&entity, azrtti_typeid<AZ::Entity>(), prefabEntity, settings);
  199. return result.GetProcessing() == AZ::JsonSerializationResult::Processing::Completed;
  200. }
  201. bool StorePrefabEntity(
  202. const PrefabEntityIdMapper& prefabEntityIdMapper,
  203. AzToolsFramework::Prefab::PrefabDom& prefabDom,
  204. AzToolsFramework::Prefab::PrefabDomValue& prefabEntity,
  205. const AZ::Entity& entity)
  206. {
  207. AZ::JsonSerializerSettings settings;
  208. settings.m_metadata.Add(static_cast<const AZ::JsonEntityIdSerializer::JsonEntityIdMapper*>(&prefabEntityIdMapper));
  209. auto result = AZ::JsonSerialization::Store(
  210. prefabEntity, prefabDom.GetAllocator(), &entity, /*defaultObject*/nullptr, azrtti_typeid<AZ::Entity>(), settings);
  211. return result.GetProcessing() == AZ::JsonSerializationResult::Processing::Completed;
  212. }
  213. } // namespace PhysX::Utils