diff --git a/Runtime/Scripts/AnimationBakingSystem.cs b/Runtime/Scripts/AnimationBakingSystem.cs new file mode 100644 index 0000000..2c26cf8 --- /dev/null +++ b/Runtime/Scripts/AnimationBakingSystem.cs @@ -0,0 +1,37 @@ +using Unity.Collections; +using Unity.Entities; +using Unity.Transforms; +using UnityEngine; + +namespace TAO.VertexAnimation +{ + +[RequireMatchingQueriesForUpdate] +public partial class AnimationBakingSystem : SystemBase +{ + protected override void OnUpdate() + { + EntityCommandBuffer entityCommandBuffer = new EntityCommandBuffer( Allocator.Temp ); + foreach (var (animator, wait) in + SystemAPI.Query, RefRW>()) + { + DynamicBuffer children = EntityManager.GetBuffer < Child >( wait.ValueRO.AnimatorEntity ); + Debug.Log( children.Length ); + animator.ValueRW.SkinnedMeshes = new NativeArray < Entity >( children.Length, Allocator.Persistent ); + int i = 0; + foreach ( Child child in children ) + { + animator.ValueRW.SkinnedMeshes[i] = child.Value; + i++; + } + + wait.ValueRW.IsInitialized = true; + entityCommandBuffer.RemoveComponent( wait.ValueRO.AnimatorEntity ); + //EntityManager.RemoveComponent < AnimatorWaitingForBaking >( wait.AnimatorEntity ); + } + + entityCommandBuffer.Playback( EntityManager ); + } +} + +} diff --git a/Runtime/Scripts/AnimationBakingSystem.cs.meta b/Runtime/Scripts/AnimationBakingSystem.cs.meta new file mode 100644 index 0000000..5cc1554 --- /dev/null +++ b/Runtime/Scripts/AnimationBakingSystem.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 2720e153eb0b4e8c8cdc100f844448f2 +timeCreated: 1670713611 \ No newline at end of file diff --git a/Runtime/Scripts/AnimationCurve.cs b/Runtime/Scripts/AnimationCurve.cs new file mode 100644 index 0000000..93a2159 --- /dev/null +++ b/Runtime/Scripts/AnimationCurve.cs @@ -0,0 +1,117 @@ +using System; +using Unity.Collections; +using Unity.Entities; +using Unity.Mathematics; +using UnityEngine; + +namespace TAO.VertexAnimation +{ +public static class AnimationCurveExtention +{ + public static float[] GenerateCurveArray(this UnityEngine.AnimationCurve self, int numberOfSamples) + { + float[] returnArray = new float[numberOfSamples]; + for (int j = 0; j < numberOfSamples; j++) + { + returnArray[j] = self.Evaluate((float)j / numberOfSamples); + //Debug.Log( returnArray[j] ); + } + + return returnArray; + } + + public static EntitiesAnimationCurveReference LoadUnityAnimationCurveIntoEntitiesAnimationCurve(this UnityEngine.AnimationCurve animationCurve, int numberOfSamples) + { + float[] samplePoints = animationCurve.GenerateCurveArray(numberOfSamples); + EntitiesAnimationCurveReference component = new EntitiesAnimationCurveReference(); + using ( BlobBuilder blobBuilder = new BlobBuilder( Allocator.Temp ) ) + { + ref EntitiesAnimationCurve animationCurveEcs = ref blobBuilder.ConstructRoot < EntitiesAnimationCurve >(); + BlobBuilderArray curveSamples = blobBuilder.Allocate( ref animationCurveEcs.SampledPoints, numberOfSamples ); + for (int i = 0; i < numberOfSamples; i++) + { + // Copy data. + curveSamples[i] = samplePoints[i]; + } + + component.EntitiesCurve = blobBuilder.CreateBlobAssetReference < EntitiesAnimationCurve >( Allocator.Persistent ); + } + + component.EntitiesCurve.Value.NumberOfSamples = numberOfSamples; + return component; + } +} +[Serializable] +public struct EntitiesAnimationCurve +{ + public BlobArray < float > SampledPoints; + public int NumberOfSamples; + + public float GetValueAtTime(float time) + { + var approxSampleIndex = (NumberOfSamples - 1) * time; + var sampleIndexBelow = (int)math.floor(approxSampleIndex); + if (sampleIndexBelow >= NumberOfSamples - 1) + { + return SampledPoints[NumberOfSamples - 1]; + } + var indexRemainder = approxSampleIndex - sampleIndexBelow; + return math.lerp(SampledPoints[sampleIndexBelow], SampledPoints[sampleIndexBelow + 1], indexRemainder); + } + + +} +[Serializable] +public struct EntitiesAnimationCurveReference +{ + public BlobAssetReference EntitiesCurve; + public readonly float GetValueAtTime(float time) => EntitiesCurve.Value.GetValueAtTime(time); +} + +[Serializable] +public struct EntitiesAnimationCurveLibrary : IComponentData +{ + public NativeList < EntitiesAnimationCurveReference > CurveReferences; +} + + +public struct SampledAnimationCurve : System.IDisposable +{ + NativeArray sampledFloat; + /// Must be 2 or higher + public SampledAnimationCurve(AnimationCurve ac, int samples) + { + sampledFloat = new NativeArray(samples, Allocator.Persistent); + float timeFrom = ac.keys[0].time; + float timeTo = ac.keys[ac.keys.Length - 1].time; + float timeStep = (timeTo - timeFrom) / (samples - 1); + + for (int i = 0; i < samples; i++) + { + sampledFloat[i] = ac.Evaluate(timeFrom + (i * timeStep)); + } + } + + public void Dispose() + { + sampledFloat.Dispose(); + } + + /// Must be from 0 to 1 + public float EvaluateLerp(float time) + { + int len = sampledFloat.Length - 1; + float clamp01 = time < 0 ? 0 : (time > 1 ? 1 : time); + float floatIndex = (clamp01 * len); + int floorIndex = (int)math.floor(floatIndex); + if (floorIndex == len) + { + return sampledFloat[len]; + } + + float lowerValue = sampledFloat[floorIndex]; + float higherValue = sampledFloat[floorIndex + 1]; + return math.lerp(lowerValue, higherValue, math.frac(floatIndex)); + } +} +} diff --git a/Runtime/Scripts/AnimationCurve.cs.meta b/Runtime/Scripts/AnimationCurve.cs.meta new file mode 100644 index 0000000..ec85877 --- /dev/null +++ b/Runtime/Scripts/AnimationCurve.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 1139ec583eba4a088b003096cc28582f +timeCreated: 1670717467 \ No newline at end of file diff --git a/Runtime/Scripts/AnimationCurveAuthoring.cs b/Runtime/Scripts/AnimationCurveAuthoring.cs new file mode 100644 index 0000000..def7de1 --- /dev/null +++ b/Runtime/Scripts/AnimationCurveAuthoring.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; +using Unity.Collections; +using Unity.Entities; +using UnityEngine; + +namespace TAO.VertexAnimation +{ + +public class AnimationCurveAuthoring : MonoBehaviour +{ + public List < AnimationCurve > CurvesToConvert; + public int NumberOfSamples = 256; +} + + +public class AnimationCurveAuthoringBaker : Baker +{ + public override void Bake( AnimationCurveAuthoring authoring ) + { + EntitiesAnimationCurveLibrary curveLibrary = new EntitiesAnimationCurveLibrary(); + curveLibrary.CurveReferences = new NativeList < EntitiesAnimationCurveReference >( 5, Allocator.Persistent ); + foreach ( AnimationCurve curve in authoring.CurvesToConvert ) + { + curveLibrary.CurveReferences.Add( curve.LoadUnityAnimationCurveIntoEntitiesAnimationCurve( authoring.NumberOfSamples ) ); + } + + AddComponent( curveLibrary ); + } +} +} diff --git a/Runtime/Scripts/AnimationCurveAuthoring.cs.meta b/Runtime/Scripts/AnimationCurveAuthoring.cs.meta new file mode 100644 index 0000000..c98eb72 --- /dev/null +++ b/Runtime/Scripts/AnimationCurveAuthoring.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 600a2b395a2a4b1ebe07b9248cff83ca +timeCreated: 1670719445 \ No newline at end of file diff --git a/Runtime/Scripts/AnimationLibraryComponentAuthoring.cs b/Runtime/Scripts/AnimationLibraryComponentAuthoring.cs index b8d221b..cf89319 100644 --- a/Runtime/Scripts/AnimationLibraryComponentAuthoring.cs +++ b/Runtime/Scripts/AnimationLibraryComponentAuthoring.cs @@ -16,10 +16,11 @@ namespace TAO.VertexAnimation public bool DebugMode = false; public uint Seed; } - -public struct SkinnedMeshEntity : IBufferElementData + +public struct AnimatorWaitingForBaking : IComponentData { - public Entity Value; + public bool IsInitialized; + public Entity AnimatorEntity; } public struct AnimationLibraryComponent : IComponentData @@ -74,41 +75,36 @@ public class AnimationLibraryComponentBaker : Baker < AnimationLibraryComponentA ref VA_AnimationLibraryData animationsRef = ref animLib.Value; Random random = new Random( authoring.Seed != 0 ? authoring.Seed : 42 ); int index = random.NextInt( 20 ); + // Add animator to 'parent'. AnimatorComponent animatorComponent = new AnimatorComponent { Enabled = true, - AnimationName = animationsRef.animations[index].name, + AnimationName = animationsRef.animations[5].name, AnimationIndex = 2, AnimationIndexNext = -1, AnimationTime = 0, AnimationLibrary = animLib + }; AddComponent(animatorComponent); - AnimatorBlendStateComponent animatorStateComponent = new AnimatorBlendStateComponent { BlendingEnabled = true, - AnimationIndex = 1, - AnimationIndexNext = -1, - AnimationTime = 0 + ToAnimationIndex = 1, + BlendingCurrentTime = 0.0f, + AnimationBlendingCurveIndex = 2, + BlendingDuration = 2.5f, + AnimationTime = 0.0f, }; AddComponent( animatorStateComponent ); - - var boneEntityArray = AddBuffer(); - MeshRenderer[] meshRenderers = - authoring.transform.GetComponentsInChildren < MeshRenderer >(); - boneEntityArray.ResizeUninitialized(meshRenderers.Length); - for (int meshIndex = 0; meshIndex < meshRenderers.Length; ++meshIndex) - { - var meshEntity = GetEntity(meshRenderers[meshIndex]); - boneEntityArray[meshIndex] = new SkinnedMeshEntity {Value = meshEntity}; - } + AddComponent(new AnimatorWaitingForBaking{ AnimatorEntity = GetEntity(), IsInitialized = false}); } } + //[GenerateAuthoringComponent] public struct AnimatorComponent : IComponentData { @@ -117,17 +113,18 @@ public struct AnimatorComponent : IComponentData public int AnimationIndex; public int AnimationIndexNext; public float AnimationTime; - public float AnimationTimeNext; public BlobAssetReference AnimationLibrary; + public NativeArray < Entity > SkinnedMeshes; } public struct AnimatorBlendStateComponent : IComponentData { public bool BlendingEnabled; - public int AnimationIndex; - public int AnimationIndexNext; public float AnimationTime; - public float AnimationTimeNext; - + public int ToAnimationIndex; + public float BlendingDuration; + public float BlendingCurrentTime; + public int AnimationBlendingCurveIndex; } + } \ No newline at end of file diff --git a/Runtime/Scripts/AnimatorSystem.cs b/Runtime/Scripts/AnimatorSystem.cs index 7ddffa1..3739d85 100644 --- a/Runtime/Scripts/AnimatorSystem.cs +++ b/Runtime/Scripts/AnimatorSystem.cs @@ -1,5 +1,6 @@ using Unity.Burst; using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; using Unity.Entities; using Unity.Jobs; using Unity.Mathematics; @@ -37,8 +38,17 @@ public partial struct AnimatorSystem : ISystem EntityCommandBuffer ecb = SystemAPI.GetSingleton < BeginSimulationEntityCommandBufferSystem.Singleton >(). CreateCommandBuffer( state.WorldUnmanaged ); + RefRW < EntitiesAnimationCurveLibrary > curveLibrary = + SystemAPI.GetSingletonRW < EntitiesAnimationCurveLibrary >(); + UpdateAnimatorJob job = - new UpdateAnimatorJob { DeltaTime = deltaTime, StartIndex = 0, Ecb = ecb.AsParallelWriter() }; + new UpdateAnimatorJob + { + DeltaTime = deltaTime, + StartIndex = 0, + Ecb = ecb.AsParallelWriter(), + EntitiesAnimationCurveLibrary = curveLibrary + }; job.Run(); @@ -67,18 +77,112 @@ public partial struct UpdateAnimatorJob : IJobEntity public float DeltaTime; public int StartIndex; public EntityCommandBuffer.ParallelWriter Ecb; + [NativeDisableUnsafePtrRestriction] + public RefRW < EntitiesAnimationCurveLibrary > EntitiesAnimationCurveLibrary; [BurstCompile] public void Execute( ref AnimatorComponent animator, - ref AnimatorBlendStateComponent animatorBlendState, - in DynamicBuffer < SkinnedMeshEntity > buffer ) + ref AnimatorBlendStateComponent animatorBlendState ) { if ( animator.Enabled ) { // Get the animation lib data. ref VA_AnimationLibraryData animationsRef = ref animator.AnimationLibrary.Value; + int animationIndexNextBlend = 0; + float animationTimeNextBlend = 0.0f; + float blendValue = 0.0f; + + if ( animatorBlendState.BlendingEnabled ) + { + animatorBlendState.BlendingCurrentTime += DeltaTime; + + if ( animatorBlendState.BlendingCurrentTime > + animatorBlendState.BlendingDuration ) + { + animator.AnimationIndex = animatorBlendState.ToAnimationIndex; + animator.AnimationIndexNext = -1; + animator.AnimationTime = animatorBlendState.AnimationTime; + + animatorBlendState.BlendingEnabled = false; + animatorBlendState.ToAnimationIndex = -1; + animatorBlendState.AnimationTime = 0; + animatorBlendState.BlendingDuration = 0.0f; + animatorBlendState.BlendingCurrentTime = 0.0f; + animatorBlendState.AnimationBlendingCurveIndex = -1; + + for ( int i = 0; i < animator.SkinnedMeshes.Length; i++ ) + { + BlendingAnimationDataComponent blendingAnimationDataComponent = + new BlendingAnimationDataComponent { Value = 0.0f }; + + Ecb.SetComponent < BlendingAnimationDataComponent >( + StartIndex, + animator.SkinnedMeshes[i], + blendingAnimationDataComponent ); + + StartIndex++; + SecondAnimationDataComponent vaAnimationDataComponent2 = new SecondAnimationDataComponent(); + + vaAnimationDataComponent2.Value = new float4 + { + x = 0.0f, + y = animatorBlendState.ToAnimationIndex, + z = 0.0f, + w = animatorBlendState.ToAnimationIndex + }; + + Ecb.SetComponent < SecondAnimationDataComponent >( + StartIndex, + animator.SkinnedMeshes[i], + vaAnimationDataComponent2 ); + + StartIndex++; + } + + } + else + { + float blendTime = + 1.0f / animatorBlendState.BlendingDuration * animatorBlendState.BlendingCurrentTime; + + blendValue = EntitiesAnimationCurveLibrary.ValueRW. + CurveReferences[ + animatorBlendState.AnimationBlendingCurveIndex]. + GetValueAtTime( blendTime ); + + animatorBlendState.AnimationTime += DeltaTime * + animationsRef. + animations[animatorBlendState.ToAnimationIndex]. + frameTime; + + if ( animatorBlendState.AnimationTime > + animationsRef.animations[animatorBlendState.ToAnimationIndex].duration ) + { + // Set time. Using the difference to smoothen out animations when looping. + animatorBlendState.AnimationTime -= + animationsRef.animations[animatorBlendState.ToAnimationIndex].duration; + + //animator.animationIndexNext = vaAnimatorStateComponent.Rand.NextInt( 20 ); + } + + // Lerp animations. + // Set animation for lerp. + animationIndexNextBlend = animatorBlendState.ToAnimationIndex; + + // Calculate next frame time for lerp. + animationTimeNextBlend = animatorBlendState.AnimationTime + + ( 1.0f / animationsRef.animations[animationIndexNextBlend].maxFrames ); + + if ( animationTimeNextBlend > animationsRef.animations[animationIndexNextBlend].duration ) + { + // Set time. Using the difference to smooth out animations when looping. + animationTimeNextBlend -= animatorBlendState.AnimationTime; + } + } + } + //if ( animator.AnimationName != vaAnimatorStateComponent.CurrentAnimationName ) //{ // // Set the animation index on the AnimatorComponent to play this animation. @@ -118,50 +222,7 @@ public partial struct UpdateAnimatorJob : IJobEntity animationTimeNext -= animator.AnimationTime; } - int animationIndexNextBlend = 0; - float animationTimeNextBlend = 0.0f; - - if ( animatorBlendState.BlendingEnabled ) - { - // 'Play' the actual animation. - animatorBlendState.AnimationTime += DeltaTime * - animationsRef. - animations[animatorBlendState.AnimationIndex]. - frameTime; - - if ( animatorBlendState.AnimationTime > - animationsRef.animations[animatorBlendState.AnimationIndex].duration ) - { - // Set time. Using the difference to smoothen out animations when looping. - animatorBlendState.AnimationTime -= - animationsRef.animations[animatorBlendState.AnimationIndex].duration; - - //animator.animationIndexNext = vaAnimatorStateComponent.Rand.NextInt( 20 ); - } - - // Lerp animations. - // Set animation for lerp. - animationIndexNextBlend = animatorBlendState.AnimationIndexNext; - - if ( animationIndexNextBlend < 0 ) - { - animationIndexNextBlend = animatorBlendState.AnimationIndex; - - //animator.animationIndexNext = animationIndexNext + 1; - } - - // Calculate next frame time for lerp. - animationTimeNextBlend = animatorBlendState.AnimationTime + - ( 1.0f / animationsRef.animations[animationIndexNextBlend].maxFrames ); - - if ( animationTimeNextBlend > animationsRef.animations[animationIndexNextBlend].duration ) - { - // Set time. Using the difference to smooth out animations when looping. - animationTimeNextBlend -= animatorBlendState.AnimationTime; - } - } - - for ( int i = 0; i < buffer.Length; i++ ) + for ( int i = 0; i < animator.SkinnedMeshes.Length; i++ ) { FirstAnimationDataComponent vaAnimationDataComponent = new FirstAnimationDataComponent(); @@ -173,10 +234,24 @@ public partial struct UpdateAnimatorJob : IJobEntity w = VA_AnimationLibraryUtils.GetAnimationMapIndex( ref animationsRef, animationIndexNext ) }; - SystemAPI.SetComponent < FirstAnimationDataComponent >( buffer[i].Value, vaAnimationDataComponent ); + Ecb.SetComponent < FirstAnimationDataComponent >( + StartIndex, + animator.SkinnedMeshes[i], + vaAnimationDataComponent ); + + StartIndex++; if ( animatorBlendState.BlendingEnabled ) { + BlendingAnimationDataComponent blendingAnimationDataComponent = + new BlendingAnimationDataComponent { Value = blendValue }; + + Ecb.SetComponent < BlendingAnimationDataComponent >( + StartIndex, + animator.SkinnedMeshes[i], + blendingAnimationDataComponent ); + + StartIndex++; SecondAnimationDataComponent vaAnimationDataComponent2 = new SecondAnimationDataComponent(); vaAnimationDataComponent2.Value = new float4 @@ -184,16 +259,19 @@ public partial struct UpdateAnimatorJob : IJobEntity x = animatorBlendState.AnimationTime, y = VA_AnimationLibraryUtils.GetAnimationMapIndex( ref animationsRef, - animatorBlendState.AnimationIndex ), + animatorBlendState.ToAnimationIndex ), z = animationTimeNextBlend, w = VA_AnimationLibraryUtils.GetAnimationMapIndex( ref animationsRef, animationIndexNextBlend ) }; - SystemAPI.SetComponent < SecondAnimationDataComponent >( - buffer[i].Value, + Ecb.SetComponent < SecondAnimationDataComponent >( + StartIndex, + animator.SkinnedMeshes[i], vaAnimationDataComponent2 ); + + StartIndex++; } }