Beispiel #1
0
        /// <summary>
        /// Populates the crossfade pool with the set amount of meshes
        /// </summary>
        /// <param name="count">Amount to fill the pool with</param>
        public void PrepopulateCrossfadePool(int count)
        {
            Stack <Mesh> pool = null;

            lock (s_crossFadePool)
            {
                if (crossFadePoolIndex > -1)
                {
                    pool  = s_crossFadePool[crossFadePoolIndex];
                    count = pool.Count - count;
                    if (count <= 0)
                    {
                        return;
                    }
                }
            }
            Mesh[] meshes = AllocatedArray <Mesh> .Get(count);

            for (int i = 0; i < count; i++)
            {
                meshes[i] = GetCrossfadeFromPool();
            }
            for (int i = 0; i < count; i++)
            {
                crossFadeMesh = meshes[i];
                ReturnCrossfadeToPool();
                meshes[i] = null;
            }
            AllocatedArray <Mesh> .Return(meshes);
        }
Beispiel #2
0
            public void Clear()
            {
                _count = 0;
                AllocatedArray <int> .Return(TriangleIndex, false);

                AllocatedArray <int> .Return(VertexIndex, false);

                TriangleIndex = null;
                VertexIndex   = null;
            }
Beispiel #3
0
        protected virtual bool DisplayFrame(int previousFrame)
        {
            if (currentFrame == previousFrame || currentAnimIndex < 0)
            {
                return(false);
            }
            if (hasExposedTransforms && exposedTransformCrossfade.isFading)
            {
                if (exposedTransformCrossfade.currentFrame >= exposedTransformCrossfade.framesNeeded)
                {
                    exposedTransformCrossfade.ReturnFrame(false);
                }
                else
                {
                    exposedTransformCrossfade.exposedTransformJobHandles[exposedTransformCrossfade.currentFrame].Complete();
                    if (exposedTransformCrossfade.outputMatrix != null)
                    {
                        var         exposedTransforms = currentAnimation.ExposedTransforms;
                        Matrix4x4[] positions         = AllocatedArray <Matrix4x4> .Get(exposedTransforms.Length);

                        exposedTransformCrossfade.outputMatrix[exposedTransformCrossfade.currentFrame].CopyTo(positions);
                        for (int i = 0; i < positions.Length; i++)
                        {
                            Transform child = null;
                            if (childMap.TryGetValue(exposedTransforms[i], out child))
                            {
                                MatrixUtils.FromMatrix4x4(child, positions[i]);
                            }
                        }
                    }
                    // apply root motion
                    var  fromFrame       = exposedTransformCrossfade.fromFrame;
                    var  toFrame         = exposedTransformCrossfade.toFrame;
                    bool applyRootMotion = currentAnimation.RootMotionMode == RootMotionMode.AppliedToTransform;
                    if (applyRootMotion)
                    {
                        float      delta = exposedTransformCrossfade.currentFrame / (float)exposedTransformCrossfade.framesNeeded;
                        Vector3    pos   = Vector3.Lerp(fromFrame.rootMotionPosition, toFrame.rootMotionPosition, delta);
                        Quaternion rot   = Quaternion.Lerp(fromFrame.rootMotionRotation, toFrame.rootMotionRotation, delta);
                        transform.Translate(pos, Space.Self);
                        transform.Rotate(rot.eulerAngles * Time.deltaTime, Space.Self);
                    }
                    exposedTransformCrossfade.currentFrame++;
                    return(false);
                }
            }
            // generate frames if needed and show the current animation frame
            currentAnimation.GenerateFrame(baseMesh, currentFrame);
            currentAnimation.DisplayFrame(this, currentFrame, previousFrame);
            return(true);
        }
Beispiel #4
0
 public void PopulateFrame(int length)
 {
     if (frame == null)
     {
         frame = new CrossFadeFrameData();
     }
     if (frame.positions == null)
     {
         frame.positions = AllocatedArray <Vector3> .Get(length);
     }
     if (frame.normals == null)
     {
         frame.normals = AllocatedArray <Vector3> .Get(length);
     }
 }
Beispiel #5
0
 public void ReturnFrame()
 {
     if (frame != null)
     {
         if (frame.positions != null)
         {
             AllocatedArray <Vector3> .Return(frame.positions, false);
         }
         if (frame.normals != null)
         {
             AllocatedArray <Vector3> .Return(frame.normals, false);
         }
         frame.positions = null;
         frame.normals   = null;
     }
 }
Beispiel #6
0
 public void ReturnFrame(bool destroying)
 {
     try
     {
         if (exposedTransformJobHandles != null)
         {
             for (int i = 0; i < exposedTransformJobHandles.Length; i++)
             {
                 if (destroying || currentFrame <= i)
                 {
                     exposedTransformJobHandles[i].Complete();
                 }
             }
             AllocatedArray.Return(exposedTransformJobHandles, true);
             exposedTransformJobHandles = null;
         }
         if (exposedTransformJobs != null)
         {
             AllocatedArray.Return(exposedTransformJobs, true);
             exposedTransformJobs = null;
         }
         if (outputMatrix != null)
         {
             if (fromMatrix.IsCreated)
             {
                 fromMatrix.Dispose();
             }
             if (toMatrix.IsCreated)
             {
                 toMatrix.Dispose();
             }
             for (int i = 0; i < outputMatrix.Length; i++)
             {
                 if (outputMatrix[i].IsCreated)
                 {
                     outputMatrix[i].Dispose();
                 }
             }
             AllocatedArray.Return(outputMatrix);
             outputMatrix = null;
         }
     }
     catch (System.Exception ex)
     {
         Debug.LogException(ex);
     }
 }
Beispiel #7
0
        public void StartCrossfade(MeshFrameDataBase fromFrame, MeshFrameDataBase toFrame)
        {
            Reset(false);
            isReset        = false;
            this.fromFrame = fromFrame;
            this.toFrame   = toFrame;
            int matrixLength = fromFrame.exposedTransforms != null ? fromFrame.exposedTransforms.Length : 0;

            if (matrixLength > 0)
            {
                if (exposedTransformJobs == null)
                {
                    exposedTransformJobs = AllocatedArray <LerpMatrix4x4Job> .Get(framesNeeded);
                }
                if (exposedTransformJobHandles == null)
                {
                    exposedTransformJobHandles = AllocatedArray <JobHandle> .Get(framesNeeded);
                }
                if (outputMatrix == null)
                {
                    outputMatrix = AllocatedArray <NativeArray <Matrix4x4> > .Get(framesNeeded);
                }
                fromMatrix = new NativeArray <Matrix4x4>(matrixLength, Allocator.Persistent);
                toMatrix   = new NativeArray <Matrix4x4>(matrixLength, Allocator.Persistent);
                fromMatrix.CopyFrom(fromFrame.exposedTransforms);
                toMatrix.CopyFrom(toFrame.exposedTransforms);

                for (int i = 0; i < framesNeeded; i++)
                {
                    float delta = i / (float)framesNeeded;
                    outputMatrix[i] = new NativeArray <Matrix4x4>(matrixLength, Allocator.Persistent);
                    var matrixJob = new LerpMatrix4x4Job()
                    {
                        from   = fromMatrix,
                        to     = toMatrix,
                        output = outputMatrix[i],
                        delta  = delta
                    };
                    exposedTransformJobs[i]       = matrixJob;
                    exposedTransformJobHandles[i] = matrixJob.Schedule(matrixLength, 64);
                }
            }
        }
Beispiel #8
0
            public void PopulateArrays()
            {
                TriangleIndex = AllocatedArray <int> .Get(_reserved);

                VertexIndex = AllocatedArray <int> .Get(_reserved);
            }
Beispiel #9
0
        private void RecalculateNormals(Mesh mesh, float angle, int[] triangles, Vector3[] vertices, bool instant = false)
        {
            if (triangles == null)
            {
                if (meshInfoCache == null)
                {
                    meshInfoCache = new Dictionary <Mesh, KeyValuePair <int[], Vector3[]> >();
                }
                if (meshInfoCache.ContainsKey(mesh))
                {
                    triangles = meshInfoCache[mesh].Key;
                    vertices  = meshInfoCache[mesh].Value;
                }
                else
                {
                    triangles = mesh.GetTriangles(0);
                    vertices  = mesh.vertices;
                    meshInfoCache.Add(mesh, new KeyValuePair <int[], Vector3[]>(triangles, vertices));
                }
            }
            var triNormals = AllocatedArray <Vector3> .Get(triangles.Length / 3);

            var normals = AllocatedArray <Vector3> .Get(vertices.Length);

            angle = angle * Mathf.Deg2Rad;

            var dictionary = PooledDictionary <Vector3, VertexEntry> .Get(vertices.Length, VectorComparer);

            //Goes through all the triangles and gathers up data to be used later
            for (var i = 0; i < triangles.Length; i += 3)
            {
                int i1 = triangles[i];
                int i2 = triangles[i + 1];
                int i3 = triangles[i + 2];

                //Calculate the normal of the triangle
                Vector3 p1       = vertices[i2] - vertices[i1];
                Vector3 p2       = vertices[i3] - vertices[i1];
                Vector3 normal   = Vector3.Cross(p1, p2).normalized;
                int     triIndex = i / 3;
                triNormals[triIndex] = normal;

                VertexEntry entry;
                //VertexKey key;

                //For each of the three points of the triangle
                //  > Add this triangle as part of the triangles they're connected to.

                if (!dictionary.TryGetValue(vertices[i1], out entry))
                {
                    entry = GenericObjectPool <VertexEntry> .Get();

                    entry.PopulateArrays();
                    dictionary.Add(vertices[i1], entry);
                }
                entry.Add(i1, triIndex);

                if (!dictionary.TryGetValue(vertices[i2], out entry))
                {
                    entry = GenericObjectPool <VertexEntry> .Get();

                    entry.PopulateArrays();
                    dictionary.Add(vertices[i2], entry);
                }
                entry.Add(i2, triIndex);

                if (!dictionary.TryGetValue(vertices[i3], out entry))
                {
                    entry = GenericObjectPool <VertexEntry> .Get();

                    entry.PopulateArrays();
                    dictionary.Add(vertices[i3], entry);
                }
                entry.Add(i3, triIndex);
            }

            foreach (var kvp in dictionary)
            {
                var value = kvp.Value;
                for (var i = 0; i < value.Count; ++i)
                {
                    var sum = new Vector3();
                    for (var j = 0; j < value.Count; ++j)
                    {
                        if (value.VertexIndex[i] == value.VertexIndex[j])
                        {
                            sum += triNormals[value.TriangleIndex[j]];
                        }
                        else
                        {
                            float dot = Vector3.Dot(
                                triNormals[value.TriangleIndex[i]],
                                triNormals[value.TriangleIndex[j]]);
                            dot = Mathf.Clamp(dot, -0.99999f, 0.99999f);
                            float acos = Mathf.Acos(dot);
                            if (acos <= angle)
                            {
                                sum += triNormals[value.TriangleIndex[j]];
                            }
                        }
                    }
                    normals[value.VertexIndex[i]] = sum.normalized;
                }
                value.Clear();
                GenericObjectPool <VertexEntry> .Return(value);
            }
            dictionary.ReturnToPool();
            if (instant == false)
            {
                if (mainThreadActions == null)
                {
                    mainThreadActions = new Queue <System.Action>();
                }
                mainThreadActions.Enqueue(() =>
                {
                    if (mesh)
                    {
                        mesh.normals = normals;
                    }
                    AllocatedArray <Vector3> .Return(normals, false);
                });
            }
            else
            {
                mesh.normals = normals;
                AllocatedArray <Vector3> .Return(normals, false);
            }
        }
Beispiel #10
0
        /// <summary>
        /// The main update loop called from the MeshAnimatorManager
        /// </summary>
        /// <param name="time">Current time</param>
        public void UpdateTick(float time)
        {
            if (initialized == false)
            {
                return;
            }
            if (animationCount == 0)
            {
                return;
            }
            if (currentAnimIndex < 0 || currentAnimIndex > animationCount)
            {
                if (defaultAnimation != null)
                {
                    Play(defaultAnimation.AnimationName);
                }
                else
                {
                    Play(0);
                }
            }
            if ((isVisible == false && updateWhenOffscreen == false) || isPaused || speed == 0 || currentAnimation.playbackSpeed == 0) // return if offscreen or crossfading
            {
                return;
            }
            // update the lod level if needed
            if (LODLevels.Length > 0 && time > nextLODCheck)
            {
                if (!hasLODCamera)
                {
                    int cameraCount = Camera.allCamerasCount;
                    if (cameraCount > 0)
                    {
                        Camera[] cameras = AllocatedArray <Camera> .Get(cameraCount);

                        cameraCount = Camera.GetAllCameras(cameras);
                        LODCamera   = cameras[0].transform;
                        AllocatedArray <Camera> .Return(cameras);

                        hasLODCamera = true;
                    }
                }
                if (hasLODCamera)
                {
                    float dis      = (LODCamera.position - mTransform.position).sqrMagnitude;
                    int   lodLevel = 0;
                    for (int i = 0; i < LODLevels.Length; i++)
                    {
                        if (dis > LODLevels[i].distanceSquared)
                        {
                            lodLevel = i;
                        }
                    }
                    if (currentLodLevel != lodLevel)
                    {
                        currentLodLevel = lodLevel;
                    }
                }
                nextLODCheck = time + UnityEngine.Random.Range(0.5f, 1.5f);
            }
            // if the speed is below the normal playback speed, wait until the next frame can display
            float lodFPS = LODLevels.Length > currentLodLevel ? LODLevels[currentLodLevel].fps : FPS;

            if (lodFPS == 0.0f)
            {
                return;
            }
            float totalSpeed     = Math.Abs(currentAnimation.playbackSpeed * speed);
            float calculatedTick = 1f / lodFPS / totalSpeed;
            float tickRate       = 0.0001f;

            if (calculatedTick > 0.0001f)
            {
                tickRate = calculatedTick;
            }
            float actualDelta = time - lastFrameTime;
            bool  finished    = false;

            float pingPongMult = pingPong ? -1 : 1;

            if (speed * currentAnimation.playbackSpeed < 0)
            {
                currentAnimTime -= actualDelta * pingPongMult * totalSpeed;
            }
            else
            {
                currentAnimTime += actualDelta * pingPongMult * totalSpeed;
            }

            if (currentAnimTime < 0)
            {
                currentAnimTime = currentAnimation.length;
                finished        = true;
            }
            else if (currentAnimTime > currentAnimation.length)
            {
                if (currentAnimation.wrapMode == WrapMode.Loop)
                {
                    currentAnimTime = currentAnimTime - currentAnimation.length;
                }
                finished = true;
            }

            nextTick      = time + tickRate;
            lastFrameTime = time;

            float normalizedTime = currentAnimTime / currentAnimation.length;
            int   previousFrame  = currentFrame;
            int   totalFrames    = currentAnimation.TotalFrames;

            currentFrame = Math.Min((int)Math.Round(normalizedTime * totalFrames), totalFrames - 1);

            // do WrapMode.PingPong
            if (currentAnimation.wrapMode == WrapMode.PingPong)
            {
                if (finished)
                {
                    pingPong = !pingPong;
                }
            }

            if (finished)
            {
                bool stopUpdate = false;
                if (queuedAnims != null && queuedAnims.Count > 0)
                {
                    Play(queuedAnims.Dequeue());
                    stopUpdate = true;
                }
                else if (currentAnimation.wrapMode != WrapMode.Loop && currentAnimation.wrapMode != WrapMode.PingPong)
                {
                    nextTick   = float.MaxValue;
                    stopUpdate = true;
                }
                if (stopUpdate)
                {
                    isPaused = true;
                }
                if (OnAnimationFinished != null)
                {
                    OnAnimationFinished(currentAnimation.AnimationName);
                }
                OnAnimationCompleted(stopUpdate);
                if (stopUpdate)
                {
                    FireAnimationEvents(currentAnimation, totalSpeed, finished);
                    return;
                }
            }

            bool updateFrameTransforms = DisplayFrame(previousFrame);

            if (updateFrameTransforms && currentFrame != previousFrame)
            {
                bool applyRootMotion = currentAnimation.rootMotionMode == RootMotionMode.AppliedToTransform;
                if (hasExposedTransforms || applyRootMotion)
                {
                    MeshFrameDataBase fromFrame   = currentAnimation.GetNearestFrame(currentFrame);
                    MeshFrameDataBase targetFrame = null;
                    int   frameGap    = currentFrame % currentAnimation.FrameSkip;
                    bool  needsInterp = actualDelta > 0 && frameGap != 0;
                    float blendDelta  = 0;
                    if (needsInterp)
                    {
                        blendDelta = currentAnimation.GetInterpolatingFrames(currentFrame, out fromFrame, out targetFrame);
                    }
                    // move exposed transforms
                    if (hasExposedTransforms)
                    {
                        var exposedTransforms = currentAnimation.ExposedTransforms;
                        for (int i = 0; i < exposedTransforms.Length; i++)
                        {
                            Transform child = null;
                            if (fromFrame.exposedTransforms.Length > i && childMap.TryGetValue(exposedTransforms[i], out child))
                            {
                                if (needsInterp)
                                {
                                    Matrix4x4 c = MatrixLerp(fromFrame.exposedTransforms[i], targetFrame.exposedTransforms[i], blendDelta);
                                    MatrixUtils.FromMatrix4x4(child, c);
                                }
                                else
                                {
                                    MatrixUtils.FromMatrix4x4(child, fromFrame.exposedTransforms[i]);
                                }
                            }
                        }
                    }
                    // apply root motion
                    if (applyRootMotion)
                    {
                        if (previousFrame > currentFrame)
                        {
                            // animation looped around, apply motion for skipped frames at the end of the animation
                            for (int i = previousFrame + 1; i < currentAnimation.Frames.Length; i++)
                            {
                                MeshFrameDataBase rootFrame = currentAnimation.GetNearestFrame(i);
                                transform.Translate(rootFrame.rootMotionPosition, Space.Self);
                                transform.Rotate(rootFrame.rootMotionRotation.eulerAngles * actualDelta, Space.Self);
                            }
                            // now apply motion from first frame to current frame
                            for (int i = 0; i <= currentFrame; i++)
                            {
                                MeshFrameDataBase rootFrame = currentAnimation.GetNearestFrame(i);
                                transform.Translate(rootFrame.rootMotionPosition, Space.Self);
                                transform.Rotate(rootFrame.rootMotionRotation.eulerAngles * actualDelta, Space.Self);
                            }
                        }
                        else
                        {
                            for (int i = previousFrame + 1; i <= currentFrame; i++)
                            {
                                MeshFrameDataBase rootFrame = currentAnimation.GetNearestFrame(i);
                                transform.Translate(rootFrame.rootMotionPosition, Space.Self);
                                transform.Rotate(rootFrame.rootMotionRotation.eulerAngles * actualDelta, Space.Self);
                            }
                        }
                    }
                }
            }
            if (OnFrameUpdated != null)
            {
                OnFrameUpdated();
            }

            FireAnimationEvents(currentAnimation, totalSpeed, finished);
        }
Beispiel #11
0
        /// <summary>
        /// The main update loop called from the MeshAnimatorManager
        /// </summary>
        /// <param name="time">Current time</param>
        public void UpdateTick(float time)
        {
            if (initialized == false)
            {
                return;
            }
            if (animations.Length == 0)
            {
                return;
            }
            if (currentAnimIndex < 0 || currentAnimIndex > animations.Length)
            {
                if (defaultAnimation != null)
                {
                    Play(defaultAnimation.animationName);
                }
                else
                {
                    Play(0);
                }
            }
            if ((isVisible == false && updateWhenOffscreen == false) || isPaused || speed == 0 || currentAnimation.playbackSpeed == 0) // return if offscreen or crossfading
            {
                return;
            }
            // update the lod level if needed
            if (LODLevels.Length > 0)
            {
                bool hasLODCamera = LODCamera != null;
                if (!hasLODCamera)
                {
                    int cameraCount = Camera.allCamerasCount;
                    if (cameraCount > 0)
                    {
                        Camera[] cameras = AllocatedArray <Camera> .Get(cameraCount);

                        cameraCount = Camera.GetAllCameras(cameras);
                        LODCamera   = cameras[0].transform;
                        AllocatedArray <Camera> .Return(cameras);

                        hasLODCamera = true;
                    }
                }
                if (hasLODCamera)
                {
                    float dis      = (LODCamera.position - mTransform.position).sqrMagnitude;
                    int   lodLevel = 0;
                    for (int i = 0; i < LODLevels.Length; i++)
                    {
                        if (dis > LODLevels[i].distanceSquared)
                        {
                            lodLevel = i;
                        }
                    }
                    if (currentLodLevel != lodLevel)
                    {
                        currentLodLevel = lodLevel;
                    }
                }
            }
            // if the speed is below the normal playback speed, wait until the next frame can display
            float lodFPS = LODLevels.Length > currentLodLevel ? LODLevels[currentLodLevel].fps : FPS;

            if (lodFPS == 0.0f)
            {
                return;
            }
            float totalSpeed     = Math.Abs(currentAnimation.playbackSpeed * speed);
            float calculatedTick = 1f / lodFPS / totalSpeed;
            float tickRate       = 0.0001f;

            if (calculatedTick > 0.0001f)
            {
                tickRate = calculatedTick;
            }
            float actualDelta = time - lastFrameTime;
            bool  finished    = false;

            float pingPongMult = pingPong ? -1 : 1;

            if (speed * currentAnimation.playbackSpeed < 0)
            {
                currentAnimTime -= actualDelta * pingPongMult * totalSpeed;
            }
            else
            {
                currentAnimTime += actualDelta * pingPongMult * totalSpeed;
            }

            if (currentAnimTime < 0)
            {
                currentAnimTime = currentAnimation.length;
                finished        = true;
            }
            else if (currentAnimTime > currentAnimation.length)
            {
                if (currentAnimation.wrapMode == WrapMode.Loop)
                {
                    currentAnimTime = currentAnimTime - currentAnimation.length;
                }
                finished = true;
            }

            nextTick      = time + tickRate;
            lastFrameTime = time;

            float normalizedTime = currentAnimTime / currentAnimation.length;
            int   previousFrame  = currentFrame;

            currentFrame = Math.Min((int)Math.Round(normalizedTime * currentAnimation.totalFrames), currentAnimation.totalFrames - 1);

            // do WrapMode.PingPong
            if (currentAnimation.wrapMode == WrapMode.PingPong)
            {
                if (finished)
                {
                    pingPong = !pingPong;
                }
            }

            if (finished)
            {
                bool stopUpdate = false;
                if (queuedAnims != null && queuedAnims.Count > 0)
                {
                    Play(queuedAnims.Dequeue());
                    stopUpdate = true;
                }
                else if (currentAnimation.wrapMode != WrapMode.Loop && currentAnimation.wrapMode != WrapMode.PingPong)
                {
                    nextTick   = float.MaxValue;
                    stopUpdate = true;
                }
                OnAnimationFinished(currentAnimation.animationName);
                if (stopUpdate)
                {
                    FireAnimationEvents(currentAnimation, totalSpeed, finished);
                    return;
                }
            }

            // generate frames if needed and show the current animation frame
            currentAnimation.GenerateFrameIfNeeded(baseMesh, currentFrame);

            // if crossfading, lerp the vertices to the next frame
            if (currentCrossFade.isFading)
            {
                if (currentCrossFade.currentFrame >= currentCrossFade.framesNeeded)
                {
                    currentFrame    = 0;
                    previousFrame   = -1;
                    currentAnimTime = 0;
                    ReturnCrossfadeToPool();
                }
                else
                {
#if !THREADS_ENABLED
                    GenerateCrossfadeFrame();
#endif
                    if (currentCrossFade.generatedFrame >= currentCrossFade.currentFrame)
                    {
                        if (crossFadeMesh == null)
                        {
                            crossFadeMesh = GetCrossfadeFromPool();
                        }
                        crossFadeMesh.vertices = currentCrossFade.frame.positions;
                        crossFadeMesh.bounds   = currentCrossFade.frame.bounds;
                        if (recalculateCrossfadeNormals)
                        {
                            crossFadeMesh.RecalculateNormals();
                        }
                        meshFilter.sharedMesh = crossFadeMesh;
                        currentCrossFade.ReturnFrame();
                        currentCrossFade.currentFrame++;
                        if (currentCrossFade.currentFrame < currentCrossFade.framesNeeded)
                        {
                            EnqueueAnimatorForCrossfade(this);
                        }
                        // move exposed transforms
                        bool applyRootMotion = currentAnimation.rootMotionMode == MeshAnimation.RootMotionMode.AppliedToTransform;
                        if (hasExposedTransforms || applyRootMotion)
                        {
                            float         delta     = currentCrossFade.currentFrame / (float)currentCrossFade.framesNeeded;
                            MeshFrameData fromFrame = currentCrossFade.fromFrame;
                            MeshFrameData toFrame   = currentCrossFade.toFrame;
                            // move exposed transforms
                            if (hasExposedTransforms)
                            {
                                for (int i = 0; i < currentAnimation.exposedTransforms.Length; i++)
                                {
                                    Transform child = null;
                                    if (fromFrame.exposedTransforms.Length <= i || toFrame.exposedTransforms.Length <= i)
                                    {
                                        continue;
                                    }
                                    if (childMap.TryGetValue(currentAnimation.exposedTransforms[i], out child))
                                    {
                                        Matrix4x4 f = fromFrame.exposedTransforms[i];
                                        Matrix4x4 t = toFrame.exposedTransforms[i];
                                        Matrix4x4 c = MatrixLerp(f, t, delta);
                                        MatrixUtils.FromMatrix4x4(child, c);
                                    }
                                }
                            }
                            // apply root motion
                            if (applyRootMotion)
                            {
                                Vector3    pos = Vector3.Lerp(fromFrame.rootMotionPosition, toFrame.rootMotionPosition, delta);
                                Quaternion rot = Quaternion.Lerp(fromFrame.rootMotionRotation, toFrame.rootMotionRotation, delta);
                                transform.Translate(pos, Space.Self);
                                transform.Rotate(rot.eulerAngles * Time.deltaTime, Space.Self);
                            }
                        }
                    }
                }
            }
            if (currentCrossFade.isFading == false)
            {
                currentAnimation.DisplayFrame(meshFilter, currentFrame, previousFrame);
                if (currentFrame != previousFrame)
                {
                    bool applyRootMotion = currentAnimation.rootMotionMode == MeshAnimation.RootMotionMode.AppliedToTransform;
                    if (hasExposedTransforms || applyRootMotion)
                    {
                        MeshFrameData fromFrame   = currentAnimation.GetNearestFrame(currentFrame);
                        MeshFrameData targetFrame = null;
                        int           frameGap    = currentFrame % currentAnimation.frameSkip;
                        bool          needsInterp = actualDelta > 0 && frameGap != 0;
                        float         blendDelta  = 0;
                        if (needsInterp)
                        {
                            blendDelta = currentAnimation.GetInterpolatingFrames(currentFrame, out fromFrame, out targetFrame);
                        }
                        // move exposed transforms
                        if (hasExposedTransforms)
                        {
                            for (int i = 0; i < currentAnimation.exposedTransforms.Length; i++)
                            {
                                Transform child = null;
                                if (fromFrame.exposedTransforms.Length > i && childMap.TryGetValue(currentAnimation.exposedTransforms[i], out child))
                                {
                                    if (needsInterp)
                                    {
                                        Matrix4x4 c = MatrixLerp(fromFrame.exposedTransforms[i], targetFrame.exposedTransforms[i], blendDelta);
                                        MatrixUtils.FromMatrix4x4(child, c);
                                    }
                                    else
                                    {
                                        MatrixUtils.FromMatrix4x4(child, fromFrame.exposedTransforms[i]);
                                    }
                                }
                            }
                        }
                        // apply root motion
                        if (applyRootMotion)
                        {
                            if (previousFrame > currentFrame)
                            {
                                // animation looped around, apply motion for skipped frames at the end of the animation
                                for (int i = previousFrame + 1; i < currentAnimation.frames.Length; i++)
                                {
                                    MeshFrameData rootFrame = currentAnimation.GetNearestFrame(i);
                                    transform.Translate(rootFrame.rootMotionPosition, Space.Self);
                                    transform.Rotate(rootFrame.rootMotionRotation.eulerAngles * Time.deltaTime, Space.Self);
                                }
                                // now apply motion from first frame to current frame
                                for (int i = 0; i <= currentFrame; i++)
                                {
                                    MeshFrameData rootFrame = currentAnimation.GetNearestFrame(i);
                                    transform.Translate(rootFrame.rootMotionPosition, Space.Self);
                                    transform.Rotate(rootFrame.rootMotionRotation.eulerAngles * Time.deltaTime, Space.Self);
                                }
                            }
                            else
                            {
                                for (int i = previousFrame + 1; i <= currentFrame; i++)
                                {
                                    MeshFrameData rootFrame = currentAnimation.GetNearestFrame(i);
                                    transform.Translate(rootFrame.rootMotionPosition, Space.Self);
                                    transform.Rotate(rootFrame.rootMotionRotation.eulerAngles * Time.deltaTime, Space.Self);
                                }
                            }
                        }
                    }
                }
            }
            OnFrameUpdated();

            FireAnimationEvents(currentAnimation, totalSpeed, finished);
        }