/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ #include #include #include #include #include #include #include #include #include #include #include #include namespace EMotionFX { // Tests if the activation input port can be controlled with a non-boolean/float values from a const float node. class BlendTreeRagdollNode_ConstFloatActivateInputTest : public AnimGraphFixture , public ::testing::WithParamInterface { public: void ConstructGraph() override { AnimGraphFixture::ConstructGraph(); m_blendTreeAnimGraph = AnimGraphFactory::Create(); m_rootStateMachine = m_blendTreeAnimGraph->GetRootStateMachine(); BlendTree* blendTree = m_blendTreeAnimGraph->GetBlendTreeNode(); /* +-------------+ +---------+ +------------+ | Const Float |--->| Ragdoll |--->| Final Node | +-------------+ +---------+ +------------+ */ BlendTreeFloatConstantNode* floatConstNode = aznew BlendTreeFloatConstantNode(); floatConstNode->SetValue(GetParam()); blendTree->AddChildNode(floatConstNode); m_ragdollNode = aznew BlendTreeRagdollNode(); blendTree->AddChildNode(m_ragdollNode); BlendTreeFinalNode* finalNode = aznew BlendTreeFinalNode(); blendTree->AddChildNode(finalNode); m_ragdollNode->AddUnitializedConnection(floatConstNode, BlendTreeFloatConstantNode::PORTID_OUTPUT_RESULT, BlendTreeRagdollNode::PORTID_ACTIVATE); finalNode->AddUnitializedConnection(m_ragdollNode, BlendTreeRagdollNode::PORTID_OUTPUT_POSE, BlendTreeFinalNode::PORTID_INPUT_POSE); m_blendTreeAnimGraph->InitAfterLoading(); } void SetUp() override { AnimGraphFixture::SetUp(); m_animGraphInstance->Destroy(); m_animGraphInstance = m_blendTreeAnimGraph->GetAnimGraphInstance(m_actorInstance, m_motionSet); } AZStd::unique_ptr m_blendTreeAnimGraph; BlendTreeRagdollNode* m_ragdollNode = nullptr; }; TEST_P(BlendTreeRagdollNode_ConstFloatActivateInputTest, BlendTreeRagdollNode_ConstFloatActivateInputTest) { GetEMotionFX().Update(0.0f); const float constFloatValue = GetParam(); const bool isActivated = m_ragdollNode->IsActivated(m_animGraphInstance); EXPECT_EQ(!MCore::Math::IsFloatZero(constFloatValue), isActivated) << "Activation expected in case const float value is not zero."; } INSTANTIATE_TEST_CASE_P(BlendTreeRagdollNode_ConstFloatActivateInputTest, BlendTreeRagdollNode_ConstFloatActivateInputTest, ::testing::ValuesIn({ -1.0f, 0.0f, 0.1f, 1.0f })); /////////////////////////////////////////////////////////////////////////// struct RagdollRootNodeParam { std::string m_ragdollRootNode; bool m_ragdollRootNodeSimulated; std::vector m_ragdollConfigNodeNames; std::vector m_simulatedJointNames; }; class RagdollRootNodeFixture : public ActorFixture , public ::testing::WithParamInterface { public: void AddRagdollNodeConfig(AZStd::vector& ragdollNodes, const AZStd::string& jointName) { Physics::RagdollNodeConfiguration nodeConfig; nodeConfig.m_debugName = jointName; ragdollNodes.emplace_back(nodeConfig); } }; TEST_P(RagdollRootNodeFixture, RagdollRootNodeIsSimulatedTests) { Physics::RagdollConfiguration& ragdollConfig = m_actor->GetPhysicsSetup()->GetRagdollConfig(); AZStd::vector& ragdollNodes = ragdollConfig.m_nodes; const RagdollRootNodeParam& param = GetParam(); const AZStd::string ragdollRootNodeName = param.m_ragdollRootNode.c_str(); // Create the ragdoll config. for (const std::string& jointName : param.m_ragdollConfigNodeNames) { AddRagdollNodeConfig(ragdollNodes, jointName.c_str()); } const size_t numRagdollNodes = ragdollNodes.size(); // Create the ragdoll instance and check if the ragdoll root node is set correctly. TestRagdoll testRagdoll; EXPECT_CALL(testRagdoll, GetState(::testing::_)).Times(::testing::AnyNumber()); EXPECT_CALL(testRagdoll, GetNumNodes()).WillRepeatedly(::testing::Return(1)); EXPECT_CALL(testRagdoll, IsSimulated()).WillRepeatedly(::testing::Return(true)); EXPECT_CALL(testRagdoll, GetPosition()).WillRepeatedly(::testing::Return(AZ::Vector3::CreateZero())); EXPECT_CALL(testRagdoll, GetOrientation()).WillRepeatedly(::testing::Return(AZ::Quaternion::CreateIdentity())); m_actorInstance->SetRagdoll(&testRagdoll); RagdollInstance* ragdollInstance = m_actorInstance->GetRagdollInstance(); const AZ::Outcome rootNodeIndex = ragdollInstance->GetRootRagdollNodeIndex(); EXPECT_TRUE(rootNodeIndex.IsSuccess()) << "No root node for the ragdoll found."; EXPECT_EQ(ragdollInstance->GetRagdollRootNode()->GetNameString(), ragdollRootNodeName) << "Wrong ragdoll root node."; // Create an anim graph with a ragdoll node. AZStd::unique_ptr motionSet = AZStd::make_unique("testMotionSet"); AZStd::unique_ptr animGraph = AnimGraphFactory::Create(); BlendTree* blendTree = aznew BlendTree(); animGraph->GetRootStateMachine()->AddChildNode(blendTree); animGraph->GetRootStateMachine()->SetEntryState(blendTree); BlendTreeRagdollNode* ragdollNode = aznew BlendTreeRagdollNode(); // Set the simulated joints. AZStd::vector simulatedJointNames; for (const std::string& jointName : param.m_simulatedJointNames) { simulatedJointNames.emplace_back(jointName.c_str()); } ragdollNode->SetSimulatedJointNames(simulatedJointNames); blendTree->AddChildNode(ragdollNode); EXPECT_TRUE(animGraph->InitAfterLoading()); AnimGraphInstance* animGraphInstance = AnimGraphInstance::Create(animGraph.get(), m_actorInstance, motionSet.get()); m_actorInstance->SetAnimGraphInstance(animGraphInstance); BlendTreeRagdollNode::UniqueData* uniqueData = static_cast(animGraphInstance->FindOrCreateUniqueObjectData(ragdollNode)); ASSERT_TRUE(uniqueData) << "Cannot find unique data for ragdoll node."; // Check if the ragdoll root node is simulated or if the ragdoll is partial. EXPECT_EQ(uniqueData->m_isRagdollRootNodeSimulated, param.m_ragdollRootNodeSimulated) << "Ragdoll root node should not be simulated."; } std::vector ragdollRootNodeIsSimulatedTestValues = { // Simulated root node, in skeleton hierarchy order { "Bip01__pelvis", true, { "Bip01__pelvis", "l_upLeg", "l_loLeg" }, { "Bip01__pelvis", "l_upLeg", "l_loLeg" } }, // Simulated root node, reordered { "Bip01__pelvis", true, { "l_upLeg", "l_loLeg", "Bip01__pelvis" }, { "l_upLeg", "Bip01__pelvis", "l_loLeg" } }, // Partial ragdoll, root node not simulated, reordered { "Bip01__pelvis", false, { "l_upLeg", "l_loLeg", "Bip01__pelvis" }, { "l_upLeg", "l_loLeg" } } }; INSTANTIATE_TEST_CASE_P(RagdollRootNodeIsSimulatedTests, RagdollRootNodeFixture, ::testing::ValuesIn(ragdollRootNodeIsSimulatedTestValues)); } // namespace EMotionFX