private void InterpolateKeys(double animationTime, AnimationLayer layer, ref Quaternion rotation, ref Vector3 translation, ref Vector3 scale, PRSKey prsKey, PRSKey nextPrsKey) { var nextTime = (nextPrsKey.Time < prsKey.Time ? (nextPrsKey.Time + Animation.Duration) : nextPrsKey.Time); var blend = ( float )(animationTime / nextTime); if (prsKey.HasRotation) { rotation = Quaternion.Slerp(prsKey.Rotation, nextPrsKey.Rotation, blend); } if (prsKey.HasPosition) { translation = Vector3.Lerp(prsKey.Position * layer.PositionScale, nextPrsKey.Position * layer.PositionScale, blend); } if (prsKey.HasScale) { scale = Vector3.Lerp(prsKey.Scale * layer.ScaleScale, nextPrsKey.Scale * layer.ScaleScale, blend); } }
public static Animation ConvertFromAssimpScene(Ai.Scene aiScene, Ai.Animation aiAnimation, AnimationConverterOptions options) { var animation = new Animation(options.Version); animation.Duration = ConvertTime(aiAnimation.DurationInTicks, aiAnimation.TicksPerSecond); foreach (var aiChannel in aiAnimation.NodeAnimationChannels) { if (AssimpConverterCommon.MeshAttachmentNameRegex.IsMatch(aiChannel.NodeName)) { continue; } var nodeName = AssimpConverterCommon.UnescapeName(aiChannel.NodeName); Ai.Node node = aiScene.RootNode.FindNode(nodeName); if (node == null) { continue; } var controller = new AnimationController(options.Version) { TargetKind = TargetKind.Node, TargetName = nodeName, TargetId = GetTargetIdForNode(aiScene.RootNode, nodeName) }; var layer = new AnimationLayer(options.Version); // NodePRS only for now layer.KeyType = KeyType.NodePRS; // Fetch the unique key frame timings from all position, rotation and scale keys. var aiKeyTimings = aiChannel.PositionKeys .Select(x => x.Time) .Concat(aiChannel.RotationKeys.Select(x => x.Time)) .Concat(aiChannel.ScalingKeys.Select(x => x.Time)) .Distinct() .OrderBy(x => x) .ToList(); // Decompose the local transform of the affected node so we can use them as the base values for our keyframes node.Transform.Decompose(out var nodeBaseScale, out var nodeBaseRotation, out var nodeBaseTranslation); // Keep track of the last position, rotation and scale used to ensure that interpolation works properly var lastPosition = nodeBaseTranslation; var lastRotation = nodeBaseRotation; var lastScale = nodeBaseScale; for (var i = 0; i < aiKeyTimings.Count; i++) { var aiTime = aiKeyTimings[i]; // Start building the keyframe var key = new PRSKey(layer.KeyType) { Position = new Vector3(lastPosition.X, lastPosition.Y, lastPosition.Z), Rotation = new Quaternion(lastRotation.X, lastRotation.Y, lastRotation.Z, lastRotation.W), Scale = new Vector3(lastScale.X, lastScale.Y, lastScale.Z) }; // Fetch the Assimp keys for this time var aiPositionKey = aiChannel.PositionKeys.SingleOrDefault(x => x.Time == aiTime); var aiRotationKey = aiChannel.RotationKeys.SingleOrDefault(x => x.Time == aiTime); var aiScaleKey = aiChannel.ScalingKeys.SingleOrDefault(x => x.Time == aiTime); if (aiPositionKey != default) { key.Position = new Vector3(aiPositionKey.Value.X, aiPositionKey.Value.Y, aiPositionKey.Value.Z); lastPosition = aiPositionKey.Value; } if (aiRotationKey != default) { key.Rotation = new Quaternion(aiRotationKey.Value.X, aiRotationKey.Value.Y, aiRotationKey.Value.Z, aiRotationKey.Value.W); lastRotation = aiRotationKey.Value; } if (aiScaleKey != default) { key.Scale = new Vector3(aiScaleKey.Value.X, aiScaleKey.Value.Y, aiScaleKey.Value.Z); lastScale = aiScaleKey.Value; } key.Time = ConvertTime(aiTime, aiAnimation.TicksPerSecond); layer.Keys.Add(key); } controller.Layers.Add(layer); animation.Controllers.Add(controller); } return(animation); }