private void ProcessDenses(Clip clip, AnimationClipBindingConstant bindings, IReadOnlyDictionary <uint, string> tos)
        {
            DenseClip dense       = clip.DenseClip;
            int       streamCount = clip.StreamedClip.CurveCount;

            float[] slopeValues = new float[4];             // no slopes - 0 values
            for (int frameIndex = 0; frameIndex < dense.FrameCount; frameIndex++)
            {
                float time        = frameIndex / dense.SampleRate;
                int   frameOffset = frameIndex * dense.CurveCount;
                for (int curveIndex = 0; curveIndex < dense.CurveCount;)
                {
                    int            index         = streamCount + curveIndex;
                    GenericBinding binding       = bindings.FindBinding(index);
                    string         path          = GetCurvePath(tos, binding.Path);
                    int            framePosition = frameOffset + curveIndex;
                    if (binding.IsTransform)
                    {
                        AddTransformCurve(time, binding.TransformType, dense.SampleArray, slopeValues, slopeValues, framePosition, path);
                        curveIndex += binding.TransformType.GetDimension();
                    }
                    else if (binding.CustomType == BindingCustomType.None)
                    {
                        AddDefaultCurve(binding, path, time, dense.SampleArray[framePosition]);
                        curveIndex++;
                    }
                    else
                    {
                        AddCustomCurve(bindings, binding, path, time, dense.SampleArray[framePosition]);
                        curveIndex++;
                    }
                }
            }
        }
        private void ProcessConstant(Clip clip, AnimationClipBindingConstant bindings, IReadOnlyDictionary <uint, string> tos, float lastFrame)
        {
            ConstantClip constant    = clip.ConstantClip;
            int          streamCount = clip.StreamedClip.CurveCount;
            int          denseCount  = clip.DenseClip.CurveCount;

            float[] slopeValues = new float[4];             // no slopes - 0 values

            // only first and last frames
            float time = 0.0f;

            for (int i = 0; i < 2; i++, time += lastFrame)
            {
                for (int curveIndex = 0; curveIndex < constant.Constants.Count;)
                {
                    int index = streamCount + denseCount + curveIndex;
                    if (!GetGenericBinding(bindings, index, out GenericBinding binding))
                    {
                        curveIndex++;
                        continue;
                    }

                    string path = GetCurvePath(tos, binding.Path);
                    AddComplexCurve(time, binding.BindingType, constant.Constants, slopeValues, slopeValues, curveIndex, path);
                    curveIndex += binding.BindingType.GetDimension();
                }
            }
        }
        private void ProcessDenses(Clip clip, AnimationClipBindingConstant bindings, IReadOnlyDictionary <uint, string> tos)
        {
            DenseClip dense       = clip.DenseClip;
            int       streamCount = clip.StreamedClip.CurveCount;

            float[] slopeValues = new float[4];             // no slopes - 0 values
            for (int frameIndex = 0; frameIndex < dense.FrameCount; frameIndex++)
            {
                float time        = frameIndex / dense.SampleRate;
                int   frameOffset = frameIndex * dense.CurveCount;
                for (int curveIndex = 0; curveIndex < dense.CurveCount;)
                {
                    int index = streamCount + curveIndex;
                    if (!GetGenericBinding(bindings, index, out GenericBinding binding))
                    {
                        curveIndex++;
                        continue;
                    }

                    string path = GetCurvePath(tos, binding.Path);
                    AddComplexCurve(time, binding.BindingType, dense.SampleArray, slopeValues, slopeValues, frameOffset + curveIndex, path);
                    curveIndex += binding.BindingType.GetDimension();
                }
            }
        }
        private static bool GetGenericBinding(AnimationClipBindingConstant bindings, int index, out GenericBinding binding)
        {
            binding = bindings.FindBinding(index);
            if (binding.ClassID == ClassIDType.Transform)
            {
                return(true);
            }
#warning TODO: humanoid
            return(false);
        }
        private void ProcessStreams(IReadOnlyList <StreamedFrame> streamFrames, AnimationClipBindingConstant bindings, IReadOnlyDictionary <uint, string> tos, float sampleRate)
        {
            float[] curveValues    = new float[4];
            float[] inSlopeValues  = new float[4];
            float[] outSlopeValues = new float[4];
            float   interval       = 1.0f / sampleRate;

            // first (index [0]) stream frame is for slope calculation for the first real frame (index [1])
            // last one (index [count - 1]) is +Infinity
            // it is made for slope processing, but we don't need them
            for (int frameIndex = 1; frameIndex < streamFrames.Count - 1; frameIndex++)
            {
                StreamedFrame frame = streamFrames[frameIndex];
                for (int curveIndex = 0; curveIndex < frame.Curves.Count;)
                {
                    StreamedCurveKey curve   = frame.Curves[curveIndex];
                    GenericBinding   binding = bindings.FindBinding(curve.Index);
                    GetPreviousFrame(streamFrames, curve.Index, frameIndex, out int prevFrameIndex, out int prevCurveIndex);

                    string path = GetCurvePath(tos, binding.Path);
                    if (binding.IsTransform)
                    {
                        int dimension = binding.TransformType.GetDimension();
                        for (int key = 0; key < dimension; key++)
                        {
                            StreamedCurveKey keyCurve     = frame.Curves[curveIndex];
                            StreamedFrame    prevFrame    = streamFrames[prevFrameIndex];
                            StreamedCurveKey prevKeyCurve = prevFrame.Curves[prevCurveIndex + key];
                            float            deltaTime    = frame.Time - prevFrame.Time;
                            curveValues[key]    = keyCurve.Value;
                            inSlopeValues[key]  = prevKeyCurve.CalculateNextInSlope(deltaTime, keyCurve.Value);
                            outSlopeValues[key] = keyCurve.OutSlope;
                            curveIndex          = GetNextCurve(frame, curveIndex);
                        }

                        AddTransformCurve(frame.Time, binding.TransformType, curveValues, inSlopeValues, outSlopeValues, 0, path);
                    }
                    else if (binding.CustomType == BindingCustomType.None)
                    {
                        AddDefaultCurve(binding, path, frame.Time, frame.Curves[curveIndex].Value);
                        curveIndex = GetNextCurve(frame, curveIndex);
                    }
                    else
                    {
                        AddCustomCurve(bindings, binding, path, frame.Time, frame.Curves[curveIndex].Value);
                        curveIndex = GetNextCurve(frame, curveIndex);
                    }
                }
            }
        }
        private void AddPPtrKeyframe(PPtrCurve curve, AnimationClipBindingConstant bindings, float time, int index)
        {
            if (!m_pptrs.TryGetValue(curve, out List <PPtrKeyframe> pptrCurve))
            {
                pptrCurve = new List <PPtrKeyframe>();
                m_pptrs.Add(curve, pptrCurve);
                AddPPtrKeyframe(curve, bindings, 0.0f, index - 1);
            }

            PPtr <Object> value   = bindings.PptrCurveMapping[index];
            PPtrKeyframe  pptrKey = new PPtrKeyframe(time, value);

            pptrCurve.Add(pptrKey);
        }
        public void Process(Clip clip, AnimationClipBindingConstant bindings, IReadOnlyDictionary <uint, string> tos)
        {
            IReadOnlyList <StreamedFrame> streamedFrames = clip.StreamedClip.GenerateFrames(m_version, m_platform, m_flags);
            float lastDenseFrame  = clip.DenseClip.FrameCount / clip.DenseClip.SampleRate;
            float lastSampleFrame = streamedFrames.Count > 1 ? streamedFrames[streamedFrames.Count - 2].Time : 0.0f;
            float lastFrame       = Math.Max(lastDenseFrame, lastSampleFrame);

            Clear();
            ProcessStreams(streamedFrames, bindings, tos);
            ProcessDenses(clip, bindings, tos);
            if (Clip.IsReadConstantClip(m_version))
            {
                ProcessConstant(clip, bindings, tos, lastFrame);
            }
            CreateCurves();
        }
        private void ProcessStreams(IReadOnlyList <StreamedFrame> streamFrames, AnimationClipBindingConstant bindings, IReadOnlyDictionary <uint, string> tos)
        {
            float[] curveValues    = new float[4];
            float[] inSlopeValues  = new float[4];
            float[] outSlopeValues = new float[4];

            // first (index [0]) stream frame is for slope calculation for the first real frame (index [1])
            // last one (index [count - 1]) is +Infinity
            // it is made for slope processing, but we don't need them
            for (int frameIndex = 1; frameIndex < streamFrames.Count - 1; frameIndex++)
            {
                StreamedFrame frame = streamFrames[frameIndex];
                for (int curveIndex = 0; curveIndex < frame.Curves.Count;)
                {
                    StreamedCurveKey curve = frame.Curves[curveIndex];
                    if (!GetGenericBinding(bindings, curve.Index, out GenericBinding binding))
                    {
                        curveIndex++;
                        continue;
                    }

                    //FindPreviousCurve(streamFrames, curve.Index, frameIndex, out int prevFrameIndex, out int prevCurveIndex);
                    //FindNextCurve(streamFrames, curve.Index, frameIndex, out int nextFrameIndex, out int nextCurveIndex);

                    string path      = GetCurvePath(tos, binding.Path);
                    int    dimension = binding.BindingType.GetDimension();
                    for (int key = 0; key < dimension; key++)
                    {
                        StreamedCurveKey keyCurve = frame.Curves[curveIndex + key];
                        //StreamedFrame prevFrame = streamFrames[prevFrameIndex];
                        //StreamedFrame nextFrame = streamFrames[nextFrameIndex];
                        //StreamedCurveKey prevKeyCurve = prevFrame.Curves[prevCurveIndex + key];
                        //StreamedCurveKey nextKeyCurve = nextFrame.Curves[nextCurveIndex + key];
                        curveValues[key] = keyCurve.Value;
#warning TODO: TCB to in/out slope
                        //inSlopeValues[key] = prevKeyCurve.CalculateNextInTangent(keyCurve.Value, nextKeyCurve.Value);
                        //outSlopeValues[key] = keyCurve.CalculateOutTangent(prevKeyCurve.Value, nextKeyCurve.Value);
                    }

                    AddComplexCurve(frame.Time, binding.BindingType, curveValues, inSlopeValues, outSlopeValues, 0, path);
                    curveIndex += dimension;
                }
            }
        }
        private void ProcessInner()
        {
            Clip clip = m_clip.MuscleClip.Clip;
            AnimationClipBindingConstant       bindings = m_clip.ClipBindingConstant;
            IReadOnlyDictionary <uint, string> tos      = m_clip.FindTOS();

            IReadOnlyList <StreamedFrame> streamedFrames = clip.StreamedClip.GenerateFrames(Version, Platform, Flags);
            float lastDenseFrame  = clip.DenseClip.FrameCount / clip.DenseClip.SampleRate;
            float lastSampleFrame = streamedFrames.Count > 1 ? streamedFrames[streamedFrames.Count - 2].Time : 0.0f;
            float lastFrame       = Math.Max(lastDenseFrame, lastSampleFrame);

            ProcessStreams(streamedFrames, bindings, tos, clip.DenseClip.SampleRate);
            ProcessDenses(clip, bindings, tos);
            if (Clip.IsReadConstantClip(Version))
            {
                ProcessConstant(clip, bindings, tos, lastFrame);
            }
            CreateCurves();
        }
        private void AddCustomCurve(AnimationClipBindingConstant bindings, GenericBinding binding, string path, float time, float value)
        {
            switch (binding.CustomType)
            {
            case BindingCustomType.AnimatorMuscle:
                AddAnimatorMuscleCurve(binding, path, time, value);
                break;

            default:
                string attribute = m_customCurveResolver.ToAttributeName(binding.CustomType, binding.Attribute, path);
                if (binding.IsPPtrCurve)
                {
                    PPtrCurve curve = new PPtrCurve(path, attribute, binding.ClassID, binding.Script.CastTo <MonoScript>());
                    AddPPtrKeyframe(curve, bindings, time, (int)value);
                }
                else
                {
                    FloatCurve curve = new FloatCurve(path, attribute, binding.ClassID, binding.Script.CastTo <MonoScript>());
                    AddFloatKeyframe(curve, time, value);
                }
                break;
            }
        }
        private void ProcessConstant(Clip clip, AnimationClipBindingConstant bindings, IReadOnlyDictionary <uint, string> tos, float lastFrame)
        {
            ConstantClip constant    = clip.ConstantClip;
            int          streamCount = clip.StreamedClip.CurveCount;
            int          denseCount  = clip.DenseClip.CurveCount;

            float[] slopeValues = new float[4];             // no slopes - 0 values

            // only first and last frames
            float time = 0.0f;

            for (int i = 0; i < 2; i++, time += lastFrame)
            {
                for (int curveIndex = 0; curveIndex < constant.Constants.Count;)
                {
                    int            index   = streamCount + denseCount + curveIndex;
                    GenericBinding binding = bindings.FindBinding(index);
                    string         path    = GetCurvePath(tos, binding.Path);
                    if (binding.IsTransform)
                    {
                        AddTransformCurve(time, binding.TransformType, constant.Constants, slopeValues, slopeValues, curveIndex, path);
                        curveIndex += binding.TransformType.GetDimension();
                    }
                    else if (binding.CustomType == BindingCustomType.None)
                    {
                        AddDefaultCurve(binding, path, time, constant.Constants[curveIndex]);
                        curveIndex++;
                    }
                    else
                    {
                        AddCustomCurve(bindings, binding, path, time, constant.Constants[curveIndex]);
                        curveIndex++;
                    }
                }
            }
        }