public AnimationPackage(BinaryReader reader)
            : base(reader)
        {
            FileName = reader.ReadString(24);
            ClipCount = reader.ReadInt32();

            Clips = new Clip[ClipCount];

            for (int i = 0; i < ClipCount; ++i)
            {
                var clip = Clips[i] = new Clip(reader);
                _namedClips.Add(clip.Name, clip);
            }
        }
        private static UnityEngine.AnimationClip Convert(Clip animation, FrameContainer frames,
            out UVector3 rootStart, out UVector3 rootEnd)
        {
            var clip = new UnityEngine.AnimationClip();
            clip.legacy = true;

            var rotateAxes = new[] {
                new { Name = "x", Mask = new UVector4(1f, 0f, 0f, 0f) },
                new { Name = "y", Mask = new UVector4(0f, 1f, 0f, 0f) },
                new { Name = "z", Mask = new UVector4(0f, 0f, 1f, 0f) },
                new { Name = "w", Mask = new UVector4(0f, 0f, 0f, 1f) }
            };

            var translateAxes = new[] {
                new { Name = "x", Mask = new UVector3(1f, 0f, 0f) },
                new { Name = "y", Mask = new UVector3(0f, 1f, 0f) },
                new { Name = "z", Mask = new UVector3(0f, 0f, 1f) },
            };

            var root = animation.Bones.FirstOrDefault(x => x.BoneId == 0);
            if (root != null && root.FrameCount > 0) {
                rootStart = Types.Convert(root.Frames.First().Translation);
                rootEnd = Types.Convert(root.Frames.Last().Translation);
            } else {
                rootStart = rootEnd = UVector3.zero;
            }

            foreach (var bone in animation.Bones) {
                var bFrames = bone.Frames;
                var frame = frames.GetByBoneId(bone.BoneId);

                string bonePath = frame.Path;

                var axisAngle = bFrames.ToDictionary(x => x, x => {
                    var q = Types.Convert(x.Rotation);
                    float ang; UnityEngine.Vector3 axis;
                    q.ToAngleAxis(out ang, out axis);
                    return new UVector4(q.x, q.y, q.z, q.w);
                });

                foreach (var axis in rotateAxes) {
                    var keys = bFrames
                        .Select(x => new Keyframe(x.Time * TimeScale,
                            UVector4.Dot(axisAngle[x], axis.Mask)))
                        .ToArray();

                    clip.SetCurve(bonePath, typeof(Transform), "localRotation." + axis.Name,
                        new UnityEngine.AnimationCurve(keys));
                }

                var converted = bFrames.Select(x => Types.Convert(x.Translation)).ToArray();

                if (!converted.Any(x => !x.Equals(UVector3.zero))) continue;

                var anyVelocities = false;
                var deltaVals = converted.Select((x, i) => {
                    var prev = Math.Max(0, i - 1);
                    var next = Math.Min(i + 1, converted.Length - 1);

                    var prevTime = bFrames[prev].Time * TimeScale;
                    var nextTime = bFrames[next].Time * TimeScale;

                    return prevTime == nextTime || !(anyVelocities = true) ? UVector3.zero
                        : (converted[next] - converted[prev]) / (nextTime - prevTime);
                }).ToArray();

                foreach (var translateAxis in translateAxes) {
                    var positions = bFrames
                        .Select((x, i) => new Keyframe(x.Time * TimeScale,
                            UVector3.Dot(frame.transform.localPosition + converted[i], translateAxis.Mask)))
                        .ToArray();

                    var deltas = bFrames.Select((x, i) => new Keyframe(x.Time * TimeScale,
                        UVector3.Dot(deltaVals[i], translateAxis.Mask))).ToArray();

                    clip.SetCurve(bonePath, typeof(Transform), "localPosition." + translateAxis.Name,
                        new UnityEngine.AnimationCurve(positions));

                    if (!anyVelocities) continue;

                    clip.SetCurve(bonePath, typeof(BFrame), "LocalVelocity." + translateAxis.Name,
                        new UnityEngine.AnimationCurve(deltas));
                }
            }

            clip.wrapMode = WrapMode.Loop;
            clip.EnsureQuaternionContinuity();

            return clip;
        }
 private Animation(Clip anim, FrameContainer frames)
 {
     _clip = Convert(anim, frames, out _rootStart, out _rootEnd);
 }