[LYN-3013] Github TQO Animation: MorphTarget has data integrity issue (#237)

* Added error reporting for data integrity issues for non-uniform motion data.
* The actual issue was a mismatch between the end times of the morph and the skeletal animations. They need to match in EMotionFX.
* The morph target animation exported a keyframe too much.
This commit is contained in:
Benjamin Jillich
2021-04-22 18:53:27 +02:00
committed by GitHub
parent dbcb2f9916
commit ba324b8806
2 changed files with 54 additions and 16 deletions
@@ -51,6 +51,7 @@ namespace AZ
AZ_Warning("AnimationImporter", false, "Animation ticks per second should not be zero, defaulting to %d keyframes for animation.", keysSize);
return keysSize;
}
const double totalTicks = duration / ticksPerSecond;
AZ::u32 numKeys = keysSize;
// +1 because the animation is from [0, duration] - we have a keyframe at the end of the duration which needs to be included
@@ -422,10 +423,12 @@ namespace AZ
// If there is no bone animation on the current node, then generate one here.
AZStd::shared_ptr<SceneData::GraphData::AnimationData> createdAnimationData =
AZStd::make_shared<SceneData::GraphData::AnimationData>();
createdAnimationData->ReserveKeyFrames(
animation->mDuration +
1); // +1 because we start at 0 and the last keyframe is at mDuration instead of mDuration-1
createdAnimationData->SetTimeStepBetweenFrames(1.0 / animation->mTicksPerSecond);
const size_t numKeyframes = animation->mDuration + 1; // +1 because we start at 0 and the last keyframe is at mDuration instead of mDuration-1
createdAnimationData->ReserveKeyFrames(numKeyframes);
const double timeStepBetweenFrames = 1.0 / animation->mTicksPerSecond;
createdAnimationData->SetTimeStepBetweenFrames(timeStepBetweenFrames);
// Set every frame of the animation to the start location of the node.
aiMatrix4x4 combinedTransform = GetConcatenatedLocalTransform(currentNode);
@@ -527,7 +530,7 @@ namespace AZ
// are less predictable than just using a fixed time step.
// AssImp documentation claims animation->mDuration is the duration of the animation in ticks, but
// not all animations we've tested follow that pattern. Sometimes duration is in seconds.
const AZ::u32 numKeyFrames = GetNumKeyFrames(
const size_t numKeyFrames = GetNumKeyFrames(
AZStd::max(AZStd::max(anim->mNumScalingKeys, anim->mNumPositionKeys), anim->mNumRotationKeys),
animation->mDuration,
animation->mTicksPerSecond);
@@ -543,8 +546,10 @@ namespace AZ
for (AZ::u32 frame = 0; frame < numKeyFrames; ++frame)
{
const double time = GetTimeForFrame(frame, animation->mTicksPerSecond);
aiVector3D scale = aiVector3D(1.f, 1.f, 1.f), position = aiVector3D(0.f, 0.f, 0.f);
aiQuaternion rotation(1.f, 0.f, 0.f, 0.f);
aiVector3D scale(1.0f, 1.0f, 1.0f);
aiVector3D position(0.0f, 0.0f, 0.0f);
aiQuaternion rotation(1.0f, 0.0f, 0.0f, 0.0f);
if (!SampleKeyFrame(scale, anim->mScalingKeys, anim->mNumScalingKeys, time, lastScaleIndex) ||
!SampleKeyFrame(position, anim->mPositionKeys, anim->mNumPositionKeys, time, lastPositionIndex) ||
!SampleKeyFrame(rotation, anim->mRotationKeys, anim->mNumRotationKeys, time, lastRotationIndex))
@@ -553,7 +558,6 @@ namespace AZ
}
aiMatrix4x4 transform(scale, rotation, position);
DataTypes::MatrixType animTransform = AssImpSDKWrapper::AssImpTypeConverter::ToTransform(transform);
context.m_sourceSceneSystem.SwapTransformForUpAxis(animTransform);
@@ -618,7 +622,7 @@ namespace AZ
AZStd::shared_ptr<SceneData::GraphData::BlendShapeAnimationData> morphAnimNode =
AZStd::make_shared<SceneData::GraphData::BlendShapeAnimationData>();
const AZ::u32 numKeyFrames = GetNumKeyFrames(keys.size(), animation->mDuration, animation->mTicksPerSecond);
const size_t numKeyFrames = GetNumKeyFrames(keys.size(), animation->mDuration, animation->mTicksPerSecond);
morphAnimNode->ReserveKeyFrames(numKeyFrames);
morphAnimNode->SetTimeStepBetweenFrames(s_defaultTimeStepBetweenFrames);
@@ -627,7 +631,7 @@ namespace AZ
const AZ::u32 maxKeys = keys.size();
AZ::u32 keyIdx = 0;
for (AZ::u32 frame = 0; frame <= numKeyFrames; ++frame)
for (AZ::u32 frame = 0; frame < numKeyFrames; ++frame)
{
const double time = GetTimeForFrame(frame, animation->mTicksPerSecond);
@@ -640,7 +644,6 @@ namespace AZ
morphAnimNode->AddKeyFrame(weight);
}
const size_t dotIndex = nodeName.find_last_of('.');
nodeName = nodeName.substr(dotIndex + 1);
@@ -258,8 +258,16 @@ namespace EMotionFX
}
else if (!timeValues.empty())
{
if (!AZ::IsClose(timeValues.front(), startTime, AZ::Constants::FloatEpsilon) || !AZ::IsClose(timeValues.back(), endTime, AZ::Constants::FloatEpsilon))
if (!AZ::IsClose(timeValues.front(), startTime, AZ::Constants::FloatEpsilon))
{
AZ_Error("EMotionFX", false, "No keyframe present at the start of the animation (%f). The first keyframe is at %f.",
startTime, timeValues.front());
return false;
}
if (!AZ::IsClose(timeValues.back(), endTime, AZ::Constants::FloatEpsilon))
{
AZ_Error("EMotionFX", false, "No keyframe present at the end of the animation (%f). The last keyframe is at %f.",
endTime, timeValues.back());
return false;
}
}
@@ -271,14 +279,25 @@ namespace EMotionFX
{
for (const JointData& jointData : m_jointData)
{
if ((jointData.m_positionTrack.m_times.size() != jointData.m_positionTrack.m_values.size()) || (jointData.m_rotationTrack.m_times.size() != jointData.m_rotationTrack.m_values.size()))
if (jointData.m_positionTrack.m_times.size() != jointData.m_positionTrack.m_values.size())
{
AZ_Error("EMotionFX", false, "Number of position keyframe times (%d) does not match the number of keyframe values (%d).",
jointData.m_positionTrack.m_times.size(), jointData.m_positionTrack.m_values.size());
return false;
}
if (jointData.m_rotationTrack.m_times.size() != jointData.m_rotationTrack.m_values.size())
{
AZ_Error("EMotionFX", false, "Number of rotation keyframe times (%d) does not match the number of keyframe values (%d).",
jointData.m_rotationTrack.m_times.size(), jointData.m_rotationTrack.m_values.size());
return false;
}
#ifndef EMFX_SCALE_DISABLED
if (jointData.m_scaleTrack.m_times.size() != jointData.m_scaleTrack.m_values.size())
{
AZ_Error("EMotionFX", false, "Number of scale keyframe times (%d) does not match the number of keyframe values (%d).",
jointData.m_scaleTrack.m_times.size(), jointData.m_scaleTrack.m_values.size());
return false;
}
@@ -288,7 +307,8 @@ namespace EMotionFX
}
#endif
if (!VerifyKeyTrackTimeIntegrity(jointData.m_positionTrack.m_times) || !VerifyKeyTrackTimeIntegrity(jointData.m_rotationTrack.m_times))
if (!VerifyKeyTrackTimeIntegrity(jointData.m_positionTrack.m_times) ||
!VerifyKeyTrackTimeIntegrity(jointData.m_rotationTrack.m_times))
{
return false;
}
@@ -300,7 +320,8 @@ namespace EMotionFX
bool firstCheck = true;
for (const JointData& jointData : m_jointData)
{
if (!VerifyStartEndTimeIntegrity(jointData.m_positionTrack.m_times, firstCheck, startTime, endTime) || !VerifyStartEndTimeIntegrity(jointData.m_rotationTrack.m_times, firstCheck, startTime, endTime))
if (!VerifyStartEndTimeIntegrity(jointData.m_positionTrack.m_times, firstCheck, startTime, endTime) ||
!VerifyStartEndTimeIntegrity(jointData.m_rotationTrack.m_times, firstCheck, startTime, endTime))
{
return false;
}
@@ -317,6 +338,8 @@ namespace EMotionFX
{
if (morphData.m_track.m_times.size() != morphData.m_track.m_values.size())
{
AZ_Error("EMotionFX", false, "Number of morph keyframe times (%d) does not match the number of keyframe values (%d).",
morphData.m_track.m_times.size(), morphData.m_track.m_values.size());
return false;
}
@@ -333,7 +356,17 @@ namespace EMotionFX
for (const FloatData& floatData : m_floatData)
{
if (floatData.m_track.m_times.size() != floatData.m_track.m_values.size() || !VerifyStartEndTimeIntegrity(floatData.m_track.m_times, firstCheck, startTime, endTime) || !VerifyKeyTrackTimeIntegrity(floatData.m_track.m_times))
if (floatData.m_track.m_times.size() != floatData.m_track.m_values.size())
{
AZ_Error("EMotionFX", false, "Number of float keyframe times (%d) does not match the number of keyframe values (%d).",
floatData.m_track.m_times.size(), floatData.m_track.m_values.size());
return false;
}
if (!VerifyStartEndTimeIntegrity(floatData.m_track.m_times, firstCheck, startTime, endTime))
{
return false;
}
if (!VerifyKeyTrackTimeIntegrity(floatData.m_track.m_times))
{
return false;
}
@@ -657,6 +690,8 @@ namespace EMotionFX
{
if (curTime < prevKeyTime)
{
AZ_Error("EMotionFX", false, "Keyframe times need to be ascending. Current keyframe time (%f) is smaller than the previous (%f).",
curTime, prevKeyTime);
return false;
}
prevKeyTime = curTime;