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/Gems/MotionMatching/Code/Source/TrajectoryHistory.cpp

168 lines
5.9 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 <TrajectoryHistory.h>
#include <EMotionFX/Source/ActorInstance.h>
#include <EMotionFX/Source/TransformData.h>
#include <EMotionFX/Source/EMotionFXManager.h>
namespace EMotionFX::MotionMatching
{
TrajectoryHistory::Sample operator*(TrajectoryHistory::Sample sample, float weight)
{
return {sample.m_position * weight, sample.m_facingDirection * weight};
}
TrajectoryHistory::Sample operator*(float weight, TrajectoryHistory::Sample sample)
{
return {weight * sample.m_position, weight * sample.m_facingDirection};
}
TrajectoryHistory::Sample operator+(TrajectoryHistory::Sample lhs, const TrajectoryHistory::Sample& rhs)
{
return {lhs.m_position + rhs.m_position, lhs.m_facingDirection + rhs.m_facingDirection};
}
void TrajectoryHistory::Init(const Pose& pose, size_t jointIndex, const AZ::Vector3& facingAxisDir, float numSecondsToTrack)
{
AZ_Assert(numSecondsToTrack > 0.0f, "Number of seconds to track has to be greater than zero.");
Clear();
m_jointIndex = jointIndex;
m_facingAxisDir = facingAxisDir;
m_numSecondsToTrack = numSecondsToTrack;
// Pre-fill the history with samples from the current joint position.
PrefillSamples(pose, /*timeDelta=*/1.0f / 60.0f);
}
void TrajectoryHistory::AddSample(const Pose& pose)
{
Sample sample;
const Transform worldSpaceTransform = pose.GetWorldSpaceTransform(m_jointIndex);
sample.m_position = worldSpaceTransform.m_position;
sample.m_facingDirection = worldSpaceTransform.TransformVector(m_facingAxisDir).GetNormalizedSafe();
// The new key will be added at the end of the keytrack.
m_keytrack.AddKey(m_currentTime, sample);
while (m_keytrack.GetNumKeys() > 2 &&
((m_keytrack.GetKey(m_keytrack.GetNumKeys() - 2)->GetTime() - m_keytrack.GetFirstTime()) > m_numSecondsToTrack))
{
m_keytrack.RemoveKey(0); // Remove first (oldest) key
}
}
void TrajectoryHistory::PrefillSamples(const Pose& pose, float timeDelta)
{
const size_t numKeyframes = aznumeric_caster<>(m_numSecondsToTrack / timeDelta);
for (size_t i = 0; i < numKeyframes; ++i)
{
AddSample(pose);
Update(timeDelta);
}
}
void TrajectoryHistory::Clear()
{
m_jointIndex = 0;
m_currentTime = 0.0f;
m_keytrack.ClearKeys();
}
void TrajectoryHistory::Update(float timeDelta)
{
m_currentTime += timeDelta;
}
TrajectoryHistory::Sample TrajectoryHistory::Evaluate(float time) const
{
if (m_keytrack.GetNumKeys() == 0)
{
return {};
}
return m_keytrack.GetValueAtTime(m_keytrack.GetLastTime() - time);
}
TrajectoryHistory::Sample TrajectoryHistory::EvaluateNormalized(float normalizedTime) const
{
const float firstTime = m_keytrack.GetFirstTime();
const float lastTime = m_keytrack.GetLastTime();
const float range = lastTime - firstTime;
const float time = (1.0f - normalizedTime) * range + firstTime;
return m_keytrack.GetValueAtTime(time);
}
void TrajectoryHistory::DebugDraw(AzFramework::DebugDisplayRequests& debugDisplay, const AZ::Color& color, float timeStart) const
{
const size_t numKeyframes = m_keytrack.GetNumKeys();
if (numKeyframes == 0)
{
return;
}
// Clip some of the newest samples.
const float adjustedLastTime = m_keytrack.GetLastTime() - timeStart;
size_t adjustedLastKey = m_keytrack.FindKeyNumber(adjustedLastTime);
if (adjustedLastKey == InvalidIndex)
{
adjustedLastKey = m_keytrack.GetNumKeys() - 1;
}
const float firstTime = m_keytrack.GetFirstTime();
const float range = adjustedLastTime - firstTime;
debugDisplay.DepthTestOff();
for (size_t i = 0; i < adjustedLastKey; ++i)
{
const float time = m_keytrack.GetKey(i)->GetTime();
const float normalized = (time - firstTime) / range;
if (normalized < 0.3f)
{
continue;
}
// Decrease size and fade out alpha the older the sample is.
AZ::Color finalColor = color;
finalColor.SetA(finalColor.GetA() * 0.6f * normalized);
const float markerSize = m_debugMarkerSize * 0.7f * normalized;
const Sample currentSample = m_keytrack.GetKey(i)->GetValue();
debugDisplay.SetColor(finalColor);
debugDisplay.DrawBall(currentSample.m_position, markerSize, /*drawShaded=*/false);
const float facingDirectionLength = m_debugMarkerSize * 10.0f * normalized;
debugDisplay.DrawLine(currentSample.m_position, currentSample.m_position + currentSample.m_facingDirection * facingDirectionLength);
}
}
void TrajectoryHistory::DebugDrawSampled(AzFramework::DebugDisplayRequests& debugDisplay,
size_t numSamples,
const AZ::Color& color) const
{
debugDisplay.DepthTestOff();
debugDisplay.SetColor(color);
Sample lastSample = EvaluateNormalized(0.0f);
for (size_t i = 0; i < numSamples; ++i)
{
const float sampleTime = i / static_cast<float>(numSamples - 1);
const Sample currentSample = EvaluateNormalized(sampleTime);
if (i > 0)
{
debugDisplay.DrawLine(lastSample.m_position, currentSample.m_position);
}
debugDisplay.DrawBall(currentSample.m_position, m_debugMarkerSize, /*drawShaded=*/false);
lastSample = currentSample;
}
}
} // namespace EMotionFX::MotionMatching