3
0

MorphTargetPipelineTests.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  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 "InitSceneAPIFixture.h"
  9. #include <AzCore/std/smart_ptr/make_shared.h>
  10. #include <AzCore/std/smart_ptr/unique_ptr.h>
  11. #include <AzCore/std/string/conversions.h>
  12. #include <AzToolsFramework/UI/PropertyEditor/PropertyManagerComponent.h>
  13. #include <SceneAPI/SceneCore/Mocks/Containers/MockScene.h>
  14. #include <SceneAPI/SceneData/GraphData/BoneData.h>
  15. #include <SceneAPI/SceneData/GraphData/MeshData.h>
  16. #include <SceneAPI/SceneData/GraphData/BlendShapeData.h>
  17. #include <EMotionFX/Source/Node.h>
  18. #include <EMotionFX/Source/Actor.h>
  19. #include <EMotionFX/Source/ActorInstance.h>
  20. #include <EMotionFX/Source/Mesh.h>
  21. #include <EMotionFX/Source/MorphSetup.h>
  22. #include <EMotionFX/Source/MorphSetupInstance.h>
  23. #include <EMotionFX/Source/MorphTarget.h>
  24. #include <EMotionFX/Source/Node.h>
  25. #include <EMotionFX/Source/Skeleton.h>
  26. #include <EMotionFX/Pipeline/RCExt/Actor/ActorBuilder.h>
  27. #include <EMotionFX/Pipeline/RCExt/Actor/MorphTargetExporter.h>
  28. #include <EMotionFX/Pipeline/RCExt/ExportContexts.h>
  29. #include <EMotionFX/Pipeline/SceneAPIExt/Groups/ActorGroup.h>
  30. #include <EMotionFX/Pipeline/SceneAPIExt/Rules/MorphTargetRule.h>
  31. #include <Tests/TestAssetCode/SimpleActors.h>
  32. #include <Tests/TestAssetCode/ActorFactory.h>
  33. namespace EMotionFX
  34. {
  35. // This fixture is responsible for creating the scene description used by
  36. // the morph target pipeline tests
  37. using MorphTargetPipelineFixtureBase = InitSceneAPIFixture<
  38. AZ::AssetManagerComponent,
  39. AZ::JobManagerComponent,
  40. AZ::StreamerComponent,
  41. AzToolsFramework::Components::PropertyManagerComponent,
  42. EMotionFX::Integration::SystemComponent,
  43. EMotionFX::Pipeline::ActorBuilder,
  44. EMotionFX::Pipeline::MorphTargetExporter
  45. >;
  46. class MorphTargetPipelineFixture
  47. : public MorphTargetPipelineFixtureBase
  48. {
  49. public:
  50. void SetUp() override
  51. {
  52. MorphTargetPipelineFixtureBase::SetUp();
  53. m_actor = ActorFactory::CreateAndInit<SimpleJointChainActor>(0);
  54. // Set up the scene graph
  55. m_scene = new AZ::SceneAPI::Containers::MockScene("MockScene");
  56. m_scene->SetOriginalSceneOrientation(AZ::SceneAPI::Containers::Scene::SceneOrientation::ZUp);
  57. AZ::SceneAPI::Containers::SceneGraph& graph = m_scene->GetGraph();
  58. AZStd::shared_ptr<AZ::SceneData::GraphData::BoneData> boneData = AZStd::make_shared<AZ::SceneData::GraphData::BoneData>();
  59. graph.AddChild(graph.GetRoot(), "testRootBone", boneData);
  60. // Set up our base shape
  61. AZStd::shared_ptr<AZ::SceneData::GraphData::MeshData> meshData = AZStd::make_shared<AZ::SceneData::GraphData::MeshData>();
  62. AZStd::vector<AZ::Vector3> unmorphedVerticies
  63. {
  64. AZ::Vector3(0.0f, 0.0f, 0.0f),
  65. AZ::Vector3(1.0f, 0.0f, 0.0f),
  66. AZ::Vector3(0.0f, 1.0f, 0.0f)
  67. };
  68. for (const AZ::Vector3& vertex : unmorphedVerticies)
  69. {
  70. meshData->AddPosition(vertex);
  71. }
  72. meshData->AddNormal(AZ::Vector3(0.0f, 0.0f, 1.0f));
  73. meshData->AddNormal(AZ::Vector3(0.0f, 0.0f, 1.0f));
  74. meshData->AddNormal(AZ::Vector3(0.0f, 0.0f, 1.0f));
  75. meshData->SetVertexIndexToControlPointIndexMap(0, 0);
  76. meshData->SetVertexIndexToControlPointIndexMap(1, 1);
  77. meshData->SetVertexIndexToControlPointIndexMap(2, 2);
  78. meshData->AddFace(0, 1, 2);
  79. AZ::SceneAPI::Containers::SceneGraph::NodeIndex meshNodeIndex = graph.AddChild(graph.GetRoot(), "testMesh", meshData);
  80. // Set up the morph targets
  81. AZStd::vector<AZStd::vector<AZ::Vector3>> morphedVertices
  82. {{
  83. {
  84. // Morph target 1
  85. AZ::Vector3(0.0f, 0.0f, 0.0f),
  86. AZ::Vector3(1.0f, 0.0f, 1.0f), // this one is different
  87. AZ::Vector3(0.0f, 1.0f, 0.0f)
  88. },
  89. {
  90. // Morph target 2
  91. AZ::Vector3(0.0f, 0.0f, 0.0f),
  92. AZ::Vector3(1.0f, 0.0f, 0.0f),
  93. AZ::Vector3(0.0f, 1.0f, 1.0f) // this one is different
  94. },
  95. }};
  96. const size_t morphTargetCount = morphedVertices.size();
  97. for (size_t morphIndex = 0; morphIndex < morphTargetCount; ++morphIndex)
  98. {
  99. AZStd::shared_ptr<AZ::SceneData::GraphData::BlendShapeData> blendShapeData = AZStd::make_shared<AZ::SceneData::GraphData::BlendShapeData>();
  100. const AZStd::vector<AZ::Vector3>& verticesForThisMorph = morphedVertices.at(morphIndex);
  101. const uint vertexCount = static_cast<uint>(verticesForThisMorph.size());
  102. for (uint vertexIndex = 0; vertexIndex < vertexCount; ++vertexIndex)
  103. {
  104. blendShapeData->AddPosition(verticesForThisMorph.at(vertexIndex));
  105. blendShapeData->AddNormal(AZ::Vector3::CreateAxisZ());
  106. blendShapeData->SetVertexIndexToControlPointIndexMap(vertexIndex, vertexIndex);
  107. }
  108. blendShapeData->AddFace({{0, 1, 2}});
  109. AZStd::string morphTargetName("testMorphTarget");
  110. morphTargetName += AZStd::to_string(static_cast<int>(morphIndex));
  111. graph.AddChild(meshNodeIndex, morphTargetName.c_str(), blendShapeData);
  112. }
  113. }
  114. AZ::SceneAPI::Events::ProcessingResult Process(EMotionFX::Pipeline::Group::ActorGroup& actorGroup)
  115. {
  116. AZ::SceneAPI::Events::ProcessingResultCombiner result;
  117. AZStd::vector<AZStd::string> materialReferences;
  118. EMotionFX::Pipeline::ActorBuilderContext actorBuilderContext(*m_scene, "tmp", actorGroup, m_actor.get(), materialReferences, AZ::RC::Phase::Construction);
  119. result += AZ::SceneAPI::Events::Process(actorBuilderContext);
  120. result += AZ::SceneAPI::Events::Process<EMotionFX::Pipeline::ActorBuilderContext>(actorBuilderContext, AZ::RC::Phase::Filling);
  121. result += AZ::SceneAPI::Events::Process<EMotionFX::Pipeline::ActorBuilderContext>(actorBuilderContext, AZ::RC::Phase::Finalizing);
  122. return result.GetResult();
  123. }
  124. void TearDown() override
  125. {
  126. m_actor.reset();
  127. delete m_scene;
  128. MorphTargetPipelineFixtureBase::TearDown();
  129. }
  130. EMotionFX::Mesh* GetMesh(const EMotionFX::Actor* actor)
  131. {
  132. Skeleton* skeleton = actor->GetSkeleton();
  133. EMotionFX::Mesh* mesh = nullptr;
  134. const size_t numNodes = skeleton->GetNumNodes();
  135. for (size_t nodeNum = 0; nodeNum < numNodes; ++nodeNum)
  136. {
  137. if (mesh)
  138. {
  139. // We should only have one node that has a mesh
  140. EXPECT_FALSE(actor->GetMesh(0, nodeNum)) << "More than one mesh found on built actor";
  141. }
  142. else
  143. {
  144. mesh = actor->GetMesh(0, nodeNum);
  145. AZ_Printf("EMotionFX", "%s node name", skeleton->GetNode(nodeNum)->GetName());
  146. }
  147. }
  148. return mesh;
  149. }
  150. AZStd::unique_ptr<Actor> m_actor;
  151. AZ::SceneAPI::Containers::Scene* m_scene;
  152. };
  153. class MorphTargetCreationTestFixture : public MorphTargetPipelineFixture,
  154. public ::testing::WithParamInterface<std::vector<std::string>>
  155. {
  156. };
  157. TEST_P(MorphTargetCreationTestFixture, TestMorphTargetCreation)
  158. {
  159. const std::vector<std::string>& selectedMorphTargets = GetParam();
  160. // Set up the actor group, which controls which parts of the scene graph
  161. // are used to generate the actor
  162. EMotionFX::Pipeline::Group::ActorGroup actorGroup;
  163. actorGroup.SetSelectedRootBone("testRootBone");
  164. // TODO: replace the test with atom mesh.
  165. // actorGroup.GetSceneNodeSelectionList().AddSelectedNode("testMesh");
  166. // actorGroup.GetBaseNodeSelectionList().AddSelectedNode("testMesh");
  167. AZStd::shared_ptr<EMotionFX::Pipeline::Rule::MorphTargetRule> morphTargetRule = AZStd::make_shared<EMotionFX::Pipeline::Rule::MorphTargetRule>();
  168. for (const std::string& selectedMorphTarget : selectedMorphTargets)
  169. {
  170. morphTargetRule->GetSceneNodeSelectionList().AddSelectedNode(("testMesh." + selectedMorphTarget).c_str());
  171. }
  172. actorGroup.GetRuleContainer().AddRule(morphTargetRule);
  173. const AZ::SceneAPI::Events::ProcessingResult result = Process(actorGroup);
  174. ASSERT_EQ(result, AZ::SceneAPI::Events::ProcessingResult::Success) << "Failed to build actor";
  175. const MorphSetup* morphSetup = m_actor->GetMorphSetup(0);
  176. if (selectedMorphTargets.empty())
  177. {
  178. ASSERT_FALSE(morphSetup) << "A morph setup was created when the blend shape rule specified no nodes";
  179. // That's all we can verify for the case where no morph targets
  180. // were selected for export
  181. return;
  182. }
  183. ASSERT_TRUE(morphSetup) << "No morph setup was created";
  184. const size_t expectedNumMorphTargets = selectedMorphTargets.size();
  185. ASSERT_EQ(morphSetup->GetNumMorphTargets(), expectedNumMorphTargets) << "Morph setup should contain " << expectedNumMorphTargets << " morph target(s)";
  186. EMotionFX::Integration::EMotionFXPtr<EMotionFX::ActorInstance> actorInstance = EMotionFX::Integration::EMotionFXPtr<EMotionFX::ActorInstance>::MakeFromNew(EMotionFX::ActorInstance::Create(m_actor.get()));
  187. const Mesh* mesh = GetMesh(m_actor.get());
  188. // TODO: replace the test with atom mesh.
  189. if (!mesh)
  190. {
  191. return;
  192. }
  193. const size_t numMorphTargets = morphSetup->GetNumMorphTargets();
  194. for (size_t morphTargetIndex = 0; morphTargetIndex < numMorphTargets; ++morphTargetIndex)
  195. {
  196. const MorphTarget* morphTarget = morphSetup->GetMorphTarget(morphTargetIndex);
  197. EXPECT_STREQ(morphTarget->GetName(), selectedMorphTargets[morphTargetIndex].c_str()) << "Morph target's name is incorrect";
  198. const AZ::SceneAPI::Containers::SceneGraph& graph = m_scene->GetGraph();
  199. // Verify that the unmorphed vertices are what we expect
  200. const AZ::Vector3* const positions = static_cast<AZ::Vector3*>(mesh->FindVertexData(EMotionFX::Mesh::ATTRIB_POSITIONS));
  201. AZStd::vector<AZ::Vector3> gotUnmorphedVertices;
  202. uint32 numVertices = mesh->GetNumVertices();
  203. for (uint32 vertexNum = 0; vertexNum < numVertices; ++vertexNum)
  204. {
  205. gotUnmorphedVertices.emplace_back(
  206. positions[vertexNum].GetX(),
  207. positions[vertexNum].GetY(),
  208. positions[vertexNum].GetZ()
  209. );
  210. }
  211. // Get the unmorphed vertices from the scene data
  212. const AZStd::shared_ptr<const AZ::SceneAPI::DataTypes::IMeshData> meshData = azrtti_cast<const AZ::SceneAPI::DataTypes::IMeshData*>(graph.GetNodeContent(graph.Find("testMesh")));
  213. AZStd::vector<AZ::Vector3> expectedUnmorphedVertices;
  214. numVertices = meshData->GetVertexCount();
  215. for (uint vertexNum = 0; vertexNum < numVertices; ++vertexNum)
  216. {
  217. expectedUnmorphedVertices.emplace_back(meshData->GetPosition(meshData->GetControlPointIndex(vertexNum)));
  218. }
  219. EXPECT_EQ(gotUnmorphedVertices, expectedUnmorphedVertices);
  220. // Now apply the morph, and verify the morphed vertices against
  221. // what we expect
  222. actorInstance->GetMorphSetupInstance()->GetMorphTarget(morphTargetIndex)->SetManualMode(true);
  223. actorInstance->GetMorphSetupInstance()->GetMorphTarget(morphTargetIndex)->SetWeight(1.0f);
  224. actorInstance->UpdateTransformations(0.0f, true);
  225. actorInstance->UpdateMeshDeformers(0.0f);
  226. const AZ::Vector3* const morphedPositions = static_cast<AZ::Vector3*>(mesh->FindVertexData(EMotionFX::Mesh::ATTRIB_POSITIONS));
  227. AZStd::vector<AZ::Vector3> gotMorphedVertices;
  228. numVertices = mesh->GetNumVertices();
  229. for (uint32 vertexNum = 0; vertexNum < numVertices; ++vertexNum)
  230. {
  231. gotMorphedVertices.emplace_back(
  232. morphedPositions[vertexNum].GetX(),
  233. morphedPositions[vertexNum].GetY(),
  234. morphedPositions[vertexNum].GetZ()
  235. );
  236. }
  237. // Get the morphed vertices from the scene data
  238. AZStd::string morphTargetSceneNodeName("testMesh.");
  239. morphTargetSceneNodeName += selectedMorphTargets[morphTargetIndex].c_str();
  240. const AZStd::shared_ptr<const AZ::SceneAPI::DataTypes::IBlendShapeData> morphTargetData = azrtti_cast<const AZ::SceneAPI::DataTypes::IBlendShapeData*>(graph.GetNodeContent(graph.Find(morphTargetSceneNodeName)));
  241. AZStd::vector<AZ::Vector3> expectedMorphedVertices;
  242. numVertices = morphTargetData->GetVertexCount();
  243. for (uint vertexNum = 0; vertexNum < numVertices; ++vertexNum)
  244. {
  245. expectedMorphedVertices.emplace_back(morphTargetData->GetPosition(morphTargetData->GetControlPointIndex(vertexNum)));
  246. }
  247. EXPECT_EQ(gotMorphedVertices, expectedMorphedVertices);
  248. // Reset the morph target weight so that the next iteration compares against the unmorphed mesh
  249. actorInstance->GetMorphSetupInstance()->GetMorphTarget(morphTargetIndex)->SetWeight(0.0f);
  250. actorInstance->UpdateTransformations(0.0f, true);
  251. actorInstance->UpdateMeshDeformers(0.0f);
  252. }
  253. }
  254. // Note that these values are instantiated before the SystemAllocator is
  255. // created, so we can't use AZStd::vector
  256. INSTANTIATE_TEST_CASE_P(TestMorphTargetCreation, MorphTargetCreationTestFixture,
  257. ::testing::Values(
  258. std::vector<std::string> {},
  259. std::vector<std::string> {"testMorphTarget0"},
  260. std::vector<std::string> {"testMorphTarget1"},
  261. std::vector<std::string> {"testMorphTarget0", "testMorphTarget1"}
  262. )
  263. );
  264. }