EditorClothComponentTest.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  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 <AzTest/AzTest.h>
  9. #include <ISystem.h>
  10. #include <AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.h>
  11. #include <Components/ClothComponent.h>
  12. #include <Components/EditorClothComponent.h>
  13. #include <ActorHelper.h>
  14. #include <Integration/Editor/Components/EditorActorComponent.h>
  15. namespace NvCloth
  16. {
  17. namespace Internal
  18. {
  19. extern const char* const StatusMessageSelectNode;
  20. extern const char* const StatusMessageNoAsset;
  21. extern const char* const StatusMessageNoClothNodes;
  22. }
  23. }
  24. namespace UnitTest
  25. {
  26. //! Sets up a mock global environment to
  27. //! change between server and client.
  28. class NvClothEditorClothComponent
  29. : public ::testing::Test
  30. {
  31. public:
  32. const AZStd::string JointRootName = "root_node";
  33. const AZStd::string MeshNodeName = "cloth_mesh_node";
  34. const AZStd::vector<AZ::Vector3> MeshVertices = {{
  35. AZ::Vector3(-1.0f, 0.0f, 0.0f),
  36. AZ::Vector3(1.0f, 0.0f, 0.0f),
  37. AZ::Vector3(0.0f, 1.0f, 0.0f)
  38. }};
  39. const AZStd::vector<NvCloth::SimIndexType> MeshIndices = {{
  40. 0, 1, 2
  41. }};
  42. const AZStd::vector<AZ::Vector2> MeshUVs = {{
  43. AZ::Vector2(0.0f, 0.0f),
  44. AZ::Vector2(1.0f, 0.0f),
  45. AZ::Vector2(0.5f, 1.0f)
  46. }};
  47. // [inverse mass, motion constrain radius, backstop offset, backstop radius]
  48. const AZStd::vector<AZ::Color> MeshClothData = {{
  49. AZ::Color(0.75f, 0.6f, 0.5f, 0.1f),
  50. AZ::Color(1.0f, 0.16f, 0.1f, 1.0f),
  51. AZ::Color(0.25f, 1.0f, 0.9f, 0.5f)
  52. }};
  53. const AZ::u32 LodLevel = 0;
  54. static void SetUpTestCase();
  55. static void TearDownTestCase();
  56. protected:
  57. AZStd::unique_ptr<AZ::Entity> CreateInactiveEditorEntity(const char* entityName);
  58. AZStd::unique_ptr<AZ::Entity> CreateActiveGameEntityFromEditorEntity(AZ::Entity* editorEntity);
  59. private:
  60. static AZStd::unique_ptr<SSystemGlobalEnvironment> s_mockGEnv;
  61. static SSystemGlobalEnvironment* s_previousGEnv;
  62. };
  63. AZStd::unique_ptr<SSystemGlobalEnvironment> NvClothEditorClothComponent::s_mockGEnv;
  64. SSystemGlobalEnvironment* NvClothEditorClothComponent::s_previousGEnv = nullptr;
  65. void NvClothEditorClothComponent::SetUpTestCase()
  66. {
  67. // override global environment
  68. s_previousGEnv = gEnv;
  69. s_mockGEnv = AZStd::make_unique<SSystemGlobalEnvironment>();
  70. gEnv = s_mockGEnv.get();
  71. #if !defined(CONSOLE)
  72. // Set environment to not be a server by default.
  73. gEnv->SetIsDedicated(false);
  74. #endif
  75. }
  76. void NvClothEditorClothComponent::TearDownTestCase()
  77. {
  78. // restore global environment
  79. gEnv = s_previousGEnv;
  80. s_mockGEnv.reset();
  81. s_previousGEnv = nullptr;
  82. }
  83. AZStd::unique_ptr<AZ::Entity> NvClothEditorClothComponent::CreateInactiveEditorEntity(const char* entityName)
  84. {
  85. AZ::Entity* entity = nullptr;
  86. UnitTest::CreateDefaultEditorEntity(entityName, &entity);
  87. entity->Deactivate();
  88. return AZStd::unique_ptr<AZ::Entity>(entity);
  89. }
  90. AZStd::unique_ptr<AZ::Entity> NvClothEditorClothComponent::CreateActiveGameEntityFromEditorEntity(AZ::Entity* editorEntity)
  91. {
  92. auto gameEntity = AZStd::make_unique<AZ::Entity>();
  93. AzToolsFramework::ToolsApplicationRequestBus::Broadcast(
  94. &AzToolsFramework::ToolsApplicationRequests::PreExportEntity, *editorEntity, *gameEntity);
  95. gameEntity->Init();
  96. gameEntity->Activate();
  97. return gameEntity;
  98. }
  99. TEST_F(NvClothEditorClothComponent, EditorClothComponent_DependencyMissing_EntityIsInvalid)
  100. {
  101. auto entity = CreateInactiveEditorEntity("ClothComponentEditorEntity");
  102. entity->CreateComponent<NvCloth::EditorClothComponent>();
  103. // the entity should not be in a valid state because the cloth component requires a mesh or an actor component
  104. AZ::Entity::DependencySortOutcome sortOutcome = entity->EvaluateDependenciesGetDetails();
  105. EXPECT_FALSE(sortOutcome.IsSuccess());
  106. EXPECT_TRUE(sortOutcome.GetError().m_code == AZ::Entity::DependencySortResult::MissingRequiredService);
  107. }
  108. TEST_F(NvClothEditorClothComponent, EditorClothComponent_ActorDependencySatisfied_EntityIsValid)
  109. {
  110. auto entity = CreateInactiveEditorEntity("ClothComponentEditorEntity");
  111. entity->CreateComponent<NvCloth::EditorClothComponent>();
  112. entity->CreateComponent<EMotionFX::Integration::EditorActorComponent>();
  113. // the entity should be in a valid state because the cloth component requirement is satisfied
  114. AZ::Entity::DependencySortOutcome sortOutcome = entity->EvaluateDependenciesGetDetails();
  115. EXPECT_TRUE(sortOutcome.IsSuccess());
  116. }
  117. TEST_F(NvClothEditorClothComponent, EditorClothComponent_MultipleClothComponents_EntityIsValid)
  118. {
  119. auto entity = CreateInactiveEditorEntity("ClothComponentEditorEntity");
  120. entity->CreateComponent<NvCloth::EditorClothComponent>();
  121. entity->CreateComponent<EMotionFX::Integration::EditorActorComponent>();
  122. // the cloth component should be compatible with multiple cloth components
  123. entity->CreateComponent<NvCloth::EditorClothComponent>();
  124. entity->CreateComponent<NvCloth::EditorClothComponent>();
  125. // the entity should be in a valid state because the cloth component requirement is satisfied
  126. AZ::Entity::DependencySortOutcome sortOutcome = entity->EvaluateDependenciesGetDetails();
  127. EXPECT_TRUE(sortOutcome.IsSuccess());
  128. }
  129. TEST_F(NvClothEditorClothComponent, EditorClothComponent_ClothWithActor_CorrectRuntimeComponents)
  130. {
  131. // create an editor entity with a cloth component and an actor component
  132. auto editorEntity = CreateInactiveEditorEntity("ClothComponentEditorEntity");
  133. editorEntity->CreateComponent<NvCloth::EditorClothComponent>();
  134. editorEntity->CreateComponent<EMotionFX::Integration::EditorActorComponent>();
  135. editorEntity->Activate();
  136. auto gameEntity = CreateActiveGameEntityFromEditorEntity(editorEntity.get());
  137. // check that the runtime entity has the expected components
  138. EXPECT_TRUE(gameEntity->FindComponent<NvCloth::ClothComponent>() != nullptr);
  139. EXPECT_TRUE(gameEntity->FindComponent<EMotionFX::Integration::ActorComponent>() != nullptr);
  140. }
  141. TEST_F(NvClothEditorClothComponent, EditorClothComponent_OnActivationNoMeshCreated_ReturnsMeshNodeListWithNoAssetMessage)
  142. {
  143. auto editorEntity = CreateInactiveEditorEntity("ClothComponentEditorEntity");
  144. auto* editorClothComponent = editorEntity->CreateComponent<NvCloth::EditorClothComponent>();
  145. editorEntity->CreateComponent<EMotionFX::Integration::EditorActorComponent>();
  146. editorEntity->Activate();
  147. const NvCloth::MeshNodeList& meshNodeList = editorClothComponent->GetMeshNodeList();
  148. ASSERT_EQ(meshNodeList.size(), 1);
  149. EXPECT_TRUE(meshNodeList[0] == NvCloth::Internal::StatusMessageNoAsset);
  150. }
  151. // [TODO LYN-1891]
  152. // Revisit when Cloth Component Mesh works with Actors adapted to Atom models.
  153. // Editor Cloth component now uses the new AZ::Render::MeshComponentNotificationBus::OnModelReady
  154. // notification and this test does not setup a model yet.
  155. TEST_F(NvClothEditorClothComponent, DISABLED_EditorClothComponent_OnMeshCreatedWithEmptyActor_ReturnsMeshNodeListWithNoClothMessage)
  156. {
  157. auto editorEntity = CreateInactiveEditorEntity("ClothComponentEditorEntity");
  158. auto* editorClothComponent = editorEntity->CreateComponent<NvCloth::EditorClothComponent>();
  159. auto* editorActorComponent = editorEntity->CreateComponent<EMotionFX::Integration::EditorActorComponent>();
  160. editorEntity->Activate();
  161. {
  162. auto actor = AZStd::make_unique<ActorHelper>("actor_test");
  163. actor->FinishSetup();
  164. editorActorComponent->SetActorAsset(CreateAssetFromActor(AZStd::move(actor)));
  165. }
  166. const NvCloth::MeshNodeList& meshNodeList = editorClothComponent->GetMeshNodeList();
  167. ASSERT_EQ(meshNodeList.size(), 1);
  168. EXPECT_TRUE(meshNodeList[0] == NvCloth::Internal::StatusMessageNoClothNodes);
  169. }
  170. // [TODO LYN-1891]
  171. // Revisit when Cloth Component Mesh works with Actors adapted to Atom models.
  172. // Editor Cloth component now uses the new AZ::Render::MeshComponentNotificationBus::OnModelReady
  173. // notification and this test does not setup a model yet.
  174. TEST_F(NvClothEditorClothComponent, DISABLED_EditorClothComponent_OnMeshCreatedWithActorWithoutClothMesh_ReturnsMeshNodeListWithNoClothMessage)
  175. {
  176. auto editorEntity = CreateInactiveEditorEntity("ClothComponentEditorEntity");
  177. auto* editorClothComponent = editorEntity->CreateComponent<NvCloth::EditorClothComponent>();
  178. auto* editorActorComponent = editorEntity->CreateComponent<EMotionFX::Integration::EditorActorComponent>();
  179. editorEntity->Activate();
  180. {
  181. auto actor = AZStd::make_unique<ActorHelper>("actor_test");
  182. auto jointRootIndex = actor->AddJoint(JointRootName);
  183. actor->SetMesh(LodLevel, jointRootIndex, CreateEMotionFXMesh(MeshVertices, MeshIndices, {}, MeshUVs));
  184. actor->FinishSetup();
  185. editorActorComponent->SetActorAsset(CreateAssetFromActor(AZStd::move(actor)));
  186. }
  187. const NvCloth::MeshNodeList& meshNodeList = editorClothComponent->GetMeshNodeList();
  188. ASSERT_EQ(meshNodeList.size(), 1);
  189. EXPECT_TRUE(meshNodeList[0] == NvCloth::Internal::StatusMessageNoClothNodes);
  190. }
  191. // [TODO LYN-1891]
  192. // Revisit when Cloth Component Mesh works with Actors adapted to Atom models.
  193. // Editor Cloth component now uses the new AZ::Render::MeshComponentNotificationBus::OnModelReady
  194. // notification and this test does not setup a model yet.
  195. TEST_F(NvClothEditorClothComponent, DISABLED_EditorClothComponent_OnMeshCreatedWithActorWithClothMesh_ReturnsValidMeshNodeList)
  196. {
  197. auto editorEntity = CreateInactiveEditorEntity("ClothComponentEditorEntity");
  198. auto* editorClothComponent = editorEntity->CreateComponent<NvCloth::EditorClothComponent>();
  199. auto* editorActorComponent = editorEntity->CreateComponent<EMotionFX::Integration::EditorActorComponent>();
  200. editorEntity->Activate();
  201. {
  202. auto actor = AZStd::make_unique<ActorHelper>("actor_test");
  203. actor->AddJoint(JointRootName);
  204. auto meshNodeIndex = actor->AddJoint(MeshNodeName, AZ::Transform::CreateIdentity(), JointRootName);
  205. actor->SetMesh(LodLevel, meshNodeIndex, CreateEMotionFXMesh(MeshVertices, MeshIndices, {}, MeshUVs/*, MeshClothData*/));
  206. actor->FinishSetup();
  207. editorActorComponent->SetActorAsset(CreateAssetFromActor(AZStd::move(actor)));
  208. }
  209. const NvCloth::MeshNodeList& meshNodeList = editorClothComponent->GetMeshNodeList();
  210. ASSERT_EQ(meshNodeList.size(), 2);
  211. EXPECT_TRUE(meshNodeList[0] == NvCloth::Internal::StatusMessageSelectNode);
  212. EXPECT_TRUE(meshNodeList[1] == MeshNodeName);
  213. }
  214. TEST_F(NvClothEditorClothComponent, EditorClothComponent_OnMeshCreatedWithActorWithNoBackstop_ReturnsEmptyMeshNodesWithBackstopData)
  215. {
  216. // [inverse mass, motion constrain radius, backstop offset, backstop radius]
  217. const AZStd::vector<AZ::Color> meshClothDataNoBackstop = {{
  218. AZ::Color(0.75f, 1.0f, 0.5f, 0.0f),
  219. AZ::Color(1.0f, 1.0f, 0.5f, 0.0f),
  220. AZ::Color(0.25f, 1.0f, 0.5f, 0.0f)
  221. }};
  222. auto editorEntity = CreateInactiveEditorEntity("ClothComponentEditorEntity");
  223. auto* editorClothComponent = editorEntity->CreateComponent<NvCloth::EditorClothComponent>();
  224. auto* editorActorComponent = editorEntity->CreateComponent<EMotionFX::Integration::EditorActorComponent>();
  225. editorEntity->Activate();
  226. {
  227. auto actor = AZStd::make_unique<ActorHelper>("actor_test");
  228. actor->AddJoint(JointRootName);
  229. auto meshNodeIndex = actor->AddJoint(MeshNodeName, AZ::Transform::CreateIdentity(), JointRootName);
  230. actor->SetMesh(LodLevel, meshNodeIndex, CreateEMotionFXMesh(MeshVertices, MeshIndices, {}, MeshUVs/*, meshClothDataNoBackstop*/));
  231. actor->FinishSetup();
  232. editorActorComponent->SetActorAsset(CreateAssetFromActor(AZStd::move(actor)));
  233. }
  234. const auto& meshNodesWithBackstopData = editorClothComponent->GetMeshNodesWithBackstopData();
  235. EXPECT_TRUE(meshNodesWithBackstopData.empty());
  236. }
  237. // [TODO LYN-1891]
  238. // Revisit when Cloth Component Mesh works with Actors adapted to Atom models.
  239. // Editor Cloth component now uses the new AZ::Render::MeshComponentNotificationBus::OnModelReady
  240. // notification and this test does not setup a model yet.
  241. TEST_F(NvClothEditorClothComponent, DISABLED_EditorClothComponent_OnMeshCreatedWithActorWithBackstop_ReturnsValidMeshNodesWithBackstopData)
  242. {
  243. auto editorEntity = CreateInactiveEditorEntity("ClothComponentEditorEntity");
  244. auto* editorClothComponent = editorEntity->CreateComponent<NvCloth::EditorClothComponent>();
  245. auto* editorActorComponent = editorEntity->CreateComponent<EMotionFX::Integration::EditorActorComponent>();
  246. editorEntity->Activate();
  247. {
  248. auto actor = AZStd::make_unique<ActorHelper>("actor_test");
  249. actor->AddJoint(JointRootName);
  250. auto meshNodeIndex = actor->AddJoint(MeshNodeName, AZ::Transform::CreateIdentity(), JointRootName);
  251. actor->SetMesh(LodLevel, meshNodeIndex, CreateEMotionFXMesh(MeshVertices, MeshIndices, {}, MeshUVs/*, MeshClothData*/));
  252. actor->FinishSetup();
  253. editorActorComponent->SetActorAsset(CreateAssetFromActor(AZStd::move(actor)));
  254. }
  255. const auto& meshNodesWithBackstopData = editorClothComponent->GetMeshNodesWithBackstopData();
  256. EXPECT_EQ(meshNodesWithBackstopData.size(), 1);
  257. EXPECT_TRUE(meshNodesWithBackstopData.find(MeshNodeName) != meshNodesWithBackstopData.end());
  258. }
  259. // [TODO LYN-1891]
  260. // Revisit when Cloth Component Mesh works with Actors adapted to Atom models.
  261. // Editor Cloth component now uses the new AZ::Render::MeshComponentNotificationBus::OnModelReady
  262. // notification and this test does not setup a model yet.
  263. TEST_F(NvClothEditorClothComponent, DISABLED_EditorClothComponent_OnModelPreDestroy_ReturnsMeshNodeListWithNoAssetMessage)
  264. {
  265. auto editorEntity = CreateInactiveEditorEntity("ClothComponentEditorEntity");
  266. auto* editorClothComponent = editorEntity->CreateComponent<NvCloth::EditorClothComponent>();
  267. auto* editorActorComponent = editorEntity->CreateComponent<EMotionFX::Integration::EditorActorComponent>();
  268. editorEntity->Activate();
  269. {
  270. auto actor = AZStd::make_unique<ActorHelper>("actor_test");
  271. actor->AddJoint(JointRootName);
  272. auto meshNodeIndex = actor->AddJoint(MeshNodeName, AZ::Transform::CreateIdentity(), JointRootName);
  273. actor->SetMesh(LodLevel, meshNodeIndex, CreateEMotionFXMesh(MeshVertices, MeshIndices, {}, MeshUVs/*, MeshClothData*/));
  274. actor->FinishSetup();
  275. editorActorComponent->SetActorAsset(CreateAssetFromActor(AZStd::move(actor)));
  276. }
  277. editorClothComponent->OnModelPreDestroy();
  278. const NvCloth::MeshNodeList& meshNodeList = editorClothComponent->GetMeshNodeList();
  279. const auto& meshNodesWithBackstopData = editorClothComponent->GetMeshNodesWithBackstopData();
  280. ASSERT_EQ(meshNodeList.size(), 1);
  281. EXPECT_TRUE(meshNodeList[0] == NvCloth::Internal::StatusMessageNoAsset);
  282. EXPECT_TRUE(meshNodesWithBackstopData.empty());
  283. }
  284. } // namespace UnitTest