/// <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); }
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 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); } }
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 void PopulateArrays() { TriangleIndex = AllocatedArray <int> .Get(_reserved); VertexIndex = AllocatedArray <int> .Get(_reserved); }
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); } }
/// <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); }
/// <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); }