You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Code/Editor/TrackView/DirectorNodeAnimator.cpp

245 lines
8.8 KiB
C++

/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "EditorDefs.h"
#include "DirectorNodeAnimator.h"
// CryCommon
#include <CryCommon/Maestro/Types/AnimParamType.h> // for AnimParamType
// Editor
#include "TrackView/TrackViewSequenceManager.h" // for CTrackViewSequence
////////////////////////////////////////////////////////////////////////////
CDirectorNodeAnimator::CDirectorNodeAnimator([[maybe_unused]] CTrackViewAnimNode* pDirectorNode)
{
}
////////////////////////////////////////////////////////////////////////////
void CDirectorNodeAnimator::Animate(CTrackViewAnimNode* pNode, const SAnimContext& ac)
{
if (!pNode->IsActiveDirector())
{
// Don't animate if it's not the sequence track of the active director
return;
}
CTrackViewTrack* pSequenceTrack = pNode->GetTrackForParameter(AnimParamType::Sequence);
if (pSequenceTrack && !pSequenceTrack->IsDisabled())
{
std::vector<CTrackViewSequence*> inactiveSequences;
std::vector<CTrackViewSequence*> activeSequences;
// Construct sets of sequences that need to be bound/unbound at this point
const float time = ac.time;
const unsigned int numKeys = pSequenceTrack->GetKeyCount();
for (unsigned int i = 0; i < numKeys; ++i)
{
CTrackViewKeyHandle keyHandle = pSequenceTrack->GetKey(i);
ISequenceKey sequenceKey;
keyHandle.GetKey(&sequenceKey);
CTrackViewSequence* pSequence = GetSequenceFromSequenceKey(sequenceKey);
if (pSequence)
{
if (sequenceKey.time <= time)
{
stl::push_back_unique(activeSequences, pSequence);
stl::find_and_erase(inactiveSequences, pSequence);
}
else
{
if (!stl::find(activeSequences, pSequence))
{
stl::push_back_unique(inactiveSequences, pSequence);
}
}
}
}
// Unbind must occur before binding, because entities can be referenced in multiple sequences
for (auto iter = inactiveSequences.begin(); iter != inactiveSequences.end(); ++iter)
{
CTrackViewSequence* pSequence = *iter;
if (pSequence->IsBoundToEditorObjects())
{
// No notifications because unbinding would call ForceAnimation again
CTrackViewSequenceNoNotificationContext context(pSequence);
pSequence->UnBindFromEditorObjects();
}
}
// Now bind sequences
for (auto iter = activeSequences.begin(); iter != activeSequences.end(); ++iter)
{
CTrackViewSequence* pSequence = *iter;
if (!pSequence->IsBoundToEditorObjects())
{
// No notifications because binding would call ForceAnimation again
CTrackViewSequenceNoNotificationContext context(pSequence);
pSequence->BindToEditorObjects();
// Make sure the sequence is active, harmless to call if the sequences is already
// active. The sequence may not be active in the Editor if this key was just created.
pSequence->Activate();
}
}
// Animate sub sequences
ForEachActiveSequence(ac, pSequenceTrack, true,
[&](CTrackViewSequence* pSequence, const SAnimContext& newAnimContext)
{
pSequence->Animate(newAnimContext);
},
[&](CTrackViewSequence* pSequence, [[maybe_unused]] const SAnimContext& newAnimContext)
{
pSequence->Reset(false);
}
);
}
}
////////////////////////////////////////////////////////////////////////////
void CDirectorNodeAnimator::Render(CTrackViewAnimNode* pNode, const SAnimContext& ac)
{
if (!pNode->IsActiveDirector())
{
// Don't animate if it's not the sequence track of the active director
return;
}
CTrackViewTrack* pSequenceTrack = pNode->GetTrackForParameter(AnimParamType::Sequence);
if (pSequenceTrack && !pSequenceTrack->IsDisabled())
{
// Render sub sequences
ForEachActiveSequence(ac, pSequenceTrack, false,
[&](CTrackViewSequence* pSequence, [[maybe_unused]] const SAnimContext& newAnimContext)
{
pSequence->Render(newAnimContext);
},
[&]([[maybe_unused]] CTrackViewSequence* pSequence, [[maybe_unused]] const SAnimContext& newAnimContext) {}
);
}
}
////////////////////////////////////////////////////////////////////////////
void CDirectorNodeAnimator::ForEachActiveSequence(const SAnimContext& ac, CTrackViewTrack* pSequenceTrack,
const bool bHandleOtherKeys, std::function<void(CTrackViewSequence*, const SAnimContext&)> animateFunction,
std::function<void(CTrackViewSequence*, const SAnimContext&)> resetFunction)
{
const unsigned int numKeys = pSequenceTrack->GetKeyCount();
if (bHandleOtherKeys)
{
// Reset all non-active sequences first
for (unsigned int i = 0; i < numKeys; ++i)
{
CTrackViewKeyHandle keyHandle = pSequenceTrack->GetKey(i);
ISequenceKey sequenceKey;
keyHandle.GetKey(&sequenceKey);
CTrackViewSequence* pSequence = GetSequenceFromSequenceKey(sequenceKey);
if (pSequence)
{
SAnimContext newAnimContext = ac;
const float duration = sequenceKey.fDuration;
const float sequenceTime = ac.time - sequenceKey.time + sequenceKey.fStartTime;
const float sequenceDuration = duration + sequenceKey.fStartTime;
newAnimContext.time = std::min(sequenceTime, sequenceDuration);
const bool bInsideKeyRange = (sequenceTime >= 0.0f) && (sequenceTime <= sequenceDuration);
if (!bInsideKeyRange)
{
if (ac.forcePlay && sequenceTime >= 0.0f && newAnimContext.time != pSequence->GetTime())
{
// If forcing animation force previous keys to their last playback position
animateFunction(pSequence, newAnimContext);
}
resetFunction(pSequence, newAnimContext);
}
}
}
}
for (unsigned int i = 0; i < numKeys; ++i)
{
CTrackViewKeyHandle keyHandle = pSequenceTrack->GetKey(i);
ISequenceKey sequenceKey;
keyHandle.GetKey(&sequenceKey);
CTrackViewSequence* pSequence = GetSequenceFromSequenceKey(sequenceKey);
if (pSequence)
{
SAnimContext newAnimContext = ac;
const float duration = sequenceKey.fDuration;
const float sequenceTime = ac.time - sequenceKey.time + sequenceKey.fStartTime;
const float sequenceDuration = duration + sequenceKey.fStartTime;
newAnimContext.time = std::min(sequenceTime, sequenceDuration);
const bool bInsideKeyRange = (sequenceTime >= 0.0f) && (sequenceTime <= sequenceDuration);
if ((bInsideKeyRange && (newAnimContext.time != pSequence->GetTime() || ac.forcePlay)))
{
animateFunction(pSequence, newAnimContext);
}
}
}
}
////////////////////////////////////////////////////////////////////////////
void CDirectorNodeAnimator::UnBind([[maybe_unused]] CTrackViewAnimNode* pNode)
{
const CTrackViewSequenceManager* pSequenceManager = GetIEditor()->GetSequenceManager();
const unsigned int numSequences = pSequenceManager->GetCount();
for (unsigned int sequenceIndex = 0; sequenceIndex < numSequences; ++sequenceIndex)
{
CTrackViewSequence* pSequence = pSequenceManager->GetSequenceByIndex(sequenceIndex);
if (pSequence->IsActiveSequence())
{
// Don't care about the active sequence
continue;
}
if (pSequence->IsBoundToEditorObjects())
{
pSequence->UnBindFromEditorObjects();
}
}
}
/*static*/ CTrackViewSequence* CDirectorNodeAnimator::GetSequenceFromSequenceKey(const ISequenceKey& sequenceKey)
{
CTrackViewSequence* retSequence = nullptr;
const CTrackViewSequenceManager* sequenceManager = GetIEditor()->GetSequenceManager();
if (sequenceManager)
{
if (sequenceKey.sequenceEntityId.IsValid())
{
retSequence = sequenceManager->GetSequenceByEntityId(sequenceKey.sequenceEntityId);
AZ_Assert(retSequence, "Null sequence returned when a Sequence Component was expected.");
}
}
return retSequence;
}