using System; using System.Collections.Generic; namespace Oni.Totoro { internal class AnimationDaeReader { private Animation animation; private Dae.Scene scene; private int startFrame; private int endFrame; private Body body; private int frameCount; public void Read(Animation targetAnimation) { animation = targetAnimation; body = BodyDaeReader.Read(scene); ComputeFrameCount(); ImportTranslation(); ImportRotations(); animation.ComputeExtents(body); } public Dae.Scene Scene { get { return scene; } set { scene = value; } } public int StartFrame { get { return startFrame; } set { startFrame = value; } } public int EndFrame { get { return endFrame; } set { endFrame = value; } } private void ComputeFrameCount() { float maxTime = float.MinValue; var inputs = body.Nodes .SelectMany(n => n.DaeNode.Transforms).Where(t => t.HasAnimations) .SelectMany(t => t.Animations).Where(a => a != null) .SelectMany(a => a.Inputs).Where(i => i.Semantic == Dae.Semantic.Input); foreach (var input in inputs) maxTime = Math.Max(maxTime, input.Source.FloatData.Max()); float maxFrameF = maxTime * 60.0f; int maxFrame; if (maxFrameF - Math.Round(maxFrameF) < 0.0005) maxFrame = FMath.RoundToInt32(maxFrameF); else maxFrame = FMath.TruncateToInt32(maxFrameF); //Console.Error.WriteLine("Info: The last keyframe time is {0}s. The animation length is {1} (at 60fps).", // maxTime, maxFrame + 1); if (endFrame == 0) { endFrame = maxFrame; } else if (endFrame > maxFrame) { Console.Error.WriteLine("Warning: the specified animation end frame ({0}) is beyond the last key frame ({1}), using the last frame instead", endFrame, maxFrame); endFrame = maxFrame; } if (startFrame >= maxFrame) { Console.Error.WriteLine("Warning: the specified animation start frame ({0}) is beyond the last key frame ({1}), using 0 instead", startFrame, maxFrame); startFrame = 0; } frameCount = endFrame - startFrame; } private void ImportTranslation() { var rootNode = body.Nodes[0].DaeNode; var translate = rootNode.Transforms[0] as Dae.TransformTranslate; if (translate == null) { animation.Heights.AddRange(Enumerable.Repeat(0.0f, frameCount)); animation.Velocities.AddRange(Enumerable.Repeat(Vector2.Zero, frameCount)); } else { animation.Heights.AddRange(Sample(translate, 1, endFrame - 1)); var x = Sample(translate, 0, endFrame); var z = Sample(translate, 2, endFrame); for (int i = 1; i < x.Length; i++) animation.Velocities.Add(new Vector2(x[i] - x[i - 1], z[i] - z[i - 1])); } } private void ImportRotations() { animation.FrameSize = 16; foreach (var node in body.Nodes.Select(n => n.DaeNode)) { var keys = new List(); animation.Rotations.Add(keys); var rotations = new List(); var angles = new List(); foreach (var transform in node.Transforms) { var rotate = transform as Dae.TransformRotate; if (rotate != null) { rotations.Add(rotate); angles.Add(Sample(rotate, 3, endFrame - 1)); } } for (int i = 0; i < frameCount; i++) { var q = Quaternion.Identity; for (int j = 0; j < rotations.Count; j++) q *= Quaternion.CreateFromAxisAngle(rotations[j].Axis, MathHelper.ToRadians(angles[j][i])); keys.Add(new KeyFrame { Duration = 1, Rotation = q.ToVector4() }); } } } private float[] Sample(Dae.Transform transform, int index, int endFrame) { Dae.Sampler sampler = null; if (transform.HasAnimations) sampler = transform.Animations[index]; if (sampler == null) { var value = transform.Values[index]; var values = new float[endFrame - startFrame + 1]; for (int i = 0; i < values.Length; i++) values[i] = value; return values; } return sampler.Sample(startFrame, endFrame); } } }