/// <summary> /// Gets the bone transform for a certain time considering key frame interpolation. /// </summary> /// <param name="channelIndex">The index in <see cref="_channels"/>.</param> /// <param name="timeIndex">The index in <see cref="_times"/>.</param> /// <param name="time">The animation time.</param> /// <returns>The animation value.</returns> private SrtTransform GetBoneTransform(int channelIndex, int timeIndex, TimeSpan time) { // Get index in the key frames list using the _indices lookup table. int keyFrameIndex = _indices[timeIndex * _channels.Length + channelIndex]; if (EnableInterpolation && keyFrameIndex + 1 < ((Array)_keyFrames[channelIndex]).Length) { // ----- Key frame interpolation. // Get the key frame before and after the specified time. TimeSpan previousTime, nextTime; SrtTransform previousTransform, nextTransform; GetBoneKeyFrames(channelIndex, keyFrameIndex, out previousTime, out previousTransform, out nextTime, out nextTransform); float parameter = (float)(time.Ticks - previousTime.Ticks) / (nextTime - previousTime).Ticks; SrtTransform.Interpolate(ref previousTransform, ref nextTransform, parameter, ref previousTransform); return(previousTransform); } // ----- No key frame interpolation. //TimeSpan time; SrtTransform transform; GetBoneKeyFrame(channelIndex, keyFrameIndex, out time, out transform); return(transform); }
/// <summary> /// Applies the weight to a given bone by blending the bone transforms. /// </summary> /// <param name="originalTransform"> /// In: The original bone transform. /// </param> /// <param name="targetTransform"> /// In: The target bone transform.<br/> /// Out: The blended bone transform. /// </param> internal void BlendBoneTransform(ref SrtTransform originalTransform, ref SrtTransform targetTransform) { targetTransform = SrtTransform.Interpolate(originalTransform, targetTransform, Weight); }
/// <exception cref="AnimationException"> /// This animation is not frozen. <see cref="Freeze"/> must be called before the animation can /// be used. /// </exception> /// <exception cref="ArgumentNullException"> /// <paramref name="defaultSource"/>, <paramref name="defaultTarget"/> or /// <paramref name="result"/> is <see langword="null"/>. /// </exception> /// <inheritdoc/> public void GetValue(TimeSpan time, ref SkeletonPose defaultSource, ref SkeletonPose defaultTarget, ref SkeletonPose result) { if (!IsFrozen) { throw new AnimationException("This animation is not frozen. Freeze() must be called before the animation can be used."); } if (defaultSource == null) { throw new ArgumentNullException("defaultSource"); } if (defaultTarget == null) { throw new ArgumentNullException("defaultTarget"); } if (result == null) { throw new ArgumentNullException("result"); } TimeSpan?animationTime = GetAnimationTime(time); if (animationTime == null) { // Animation is inactive and does not produce any output. if (defaultSource == result) { return; } // Copy bone transforms of defaultSource to result for the animated channels. for (int i = 0; i < _channels.Length; i++) { int boneIndex = _channels[i]; if (boneIndex >= result.BoneTransforms.Length) { break; } result.BoneTransforms[boneIndex] = defaultSource.BoneTransforms[boneIndex]; } return; } time = animationTime.Value; // Clamp time to allowed range. var startTime = _times[0]; var endTime = _totalDuration; if (time < startTime) { time = startTime; } if (time > endTime) { time = endTime; } int timeIndex = GetTimeIndex(time); if (!IsAdditive) { // Evaluate animation. for (int channelIndex = 0; channelIndex < _channels.Length; channelIndex++) { int boneIndex = _channels[channelIndex]; if (boneIndex >= result.BoneTransforms.Length) { break; } Debug.Assert(((Array)_keyFrames[channelIndex]).Length > 0, "Each channel must have at least 1 key frame."); float weight = _weights[channelIndex]; if (weight == 0 && defaultSource != result) { // This channel is inactive. result.BoneTransforms[boneIndex] = defaultSource.BoneTransforms[boneIndex]; } else if (weight == 1) { // Channel is fully active. result.BoneTransforms[boneIndex] = GetBoneTransform(channelIndex, timeIndex, time); } else { // Mix channel with source. SrtTransform boneTransform = GetBoneTransform(channelIndex, timeIndex, time); SrtTransform.Interpolate(ref defaultSource.BoneTransforms[boneIndex], ref boneTransform, weight, ref boneTransform); result.BoneTransforms[boneIndex] = boneTransform; } } } else { // Additive animation. for (int channelIndex = 0; channelIndex < _channels.Length; channelIndex++) { int boneIndex = _channels[channelIndex]; if (boneIndex >= result.BoneTransforms.Length) { break; } Debug.Assert(((Array)_keyFrames[channelIndex]).Length > 0, "Each channel must have at least 1 key frame."); float weight = _weights[channelIndex]; if (weight == 0 && defaultSource != result) { // Channel is inactive. result.BoneTransforms[boneIndex] = defaultSource.BoneTransforms[boneIndex]; } else if (weight == 1) { // Channel is fully active. result.BoneTransforms[boneIndex] = defaultSource.BoneTransforms[boneIndex] * GetBoneTransform(channelIndex, timeIndex, time); } else { // Add only a part of this animation value. SrtTransform boneTransform = GetBoneTransform(channelIndex, timeIndex, time); SrtTransform identity = SrtTransform.Identity; SrtTransform.Interpolate(ref identity, ref boneTransform, weight, ref boneTransform); result.BoneTransforms[boneIndex] = defaultSource.BoneTransforms[boneIndex] * boneTransform; } } } }