/* * 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 namespace EMotionFX { struct AnimGraphEventTestParm { int m_numStates; float m_transitionBlendTime; float m_conditionCountDownTime; float m_simulationTime; float m_expectedFps; float m_fpsVariance; }; class AnimGraphEventTestFixture : public AnimGraphFixture , public ::testing::WithParamInterface { public: void ConstructGraph() override { const AnimGraphEventTestParm& params = GetParam(); AnimGraphFixture::ConstructGraph(); /* +-------+ +---+ +---+ +---+ | Start |--->| A |--->| B |---> ... --->| N | +-------+ +---+ +---+ +---+ */ AnimGraphNode* stateStart = aznew AnimGraphMotionNode(); stateStart->SetName("Start"); m_rootStateMachine->AddChildNode(stateStart); m_rootStateMachine->SetEntryState(stateStart); const char startChar = 'A'; AnimGraphNode* prevState = stateStart; for (int i = 0; i < params.m_numStates; ++i) { AnimGraphNode* state = aznew AnimGraphMotionNode(); state->SetName(AZStd::string(1, startChar + i).c_str()); m_rootStateMachine->AddChildNode(state); AddTransitionWithTimeCondition(prevState, state, /*blendTime*/params.m_transitionBlendTime, /*countDownTime*/params.m_conditionCountDownTime); prevState = state; } } void SetUp() override { AnimGraphFixture::SetUp(); MotionSet::MotionEntry* motionEntry = AddMotionEntry("testMotion", 1.0); // Assign a motion to all our motion nodes const AZ::u32 numStates = m_rootStateMachine->GetNumChildNodes(); for (AZ::u32 i = 0; i < numStates; ++i) { AnimGraphMotionNode* motionNode = azdynamic_cast(m_rootStateMachine->GetChildNode(i)); if (motionNode) { motionNode->AddMotionId(motionEntry->GetId()); } } m_eventHandler = aznew AnimGraphEventHandlerCounter(); m_animGraphInstance->AddEventHandler(m_eventHandler); } void TearDown() override { m_animGraphInstance->RemoveEventHandler(m_eventHandler); delete m_eventHandler; AnimGraphFixture::TearDown(); } void SimulateTest(float simulationTime, float expectedFps, float fpsVariance) { Simulate(simulationTime, expectedFps, fpsVariance, /*preCallback*/[this](AnimGraphInstance* animGraphInstance) { }, /*postCallback*/[this](AnimGraphInstance* animGraphInstance) { const int numStates = GetParam().m_numStates; const int numExpectedEvents = numStates + 1 /*numDeferEnter*/; // +1 because we defer the entering to entrance state at the beginning of update. EXPECT_EQ(this->m_eventHandler->m_numStatesEntering, numExpectedEvents); EXPECT_EQ(this->m_eventHandler->m_numStatesEntered, numExpectedEvents); EXPECT_EQ(this->m_eventHandler->m_numStatesExited, numExpectedEvents); EXPECT_EQ(this->m_eventHandler->m_numStatesEnded, numExpectedEvents); EXPECT_EQ(this->m_eventHandler->m_numTransitionsStarted, numStates); EXPECT_EQ(this->m_eventHandler->m_numTransitionsEnded, numStates); }, /*preUpdateCallback*/[this](AnimGraphInstance*, float, float, int) {}, /*postUpdateCallback*/[this](AnimGraphInstance*, float, float, int) {}); const int numStates = GetParam().m_numStates; if (numStates > 1) { // Rewind state machine and check the event numbers again m_rootStateMachine->Rewind(m_animGraphInstance); GetEMotionFX().Update(1.0f/expectedFps); const int numExpectedEvents = numStates + 1 /*rewind*/ + 2 /*numDeferEnter*/; EXPECT_EQ(this->m_eventHandler->m_numStatesEntering, numExpectedEvents); EXPECT_EQ(this->m_eventHandler->m_numStatesEntered, numExpectedEvents); EXPECT_EQ(this->m_eventHandler->m_numStatesExited, numExpectedEvents); EXPECT_EQ(this->m_eventHandler->m_numStatesEnded, numExpectedEvents); EXPECT_EQ(this->m_eventHandler->m_numTransitionsStarted, numStates); EXPECT_EQ(this->m_eventHandler->m_numTransitionsEnded, numStates); } } AnimGraphEventHandlerCounter* m_eventHandler = nullptr; }; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// std::vector animGraphEventTestData { // Stable framerate { 1,/*m_numStates*/ 1.0f,/*m_transitionBlendTime*/ 1.0f,/*m_conditionCountDownTime*/ 20.0f,/*m_simulationTime*/ 60.0f,/*m_expectedFps*/ 0.0f/*m_fpsVariance*/ }, { 2, 1.0f, 1.0f, 20.0f, 60.0f, 0.0f }, { 3, 1.0f, 1.0f, 60.0f, 60.0f, 0.0f }, { 8, 0.1f, 0.1f, 60.0f, 60.0f, 0.0f }, { 16, 1.0f, 1.0f, 60.0f, 60.0f, 0.0f }, // Unstable framerates { 16, 1.0f, 1.0f, 60.0f, 30.0f, 1.0f }, { 16, 1.0f, 1.0f, 60.0f, 10.0f, 1.0f } }; TEST_P(AnimGraphEventTestFixture, TestAnimGraphEvents) { const AnimGraphEventTestParm& params = GetParam(); SimulateTest(params.m_simulationTime, params.m_expectedFps, params.m_fpsVariance); } INSTANTIATE_TEST_CASE_P(TestAnimGraphEvents, AnimGraphEventTestFixture, ::testing::ValuesIn(animGraphEventTestData)); } // EMotionFX