public void InterpolateTest() { SrtTransform a = new SrtTransform(new Vector3F(1, 2, 3), new QuaternionF(1, 2, 3, 4).Normalized, new Vector3F(4, 5, 6)); SrtTransform b = a; var c = SrtTransform.Interpolate(a, b, 0.5f); Assert.AreEqual(a, c); b = new SrtTransform(new Vector3F(7, 9, 8), new QuaternionF(6, 6, 4, 2).Normalized, new Vector3F(-2, 4, -9)); c = SrtTransform.Interpolate(a, b, 0); Assert.AreEqual(a, c); c = SrtTransform.Interpolate(a, b, 1); Assert.AreEqual(b, c); c = SrtTransform.Interpolate(a, b, 0.3f); Assert.AreEqual(a.Translation * 0.7f + b.Translation * 0.3f, c.Translation); Assert.AreEqual(a.Scale * 0.7f + b.Scale * 0.3f, c.Scale); Assert.IsTrue(QuaternionF.AreNumericallyEqual( new QuaternionF( a.Rotation.W * 0.7f + b.Rotation.W * 0.3f, a.Rotation.V * 0.7f + b.Rotation.V * 0.3f).Normalized, c.Rotation)); }
/// <inheritdoc/> /// <exception cref="ArgumentNullException"> /// <paramref name="source"/>, <paramref name="target"/> or <paramref name="result"/> is /// <see langword="null"/>. /// </exception> public void Interpolate(ref SkeletonPose source, ref SkeletonPose target, float parameter, ref SkeletonPose result) { if (source == null) { throw new ArgumentNullException("source"); } if (target == null) { throw new ArgumentNullException("target"); } if (result == null) { throw new ArgumentNullException("result"); } var sourceTransforms = source.BoneTransforms; var targetTransforms = target.BoneTransforms; var resultTransforms = result.BoneTransforms; for (int i = 0; i < resultTransforms.Length; i++) { SrtTransform.Interpolate(ref sourceTransforms[i], ref targetTransforms[i], parameter, ref resultTransforms[i]); } result.Invalidate(); }
/// <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); }
public void InterpolationTest() { var traits = SrtTransformTraits.Instance; var value0 = NextRandomValue(); var value1 = NextRandomValue(); Assert.IsTrue(SrtTransform.AreNumericallyEqual(value0, traits.Interpolate(value0, value1, 0.0f))); Assert.IsTrue(SrtTransform.AreNumericallyEqual(value1, traits.Interpolate(value0, value1, 1.0f)) || SrtTransform.AreNumericallyEqual(new SrtTransform(value1.Scale, -value1.Rotation, value1.Translation), traits.Interpolate(value0, value1, 1.0f))); Assert.IsTrue(SrtTransform.AreNumericallyEqual(SrtTransform.Interpolate(value0, value1, 0.75f), traits.Interpolate(value0, value1, 0.75f))); }
/// <inheritdoc/> public void Interpolate(ref SrtTransform source, ref SrtTransform target, float parameter, ref SrtTransform result) { SrtTransform.Interpolate(ref source, ref target, parameter, ref result); }
/// <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; } } } }
/// <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); }