GNMeshController.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  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 <Editor/Common/GNConstants.h>
  9. #include <Editor/EBus/EditorGeomNodesComponentBus.h>
  10. #include <Editor/Rendering/GNMeshController.h>
  11. #include <Editor/Rendering/GNRenderMesh.h>
  12. #include <Editor/UI/Utils.h>
  13. #include <AtomLyIntegration/CommonFeatures/Material/MaterialComponentConstants.h>
  14. #include <AtomLyIntegration/CommonFeatures/Mesh/MeshComponentBus.h>
  15. #include <AtomLyIntegration/CommonFeatures/Mesh/MeshComponentConstants.h>
  16. #include <AzCore/Component/NonUniformScaleBus.h>
  17. #include <AzCore/Debug/Profiler.h>
  18. #include <AzCore/Utils/Utils.h>
  19. #include <AzCore/std/string/regex.h>
  20. #include <AzToolsFramework/API/EntityCompositionRequestBus.h>
  21. #include <AzToolsFramework/API/ToolsApplicationAPI.h>
  22. #include <AzToolsFramework/Entity/EditorEntityHelpers.h>
  23. #include <AzToolsFramework/ToolsComponents/TransformComponent.h>
  24. namespace GeomNodes
  25. {
  26. GNMeshController::GNMeshController(AZ::EntityId entityId)
  27. : m_entityId(entityId)
  28. {
  29. if (!m_renderMesh)
  30. {
  31. m_renderMesh = AZStd::make_unique<GNRenderMesh>(m_entityId);
  32. }
  33. AZ::TransformNotificationBus::Handler::BusConnect(m_entityId);
  34. AzFramework::AssetCatalogEventBus::Handler::BusConnect();
  35. m_world = AZ::Transform::CreateIdentity();
  36. AZ::TransformBus::EventResult(m_world, m_entityId, &AZ::TransformBus::Events::GetWorldTM);
  37. }
  38. GNMeshController::~GNMeshController()
  39. {
  40. AzFramework::AssetCatalogEventBus::Handler::BusDisconnect();
  41. AzToolsFramework::EditorComponentSelectionRequestsBus::Handler::BusDisconnect();
  42. AzFramework::BoundsRequestBus::Handler::BusDisconnect();
  43. AZ::TransformNotificationBus::Handler::BusDisconnect();
  44. m_renderMesh.reset();
  45. }
  46. AZ::Aabb GNMeshController::GetEditorSelectionBoundsViewport([[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo)
  47. {
  48. return GetWorldBounds();
  49. }
  50. bool GNMeshController::EditorSelectionIntersectRayViewport(
  51. [[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo, const AZ::Vector3& src, const AZ::Vector3& dir, float& distance)
  52. {
  53. AZ_PROFILE_FUNCTION(AzToolsFramework);
  54. if (!m_renderMesh->GetModel())
  55. {
  56. return false;
  57. }
  58. AZ::Transform transform = AZ::Transform::CreateIdentity();
  59. AZ::TransformBus::EventResult(transform, m_entityId, &AZ::TransformBus::Events::GetWorldTM);
  60. AZ::Vector3 nonUniformScale = AZ::Vector3::CreateOne();
  61. AZ::NonUniformScaleRequestBus::EventResult(nonUniformScale, m_entityId, &AZ::NonUniformScaleRequests::GetScale);
  62. float t;
  63. AZ::Vector3 ignoreNormal;
  64. constexpr float rayLength = 1000.0f;
  65. if (m_renderMesh->GetModel()->RayIntersection(transform, nonUniformScale, src, dir * rayLength, t, ignoreNormal))
  66. {
  67. distance = rayLength * t;
  68. return true;
  69. }
  70. return false;
  71. }
  72. bool GNMeshController::SupportsEditorRayIntersect()
  73. {
  74. return true;
  75. }
  76. AZ::Aabb GNMeshController::GetWorldBounds()
  77. {
  78. if (!m_worldAabb.has_value())
  79. {
  80. m_worldAabb = GetLocalBounds();
  81. m_worldAabb->ApplyTransform(m_world);
  82. }
  83. return m_worldAabb.value();
  84. }
  85. AZ::Aabb GNMeshController::GetLocalBounds()
  86. {
  87. if (!m_localAabb.has_value() && m_modelData.MeshCount() > 0)
  88. {
  89. m_localAabb = m_modelData.GetAabb();
  90. }
  91. return m_localAabb.value();
  92. }
  93. void GNMeshController::RebuildRenderMesh()
  94. {
  95. if (!m_materialWaitList.empty())
  96. return;
  97. m_worldAabb.reset();
  98. m_localAabb.reset();
  99. if (m_modelData.MeshCount() > 0)
  100. {
  101. AZ::SystemTickBus::QueueFunction(
  102. [=]()
  103. {
  104. AzToolsFramework::EditorComponentSelectionRequestsBus::Handler::BusDisconnect();
  105. AzFramework::BoundsRequestBus::Handler::BusDisconnect();
  106. m_renderMesh->BuildMesh(m_modelData);
  107. m_renderMesh->UpdateTransform(m_world);
  108. AzFramework::BoundsRequestBus::Handler::BusConnect(m_entityId);
  109. AzToolsFramework::EditorComponentSelectionRequestsBus::Handler::BusConnect(m_entityId);
  110. EditorGeomNodesComponentRequestBus::Event(m_entityId, &EditorGeomNodesComponentRequests::SetWorkInProgress, false);
  111. });
  112. }
  113. }
  114. void GNMeshController::ReadData(AZ::u64 mapId)
  115. {
  116. m_modelData.ReadData(mapId);
  117. // set the render mesh's material list here so it only get the materials for the current object and not the whole scene.
  118. MaterialList materialList;
  119. AZStd::string materialFilePath = AZStd::string(AssetsFolderPath) + m_blenderFilename + "/" + MaterialsFolder.data() + "/";
  120. for (auto materialName : m_modelData.GetMaterials())
  121. {
  122. AZStd::string azMaterialPath = materialFilePath + materialName + AzMaterialExtension.data();
  123. materialList.push_back(azMaterialPath);
  124. }
  125. m_renderMesh->SetMaterialPathList(materialList);
  126. }
  127. void GNMeshController::LoadMaterials(const rapidjson::Value& materialArray)
  128. {
  129. m_materialWaitList.clear();
  130. // iterate through the material arrays and write them into files.
  131. AZStd::string projectRootPath = GetProjectRoot() + "/";
  132. AZStd::string materialFilePath = AZStd::string(AssetsFolderPath) + m_blenderFilename + "/" + MaterialsFolder.data() + "/";
  133. for (rapidjson::Value::ConstValueIterator itr = materialArray.Begin(); itr != materialArray.End(); ++itr)
  134. {
  135. const auto matItr = itr->MemberBegin();
  136. AZStd::string materialName = matItr->name.GetString();
  137. AZStd::string materialContent = matItr->value.GetString();
  138. AZStd::string fullFilePath = projectRootPath + materialFilePath + materialName + MaterialExtension.data();
  139. AZ::Utils::WriteFile(materialContent, fullFilePath.c_str());
  140. AZStd::string azMaterialPath = materialFilePath + materialName + AzMaterialExtension.data();
  141. if (AZ::IO::FileIOBase::GetInstance()->Exists(azMaterialPath.c_str()))
  142. {
  143. AZ::Data::AssetId materialAssetId;
  144. EBUS_EVENT_RESULT(
  145. materialAssetId,
  146. AZ::Data::AssetCatalogRequestBus,
  147. GetAssetIdByPath,
  148. azMaterialPath.c_str(),
  149. AZ::Data::s_invalidAssetType,
  150. false);
  151. // If found, notify mesh that the mesh data is assigned and material is ready.
  152. if (!materialAssetId.IsValid())
  153. {
  154. m_materialWaitList.push_back(azMaterialPath);
  155. }
  156. }
  157. else
  158. {
  159. m_materialWaitList.push_back(azMaterialPath);
  160. }
  161. }
  162. }
  163. void GNMeshController::SetFileName(const AZStd::string& path)
  164. {
  165. AzFramework::StringFunc::Path::GetFileName(path.c_str(), m_blenderFilename);
  166. AZStd::regex reg("[^\\w\\s]+");
  167. m_blenderFilename = AZStd::regex_replace(m_blenderFilename, reg, "_");
  168. }
  169. AZStd::string GNMeshController::GenerateFBXPath()
  170. {
  171. AZStd::string fullFilePath = GetProjectRoot() + "/";
  172. AZStd::string filePath = AZStd::string(AssetsFolderPath) + m_blenderFilename + "/";
  173. fullFilePath += filePath + GenerateModelAssetName() + FbxExtension.data();
  174. return fullFilePath;
  175. }
  176. AZStd::string GNMeshController::GenerateModelAssetName()
  177. {
  178. return m_blenderFilename + "_" + AZStd::string::format("%llu", (AZ::u64)m_entityId);
  179. }
  180. void GNMeshController::OnTransformChanged([[maybe_unused]] const AZ::Transform& local, const AZ::Transform& world)
  181. {
  182. m_worldAabb.reset();
  183. m_localAabb.reset();
  184. m_world = world;
  185. if (m_renderMesh)
  186. {
  187. m_renderMesh->UpdateTransform(world);
  188. }
  189. }
  190. void GNMeshController::OnCatalogAssetAdded(const AZ::Data::AssetId& assetId)
  191. {
  192. AZ::Data::AssetInfo assetInfo;
  193. EBUS_EVENT_RESULT(assetInfo, AZ::Data::AssetCatalogRequestBus, GetAssetInfoById, assetId);
  194. // note that this will get called twice, once with the real assetId and once with legacy assetId.
  195. // we only want to add the real asset to the list, in which the assetId passed in is equal to the final assetId returned
  196. // otherwise, you look up assetId (and its a legacy assetId) and the actual asset will be different.
  197. if ((assetInfo.m_assetId.IsValid()) && (assetInfo.m_assetId == assetId))
  198. {
  199. AZStd::string assetName;
  200. AzFramework::StringFunc::Path::GetFileName(assetInfo.m_relativePath.c_str(), assetName);
  201. bool workInProgress = false;
  202. EditorGeomNodesComponentRequestBus::EventResult(
  203. workInProgress, m_entityId, &EditorGeomNodesComponentRequests::GetWorkInProgress);
  204. if (workInProgress && (assetName == GenerateModelAssetName()))
  205. {
  206. auto entity = AzToolsFramework::GetEntity(m_entityId);
  207. auto transformComponent = entity->FindComponent<AzToolsFramework::Components::TransformComponent>();
  208. AZ::EntityId parentId = transformComponent->GetParentId();
  209. auto worldTransform = transformComponent->GetWorldTM();
  210. AZ::EntityId entityId;
  211. EBUS_EVENT_RESULT(entityId, AzToolsFramework::EditorRequests::Bus, CreateNewEntity, parentId);
  212. AzToolsFramework::EntityIdList entityIdList = { entityId };
  213. AzToolsFramework::EntityCompositionRequests::AddComponentsOutcome addedComponentsResult =
  214. AZ::Failure(AZStd::string("Failed to call AddComponentsToEntities on EntityCompositionRequestBus"));
  215. AzToolsFramework::EntityCompositionRequestBus::BroadcastResult(
  216. addedComponentsResult,
  217. &AzToolsFramework::EntityCompositionRequests::AddComponentsToEntities,
  218. entityIdList,
  219. AZ::ComponentTypeList{ AZ::Render::EditorMeshComponentTypeId });
  220. if (addedComponentsResult.IsSuccess())
  221. {
  222. AZ::TransformBus::Event(entityId, &AZ::TransformBus::Events::SetWorldTM, worldTransform);
  223. AZ::Render::MeshComponentRequestBus::Event(
  224. entityId, &AZ::Render::MeshComponentRequestBus::Events::SetModelAssetPath, assetInfo.m_relativePath);
  225. EBUS_EVENT(
  226. AzToolsFramework::ToolsApplicationRequests::Bus,
  227. DeleteEntitiesAndAllDescendants,
  228. AzToolsFramework::EntityIdList{ m_entityId });
  229. }
  230. EditorGeomNodesComponentRequestBus::Event(m_entityId, &EditorGeomNodesComponentRequests::SetWorkInProgress, false);
  231. }
  232. else
  233. {
  234. if (!m_materialWaitList.empty())
  235. {
  236. auto iter = AZStd::find(m_materialWaitList.begin(), m_materialWaitList.end(), assetInfo.m_relativePath);
  237. if (iter != m_materialWaitList.end())
  238. {
  239. m_materialWaitList.erase(iter);
  240. if (m_materialWaitList.empty())
  241. {
  242. RebuildRenderMesh();
  243. }
  244. }
  245. }
  246. }
  247. }
  248. }
  249. void GNMeshController::OnCatalogAssetChanged(const AZ::Data::AssetId& assetId)
  250. {
  251. OnCatalogAssetAdded(assetId);
  252. }
  253. } // namespace GeomNodes