DefaultProceduralPrefab.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601
  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/DefaultProceduralPrefab.h>
  9. #include <PrefabGroup/PrefabGroupBus.h>
  10. #include <AzCore/Serialization/SerializeContext.h>
  11. #include <AzCore/RTTI/BehaviorContext.h>
  12. #include <AzToolsFramework/Entity/EntityUtilityComponent.h>
  13. #include <AzToolsFramework/Prefab/PrefabLoaderScriptingBus.h>
  14. #include <AzToolsFramework/Prefab/PrefabSystemComponentInterface.h>
  15. #include <AzToolsFramework/Prefab/PrefabSystemScriptingBus.h>
  16. #include <AzToolsFramework/ToolsComponents/TransformComponent.h>
  17. #include <SceneAPI/SceneCore/Containers/Scene.h>
  18. #include <SceneAPI/SceneCore/Containers/SceneManifest.h>
  19. #include <SceneAPI/SceneCore/Containers/Views/SceneGraphDownwardsIterator.h>
  20. #include <SceneAPI/SceneCore/DataTypes/DataTypeUtilities.h>
  21. #include <SceneAPI/SceneCore/DataTypes/GraphData/ICustomPropertyData.h>
  22. #include <SceneAPI/SceneCore/DataTypes/GraphData/IMeshData.h>
  23. #include <SceneAPI/SceneCore/DataTypes/GraphData/ITransform.h>
  24. #include <SceneAPI/SceneData/Groups/MeshGroup.h>
  25. #include <SceneAPI/SceneData/Rules/CoordinateSystemRule.h>
  26. #include <SceneAPI/SceneData/Rules/LodRule.h>
  27. #include <SceneAPI/SceneData/Rules/UnmodifiableRule.h>
  28. #include <AzCore/Component/EntityId.h>
  29. namespace AZ
  30. {
  31. AZ_TYPE_INFO_SPECIALIZE(AZ::SceneAPI::PrefabGroupRequests::ManifestUpdates, "{B84CBFB5-4630-4484-AE69-A4155A8B0D9B}");
  32. }
  33. namespace AZ::SceneAPI
  34. {
  35. struct PrefabGroupNotificationHandler final
  36. : public AZ::SceneAPI::PrefabGroupNotificationBus::Handler
  37. , public AZ::BehaviorEBusHandler
  38. {
  39. AZ_EBUS_BEHAVIOR_BINDER(
  40. PrefabGroupNotificationHandler,
  41. "{F1962BD1-D722-4C5F-A883-76F1004C3247}",
  42. AZ::SystemAllocator,
  43. OnUpdatePrefabEntity);
  44. virtual ~PrefabGroupNotificationHandler() = default;
  45. void OnUpdatePrefabEntity(const AZ::EntityId& prefabEntity) override
  46. {
  47. Call(FN_OnUpdatePrefabEntity, prefabEntity);
  48. }
  49. };
  50. DefaultProceduralPrefabGroup::DefaultProceduralPrefabGroup()
  51. {
  52. PrefabGroupEventBus::Handler::BusConnect();
  53. }
  54. DefaultProceduralPrefabGroup::~DefaultProceduralPrefabGroup()
  55. {
  56. PrefabGroupEventBus::Handler::BusDisconnect();
  57. }
  58. void DefaultProceduralPrefabGroup::Reflect(ReflectContext* context)
  59. {
  60. if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
  61. {
  62. behaviorContext->EBus<PrefabGroupNotificationBus>("PrefabGroupNotificationBus")
  63. ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
  64. ->Attribute(AZ::Script::Attributes::Module, "prefab")
  65. ->Handler<PrefabGroupNotificationHandler>()
  66. ->Event("OnUpdatePrefabEntity", &PrefabGroupNotificationBus::Events::OnUpdatePrefabEntity);
  67. behaviorContext->EBus<PrefabGroupEventBus>("PrefabGroupEventBus")
  68. ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
  69. ->Attribute(Script::Attributes::Module, "prefab")
  70. ->Event("GeneratePrefabGroupManifestUpdates", &PrefabGroupRequests::GeneratePrefabGroupManifestUpdates);
  71. }
  72. }
  73. AZStd::optional<PrefabGroupRequests::ManifestUpdates> DefaultProceduralPrefabGroup::GeneratePrefabGroupManifestUpdates(
  74. const Scene& scene) const
  75. {
  76. auto nodeDataMap = CalculateNodeDataMap(scene);
  77. if (nodeDataMap.empty())
  78. {
  79. return {};
  80. }
  81. // compute the filenames of the scene file
  82. AZStd::string relativeSourcePath = scene.GetSourceFilename();
  83. AZStd::string watchFolder = scene.GetWatchFolder() + "/";
  84. // the watch folder and forward slash is used in the asset hint path of the file
  85. AZ::StringFunc::Replace(relativeSourcePath, "\\", "/");
  86. AZ::StringFunc::Replace(watchFolder, "\\", "/");
  87. AZ::StringFunc::Replace(relativeSourcePath, watchFolder.c_str(), "");
  88. AZ::StringFunc::Replace(relativeSourcePath, ".", "_");
  89. AZStd::string filenameOnly{ relativeSourcePath };
  90. AZ::StringFunc::Path::GetFileName(filenameOnly.c_str(), filenameOnly);
  91. AZ::StringFunc::Path::ReplaceExtension(filenameOnly, "procprefab");
  92. ManifestUpdates manifestUpdates;
  93. auto nodeEntityMap = CreateNodeEntityMap(manifestUpdates, nodeDataMap, scene, relativeSourcePath);
  94. if (nodeEntityMap.empty())
  95. {
  96. return {};
  97. }
  98. auto entities = FixUpEntityParenting(nodeEntityMap, scene.GetGraph(), nodeDataMap);
  99. if (entities.empty())
  100. {
  101. return {};
  102. }
  103. if (CreatePrefabGroupManifestUpdates(manifestUpdates, scene, entities, filenameOnly, relativeSourcePath) == false )
  104. {
  105. return {};
  106. }
  107. return AZStd::make_optional(AZStd::move(manifestUpdates));
  108. }
  109. DefaultProceduralPrefabGroup::NodeDataMap DefaultProceduralPrefabGroup::CalculateNodeDataMap(
  110. const Containers::Scene& scene) const
  111. {
  112. auto graph = scene.GetGraph();
  113. const auto view = Containers::Views::MakeSceneGraphDownwardsView<Containers::Views::BreadthFirst>(
  114. graph,
  115. graph.GetRoot(),
  116. graph.GetContentStorage().cbegin(),
  117. true);
  118. if (view.empty())
  119. {
  120. return {};
  121. }
  122. NodeDataMap nodeDataMap;
  123. for (auto it = view.begin(); it != view.end(); ++it)
  124. {
  125. Containers::SceneGraph::NodeIndex currentIndex = graph.ConvertToNodeIndex(it.GetHierarchyIterator());
  126. const auto currentContent = graph.GetNodeContent(currentIndex);
  127. if (currentContent)
  128. {
  129. if (azrtti_istypeof<AZ::SceneAPI::DataTypes::IMeshData>(currentContent.get()))
  130. {
  131. // get the MeshData child node index values for Transform and CustomPropertyData
  132. auto childIndex = it.GetHierarchyIterator()->GetChildIndex();
  133. NodeDataForEntity nodeDataForEntity;
  134. nodeDataForEntity.m_meshIndex = currentIndex;
  135. while (childIndex.IsValid())
  136. {
  137. const auto childContent = graph.GetNodeContent(childIndex);
  138. if (childContent)
  139. {
  140. if (azrtti_istypeof<AZ::SceneAPI::DataTypes::ITransform>(childContent.get()))
  141. {
  142. if (!nodeDataForEntity.m_transformIndex.IsValid())
  143. {
  144. // The first child transform of the mesh is applied to the mesh entity
  145. nodeDataForEntity.m_transformIndex = childIndex;
  146. }
  147. else
  148. {
  149. // All other child transforms of the mesh represent unique entities
  150. NodeDataForEntity newNodeDataForEntity;
  151. newNodeDataForEntity.m_transformIndex = childIndex;
  152. nodeDataMap.emplace(NodeDataMapEntry{ childIndex, AZStd::move(newNodeDataForEntity) });
  153. }
  154. }
  155. else if (azrtti_istypeof<AZ::SceneAPI::DataTypes::ICustomPropertyData>(childContent.get()))
  156. {
  157. nodeDataForEntity.m_propertyMapIndex = childIndex;
  158. }
  159. }
  160. childIndex = graph.GetNodeSibling(childIndex);
  161. }
  162. nodeDataMap.emplace(NodeDataMapEntry{ currentIndex, AZStd::move(nodeDataForEntity) });
  163. }
  164. else if (azrtti_istypeof<AZ::SceneAPI::DataTypes::ITransform>(currentContent.get()))
  165. {
  166. // Check if this transform node is not associated with any meshes
  167. auto parentNodeIndex = graph.GetNodeParent(currentIndex);
  168. const auto parentContent = graph.GetNodeContent(parentNodeIndex);
  169. if (!azrtti_istypeof<AZ::SceneAPI::DataTypes::IMeshData>(parentContent.get()))
  170. {
  171. NodeDataForEntity nodeDataForEntity;
  172. nodeDataForEntity.m_transformIndex = currentIndex;
  173. nodeDataMap.emplace(NodeDataMapEntry{ currentIndex, AZStd::move(nodeDataForEntity) });
  174. }
  175. }
  176. }
  177. }
  178. return nodeDataMap;
  179. }
  180. bool DefaultProceduralPrefabGroup::AddEditorMaterialComponent(
  181. const AZ::EntityId& entityId,
  182. const DataTypes::ICustomPropertyData& propertyData) const
  183. {
  184. const auto propertyMaterialPathIterator = propertyData.GetPropertyMap().find("o3de_default_material");
  185. if (propertyMaterialPathIterator == propertyData.GetPropertyMap().end())
  186. {
  187. // skip these assignment since the default material override was not provided
  188. return true;
  189. }
  190. const AZStd::any& propertyMaterialPath = propertyMaterialPathIterator->second;
  191. if (propertyMaterialPath.empty() || propertyMaterialPath.is<AZStd::string>() == false)
  192. {
  193. AZ_Error("prefab", false, "The 'o3de_default_material' custom property value type must be a string."
  194. "This will need to be fixed in the DCC tool and re-export the file asset.");
  195. return false;
  196. }
  197. // find asset path via node data
  198. const AZStd::string* materialAssetPath = AZStd::any_cast<AZStd::string>(&propertyMaterialPath);
  199. if (materialAssetPath->empty())
  200. {
  201. AZ_Error("prefab", false, "Material asset path must not be empty.");
  202. return false;
  203. }
  204. // create a material component for this entity's mesh to render with
  205. AzFramework::BehaviorComponentId editorMaterialComponent;
  206. AzToolsFramework::EntityUtilityBus::BroadcastResult(
  207. editorMaterialComponent,
  208. &AzToolsFramework::EntityUtilityBus::Events::GetOrAddComponentByTypeName,
  209. entityId,
  210. "EditorMaterialComponent");
  211. if (editorMaterialComponent.IsValid() == false)
  212. {
  213. AZ_Warning("prefab", false, "Could not add the EditorMaterialComponent component; project needs Atom enabled.");
  214. return {};
  215. }
  216. // the material product asset such as 'myassets/path/to/cool.azmaterial' is assigned via hint
  217. auto materialAssetJson = AZStd::string::format(
  218. R"JSON(
  219. {"Controller":{"Configuration":{"materials":[{"Value":{"MaterialAsset":{"assetHint":"%s"}}}]}}}
  220. )JSON", materialAssetPath->c_str());
  221. bool result = false;
  222. AzToolsFramework::EntityUtilityBus::BroadcastResult(
  223. result,
  224. &AzToolsFramework::EntityUtilityBus::Events::UpdateComponentForEntity,
  225. entityId,
  226. editorMaterialComponent,
  227. materialAssetJson);
  228. AZ_Error("prefab", result, "UpdateComponentForEntity failed for EditorMaterialComponent component");
  229. return result;
  230. }
  231. bool DefaultProceduralPrefabGroup::AddEditorMeshComponent(
  232. const AZ::EntityId& entityId,
  233. const AZStd::string& relativeSourcePath,
  234. const AZStd::string& meshGroupName,
  235. const AZStd::string& sourceFileExtension) const
  236. {
  237. // Since the mesh component lives in a gem, then create it by name
  238. AzFramework::BehaviorComponentId editorMeshComponent;
  239. AzToolsFramework::EntityUtilityBus::BroadcastResult(
  240. editorMeshComponent,
  241. &AzToolsFramework::EntityUtilityBus::Events::GetOrAddComponentByTypeName,
  242. entityId,
  243. "{DCE68F6E-2E16-4CB4-A834-B6C2F900A7E9} AZ::Render::EditorMeshComponent");
  244. if (editorMeshComponent.IsValid() == false)
  245. {
  246. AZ_Warning("prefab", false, "Could not add the EditorMeshComponent component; project needs Atom enabled.");
  247. return {};
  248. }
  249. // assign mesh asset id hint using JSON
  250. AZ::IO::Path modelAssetPath(relativeSourcePath, '/');
  251. modelAssetPath.ReplaceFilename(AZ::IO::PathView(meshGroupName));
  252. modelAssetPath.ReplaceExtension(AZ::IO::PathView(sourceFileExtension));
  253. auto meshAssetJson = AZStd::string::format(
  254. R"JSON(
  255. {"Controller": {"Configuration": {"ModelAsset": { "assetHint": "%s.azmodel"}}}}
  256. )JSON", modelAssetPath.LexicallyNormal().String().c_str());
  257. bool result = false;
  258. AzToolsFramework::EntityUtilityBus::BroadcastResult(
  259. result,
  260. &AzToolsFramework::EntityUtilityBus::Events::UpdateComponentForEntity,
  261. entityId,
  262. editorMeshComponent,
  263. meshAssetJson);
  264. AZ_Error("prefab", result, "UpdateComponentForEntity failed for EditorMeshComponent component");
  265. return result;
  266. }
  267. bool DefaultProceduralPrefabGroup::CreateMeshGroupAndComponents(
  268. ManifestUpdates& manifestUpdates,
  269. AZ::EntityId entityId,
  270. const NodeDataForEntity& nodeData,
  271. const NodeDataMap& nodeDataMap,
  272. const Containers::Scene& scene,
  273. const AZStd::string& relativeSourcePath) const
  274. {
  275. AZStd::shared_ptr<SceneData::MeshGroup> meshGroup(BuildMeshGroupForNode(scene, nodeData, nodeDataMap));
  276. manifestUpdates.emplace_back(meshGroup);
  277. if (AddEditorMeshComponent(entityId, relativeSourcePath, meshGroup->GetName(), scene.GetSourceExtension()) == false)
  278. {
  279. return false;
  280. }
  281. const auto& graph = scene.GetGraph();
  282. const auto propertyDataIndex = nodeData.m_propertyMapIndex;
  283. if (propertyDataIndex.IsValid())
  284. {
  285. const auto customPropertyData = azrtti_cast<const DataTypes::ICustomPropertyData*>(graph.GetNodeContent(propertyDataIndex));
  286. if (!customPropertyData)
  287. {
  288. AZ_Error("prefab", false, "Missing custom property data content for node.");
  289. return false;
  290. }
  291. if (AddEditorMaterialComponent(entityId, *(customPropertyData.get())) == false)
  292. {
  293. return false;
  294. }
  295. }
  296. return true;
  297. }
  298. AZStd::vector<AZStd::shared_ptr<DataTypes::IManifestObject>> DefaultProceduralPrefabGroup::GenerateDefaultPrefabMeshGroups(
  299. const Scene& scene) const
  300. {
  301. AZStd::vector<AZStd::shared_ptr<DataTypes::IManifestObject>> newMeshGroups;
  302. auto nodeDataMap = CalculateNodeDataMap(scene);
  303. if (nodeDataMap.empty())
  304. {
  305. return newMeshGroups;
  306. }
  307. for (const auto& entry : nodeDataMap)
  308. {
  309. newMeshGroups.push_back(BuildMeshGroupForNode(scene, entry.second, nodeDataMap));
  310. }
  311. return newMeshGroups;
  312. }
  313. AZStd::shared_ptr<SceneData::MeshGroup> DefaultProceduralPrefabGroup::BuildMeshGroupForNode(
  314. const Scene& scene,
  315. const NodeDataForEntity& nodeData,
  316. const NodeDataMap& nodeDataMap) const
  317. {
  318. const auto meshNodeIndex = nodeData.m_meshIndex;
  319. const auto& graph = scene.GetGraph();
  320. const auto meshNodeName = graph.GetNodeName(meshNodeIndex);
  321. const auto meshSubId =
  322. DataTypes::Utilities::CreateStableUuid(scene, azrtti_typeid<SceneData::MeshGroup>(), meshNodeName.GetPath());
  323. AZStd::string meshGroupName = "default_";
  324. meshGroupName += scene.GetName();
  325. meshGroupName += meshSubId.ToFixedString().c_str();
  326. // clean up the mesh group name
  327. AZStd::replace_if(
  328. meshGroupName.begin(),
  329. meshGroupName.end(),
  330. [](char c)
  331. {
  332. return (!AZStd::is_alnum(c) && c != '_');
  333. },
  334. '_');
  335. AZStd::string meshNodePath{ meshNodeName.GetPath() };
  336. auto meshGroup = AZStd::make_shared<SceneData::MeshGroup>();
  337. meshGroup->SetName(meshGroupName);
  338. meshGroup->GetSceneNodeSelectionList().AddSelectedNode(AZStd::move(meshNodePath));
  339. for (const auto& meshGoupNamePair : nodeDataMap)
  340. {
  341. if (meshGoupNamePair.second.m_meshIndex.IsValid() && meshGoupNamePair.second.m_meshIndex != meshNodeIndex)
  342. {
  343. const auto nodeName = graph.GetNodeName(meshGoupNamePair.second.m_meshIndex);
  344. meshGroup->GetSceneNodeSelectionList().RemoveSelectedNode(nodeName.GetPath());
  345. }
  346. }
  347. meshGroup->OverrideId(meshSubId);
  348. // tag this mesh group as a "default mesh group" using this rule
  349. meshGroup->GetRuleContainer().AddRule(AZStd::make_shared<AZ::SceneAPI::SceneData::ProceduralMeshGroupRule>());
  350. // Don't let users edit these mesh groups, because they're procedural they'll be re-generated and overwrite any changes.
  351. meshGroup->GetRuleContainer().AddRule(AZStd::make_shared<AZ::SceneAPI::SceneData::UnmodifiableRule>());
  352. // this clears out the mesh coordinates each mesh group will be rotated and translated
  353. // using the attached scene graph node
  354. auto coordinateSystemRule = AZStd::make_shared<AZ::SceneAPI::SceneData::CoordinateSystemRule>();
  355. coordinateSystemRule->SetUseAdvancedData(true);
  356. coordinateSystemRule->SetRotation(AZ::Quaternion::CreateIdentity());
  357. coordinateSystemRule->SetTranslation(AZ::Vector3::CreateZero());
  358. coordinateSystemRule->SetScale(1.0f);
  359. meshGroup->GetRuleContainer().AddRule(coordinateSystemRule);
  360. // create an empty LOD rule in order to skip the LOD buffer creation
  361. meshGroup->GetRuleContainer().AddRule(AZStd::make_shared<AZ::SceneAPI::SceneData::LodRule>());
  362. return meshGroup;
  363. }
  364. DefaultProceduralPrefabGroup::NodeEntityMap DefaultProceduralPrefabGroup::CreateNodeEntityMap(
  365. ManifestUpdates& manifestUpdates,
  366. const NodeDataMap& nodeDataMap,
  367. const Containers::Scene& scene,
  368. const AZStd::string& relativeSourcePath) const
  369. {
  370. NodeEntityMap nodeEntityMap;
  371. const auto& graph = scene.GetGraph();
  372. for (const auto& entry : nodeDataMap)
  373. {
  374. const auto thisNodeIndex = entry.first;
  375. const auto meshNodeIndex = entry.second.m_meshIndex;
  376. Containers::SceneGraph::NodeIndex nodeIndexForEntityName;
  377. nodeIndexForEntityName = meshNodeIndex.IsValid() ? meshNodeIndex : thisNodeIndex;
  378. const auto nodeNameForEntity = graph.GetNodeName(nodeIndexForEntityName);
  379. // create an entity for each node data entry
  380. AZ::EntityId entityId;
  381. AzToolsFramework::EntityUtilityBus::BroadcastResult(
  382. entityId, &AzToolsFramework::EntityUtilityBus::Events::CreateEditorReadyEntity, nodeNameForEntity.GetName());
  383. if (entityId.IsValid() == false)
  384. {
  385. return {};
  386. }
  387. if (meshNodeIndex.IsValid())
  388. {
  389. if (!CreateMeshGroupAndComponents(manifestUpdates,
  390. entityId,
  391. entry.second,
  392. nodeDataMap,
  393. scene,
  394. relativeSourcePath))
  395. {
  396. return {};
  397. }
  398. }
  399. nodeEntityMap.emplace(thisNodeIndex, AZStd::make_pair(entityId, nodeNameForEntity.GetName()));
  400. }
  401. return nodeEntityMap;
  402. }
  403. DefaultProceduralPrefabGroup::EntityIdMap DefaultProceduralPrefabGroup::FixUpEntityParenting(
  404. const NodeEntityMap& nodeEntityMap,
  405. const Containers::SceneGraph& graph,
  406. const NodeDataMap& nodeDataMap) const
  407. {
  408. EntityIdMap entities;
  409. for (const auto& nodeEntity : nodeEntityMap)
  410. {
  411. const AZStd::pair<AZ::EntityId, AzToolsFramework::Prefab::EntityAlias>& entityIdAliasPair = nodeEntity.second;
  412. entities.emplace(entityIdAliasPair.first, entityIdAliasPair.second);
  413. // find matching parent EntityId (if any)
  414. AZ::EntityId parentEntityId;
  415. const auto thisNodeIndex = nodeEntity.first;
  416. auto parentNodeIndex = graph.GetNodeParent(thisNodeIndex);
  417. while (parentNodeIndex.IsValid())
  418. {
  419. auto parentNodeIterator = nodeDataMap.find(parentNodeIndex);
  420. if (nodeDataMap.end() != parentNodeIterator)
  421. {
  422. auto parentEntiyIterator = nodeEntityMap.find(parentNodeIterator->first);
  423. if (nodeEntityMap.end() != parentEntiyIterator)
  424. {
  425. parentEntityId = parentEntiyIterator->second.first;
  426. break;
  427. }
  428. }
  429. else if (graph.HasNodeParent(parentNodeIndex))
  430. {
  431. parentNodeIndex = graph.GetNodeParent(parentNodeIndex);
  432. }
  433. else
  434. {
  435. parentNodeIndex = {};
  436. }
  437. }
  438. AZ::Entity* entity = AZ::Interface<AZ::ComponentApplicationRequests>::Get()->FindEntity(entityIdAliasPair.first);
  439. auto* entityTransform = entity->FindComponent<AzToolsFramework::Components::TransformComponent>();
  440. if (!entityTransform)
  441. {
  442. return {};
  443. }
  444. // parent entities
  445. if (parentEntityId.IsValid())
  446. {
  447. entityTransform->SetParent(parentEntityId);
  448. }
  449. auto thisNodeIterator = nodeDataMap.find(thisNodeIndex);
  450. AZ_Assert(thisNodeIterator != nodeDataMap.end(), "This node index missing.");
  451. auto thisTransformIndex = thisNodeIterator->second.m_transformIndex;
  452. // get node matrix data to set the entity's local transform
  453. const auto nodeTransform = azrtti_cast<const DataTypes::ITransform*>(graph.GetNodeContent(thisTransformIndex));
  454. if (nodeTransform)
  455. {
  456. entityTransform->SetLocalTM(AZ::Transform::CreateFromMatrix3x4(nodeTransform->GetMatrix()));
  457. }
  458. else
  459. {
  460. entityTransform->SetLocalTM(AZ::Transform::CreateUniformScale(1.0f));
  461. }
  462. PrefabGroupNotificationBus::Broadcast(&PrefabGroupNotificationBus::Events::OnUpdatePrefabEntity, entityIdAliasPair.first);
  463. }
  464. return entities;
  465. }
  466. bool DefaultProceduralPrefabGroup::CreatePrefabGroupManifestUpdates(
  467. ManifestUpdates& manifestUpdates,
  468. const Containers::Scene& scene,
  469. const EntityIdMap& entities,
  470. const AZStd::string& filenameOnly,
  471. const AZStd::string& relativeSourcePath) const
  472. {
  473. AZStd::string prefabTemplateName{ relativeSourcePath };
  474. AZ::StringFunc::Path::ReplaceFullName(prefabTemplateName, filenameOnly.c_str());
  475. AZ::StringFunc::Replace(prefabTemplateName, "\\", "/"); // the source folder uses forward slash
  476. // clear out any previously created prefab template for this path
  477. auto* prefabSystemComponentInterface = AZ::Interface<AzToolsFramework::Prefab::PrefabSystemComponentInterface>::Get();
  478. AzToolsFramework::Prefab::TemplateId prefabTemplateId =
  479. prefabSystemComponentInterface->GetTemplateIdFromFilePath({ prefabTemplateName.c_str() });
  480. if (prefabTemplateId != AzToolsFramework::Prefab::InvalidTemplateId)
  481. {
  482. prefabSystemComponentInterface->RemoveTemplate(prefabTemplateId);
  483. prefabTemplateId = AzToolsFramework::Prefab::InvalidTemplateId;
  484. }
  485. // create prefab group for entire stack
  486. AzToolsFramework::Prefab::PrefabSystemScriptingBus::BroadcastResult(
  487. prefabTemplateId,
  488. &AzToolsFramework::Prefab::PrefabSystemScriptingBus::Events::CreatePrefabTemplateWithCustomEntityAliases,
  489. entities,
  490. prefabTemplateName);
  491. if (prefabTemplateId == AzToolsFramework::Prefab::InvalidTemplateId)
  492. {
  493. AZ_Error("prefab", false, "Could not create a prefab template for entities.");
  494. return false;
  495. }
  496. // Convert the prefab to a JSON string
  497. AZ::Outcome<AZStd::string, void> outcome;
  498. AzToolsFramework::Prefab::PrefabLoaderScriptingBus::BroadcastResult(
  499. outcome,
  500. &AzToolsFramework::Prefab::PrefabLoaderScriptingBus::Events::SaveTemplateToString,
  501. prefabTemplateId);
  502. if (outcome.IsSuccess() == false)
  503. {
  504. AZ_Error("prefab", false, "Could not create JSON string for template; maybe NaN values in the template?");
  505. return false;
  506. }
  507. AzToolsFramework::Prefab::PrefabDom prefabDom;
  508. prefabDom.Parse(outcome.GetValue().c_str());
  509. auto prefabGroup = AZStd::make_shared<AZ::SceneAPI::SceneData::PrefabGroup>();
  510. prefabGroup->SetName(prefabTemplateName);
  511. prefabGroup->SetPrefabDom(AZStd::move(prefabDom));
  512. prefabGroup->SetId(DataTypes::Utilities::CreateStableUuid(
  513. scene,
  514. azrtti_typeid<AZ::SceneAPI::SceneData::PrefabGroup>(),
  515. prefabTemplateName));
  516. manifestUpdates.emplace_back(prefabGroup);
  517. return true;
  518. }
  519. }