public void Reset(bool destroying) { if (!isReset) { ReturnFrame(destroying); isFading = false; endTime = 0; currentFrame = 0; framesNeeded = 0; fromFrame = null; toFrame = null; isReset = true; } }
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); } } }
public float GetInterpolatingFrames(int frame, out MeshFrameDataBase previousFrame, out MeshFrameDataBase nextFrame) { bool needsInterp = frame % frameSkip != 0; var frameData = Frames; if (!needsInterp) { int frameIndex = frame / frameSkip; previousFrame = frameData[frameIndex]; if (frameIndex + 1 >= frameData.Length) { nextFrame = frameData[0]; } else { nextFrame = frameData[frameIndex + 1]; } return(0); } float framePerc = (float)frame / (float)(frameSkip * frameData.Length); int skipFrame = (int)(framePerc * frameData.Length); float prevFramePerc = (float)skipFrame / (float)frameData.Length; float nextFramePerc = Mathf.Clamp01((float)(skipFrame + 1) / (float)frameData.Length); previousFrame = frameData[skipFrame]; if (skipFrame + 1 >= frameData.Length) { nextFrame = frameData[0]; } else { nextFrame = frameData[skipFrame + 1]; } float lerpVal = Mathf.Lerp(0, 1, (framePerc - prevFramePerc) / (nextFramePerc - prevFramePerc)); return(lerpVal); }
/// <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); }
public abstract void SetFrameData(int frame, MeshFrameDataBase frameData);