From ed4c6fc1e892eaaa44b261ab0be10c1734e40747 Mon Sep 17 00:00:00 2001 From: Oleksii Date: Mon, 11 Mar 2019 13:24:30 +0200 Subject: [PATCH 1/6] Add warning when shader macro not found Shader gen material macros is ignored if m_pShaderCommonGlobalFlag has 64 values and new values is skipped. Issue reproduces when you create new shaders and introduce new ShaderGen flags. --- dev/Code/CryEngine/RenderDll/Common/Shaders/ShaderParse.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/dev/Code/CryEngine/RenderDll/Common/Shaders/ShaderParse.cpp b/dev/Code/CryEngine/RenderDll/Common/Shaders/ShaderParse.cpp index a1199db0fc..74466aa9b3 100644 --- a/dev/Code/CryEngine/RenderDll/Common/Shaders/ShaderParse.cpp +++ b/dev/Code/CryEngine/RenderDll/Common/Shaders/ShaderParse.cpp @@ -737,6 +737,7 @@ uint64 CShaderMan::mfGetShaderGlobalMaskGenFromString(const char* szShaderGen) { nMaskGen |= pIter->second; } + AZ_Warning("Shaders", pIter != pEnd, "Failed to find property \"%s\". Shader macro is ignored.", pCurrFlag); } return nMaskGen; From 934d5b12ae15bfe9a02b21db9d496da1be954c75 Mon Sep 17 00:00:00 2001 From: Oleksii Leleka Date: Mon, 11 Mar 2019 14:30:18 +0200 Subject: [PATCH 2/6] Revert to LY 1.17 This reverts commit ed4c6fc1e892eaaa44b261ab0be10c1734e40747. --- dev/Code/CryEngine/RenderDll/Common/Shaders/ShaderParse.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/dev/Code/CryEngine/RenderDll/Common/Shaders/ShaderParse.cpp b/dev/Code/CryEngine/RenderDll/Common/Shaders/ShaderParse.cpp index 74466aa9b3..a1199db0fc 100644 --- a/dev/Code/CryEngine/RenderDll/Common/Shaders/ShaderParse.cpp +++ b/dev/Code/CryEngine/RenderDll/Common/Shaders/ShaderParse.cpp @@ -737,7 +737,6 @@ uint64 CShaderMan::mfGetShaderGlobalMaskGenFromString(const char* szShaderGen) { nMaskGen |= pIter->second; } - AZ_Warning("Shaders", pIter != pEnd, "Failed to find property \"%s\". Shader macro is ignored.", pCurrFlag); } return nMaskGen; From cc4382280b645ec91328e64afc9e76168b28b3ff Mon Sep 17 00:00:00 2001 From: Smbat Makiyan Date: Wed, 15 Jan 2020 13:03:28 +0200 Subject: [PATCH 3/6] AudioEngine Gem memroy allocation fixes --- .../Code/Source/Engine/AudioInput/AudioInputFile.cpp | 4 ++-- .../Code/Source/Engine/AudioInput/AudioInputStream.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dev/Gems/AudioEngineWwise/Code/Source/Engine/AudioInput/AudioInputFile.cpp b/dev/Gems/AudioEngineWwise/Code/Source/Engine/AudioInput/AudioInputFile.cpp index 2de197aa81..b9198860e8 100644 --- a/dev/Gems/AudioEngineWwise/Code/Source/Engine/AudioInput/AudioInputFile.cpp +++ b/dev/Gems/AudioEngineWwise/Code/Source/Engine/AudioInput/AudioInputFile.cpp @@ -87,7 +87,7 @@ namespace Audio if (IsOk()) { // Allocate a new buffer to hold the data... - m_dataPtr = new AZ::u8[m_dataSize]; + m_dataPtr = static_cast(azmalloc(m_dataSize)); // Read file into internal buffer... size_t bytesRead = fileStream.Read(m_dataSize, m_dataPtr); @@ -110,7 +110,7 @@ namespace Audio { if (m_dataPtr) { - delete [] m_dataPtr; + azfree(m_dataPtr); m_dataPtr = nullptr; } m_dataSize = 0; diff --git a/dev/Gems/AudioEngineWwise/Code/Source/Engine/AudioInput/AudioInputStream.cpp b/dev/Gems/AudioEngineWwise/Code/Source/Engine/AudioInput/AudioInputStream.cpp index 98618716c1..6259b5b952 100644 --- a/dev/Gems/AudioEngineWwise/Code/Source/Engine/AudioInput/AudioInputStream.cpp +++ b/dev/Gems/AudioEngineWwise/Code/Source/Engine/AudioInput/AudioInputStream.cpp @@ -34,11 +34,11 @@ namespace Audio if (m_config.m_sampleType == AudioInputSampleType::Float && m_config.m_bitsPerSample == 32) { - m_buffer.reset(new RingBuffer(numSamples)); + m_buffer.reset(aznew RingBuffer(numSamples)); } else if (m_config.m_sampleType == AudioInputSampleType::Int && m_config.m_bitsPerSample == 16) { - m_buffer.reset(new RingBuffer(numSamples)); + m_buffer.reset(aznew RingBuffer(numSamples)); } else { From 3e2d3c3e524c0e5d6e6a5555259950d329453356 Mon Sep 17 00:00:00 2001 From: Smbat Makiyan Date: Wed, 15 Jan 2020 13:13:30 +0200 Subject: [PATCH 4/6] Revert "AudioEngine Gem memroy allocation fixes" This reverts commit cc4382280b645ec91328e64afc9e76168b28b3ff. --- .../Code/Source/Engine/AudioInput/AudioInputFile.cpp | 4 ++-- .../Code/Source/Engine/AudioInput/AudioInputStream.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dev/Gems/AudioEngineWwise/Code/Source/Engine/AudioInput/AudioInputFile.cpp b/dev/Gems/AudioEngineWwise/Code/Source/Engine/AudioInput/AudioInputFile.cpp index b9198860e8..2de197aa81 100644 --- a/dev/Gems/AudioEngineWwise/Code/Source/Engine/AudioInput/AudioInputFile.cpp +++ b/dev/Gems/AudioEngineWwise/Code/Source/Engine/AudioInput/AudioInputFile.cpp @@ -87,7 +87,7 @@ namespace Audio if (IsOk()) { // Allocate a new buffer to hold the data... - m_dataPtr = static_cast(azmalloc(m_dataSize)); + m_dataPtr = new AZ::u8[m_dataSize]; // Read file into internal buffer... size_t bytesRead = fileStream.Read(m_dataSize, m_dataPtr); @@ -110,7 +110,7 @@ namespace Audio { if (m_dataPtr) { - azfree(m_dataPtr); + delete [] m_dataPtr; m_dataPtr = nullptr; } m_dataSize = 0; diff --git a/dev/Gems/AudioEngineWwise/Code/Source/Engine/AudioInput/AudioInputStream.cpp b/dev/Gems/AudioEngineWwise/Code/Source/Engine/AudioInput/AudioInputStream.cpp index 6259b5b952..98618716c1 100644 --- a/dev/Gems/AudioEngineWwise/Code/Source/Engine/AudioInput/AudioInputStream.cpp +++ b/dev/Gems/AudioEngineWwise/Code/Source/Engine/AudioInput/AudioInputStream.cpp @@ -34,11 +34,11 @@ namespace Audio if (m_config.m_sampleType == AudioInputSampleType::Float && m_config.m_bitsPerSample == 32) { - m_buffer.reset(aznew RingBuffer(numSamples)); + m_buffer.reset(new RingBuffer(numSamples)); } else if (m_config.m_sampleType == AudioInputSampleType::Int && m_config.m_bitsPerSample == 16) { - m_buffer.reset(aznew RingBuffer(numSamples)); + m_buffer.reset(new RingBuffer(numSamples)); } else { From 9dee8aecf91c1ff54569b434707ca0b363368d2a Mon Sep 17 00:00:00 2001 From: "FRAGLAB\\Andrey.Mas" Date: Thu, 5 Mar 2020 16:55:01 +0200 Subject: [PATCH 5/6] Engine crash on level unload --- dev/Code/CryEngine/Cry3DEngine/ParticleList.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/dev/Code/CryEngine/Cry3DEngine/ParticleList.h b/dev/Code/CryEngine/Cry3DEngine/ParticleList.h index b95093396d..8a2222818d 100644 --- a/dev/Code/CryEngine/Cry3DEngine/ParticleList.h +++ b/dev/Code/CryEngine/Cry3DEngine/ParticleList.h @@ -213,11 +213,9 @@ class ParticleList void clear() { // Destroy all elements, in reverse order - for (Node* p = m_pTail; p != NULL; ) + while (m_pTail) { - Node* pPrev = p->pPrev; - destroy(p); - p = pPrev; + erase(m_pTail); } reset(); } From cedff2b1ddc731cd211717d8f7bb3bd357b2ad12 Mon Sep 17 00:00:00 2001 From: Dmytro Kovalchuk Date: Mon, 25 Jan 2021 13:26:50 +0200 Subject: [PATCH 6/6] Added EmotionFX node for playing motion parameterized by string --- .../Source/AnimGraphObjectFactory.cpp | 3 + .../Source/AnimGraphParamMotionNode.cpp | 810 ++++++++++++++++++ .../Source/AnimGraphParamMotionNode.h | 151 ++++ .../Code/EMotionFX/emotionfx.waf_files | 2 + 4 files changed, 966 insertions(+) create mode 100644 dev/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphParamMotionNode.cpp create mode 100644 dev/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphParamMotionNode.h diff --git a/dev/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphObjectFactory.cpp b/dev/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphObjectFactory.cpp index 07fe98f317..f560e702bf 100644 --- a/dev/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphObjectFactory.cpp +++ b/dev/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphObjectFactory.cpp @@ -72,6 +72,7 @@ #include "AnimGraphBindPoseNode.h" #include "AnimGraphMotionNode.h" +#include "AnimGraphParamMotionNode.h" #include "AnimGraphStateMachine.h" #include "AnimGraphExitNode.h" #include "AnimGraphEntryNode.h" @@ -126,6 +127,7 @@ namespace EMotionFX AnimGraphEntryNode::Reflect(context); AnimGraphMotionNode::Reflect(context); + AnimGraphParamMotionNode::Reflect(context); BlendSpaceNode::Reflect(context); BlendSpace1DNode::Reflect(context); BlendSpace2DNode::Reflect(context); @@ -203,6 +205,7 @@ namespace EMotionFX azrtti_typeid(), azrtti_typeid(), azrtti_typeid(), + azrtti_typeid(), azrtti_typeid(), azrtti_typeid(), azrtti_typeid(), diff --git a/dev/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphParamMotionNode.cpp b/dev/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphParamMotionNode.cpp new file mode 100644 index 0000000000..8f5533722f --- /dev/null +++ b/dev/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphParamMotionNode.cpp @@ -0,0 +1,810 @@ + +#include +#include +#include "EMotionFXConfig.h" +#include "AnimGraphParamMotionNode.h" +#include "MotionInstance.h" +#include "ActorInstance.h" +#include "EventManager.h" +#include "EventHandler.h" +#include "AnimGraphInstance.h" +#include "MotionSet.h" +#include "SkeletalSubMotion.h" +#include "SkeletalMotion.h" +#include "AnimGraphManager.h" +#include "MotionManager.h" +#include "MotionInstancePool.h" +#include "EMotionFXManager.h" +#include "MotionEventTable.h" +#include "AnimGraph.h" + +#include "Parameter/StringParameter.h" + + +namespace EMotionFX +{ + static AZStd::string EMPTY_STRING; + + AZ_CLASS_ALLOCATOR_IMPL(AnimGraphParamMotionNode, AnimGraphAllocator, 0) + AZ_CLASS_ALLOCATOR_IMPL(AnimGraphParamMotionNode::UniqueData, AnimGraphObjectUniqueDataAllocator, 0) + + AnimGraphParamMotionNode::AnimGraphParamMotionNode() + : AnimGraphNode() + , m_playSpeed(1.0f) + , m_loop(true) + , m_retarget(true) + , m_reverse(false) + , m_emitEvents(true) + , m_mirrorMotion(false) + , m_motionExtraction(true) + , m_rewindOnZeroWeight(false) + , m_inPlace(false) + , m_parameterIndex(AZ::Failure()) + { + // setup the input ports + InitInputPorts(2); + SetupInputPortAsNumber("Play Speed", INPUTPORT_PLAYSPEED, PORTID_INPUT_PLAYSPEED); + SetupInputPortAsNumber("In Place", INPUTPORT_INPLACE, PORTID_INPUT_INPLACE); + + // setup the output ports + InitOutputPorts(2); + SetupOutputPortAsPose("Output Pose", OUTPUTPORT_POSE, PORTID_OUTPUT_POSE); + SetupOutputPortAsMotionInstance("Motion", OUTPUTPORT_MOTION, PORTID_OUTPUT_MOTION); + } + + + AnimGraphParamMotionNode::~AnimGraphParamMotionNode() + { + } + + + void AnimGraphParamMotionNode::Reinit() + { + OnParamChanged(); + AnimGraphNode::Reinit(); + } + + + bool AnimGraphParamMotionNode::InitAfterLoading(AnimGraph* animGraph) + { + if (!AnimGraphNode::InitAfterLoading(animGraph)) + { + return false; + } + + InitInternalAttributesForAllInstances(); + + Reinit(); + return true; + } + + + const char* AnimGraphParamMotionNode::GetPaletteName() const + { + return "ParameterMotion"; + } + + + AnimGraphObject::ECategory AnimGraphParamMotionNode::GetPaletteCategory() const + { + return AnimGraphObject::CATEGORY_SOURCES; + } + + + bool AnimGraphParamMotionNode::GetIsInPlace(AnimGraphInstance* animGraphInstance) const + { + EMotionFX::BlendTreeConnection* inPlaceConnection = GetInputPort(INPUTPORT_INPLACE).mConnection; + if (inPlaceConnection) + { + return GetInputNumberAsBool(animGraphInstance, INPUTPORT_INPLACE); + } + + return m_inPlace; + } + + void AnimGraphParamMotionNode::PostUpdate(AnimGraphInstance* animGraphInstance, float timePassedInSeconds) + { + if (mDisabled) + { + UniqueData* uniqueData = static_cast(FindOrCreateUniqueNodeData(animGraphInstance)); + RequestRefDatas(animGraphInstance); + AnimGraphRefCountedData* data = uniqueData->GetRefCountedData(); + data->ClearEventBuffer(); + data->ZeroTrajectoryDelta(); + return; + } + + // update the input nodes + EMotionFX::BlendTreeConnection* playSpeedConnection = GetInputPort(INPUTPORT_PLAYSPEED).mConnection; + if (playSpeedConnection && mDisabled == false) + { + playSpeedConnection->GetSourceNode()->PerformPostUpdate(animGraphInstance, timePassedInSeconds); + } + + // clear the event buffer + UniqueData* uniqueData = static_cast(FindOrCreateUniqueNodeData(animGraphInstance)); + RequestRefDatas(animGraphInstance); + AnimGraphRefCountedData* data = uniqueData->GetRefCountedData(); + data->ClearEventBuffer(); + data->ZeroTrajectoryDelta(); + + // trigger the motion update + MotionInstance* motionInstance = uniqueData->mMotionInstance; + if (motionInstance && !animGraphInstance->GetIsResynced(mObjectIndex)) + { + // update the time values and extract events into the event buffer + motionInstance->SetWeight(uniqueData->GetLocalWeight()); + motionInstance->UpdateByTimeValues(uniqueData->GetPreSyncTime(), uniqueData->GetCurrentPlayTime(), &data->GetEventBuffer()); + + // mark all events to be emitted from this node + data->GetEventBuffer().UpdateEmitters(this); + } + else + { + return; + } + + // make sure the motion instance is ready for sampling + if (motionInstance->GetIsReadyForSampling() == false) + { + motionInstance->InitForSampling(); + } + + // extract current delta + Transform trajectoryDelta; + const bool isMirrored = motionInstance->GetMirrorMotion(); + motionInstance->ExtractMotion(trajectoryDelta); + data->SetTrajectoryDelta(trajectoryDelta); + + // extract mirrored version of the current delta + motionInstance->SetMirrorMotion(!isMirrored); + motionInstance->ExtractMotion(trajectoryDelta); + data->SetTrajectoryDeltaMirrored(trajectoryDelta); + + // restore current mirrored flag + motionInstance->SetMirrorMotion(isMirrored); + } + + const AZStd::string& AnimGraphParamMotionNode::GetParamValue(AnimGraphInstance* animGraphInstance) const + { + if (m_parameterIndex.IsSuccess()) + { + const ValueParameter* valueParameter = mAnimGraph->FindValueParameter(m_parameterIndex.GetValue()); + + const AZ::TypeId parameterType = azrtti_typeid(valueParameter); + if (parameterType == azrtti_typeid()) + { + const AZStd::string& paramValue = static_cast(animGraphInstance->GetParameterValue(static_cast(m_parameterIndex.GetValue())))->GetValue(); + return paramValue; + } + } + return EMPTY_STRING; + } + + // top down update + void AnimGraphParamMotionNode::TopDownUpdate(AnimGraphInstance* animGraphInstance, float timePassedInSeconds) + { + UniqueData* uniqueData = static_cast(FindOrCreateUniqueNodeData(animGraphInstance)); + + // rewind when the weight reaches 0 when we want to + if (!m_loop) + { + if (uniqueData->mMotionInstance && uniqueData->GetLocalWeight() < MCore::Math::epsilon && m_rewindOnZeroWeight) + { + uniqueData->mMotionInstance->SetCurrentTime(0.0f); + uniqueData->SetCurrentPlayTime(0.0f); + uniqueData->SetPreSyncTime(0.0f); + } + } + + // sync all input nodes + HierarchicalSyncAllInputNodes(animGraphInstance, uniqueData); + + // top down update all incoming connections + for (BlendTreeConnection* connection : mConnections) + { + connection->GetSourceNode()->PerformTopDownUpdate(animGraphInstance, timePassedInSeconds); + } + } + + + // update the motion instance + void AnimGraphParamMotionNode::Update(AnimGraphInstance* animGraphInstance, float timePassedInSeconds) + { + // update the input nodes + EMotionFX::BlendTreeConnection* playSpeedConnection = GetInputPort(INPUTPORT_PLAYSPEED).mConnection; + if (playSpeedConnection && mDisabled == false) + { + UpdateIncomingNode(animGraphInstance, playSpeedConnection->GetSourceNode(), timePassedInSeconds); + } + + if (!mDisabled) + { + UpdateIncomingNode(animGraphInstance, GetInputNode(INPUTPORT_INPLACE), timePassedInSeconds); + } + + // update the motion instance (current time etc) + UniqueData* uniqueData = static_cast(FindOrCreateUniqueNodeData(animGraphInstance)); + MotionInstance* motionInstance = uniqueData->mMotionInstance; + + const AZStd::string& paramValue = GetParamValue(animGraphInstance); + if (motionInstance == nullptr || uniqueData->mMotionName != paramValue) + { + uniqueData->mReload = true; + } + + if (motionInstance == nullptr || mDisabled) + { + if (GetEMotionFX().GetIsInEditorMode()) + { + if (mDisabled == false) + { + if (motionInstance == nullptr) + { + SetHasError(uniqueData, true); + } + } + } + + uniqueData->Clear(); + return; + } + + if (GetEMotionFX().GetIsInEditorMode()) + { + SetHasError(uniqueData, false); + } + + // if there is a node connected to the speed input port, read that value and use it as internal speed if not use the playspeed property + const float customSpeed = ExtractCustomPlaySpeed(animGraphInstance); + + // all partial animations were broken: they were played only once + if (!m_loop) + { + if (uniqueData->GetLocalWeight() < MCore::Math::epsilon && m_rewindOnZeroWeight) + { + motionInstance->SetCurrentTime(0.0f); + uniqueData->SetCurrentPlayTime(0.0f); + //uniqueData->SetPreSyncTime(0.0f); + } + } + + // set the internal speed and play speeds etc + motionInstance->SetPlaySpeed(uniqueData->GetPlaySpeed()); + uniqueData->SetPlaySpeed(customSpeed); + uniqueData->SetPreSyncTime(motionInstance->GetCurrentTime()); + + // Make sure we use the correct play properties. + motionInstance->SetPlayMode(m_playInfo.mPlayMode); + motionInstance->SetRetargetingEnabled(m_playInfo.mRetarget && animGraphInstance->GetRetargetingEnabled()); + motionInstance->SetMotionEventsEnabled(m_playInfo.mEnableMotionEvents); + motionInstance->SetMirrorMotion(m_playInfo.mMirrorMotion); + motionInstance->SetEventWeightThreshold(m_playInfo.mEventWeightThreshold); + motionInstance->SetMaxLoops(m_playInfo.mNumLoops); + motionInstance->SetMotionExtractionEnabled(m_playInfo.mMotionExtractionEnabled); + motionInstance->SetIsInPlace(GetIsInPlace(animGraphInstance)); + motionInstance->SetFreezeAtLastFrame(m_playInfo.mFreezeAtLastFrame); + + if (!animGraphInstance->GetIsObjectFlagEnabled(mObjectIndex, AnimGraphInstance::OBJECTFLAGS_SYNCED) || animGraphInstance->GetIsObjectFlagEnabled(mObjectIndex, AnimGraphInstance::OBJECTFLAGS_IS_SYNCLEADER)) + { + // See where we would end up when we would forward in time. + const MotionInstance::PlayStateOut newPlayState = motionInstance->CalcPlayStateAfterUpdate(timePassedInSeconds); + + // set the current time to the new calculated time + uniqueData->ClearInheritFlags(); + uniqueData->SetCurrentPlayTime(newPlayState.m_currentTime); + motionInstance->SetLastCurrentTime(motionInstance->GetCurrentTime()); + motionInstance->SetCurrentTime(newPlayState.m_currentTime, false); + } + + uniqueData->SetDuration(motionInstance->GetDuration()); + + // make sure the motion is not paused + motionInstance->SetPause(false); + + uniqueData->SetSyncTrack(motionInstance->GetMotion()->GetEventTable()->GetSyncTrack()); + uniqueData->SetIsMirrorMotion(motionInstance->GetMirrorMotion()); + + // update some flags + if (motionInstance->GetPlayMode() == PLAYMODE_BACKWARD) + { + uniqueData->SetBackwardFlag(); + } + } + + + void AnimGraphParamMotionNode::UpdatePlayBackInfo(AnimGraphInstance* animGraphInstance) + { + m_playInfo.mPlayMode = (m_reverse) ? PLAYMODE_BACKWARD : PLAYMODE_FORWARD; + m_playInfo.mNumLoops = (m_loop) ? EMFX_LOOPFOREVER : 1; + m_playInfo.mFreezeAtLastFrame = true; + m_playInfo.mEnableMotionEvents = m_emitEvents; + m_playInfo.mMirrorMotion = m_mirrorMotion; + m_playInfo.mPlaySpeed = ExtractCustomPlaySpeed(animGraphInstance); + m_playInfo.mMotionExtractionEnabled = m_motionExtraction; + m_playInfo.mRetarget = m_retarget; + m_playInfo.mInPlace = GetIsInPlace(animGraphInstance); + } + + + // create the motion instance + MotionInstance* AnimGraphParamMotionNode::CreateMotionInstance(ActorInstance* actorInstance, UniqueData* uniqueData) + { + AnimGraphInstance* animGraphInstance = uniqueData->GetAnimGraphInstance(); + + // update the last motion ID + UpdatePlayBackInfo(animGraphInstance); + + // try to find the motion to use for this actor instance in this blend node + Motion* motion = nullptr; + PlayBackInfo playInfo = m_playInfo; + + // reset playback properties + const float curLocalWeight = uniqueData->GetLocalWeight(); + const float curGlobalWeight = uniqueData->GetGlobalWeight(); + uniqueData->Clear(); + + float currentTime = 0.0f; + // remove the motion instance if it already exists + if (uniqueData->mMotionInstance && uniqueData->mReload) + { + currentTime = uniqueData->mMotionInstance->GetCurrentTime(); + GetMotionInstancePool().Free(uniqueData->mMotionInstance); + uniqueData->mMotionInstance = nullptr; + uniqueData->mMotionSetID = MCORE_INVALIDINDEX32; + uniqueData->mMotionName = ""; + uniqueData->mReload = false; + } + + // get the motion set + MotionSet* motionSet = animGraphInstance->GetMotionSet(); + if (!motionSet) + { + if (GetEMotionFX().GetIsInEditorMode()) + { + SetHasError(uniqueData, true); + } + return nullptr; + } + + // get the motion from the motion set, load it on demand and make sure the motion loaded successfully + const AZStd::string& requestedMotionName = GetParamValue(animGraphInstance); + motion = motionSet->RecursiveFindMotionById(requestedMotionName); + + if (!motion) + { + if (GetEMotionFX().GetIsInEditorMode()) + { + SetHasError(uniqueData, true); + } + return nullptr; + } + + uniqueData->mMotionSetID = motionSet->GetID(); + + // create the motion instance + MotionInstance* motionInstance = GetMotionInstancePool().RequestNew(motion, actorInstance, playInfo.mStartNodeIndex); + motionInstance->InitFromPlayBackInfo(playInfo, true); + motionInstance->SetRetargetingEnabled(animGraphInstance->GetRetargetingEnabled() && playInfo.mRetarget); + + uniqueData->SetSyncTrack(motionInstance->GetMotion()->GetEventTable()->GetSyncTrack()); + uniqueData->SetIsMirrorMotion(motionInstance->GetMirrorMotion()); + + if (!motionInstance->GetIsReadyForSampling() && animGraphInstance->GetInitSettings().mPreInitMotionInstances) + { + motionInstance->InitForSampling(); + } + + // make sure it is not in pause mode + motionInstance->UnPause(); + motionInstance->SetIsActive(true); + motionInstance->SetWeight(1.0f, 0.0f); + motionInstance->SetCurrentTime(currentTime); + + // update play info + uniqueData->mMotionInstance = motionInstance; + uniqueData->mMotionName = requestedMotionName; + uniqueData->SetDuration(motionInstance->GetDuration()); + const float curPlayTime = motionInstance->GetCurrentTime(); + uniqueData->SetCurrentPlayTime(curPlayTime); + uniqueData->SetPreSyncTime(curPlayTime); + uniqueData->SetGlobalWeight(curGlobalWeight); + uniqueData->SetLocalWeight(curLocalWeight); + + // trigger an event + GetEventManager().OnStartMotionInstance(motionInstance, &playInfo); + return motionInstance; + } + + + // the main process method of the final node + void AnimGraphParamMotionNode::Output(AnimGraphInstance* animGraphInstance) + { + // if this motion is disabled, output the bind pose + if (mDisabled) + { + // request poses to use from the pool, so that all output pose ports have a valid pose to output to we reuse them using a pool system to save memory + RequestPoses(animGraphInstance); + AnimGraphPose* outputPose = GetOutputPose(animGraphInstance, OUTPUTPORT_POSE)->GetValue(); + ActorInstance* actorInstance = animGraphInstance->GetActorInstance(); + outputPose->InitFromBindPose(actorInstance); + return; + } + + // output the playspeed node + EMotionFX::BlendTreeConnection* playSpeedConnection = GetInputPort(INPUTPORT_PLAYSPEED).mConnection; + if (playSpeedConnection) + { + OutputIncomingNode(animGraphInstance, playSpeedConnection->GetSourceNode()); + } + + // create and register the motion instance when this is the first time its being when it hasn't been registered yet + ActorInstance* actorInstance = animGraphInstance->GetActorInstance(); + MotionInstance* motionInstance = nullptr; + UniqueData* uniqueData = static_cast(FindOrCreateUniqueNodeData(animGraphInstance)); + if (uniqueData->mReload) + { + motionInstance = CreateMotionInstance(actorInstance, uniqueData); + uniqueData->mReload = false; + } + else + { + motionInstance = uniqueData->mMotionInstance; + } + + // update the motion instance output port + GetOutputMotionInstance(animGraphInstance, OUTPUTPORT_MOTION)->SetValue(motionInstance); + + if (motionInstance == nullptr) + { + // request poses to use from the pool, so that all output pose ports have a valid pose to output to we reuse them using a pool system to save memory + RequestPoses(animGraphInstance); + AnimGraphPose* outputPose = GetOutputPose(animGraphInstance, OUTPUTPORT_POSE)->GetValue(); + outputPose->InitFromBindPose(actorInstance); + + if (GetEMotionFX().GetIsInEditorMode()) + { + SetHasError(uniqueData, true); + } + return; + } + + if (GetEMotionFX().GetIsInEditorMode()) + { + SetHasError(uniqueData, false); + } + + // make sure the motion instance is ready for sampling + if (motionInstance->GetIsReadyForSampling() == false) + { + motionInstance->InitForSampling(); + } + + EMotionFX::BlendTreeConnection* inPlaceConnection = GetInputPort(INPUTPORT_INPLACE).mConnection; + if (inPlaceConnection) + { + OutputIncomingNode(animGraphInstance, inPlaceConnection->GetSourceNode()); + } + + // request poses to use from the pool, so that all output pose ports have a valid pose to output to we reuse them using a pool system to save memory + RequestPoses(animGraphInstance); + AnimGraphPose* outputPose = GetOutputPose(animGraphInstance, OUTPUTPORT_POSE)->GetValue(); + Pose& outputTransformPose = outputPose->GetPose(); + + // fill the output with the bind pose + outputPose->InitFromBindPose(actorInstance); // TODO: is this really needed? + + // we use as input pose the same as the output, as this blend tree node takes no input + motionInstance->GetMotion()->Update(&outputTransformPose, &outputTransformPose, motionInstance); + + // compensate for motion extraction + // we already moved our actor instance's position and rotation at this point + // so we have to cancel/compensate this delta offset from the motion extraction node, so that we don't double-transform + // basically this will keep the motion in-place rather than moving it away from the origin + if (motionInstance->GetMotionExtractionEnabled() && actorInstance->GetMotionExtractionEnabled() && !motionInstance->GetMotion()->GetIsAdditive()) + { + outputTransformPose.CompensateForMotionExtractionDirect(motionInstance->GetMotion()->GetMotionExtractionFlags()); + } + + // visualize it + if (GetEMotionFX().GetIsInEditorMode() && GetCanVisualize(animGraphInstance)) + { + actorInstance->DrawSkeleton(outputPose->GetPose(), mVisualizeColor); + } + } + + + // get the motion instance for a given anim graph instance + MotionInstance* AnimGraphParamMotionNode::FindMotionInstance(AnimGraphInstance* animGraphInstance) const + { + UniqueData* uniqueData = static_cast(animGraphInstance->FindOrCreateUniqueObjectData(this)); + return uniqueData->mMotionInstance; + } + + + // set the current play time + void AnimGraphParamMotionNode::SetCurrentPlayTime(AnimGraphInstance* animGraphInstance, float timeInSeconds) + { + UniqueData* uniqueData = static_cast(animGraphInstance->FindOrCreateUniqueObjectData(this)); + uniqueData->SetCurrentPlayTime(timeInSeconds); + if (uniqueData->mMotionInstance) + { + uniqueData->mMotionInstance->SetCurrentTime(timeInSeconds); + } + } + + + // unique data constructor + AnimGraphParamMotionNode::UniqueData::UniqueData(AnimGraphNode* node, AnimGraphInstance* animGraphInstance) + : AnimGraphNodeData(node, animGraphInstance) + { + } + + AnimGraphParamMotionNode::UniqueData::~UniqueData() + { + GetMotionInstancePool().Free(mMotionInstance); + } + + void AnimGraphParamMotionNode::UniqueData::Reset() + { + // stop and delete the motion instance + if (mMotionInstance) + { + mMotionInstance->Stop(0.0f); + GetMotionInstancePool().Free(mMotionInstance); + } + + // reset the unique data + mMotionSetID = MCORE_INVALIDINDEX32; + mMotionInstance = nullptr; + mReload = true; + mPlaySpeed = 1.0f; + mCurrentTime = 0.0f; + mDuration = 0.0f; + SetSyncTrack(nullptr); + + Invalidate(); + } + + void AnimGraphParamMotionNode::UniqueData::Update() + { + AnimGraphParamMotionNode* motionNode = azdynamic_cast(mObject); + AZ_Assert(motionNode, "Unique data linked to incorrect node type."); + + AnimGraphInstance* animGraphInstance = GetAnimGraphInstance(); + + mReload = true; + motionNode->CreateMotionInstance(animGraphInstance->GetActorInstance(), this); + + // get the id of the currently used the motion set + MotionSet* motionSet = animGraphInstance->GetMotionSet(); + uint32 motionSetID = MCORE_INVALIDINDEX32; + if (motionSet) + { + motionSetID = motionSet->GetID(); + } + + // update the internally stored playback info + motionNode->UpdatePlayBackInfo(animGraphInstance); + + // update play info + if (mMotionInstance) + { + MotionInstance* motionInstance = mMotionInstance; + const float currentTime = motionInstance->GetCurrentTime(); + SetDuration(motionInstance->GetDuration()); + SetCurrentPlayTime(currentTime); + SetPreSyncTime(currentTime); + SetSyncTrack(motionInstance->GetMotion()->GetEventTable()->GetSyncTrack()); + SetIsMirrorMotion(motionInstance->GetMirrorMotion()); + } + } + + // this function will get called to rewind motion nodes as well as states etc. to reset several settings when a state gets exited + void AnimGraphParamMotionNode::Rewind(AnimGraphInstance* animGraphInstance) + { + UniqueData* uniqueData = static_cast(animGraphInstance->GetUniqueObjectData(mObjectIndex)); + + // rewind is not necessary if unique data is not created yet + if (!uniqueData) + { + return; + } + + // find the motion instance for the given anim graph and return directly in case it is invalid + MotionInstance* motionInstance = uniqueData->mMotionInstance; + if (motionInstance == nullptr) + { + return; + } + + // reset several settings to rewind the motion instance + motionInstance->ResetTimes(); + motionInstance->SetIsFrozen(false); + SetSyncIndex(animGraphInstance, MCORE_INVALIDINDEX32); + uniqueData->SetCurrentPlayTime(motionInstance->GetCurrentTime()); + uniqueData->SetDuration(motionInstance->GetDuration()); + uniqueData->SetPreSyncTime(uniqueData->GetCurrentPlayTime()); + //uniqueData->SetPlaySpeed( uniqueData->GetPlaySpeed() ); + } + + // get the speed from the connection if there is one connected, if not use the node's playspeed + float AnimGraphParamMotionNode::ExtractCustomPlaySpeed(AnimGraphInstance* animGraphInstance) const + { + EMotionFX::BlendTreeConnection* playSpeedConnection = GetInputPort(INPUTPORT_PLAYSPEED).mConnection; + + // if there is a node connected to the speed input port, read that value and use it as internal speed + float customSpeed; + if (playSpeedConnection) + { + customSpeed = MCore::Max(0.0f, GetInputNumberAsFloat(animGraphInstance, INPUTPORT_PLAYSPEED)); + } + else + { + customSpeed = m_playSpeed; // otherwise use the node's playspeed + } + + return customSpeed; + } + + void AnimGraphParamMotionNode::ReloadAndInvalidateUniqueDatas() + { + if (!mAnimGraph) + { + return; + } + + const size_t numAnimGraphInstances = mAnimGraph->GetNumAnimGraphInstances(); + for (size_t i = 0; i < numAnimGraphInstances; ++i) + { + AnimGraphInstance* animGraphInstance = mAnimGraph->GetAnimGraphInstance(i); + UniqueData* uniqueData = static_cast(animGraphInstance->GetUniqueObjectData(mObjectIndex)); + if (uniqueData) + { + uniqueData->mReload = true; + uniqueData->Invalidate(); + } + } + } + + void AnimGraphParamMotionNode::OnActorMotionExtractionNodeChanged() + { + ReloadAndInvalidateUniqueDatas(); + } + + void AnimGraphParamMotionNode::RecursiveOnChangeMotionSet(AnimGraphInstance* animGraphInstance, MotionSet* newMotionSet) + { + AnimGraphNode::RecursiveOnChangeMotionSet(animGraphInstance, newMotionSet); + UniqueData* uniqueData = static_cast(animGraphInstance->GetUniqueObjectData(mObjectIndex)); + if (uniqueData) + { + uniqueData->mReload = true; + uniqueData->Invalidate(); + } + } + + void AnimGraphParamMotionNode::OnParamChanged() + { + ReloadAndInvalidateUniqueDatas(); + + // Set the node info text. + UpdateNodeInfo(); + SyncVisualObject(); + } + + void AnimGraphParamMotionNode::UpdateNodeInfo() + { + m_parameterIndex = mAnimGraph->FindValueParameterIndexByName(m_parameterName); + SetNodeInfo(m_parameterName.c_str()); + } + + AZ::Crc32 AnimGraphParamMotionNode::GetRewindOnZeroWeightVisibility() const + { + return m_loop ? AZ::Edit::PropertyVisibility::Hide : AZ::Edit::PropertyVisibility::Show; + } + + void AnimGraphParamMotionNode::SetRewindOnZeroWeight(bool rewindOnZeroWeight) + { + m_rewindOnZeroWeight = rewindOnZeroWeight; + } + + void AnimGraphParamMotionNode::SetMotionPlaySpeed(float playSpeed) + { + m_playSpeed = playSpeed; + } + + void AnimGraphParamMotionNode::SetEmitEvents(bool emitEvents) + { + m_emitEvents = emitEvents; + } + + void AnimGraphParamMotionNode::SetMotionExtraction(bool motionExtraction) + { + m_motionExtraction = motionExtraction; + } + + void AnimGraphParamMotionNode::SetMirrorMotion(bool mirrorMotion) + { + m_mirrorMotion = mirrorMotion; + } + + void AnimGraphParamMotionNode::SetReverse(bool reverse) + { + m_reverse = reverse; + } + + void AnimGraphParamMotionNode::SetRetarget(bool retarget) + { + m_retarget = retarget; + } + + void AnimGraphParamMotionNode::SetLoop(bool loop) + { + m_loop = loop; + } + + bool AnimGraphParamMotionNode::VersionConverter(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement) + { + return true; + } + + void AnimGraphParamMotionNode::Reflect(AZ::ReflectContext* context) + { + AZ::SerializeContext* serializeContext = azrtti_cast(context); + if (!serializeContext) + { + return; + } + + serializeContext->Class() + ->Version(3, VersionConverter) + ->Field("loop", &AnimGraphParamMotionNode::m_loop) + ->Field("retarget", &AnimGraphParamMotionNode::m_retarget) + ->Field("reverse", &AnimGraphParamMotionNode::m_reverse) + ->Field("emitEvents", &AnimGraphParamMotionNode::m_emitEvents) + ->Field("mirrorMotion", &AnimGraphParamMotionNode::m_mirrorMotion) + ->Field("motionExtraction", &AnimGraphParamMotionNode::m_motionExtraction) + ->Field("inPlace", &AnimGraphParamMotionNode::m_inPlace) + ->Field("playSpeed", &AnimGraphParamMotionNode::m_playSpeed) + ->Field("rewindOnZeroWeight", &AnimGraphParamMotionNode::m_rewindOnZeroWeight) + ->Field("parameter", &AnimGraphParamMotionNode::m_parameterName) + ; + + AZ::EditContext* editContext = serializeContext->GetEditContext(); + if (!editContext) + { + return; + } + + editContext->Class("Motion", "Motion attributes") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::AutoExpand, "") + ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly) + ->DataElement(AZ_CRC("AnimGraphParameter", 0x778af55a), &AnimGraphParamMotionNode::m_parameterName, "Parameter", "The parameter name to apply the condition on.") + ->Attribute(AZ::Edit::Attributes::ChangeNotify, &AnimGraphParamMotionNode::Reinit) + ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree) + ->Attribute(AZ_CRC("AnimGraph", 0x0d53d4b3), &AnimGraphParamMotionNode::GetAnimGraph) + ->DataElement(AZ::Edit::UIHandlers::Default, &AnimGraphParamMotionNode::m_loop, "Loop", "Loop the motion?") + ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree) + ->Attribute(AZ::Edit::Attributes::ChangeNotify, &AnimGraphParamMotionNode::InvalidateUniqueDatas) + ->DataElement(AZ::Edit::UIHandlers::Default, &AnimGraphParamMotionNode::m_retarget, "Retarget", "Is this motion allowed to be retargeted?") + ->Attribute(AZ::Edit::Attributes::ChangeNotify, &AnimGraphParamMotionNode::InvalidateUniqueDatas) + ->DataElement(AZ::Edit::UIHandlers::Default, &AnimGraphParamMotionNode::m_reverse, "Reverse", "Playback reversed?") + ->Attribute(AZ::Edit::Attributes::ChangeNotify, &AnimGraphParamMotionNode::InvalidateUniqueDatas) + ->DataElement(AZ::Edit::UIHandlers::Default, &AnimGraphParamMotionNode::m_emitEvents, "Emit Events", "Emit motion events?") + ->Attribute(AZ::Edit::Attributes::ChangeNotify, &AnimGraphParamMotionNode::InvalidateUniqueDatas) + ->DataElement(AZ::Edit::UIHandlers::Default, &AnimGraphParamMotionNode::m_inPlace, "In Place", "Should the motion be in place and not move? This is most likely only used if you do not use motion extraction but your motion data moves the character away from the origin.") + ->Attribute(AZ::Edit::Attributes::ChangeNotify, &AnimGraphParamMotionNode::InvalidateUniqueDatas) + ->DataElement(AZ::Edit::UIHandlers::Default, &AnimGraphParamMotionNode::m_mirrorMotion, "Mirror Motion", "Mirror the motion?") + ->Attribute(AZ::Edit::Attributes::ChangeNotify, &AnimGraphParamMotionNode::ReloadAndInvalidateUniqueDatas) + ->DataElement(AZ::Edit::UIHandlers::Default, &AnimGraphParamMotionNode::m_motionExtraction, "Motion Extraction", "Enable motion extraction?") + ->Attribute(AZ::Edit::Attributes::ChangeNotify, &AnimGraphParamMotionNode::InvalidateUniqueDatas) + ->DataElement(AZ::Edit::UIHandlers::SpinBox, &AnimGraphParamMotionNode::m_playSpeed, "Play Speed", "The playback speed factor.") + ->Attribute(AZ::Edit::Attributes::Min, 0.0f) + ->Attribute(AZ::Edit::Attributes::Max, 100.0f) + ->Attribute(AZ::Edit::Attributes::Step, 0.05f) + ->DataElement(AZ::Edit::UIHandlers::Default, &AnimGraphParamMotionNode::m_rewindOnZeroWeight, "Rewind On Zero Weight", "Rewind the motion when its local weight is near zero. Useful to restart non-looping motions. Looping needs to be disabled for this to work.") + ->Attribute(AZ::Edit::Attributes::Visibility, &AnimGraphParamMotionNode::GetRewindOnZeroWeightVisibility) + ; + } +} // namespace EMotionFX diff --git a/dev/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphParamMotionNode.h b/dev/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphParamMotionNode.h new file mode 100644 index 0000000000..c143600566 --- /dev/null +++ b/dev/Gems/EMotionFX/Code/EMotionFX/Source/AnimGraphParamMotionNode.h @@ -0,0 +1,151 @@ + +#pragma once + +#include "EMotionFXConfig.h" +#include "AnimGraphNode.h" +#include "AnimGraphNodeData.h" +#include "PlayBackInfo.h" +#include + +namespace EMotionFX +{ + // forward declarations + class Motion; + class ActorInstance; + class MotionSet; + + /** + * + * + */ + class EMFX_API AnimGraphParamMotionNode + : public AnimGraphNode + { + public: + AZ_RTTI(AnimGraphMotionNode, "{2A41BB98-9FF2-4788-B24F-0AD17862AAED}", AnimGraphNode) + AZ_CLASS_ALLOCATOR_DECL + + enum + { + INPUTPORT_PLAYSPEED = 0, + INPUTPORT_INPLACE = 1, + + OUTPUTPORT_POSE = 0, + OUTPUTPORT_MOTION = 1 + }; + + enum + { + PORTID_INPUT_PLAYSPEED = 0, + PORTID_INPUT_INPLACE = 1, + + PORTID_OUTPUT_POSE = 0, + PORTID_OUTPUT_MOTION = 1 + }; + + class EMFX_API UniqueData + : public AnimGraphNodeData + { + EMFX_ANIMGRAPHOBJECTDATA_IMPLEMENT_LOADSAVE + public: + AZ_CLASS_ALLOCATOR_DECL + + UniqueData(AnimGraphNode* node, AnimGraphInstance* animGraphInstance); + ~UniqueData() override; + + void Reset() override; + void Update() override; + + public: + uint32 mMotionSetID = InvalidIndex32; + AZStd::string mMotionName = ""; + MotionInstance* mMotionInstance = nullptr; + bool mReload = false; + }; + + AnimGraphParamMotionNode(); + ~AnimGraphParamMotionNode(); + + void Reinit() override; + bool InitAfterLoading(AnimGraph* animGraph) override; + + bool GetHasOutputPose() const override { return true; } + bool GetCanActAsState() const override { return true; } + bool GetSupportsDisable() const override { return true; } + bool GetSupportsVisualization() const override { return true; } + bool GetSupportsPreviewMotion() const override { return true; } + bool GetNeedsNetTimeSync() const override { return true; } + AZ::Color GetVisualColor() const override { return AZ::Color(0.38f, 0.24f, 0.91f, 1.0f); } + + AnimGraphObjectData* CreateUniqueData(AnimGraphInstance* animGraphInstance) override { return aznew UniqueData(this, animGraphInstance); } + void OnActorMotionExtractionNodeChanged() override; + void RecursiveOnChangeMotionSet(AnimGraphInstance* animGraphInstance, MotionSet* newMotionSet) override; + + const char* GetPaletteName() const override; + AnimGraphObject::ECategory GetPaletteCategory() const override; + + MotionInstance* FindMotionInstance(AnimGraphInstance* animGraphInstance) const; + + AnimGraphPose* GetMainOutputPose(AnimGraphInstance* animGraphInstance) const override { return GetOutputPose(animGraphInstance, OUTPUTPORT_POSE)->GetValue(); } + + void SetCurrentPlayTime(AnimGraphInstance* animGraphInstance, float timeInSeconds) override; + void Rewind(AnimGraphInstance* animGraphInstance) override; + + void UpdatePlayBackInfo(AnimGraphInstance* animGraphInstance); + + float ExtractCustomPlaySpeed(AnimGraphInstance* animGraphInstance) const; + + float GetMotionPlaySpeed() const { return m_playSpeed; } + bool GetIsLooping() const { return m_loop; } + bool GetIsRetargeting() const { return m_retarget; } + bool GetIsReversed() const { return m_reverse; } + bool GetEmitEvents() const { return m_emitEvents; } + bool GetMirrorMotion() const { return m_mirrorMotion; } + bool GetIsMotionExtraction() const { return m_motionExtraction; } + float GetDefaultPlaySpeed() const { return m_playSpeed; } + + void SetLoop(bool loop); + void SetRetarget(bool retarget); + void SetReverse(bool reverse); + void SetEmitEvents(bool emitEvents); + void SetMirrorMotion(bool mirrorMotion); + void SetMotionExtraction(bool motionExtraction); + void SetMotionPlaySpeed(float playSpeed); + void SetRewindOnZeroWeight(bool rewindOnZeroWeight); + + bool GetIsInPlace(AnimGraphInstance* animGraphInstance) const; + + static void Reflect(AZ::ReflectContext* context); + static bool VersionConverter(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement); + + private: + static const float s_defaultWeight; + + PlayBackInfo m_playInfo; + float m_playSpeed; + bool m_loop; + bool m_retarget; + bool m_reverse; + bool m_emitEvents; + bool m_mirrorMotion; + bool m_motionExtraction; + bool m_rewindOnZeroWeight; + bool m_inPlace; + AZStd::string m_parameterName; + AZ::Outcome m_parameterIndex; + + void ReloadAndInvalidateUniqueDatas(); + MotionInstance* CreateMotionInstance(ActorInstance* actorInstance, UniqueData* uniqueData); + void TopDownUpdate(AnimGraphInstance* animGraphInstance, float timePassedInSeconds) override; + void Update(AnimGraphInstance* animGraphInstance, float timePassedInSeconds) override; + void Output(AnimGraphInstance* animGraphInstance) override; + void PostUpdate(AnimGraphInstance* animGraphInstance, float timePassedInSeconds) override; + const AZStd::string& GetParamValue(AnimGraphInstance* animGraphInstance) const; + + void OnParamChanged(); + + AZ::Crc32 GetRewindOnZeroWeightVisibility() const; + + void UpdateNodeInfo(); + }; +} // namespace EMotionFX \ No newline at end of file diff --git a/dev/Gems/EMotionFX/Code/EMotionFX/emotionfx.waf_files b/dev/Gems/EMotionFX/Code/EMotionFX/emotionfx.waf_files index 65869d573b..d9a2e11565 100644 --- a/dev/Gems/EMotionFX/Code/EMotionFX/emotionfx.waf_files +++ b/dev/Gems/EMotionFX/Code/EMotionFX/emotionfx.waf_files @@ -196,6 +196,8 @@ "Source/AnimGraphManager.h", "Source/AnimGraphMotionNode.cpp", "Source/AnimGraphMotionNode.h", + "Source/AnimGraphParamMotionNode.cpp", + "Source/AnimGraphParamMotionNode.h", "Source/AnimGraphNetworkSerializer.cpp", "Source/AnimGraphNetworkSerializer.h", "Source/AnimGraphNode.cpp",