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); }
public void UpdateTick(float time) { if (initialized == false) { return; } MeshAnimation cAnim = currentAnimation; if ((isVisible == false && updateWhenOffscreen == false) || isPaused || speed == 0 || cAnim.playbackSpeed == 0) // return if offscreen or crossfading { return; } // 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; float totalSpeed = Mathf.Abs(cAnim.playbackSpeed * speed); float tickRate = Mathf.Max(0.0001f, 1f / lodFPS / totalSpeed); float actualDelta = time - lastFrameTime; bool finished = false; float pingPongMult = pingPong ? -1 : 1; if (speed * cAnim.playbackSpeed < 0) { currentAnimTime -= actualDelta * pingPongMult * totalSpeed; } else { currentAnimTime += actualDelta * pingPongMult * totalSpeed; } if (currentAnimTime < 0) { currentAnimTime = cAnim.length; finished = true; } else if (currentAnimTime > cAnim.length) { if (cAnim.wrapMode == WrapMode.Loop) { currentAnimTime = 0; } finished = true; } nextTick = time + tickRate; lastFrameTime = time; float normalizedTime = currentAnimTime / cAnim.length; int previousFrame = currentFrame; currentFrame = Mathf.Min(Mathf.RoundToInt(normalizedTime * cAnim.totalFrames), cAnim.totalFrames - 1); // do WrapMode.PingPong if (cAnim.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 (cAnim.wrapMode != WrapMode.Loop && cAnim.wrapMode != WrapMode.PingPong) { nextTick = float.MaxValue; stopUpdate = true; } if (OnAnimationFinished != null) { OnAnimationFinished(cAnim.animationName); } if (stopUpdate) { FireAnimationEvents(cAnim, totalSpeed, finished); return; } } // generate frames if needed and show the current animation frame cAnim.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 exposedTransforms = childMap != null; bool applyRootMotion = cAnim.rootMotionMode == MeshAnimation.RootMotionMode.AppliedToTransform; if (exposedTransforms || applyRootMotion) { float delta = currentCrossFade.currentFrame / (float)currentCrossFade.framesNeeded; MeshFrameData fromFrame = currentCrossFade.fromFrame; MeshFrameData toFrame = currentCrossFade.toFrame; // move exposed transforms if (exposedTransforms) { for (int i = 0; i < cAnim.exposedTransforms.Length; i++) { Transform child = null; if (fromFrame.exposedTransforms.Length <= i || toFrame.exposedTransforms.Length <= i) { continue; } if (childMap != null && childMap.TryGetValue(cAnim.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) { cAnim.DisplayFrame(meshFilter, currentFrame, previousFrame); if (currentFrame != previousFrame) { bool exposedTransforms = childMap != null; bool applyRootMotion = cAnim.rootMotionMode == MeshAnimation.RootMotionMode.AppliedToTransform; if (exposedTransforms || applyRootMotion) { MeshFrameData frame = cAnim.GetNearestFrame(currentFrame); // move exposed transforms if (exposedTransforms) { for (int i = 0; i < cAnim.exposedTransforms.Length; i++) { Transform child = null; if (frame.exposedTransforms.Length > i && childMap != null && childMap.TryGetValue(cAnim.exposedTransforms[i], out child)) { MatrixUtils.FromMatrix4x4(child, frame.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 < cAnim.frames.Length; i++) { MeshFrameData rootFrame = cAnim.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 = cAnim.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 = cAnim.GetNearestFrame(i); transform.Translate(rootFrame.rootMotionPosition, Space.Self); transform.Rotate(rootFrame.rootMotionRotation.eulerAngles * Time.deltaTime, Space.Self); } } } } } } if (OnFrameUpdated != null) { OnFrameUpdated(); } FireAnimationEvents(cAnim, totalSpeed, finished); // update the lod level if needed if (LODLevels.Length > 0 && (LODCamera != null || Camera.main)) { if (LODCamera == null) { LODCamera = Camera.main.transform; } float dis = (LODCamera.position - mTransform.position).sqrMagnitude; int lodLevel = 0; for (int i = 0; i < LODLevels.Length; i++) { if (dis > LODLevels[i].distance * LODLevels[i].distance) { lodLevel = i; } } if (currentLodLevel != lodLevel) { currentLodLevel = lodLevel; } } }
/// <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); }