in MREUnityRuntimeLib/Animation/Animation.cs [120:253]
internal override void Update(long serverTime)
{
if (Data == null)
{
// only way for Data to be unset is if it's unloaded
if (DataSet)
{
manager.DeregisterAnimation(this);
}
return;
}
// normalize time to animation length based on wrap settings
float currentTime;
if (Weight > 0 && Speed != 0)
{
// normal operation
currentTime = (serverTime - BasisTime) * Speed / 1000;
LastWeight = Weight;
StopUpdating = false;
}
else if (!StopUpdating)
{
// supposed to stop, but run one last update
currentTime = Time;
StopUpdating = true;
}
else
{
// don't update
return;
}
currentTime = ApplyWrapMode(currentTime);
// process tracks
for (var ti = 0; ti < Data.Tracks.Length; ti++)
{
Track track = Data.Tracks[ti];
bool usesPrevFrameValue = false, usesNextFrameValue = false;
(Keyframe prevFrame, Keyframe nextFrame) = GetActiveKeyframes(ti, currentTime);
// either no keyframes, or time out of range
if (prevFrame == null)
{
continue;
}
float linearT = (currentTime - prevFrame.Time) / (nextFrame.Time - prevFrame.Time);
// get realtime value for trailing frame
JToken prevFrameValue = prevFrame.Value;
if (prevFrame.ValuePath != null)
{
if (GetPatchAtPath(prevFrame.ValuePath, out IPatchable patch))
{
prevFrameValue = TokenPool.Lease(TargetPath.TypeOfPath[prevFrame.ValuePath.Path]);
if (patch.ReadFromPath(prevFrame.ValuePath, ref prevFrameValue, 0))
{
usesPrevFrameValue = true;
}
else
{
TokenPool.Return(prevFrameValue);
continue;
}
}
else continue;
}
// get realtime value for leading frame (same as above)
JToken nextFrameValue = nextFrame.Value;
if (nextFrame.ValuePath != null)
{
if (GetPatchAtPath(nextFrame.ValuePath, out IPatchable patch))
{
nextFrameValue = TokenPool.Lease(TargetPath.TypeOfPath[nextFrame.ValuePath.Path]);
if (patch.ReadFromPath(nextFrame.ValuePath, ref nextFrameValue, 0))
{
usesNextFrameValue = true;
}
else
{
TokenPool.Return(nextFrameValue);
continue;
}
}
else continue;
}
// compute new value for targeted field
JToken outputToken = TokenPool.Lease(prevFrameValue);
Interpolations.Interpolate(prevFrameValue, nextFrameValue, linearT, ref outputToken, nextFrame.Bezier ?? track.Bezier ?? LinearEasing);
if (usesPrevFrameValue)
{
TokenPool.Return(prevFrameValue);
}
if (usesNextFrameValue)
{
TokenPool.Return(nextFrameValue);
}
// mix computed value with the result of any other anims targeting the same property
AnimationManager.AnimBlend blendData = manager.AnimBlends.GetOrCreate(
ResolvedTargetPaths[ti],
() => new AnimationManager.AnimBlend(ResolvedTargetPaths[ti]));
blendData.FinalUpdate = blendData.FinalUpdate || StopUpdating;
if (blendData.TotalWeight == 0)
{
blendData.TotalWeight = LastWeight;
if (blendData.CurrentValue == null)
{
blendData.CurrentValue = outputToken.DeepClone();
}
else
{
JToken temp = blendData.CurrentValue;
blendData.CurrentValue = outputToken;
TokenPool.Return(temp);
}
}
else
{
blendData.TotalWeight += LastWeight;
JToken temp = TokenPool.Lease(outputToken);
Interpolations.Interpolate(outputToken, blendData.CurrentValue, LastWeight / blendData.TotalWeight, ref temp, LinearEasing);
TokenPool.Return(blendData.CurrentValue);
blendData.CurrentValue = temp;
}
}
}