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/TrajectoryQuery.cpp

132 lines
5.5 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 <TrajectoryQuery.h>
#include <EMotionFX/Source/ActorInstance.h>
#include <FeatureTrajectory.h>
namespace EMotionFX::MotionMatching
{
AZ::Vector3 SampleFunction(float offset, float radius, float phase)
{
phase += 10.7;
AZ::Vector3 displacement = AZ::Vector3::CreateZero();
displacement.SetX(radius * sinf(phase * 0.7f + offset) + radius * 0.75f * cosf(phase * 2.0f + offset * 2.0f));
displacement.SetY(radius * cosf(phase * 0.4f + offset));
return displacement;
}
void TrajectoryQuery::Update(const ActorInstance* actorInstance,
const FeatureTrajectory* trajectoryFeature,
const TrajectoryHistory& trajectoryHistory,
EMode mode,
[[maybe_unused]] AZ::Vector3 targetPos,
[[maybe_unused]] AZ::Vector3 targetFacingDir,
float timeDelta,
float pathRadius,
float pathSpeed)
{
// Build the future trajectory control points.
const size_t numFutureSamples = trajectoryFeature->GetNumFutureSamples();
m_futureControlPoints.resize(numFutureSamples);
if (mode == MODE_TARGETDRIVEN)
{
const AZ::Vector3 curPos = actorInstance->GetWorldSpaceTransform().m_position;
if (curPos.IsClose(targetPos, 0.1f))
{
for (size_t i = 0; i < numFutureSamples; ++i)
{
m_futureControlPoints[i].m_position = curPos;
}
}
else
{
// NOTE: Improve it by using a curve to the target.
for (size_t i = 0; i < numFutureSamples; ++i)
{
const float sampleTime = static_cast<float>(i) / (numFutureSamples - 1);
m_futureControlPoints[i].m_position = curPos.Lerp(targetPos, sampleTime);
}
}
}
else
{
m_automaticModePhase += timeDelta * pathSpeed;
AZ::Vector3 base = SampleFunction(0.0f, pathRadius, m_automaticModePhase);
for (size_t i = 0; i < numFutureSamples; ++i)
{
const float offset = i * 0.1f;
const AZ::Vector3 curSample = SampleFunction(offset, pathRadius, m_automaticModePhase);
AZ::Vector3 displacement = curSample - base;
m_futureControlPoints[i].m_position = actorInstance->GetWorldSpaceTransform().m_position + displacement;
// Evaluate a control point slightly further into the future than the actual
// one and use the position difference as the facing direction.
const AZ::Vector3 deltaSample = SampleFunction(offset + 0.01f, pathRadius, m_automaticModePhase);
const AZ::Vector3 dir = deltaSample - curSample;
m_futureControlPoints[i].m_facingDirection = dir.GetNormalizedSafe();
}
}
// Build the past trajectory control points.
const size_t numPastSamples = trajectoryFeature->GetNumPastSamples();
m_pastControlPoints.resize(numPastSamples);
const float pastTimeRange = trajectoryFeature->GetPastTimeRange();
for (size_t i = 0; i < numPastSamples; ++i)
{
const float sampleTimeNormalized = i / static_cast<float>(numPastSamples - 1);
const TrajectoryHistory::Sample sample = trajectoryHistory.Evaluate(sampleTimeNormalized * pastTimeRange);
m_pastControlPoints[i] = { sample.m_position, sample.m_facingDirection };
}
}
void TrajectoryQuery::DebugDraw(AzFramework::DebugDisplayRequests& debugDisplay, const AZ::Color& color) const
{
DebugDrawControlPoints(debugDisplay, m_pastControlPoints, color);
DebugDrawControlPoints(debugDisplay, m_futureControlPoints, color);
}
void TrajectoryQuery::DebugDrawControlPoints(AzFramework::DebugDisplayRequests& debugDisplay,
const AZStd::vector<ControlPoint>& controlPoints,
const AZ::Color& color)
{
const float markerSize = 0.02f;
const size_t numControlPoints = controlPoints.size();
if (numControlPoints > 1)
{
debugDisplay.DepthTestOff();
debugDisplay.SetColor(color);
for (size_t i = 0; i < numControlPoints - 1; ++i)
{
const ControlPoint& current = controlPoints[i];
const AZ::Vector3& posA = current.m_position;
const AZ::Vector3& posB = controlPoints[i + 1].m_position;
const AZ::Vector3 diff = posB - posA;
debugDisplay.DrawSolidCylinder(/*center=*/(posB + posA) * 0.5f,
/*direction=*/diff.GetNormalizedSafe(),
/*radius=*/0.0025f,
/*height=*/diff.GetLength(),
/*drawShaded=*/false);
FeatureTrajectory::DebugDrawFacingDirection(debugDisplay, current.m_position, current.m_facingDirection);
}
for (const ControlPoint& controlPoint : controlPoints)
{
debugDisplay.DrawBall(controlPoint.m_position, markerSize, /*drawShaded=*/false);
FeatureTrajectory::DebugDrawFacingDirection(debugDisplay, controlPoint.m_position, controlPoint.m_facingDirection);
}
}
}
} // namespace EMotionFX::MotionMatching