Netcode Bootstrap
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 76a64ddea3967624a8916c6160d19ed8
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+177
@@ -0,0 +1,177 @@
|
||||
using Unity.Burst;
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using Unity.Deformations;
|
||||
using Unity.Entities;
|
||||
using Unity.Jobs;
|
||||
using Unity.Rendering;
|
||||
using Unity.Transforms;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
|
||||
[DisableAutoCreation]
|
||||
[UpdateAfter(typeof(RukhankaAnimationInjectionSystemGroup))]
|
||||
partial struct AnimationApplicationSystem: ISystem
|
||||
{
|
||||
private EntityQuery
|
||||
boneObjectEntitiesWithParentQuery,
|
||||
boneObjectEntitiesNoParentQuery;
|
||||
|
||||
NativeParallelHashMap<Hash128, BlobAssetReference<BoneRemapTableBlob>> rigToSkinnedMeshRemapTables;
|
||||
int newRemapTablesCounter;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[BurstCompile]
|
||||
public void OnCreate(ref SystemState ss)
|
||||
{
|
||||
var eqb0 = new EntityQueryBuilder(Allocator.Temp)
|
||||
.WithAll<AnimatorEntityRefComponent, Parent>()
|
||||
.WithAllRW<LocalTransform>();
|
||||
boneObjectEntitiesWithParentQuery = ss.GetEntityQuery(eqb0);
|
||||
|
||||
var eqb1 = new EntityQueryBuilder(Allocator.Temp)
|
||||
.WithAll<AnimatorEntityRefComponent>()
|
||||
.WithNone<Parent>()
|
||||
.WithAllRW<LocalTransform>();
|
||||
boneObjectEntitiesNoParentQuery = ss.GetEntityQuery(eqb1);
|
||||
|
||||
var rq = new EntityQueryBuilder(Allocator.Temp)
|
||||
.WithAll<AnimatorEntityRefComponent>()
|
||||
.Build(ref ss);
|
||||
ss.RequireForUpdate(rq);
|
||||
|
||||
rigToSkinnedMeshRemapTables = new NativeParallelHashMap<Hash128, BlobAssetReference<BoneRemapTableBlob>>(128, Allocator.Persistent);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[BurstCompile]
|
||||
public void OnDestroy(ref SystemState ss)
|
||||
{
|
||||
foreach (var kv in rigToSkinnedMeshRemapTables)
|
||||
kv.Value.Dispose();
|
||||
rigToSkinnedMeshRemapTables.Dispose();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[BurstCompile]
|
||||
public void OnUpdate(ref SystemState ss)
|
||||
{
|
||||
ref var runtimeData = ref SystemAPI.GetSingletonRW<RuntimeAnimationData>().ValueRW;
|
||||
|
||||
var fillRigToSkinnedMeshRemapTablesJH = FillRigToSkinBonesRemapTableCache(ref ss, ss.Dependency);
|
||||
|
||||
// Propagate local animated transforms to the entities with and without parents
|
||||
var propagateTRSToEntitiesWithParentsJH = PropagateAnimatedBonesToEntitiesTRS(ref ss, runtimeData, boneObjectEntitiesWithParentQuery, true, ss.Dependency);
|
||||
var propagateTRSToEntitiesNoParentsJH = PropagateAnimatedBonesToEntitiesTRS(ref ss, runtimeData, boneObjectEntitiesNoParentQuery, false, propagateTRSToEntitiesWithParentsJH);
|
||||
|
||||
// Make corresponding skin matrices for all skinned meshes
|
||||
var jh = JobHandle.CombineDependencies(fillRigToSkinnedMeshRemapTablesJH, propagateTRSToEntitiesNoParentsJH);
|
||||
var applySkinJH = ApplySkinning(ref ss, runtimeData, jh);
|
||||
|
||||
// Update render bounds for meshes that request this
|
||||
var updateRenderBoundsJH = UpdateRenderBounds(ref ss, runtimeData, ss.Dependency);
|
||||
|
||||
ss.Dependency = JobHandle.CombineDependencies(applySkinJH, updateRenderBoundsJH);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
unsafe JobHandle FillRigToSkinBonesRemapTableCache(ref SystemState ss, JobHandle dependsOn)
|
||||
{
|
||||
var rigDefinitionComponentLookup = SystemAPI.GetComponentLookup<RigDefinitionComponent>(true);
|
||||
|
||||
newRemapTablesCounter = 0;
|
||||
|
||||
// Count new remap tables count
|
||||
var countNumberOfNewRemapTablesJob = new CountNumberOfNewRemapTablesJob()
|
||||
{
|
||||
rigDefinitionArr = rigDefinitionComponentLookup,
|
||||
numberOfNewRemapTables = new UnsafeAtomicCounter32(UnsafeUtility.AddressOf(ref newRemapTablesCounter)),
|
||||
rigToSkinnedMeshRemapTables = rigToSkinnedMeshRemapTables
|
||||
};
|
||||
var countNumberOfNewRemapTablesJH = countNumberOfNewRemapTablesJob.ScheduleParallel(dependsOn);
|
||||
|
||||
// Reserve necessary space in remap table cache
|
||||
var increaseRigRemapTableCapacityJob = new IncreaseRigRemapTableCapacityJob()
|
||||
{
|
||||
numberOfNewRemapTables = (int*)UnsafeUtility.AddressOf(ref newRemapTablesCounter),
|
||||
rigToSkinnedMeshRemapTables = rigToSkinnedMeshRemapTables
|
||||
};
|
||||
var increaseRigRemapTableCapacityJH = increaseRigRemapTableCapacityJob.Schedule(countNumberOfNewRemapTablesJH);
|
||||
|
||||
// Fill table cache with new tables
|
||||
var fillRigToSkinBonesRemapTableCacheJob = new FillRigToSkinBonesRemapTableCacheJob()
|
||||
{
|
||||
rigDefinitionArr = rigDefinitionComponentLookup,
|
||||
rigToSkinnedMeshRemapTables = rigToSkinnedMeshRemapTables.AsParallelWriter(),
|
||||
newRemapTablesCounter = (int*)UnsafeUtility.AddressOf(ref newRemapTablesCounter),
|
||||
};
|
||||
|
||||
var rv = fillRigToSkinBonesRemapTableCacheJob.ScheduleParallelByRef(increaseRigRemapTableCapacityJH);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
JobHandle UpdateRenderBounds(ref SystemState ss, in RuntimeAnimationData runtimeData, JobHandle dependsOn)
|
||||
{
|
||||
var updateSkinnedMeshBoundsJob = new UpdateSkinnedMeshBoundsJob()
|
||||
{
|
||||
worldBonePoses = runtimeData.worldSpaceBonesBuffer,
|
||||
rigDefLookup = SystemAPI.GetComponentLookup<RigDefinitionComponent>(true)
|
||||
};
|
||||
|
||||
var updateSkinnedMeshBoundsJH = updateSkinnedMeshBoundsJob.ScheduleParallel(dependsOn);
|
||||
return updateSkinnedMeshBoundsJH;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
JobHandle PropagateAnimatedBonesToEntitiesTRS(ref SystemState ss, in RuntimeAnimationData runtimeData, EntityQuery eq, bool withParents, JobHandle dependsOn)
|
||||
{
|
||||
var propagateAnimationJob = new PropagateBoneTransformToEntityTRSJob()
|
||||
{
|
||||
boneTransforms = withParents ? runtimeData.animatedBonesBuffer : runtimeData.worldSpaceBonesBuffer,
|
||||
postTransformMatrixLookup = SystemAPI.GetComponentLookup<PostTransformMatrix>(),
|
||||
rigDefLookup = SystemAPI.GetComponentLookup<RigDefinitionComponent>(true),
|
||||
gpuEngineTagLookup = SystemAPI.GetComponentLookup<GPUAnimationEngineTag>(true)
|
||||
};
|
||||
|
||||
var jh = propagateAnimationJob.ScheduleParallel(eq, dependsOn);
|
||||
return jh;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
JobHandle ApplySkinning(ref SystemState ss, in RuntimeAnimationData runtimeData, JobHandle dependsOn)
|
||||
{
|
||||
var rigDefinitionComponentLookup = SystemAPI.GetComponentLookup<RigDefinitionComponent>(true);
|
||||
var cullAnimationsTagComponentLookup = SystemAPI.GetComponentLookup<CullAnimationsTag>(true);
|
||||
var localTransformLookup = SystemAPI.GetComponentLookup<LocalTransform>(true);
|
||||
var parentLookup = SystemAPI.GetComponentLookup<Parent>(true);
|
||||
var rigBoneLookup = SystemAPI.GetComponentLookup<AnimatorEntityRefComponent>(true);
|
||||
var gpuAnimationEngineLookup = SystemAPI.GetComponentLookup<GPUAnimationEngineTag>(true);
|
||||
|
||||
var animationApplyJob = new ApplyAnimationToSkinnedMeshJob()
|
||||
{
|
||||
boneTransforms = runtimeData.worldSpaceBonesBuffer,
|
||||
rigDefinitionLookup = rigDefinitionComponentLookup,
|
||||
rigToSkinnedMeshRemapTables = rigToSkinnedMeshRemapTables,
|
||||
cullAnimationsTagLookup = cullAnimationsTagComponentLookup,
|
||||
localTransformLookup = localTransformLookup,
|
||||
parentLookup = parentLookup,
|
||||
rigBoneLookup = rigBoneLookup,
|
||||
gpuAnimationEngineTagLookup = gpuAnimationEngineLookup
|
||||
};
|
||||
|
||||
var jh = animationApplyJob.ScheduleParallel(dependsOn);
|
||||
return jh;
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 196d4783de470ae4fa673c90f47403db
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/AnimationEngine/AnimationApplicationSystem.cs
|
||||
uploadId: 897522
|
||||
+302
@@ -0,0 +1,302 @@
|
||||
using System;
|
||||
using Unity.Burst;
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using Unity.Entities;
|
||||
using Unity.Jobs;
|
||||
using Unity.Mathematics;
|
||||
using Unity.Rendering;
|
||||
using Unity.Transforms;
|
||||
using Hash128 = Unity.Entities.Hash128;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
partial struct AnimationApplicationSystem
|
||||
{
|
||||
|
||||
//=================================================================================================================//
|
||||
|
||||
[BurstCompile]
|
||||
partial struct ApplyAnimationToSkinnedMeshJob: IJobEntity
|
||||
{
|
||||
[ReadOnly]
|
||||
public ComponentLookup<RigDefinitionComponent> rigDefinitionLookup;
|
||||
[ReadOnly]
|
||||
public ComponentLookup<CullAnimationsTag> cullAnimationsTagLookup;
|
||||
[ReadOnly]
|
||||
public ComponentLookup<Parent> parentLookup;
|
||||
[ReadOnly]
|
||||
public ComponentLookup<LocalTransform> localTransformLookup;
|
||||
[ReadOnly]
|
||||
public ComponentLookup<AnimatorEntityRefComponent> rigBoneLookup;
|
||||
[ReadOnly]
|
||||
public ComponentLookup<GPUAnimationEngineTag> gpuAnimationEngineTagLookup;
|
||||
[ReadOnly]
|
||||
public NativeList<BoneTransform> boneTransforms;
|
||||
[ReadOnly]
|
||||
public NativeParallelHashMap<Hash128, BlobAssetReference<BoneRemapTableBlob>> rigToSkinnedMeshRemapTables;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Execute(Entity e, in SkinnedMeshRendererComponent smc, in LocalTransform lt, ref DynamicBuffer<SkinMatrix> outSkinMatricesBuf)
|
||||
{
|
||||
var rigEntity = smc.animatedRigEntity;
|
||||
|
||||
if (cullAnimationsTagLookup.HasComponent(rigEntity) && cullAnimationsTagLookup.IsComponentEnabled(rigEntity))
|
||||
return;
|
||||
|
||||
if (!rigDefinitionLookup.TryGetComponent(rigEntity, out var rigDef))
|
||||
return;
|
||||
|
||||
if (smc.IsGPUAnimator(gpuAnimationEngineTagLookup))
|
||||
return;
|
||||
|
||||
var boneDataOffset = rigDef.dynamicFrameData;
|
||||
ref var boneRemapTable = ref GetBoneRemapTable(smc.smrInfoBlob, rigDef.rigBlob);
|
||||
|
||||
var absoluteBoneTransforms = RuntimeAnimationData.GetAnimationDataForRigRO(boneTransforms, boneDataOffset.bonePoseOffset, boneDataOffset.rigBoneCount);
|
||||
var skinMeshBonesInfo = smc.smrInfoBlob;
|
||||
var meshRenderToRigRootTransform = SkinnedMeshToRigRootTransform(e, smc.animatedRigEntity, lt, absoluteBoneTransforms);
|
||||
|
||||
// Iterate over all skinned mesh bones and set skin matrix from corresponding animation pose
|
||||
for (int skinnedMeshBoneIndex = 0; skinnedMeshBoneIndex < skinMeshBonesInfo.Value.bones.Length; ++skinnedMeshBoneIndex)
|
||||
{
|
||||
var animationBoneIndex = boneRemapTable.remapIndices[skinnedMeshBoneIndex];
|
||||
|
||||
// Skip bone if it is not present in animation
|
||||
if (animationBoneIndex < 0)
|
||||
continue;
|
||||
|
||||
var absBonePose = absoluteBoneTransforms[animationBoneIndex];
|
||||
absBonePose = BoneTransform.Multiply(meshRenderToRigRootTransform, absBonePose);
|
||||
var boneXForm = absBonePose.ToFloat4x4();
|
||||
|
||||
ref var boneInfo = ref skinMeshBonesInfo.Value.bones[skinnedMeshBoneIndex];
|
||||
var skinMatrix = MakeSkinMatrixForBone(ref boneInfo, boneXForm);
|
||||
outSkinMatricesBuf[skinnedMeshBoneIndex] = skinMatrix;
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
BoneTransform SkinnedMeshToRigRootTransform(Entity e, Entity selfAnimatedEntity, in LocalTransform lt, ReadOnlySpan<BoneTransform> bonePoses)
|
||||
{
|
||||
var rv = BoneTransform.Identity();
|
||||
var parent = new Parent() { Value = e };
|
||||
do
|
||||
{
|
||||
e = parent.Value;
|
||||
if (rigBoneLookup.TryGetComponent(e, out var rb) && rb.animatorEntity == selfAnimatedEntity)
|
||||
{
|
||||
var absBonePose = bonePoses[rb.boneIndexInAnimationRig];
|
||||
rv = BoneTransform.Multiply(absBonePose, rv);
|
||||
break;
|
||||
}
|
||||
var entityLocalPose = new BoneTransform(localTransformLookup[e]);
|
||||
rv = BoneTransform.Multiply(entityLocalPose, rv);
|
||||
}
|
||||
while (parentLookup.TryGetComponent(e, out parent));
|
||||
return BoneTransform.Inverse(rv);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static Hash128 CalculateBoneRemapTableHash(in BlobAssetReference<SkinnedMeshInfoBlob> skinnedMesh, in BlobAssetReference<RigDefinitionBlob> rigDef)
|
||||
{
|
||||
var rv = new Hash128(skinnedMesh.Value.hash.Value.x, skinnedMesh.Value.hash.Value.y, rigDef.Value.hash.Value.x, rigDef.Value.hash.Value.y);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ref BoneRemapTableBlob GetBoneRemapTable(in BlobAssetReference<SkinnedMeshInfoBlob> skinnedMesh, in BlobAssetReference<RigDefinitionBlob> rigDef)
|
||||
{
|
||||
var h = CalculateBoneRemapTableHash(skinnedMesh, rigDef);
|
||||
return ref rigToSkinnedMeshRemapTables[h].Value;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
SkinMatrix MakeSkinMatrixForBone(ref SkinnedMeshBoneInfo boneInfo, in float4x4 boneXForm)
|
||||
{
|
||||
var boneTransformMatrix = math.mul(boneXForm, boneInfo.bindPose);
|
||||
|
||||
var skinMatrix = new SkinMatrix() { Value = new float3x4(boneTransformMatrix.c0.xyz, boneTransformMatrix.c1.xyz, boneTransformMatrix.c2.xyz, boneTransformMatrix.c3.xyz) };
|
||||
return skinMatrix;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//=================================================================================================================//
|
||||
|
||||
[BurstCompile]
|
||||
partial struct PropagateBoneTransformToEntityTRSJob: IJobEntity
|
||||
{
|
||||
[ReadOnly]
|
||||
public NativeList<BoneTransform> boneTransforms;
|
||||
|
||||
[NativeDisableParallelForRestriction]
|
||||
public ComponentLookup<PostTransformMatrix> postTransformMatrixLookup;
|
||||
[ReadOnly]
|
||||
public ComponentLookup<RigDefinitionComponent> rigDefLookup;
|
||||
[ReadOnly]
|
||||
public ComponentLookup<GPUAnimationEngineTag> gpuEngineTagLookup;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void Execute(Entity e, in AnimatorEntityRefComponent animatorRef, ref LocalTransform lt)
|
||||
{
|
||||
if (gpuEngineTagLookup.IsComponentEnabled(animatorRef.animatorEntity))
|
||||
return;
|
||||
|
||||
var rigDefinition = rigDefLookup[animatorRef.animatorEntity];
|
||||
var boneData = RuntimeAnimationData.GetAnimationDataForRigRO(boneTransforms, rigDefinition);
|
||||
if (boneData.IsEmpty)
|
||||
return;
|
||||
|
||||
if (animatorRef.boneIndexInAnimationRig >= boneData.Length)
|
||||
return;
|
||||
|
||||
var boneTransform = boneData[animatorRef.boneIndexInAnimationRig];
|
||||
lt = boneTransform.ToLocalTransformComponent();
|
||||
if (postTransformMatrixLookup.HasComponent(e))
|
||||
{
|
||||
lt.Scale = 1;
|
||||
var ptm = float4x4.Scale(boneTransform.scale);
|
||||
postTransformMatrixLookup[e] = new PostTransformMatrix() { Value = ptm };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//=================================================================================================================//
|
||||
|
||||
[BurstCompile]
|
||||
partial struct CountNumberOfNewRemapTablesJob: IJobEntity
|
||||
{
|
||||
[ReadOnly]
|
||||
public ComponentLookup<RigDefinitionComponent> rigDefinitionArr;
|
||||
[ReadOnly]
|
||||
public NativeParallelHashMap<Hash128, BlobAssetReference<BoneRemapTableBlob>> rigToSkinnedMeshRemapTables;
|
||||
|
||||
[NativeDisableUnsafePtrRestriction]
|
||||
public UnsafeAtomicCounter32 numberOfNewRemapTables;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Execute(in SkinnedMeshRendererComponent asmc)
|
||||
{
|
||||
if (!rigDefinitionArr.TryGetComponent(asmc.animatedRigEntity, out var rigDef))
|
||||
return;
|
||||
|
||||
var h = ApplyAnimationToSkinnedMeshJob.CalculateBoneRemapTableHash(asmc.smrInfoBlob, rigDef.rigBlob);
|
||||
if (!rigToSkinnedMeshRemapTables.ContainsKey(h))
|
||||
numberOfNewRemapTables.Add(1);
|
||||
}
|
||||
}
|
||||
|
||||
//=================================================================================================================//
|
||||
|
||||
[BurstCompile]
|
||||
unsafe struct IncreaseRigRemapTableCapacityJob: IJob
|
||||
{
|
||||
public NativeParallelHashMap<Hash128, BlobAssetReference<BoneRemapTableBlob>> rigToSkinnedMeshRemapTables;
|
||||
[ReadOnly, NativeDisableUnsafePtrRestriction]
|
||||
public int *numberOfNewRemapTables;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void Execute()
|
||||
{
|
||||
rigToSkinnedMeshRemapTables.Capacity += *numberOfNewRemapTables;
|
||||
}
|
||||
}
|
||||
|
||||
//=================================================================================================================//
|
||||
|
||||
[BurstCompile]
|
||||
unsafe partial struct FillRigToSkinBonesRemapTableCacheJob: IJobEntity
|
||||
{
|
||||
[ReadOnly]
|
||||
public ComponentLookup<RigDefinitionComponent> rigDefinitionArr;
|
||||
public NativeParallelHashMap<Hash128, BlobAssetReference<BoneRemapTableBlob>>.ParallelWriter rigToSkinnedMeshRemapTables;
|
||||
[ReadOnly, NativeDisableUnsafePtrRestriction]
|
||||
public int *newRemapTablesCounter;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Execute(in SkinnedMeshRendererComponent asmc)
|
||||
{
|
||||
if (*newRemapTablesCounter == 0 || !rigDefinitionArr.TryGetComponent(asmc.animatedRigEntity, out var rigDef))
|
||||
return;
|
||||
|
||||
MakeRigToSkinnedMeshRemapTable(asmc, rigDef);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void MakeRigToSkinnedMeshRemapTable(in SkinnedMeshRendererComponent sm, in RigDefinitionComponent rigDef)
|
||||
{
|
||||
// Try cache first
|
||||
var h = ApplyAnimationToSkinnedMeshJob.CalculateBoneRemapTableHash(sm.smrInfoBlob, rigDef.rigBlob);
|
||||
if (UnsafeParallelHashMapBase<Hash128, BlobAssetReference<BoneRemapTableBlob>>
|
||||
.TryGetFirstValueAtomic(rigToSkinnedMeshRemapTables.m_Writer.m_Buffer, h, out _, out _))
|
||||
return;
|
||||
|
||||
// Make new remap table
|
||||
var rv = AnimationUtils.MakeSkinnedMeshToRigRemapTable(sm, rigDef, Allocator.Persistent);
|
||||
if (!rigToSkinnedMeshRemapTables.TryAdd(h, rv))
|
||||
rv.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
//=================================================================================================================//
|
||||
|
||||
[BurstCompile]
|
||||
[WithAll(typeof(ShouldUpdateBoundingBoxTag))]
|
||||
partial struct UpdateSkinnedMeshBoundsJob: IJobEntity
|
||||
{
|
||||
[ReadOnly]
|
||||
public ComponentLookup<RigDefinitionComponent> rigDefLookup;
|
||||
[ReadOnly]
|
||||
public NativeList<BoneTransform> worldBonePoses;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Execute(in SkinnedMeshRendererComponent asm, ref RenderBounds rbb)
|
||||
{
|
||||
var rigDefinition = rigDefLookup[asm.animatedRigEntity];
|
||||
var animationData = RuntimeAnimationData.GetAnimationDataForRigRO(worldBonePoses, rigDefinition);
|
||||
|
||||
if (animationData.Length <= asm.rootBoneIndexInRig || asm.rootBoneIndexInRig < 0)
|
||||
return;
|
||||
|
||||
// Skinned mesh root bone world pose
|
||||
var rootPose = animationData[0];
|
||||
var invRootPose = BoneTransform.Inverse(rootPose);
|
||||
|
||||
// Loop over all bones and calculate extents in root bone space
|
||||
float3 minPt = float.MaxValue;
|
||||
float3 maxPt = float.MinValue;
|
||||
for (var i = asm.rootBoneIndexInRig + 1; i < animationData.Length; ++i)
|
||||
{
|
||||
var boneWorldPose = animationData[i];
|
||||
var rootBoneSpaceBonePose = BoneTransform.Multiply(invRootPose, boneWorldPose);
|
||||
minPt = math.min(minPt, rootBoneSpaceBonePose.pos);
|
||||
maxPt = math.max(maxPt, rootBoneSpaceBonePose.pos);
|
||||
}
|
||||
|
||||
var aabb = new AABB()
|
||||
{
|
||||
Center = (minPt + maxPt) * 0.5f,
|
||||
Extents = (maxPt - minPt) * 0.5f,
|
||||
};
|
||||
|
||||
// Slightly extend result aabb to 'emulate' skin. Without proper per-vertex skin hull calculation this is reasonable approximation
|
||||
aabb.Extents *= 1.1f;
|
||||
rbb.Value = aabb;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: faa2bfe9711f2db4ea5949b9e8cf55ee
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/AnimationEngine/AnimationApplicationSystem_Jobs.cs
|
||||
uploadId: 897522
|
||||
+177
@@ -0,0 +1,177 @@
|
||||
|
||||
using System.Runtime.CompilerServices;
|
||||
using Rukhanka.Toolbox;
|
||||
using Unity.Collections;
|
||||
using Unity.Entities;
|
||||
using Unity.Mathematics;
|
||||
using FixedStringName = Unity.Collections.FixedString512Bytes;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
public enum BindingType
|
||||
{
|
||||
Unknown,
|
||||
Translation,
|
||||
Quaternion,
|
||||
EulerAngles,
|
||||
HumanMuscle,
|
||||
Scale
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct KeyFrame
|
||||
{
|
||||
public float v;
|
||||
public float inTan, outTan;
|
||||
public float time;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct AnimationEventBlob
|
||||
{
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
public BlobString name;
|
||||
#endif
|
||||
public uint nameHash;
|
||||
public float time;
|
||||
public float floatParam;
|
||||
public int intParam;
|
||||
public uint stringParamHash;
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
public BlobString stringParam;
|
||||
#endif
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct PerfectHashTableBlob
|
||||
{
|
||||
public BlobArray<uint2> pht;
|
||||
public uint seed;
|
||||
|
||||
public unsafe int Query(uint v)
|
||||
{
|
||||
return Perfect2HashTable.Query(v, seed, (uint2*)pht.GetUnsafePtr(), pht.Length);
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public enum TrackFrame
|
||||
{
|
||||
First,
|
||||
Last,
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct Track
|
||||
{
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
public FixedString64Bytes name;
|
||||
#endif
|
||||
public uint props;
|
||||
public int2 keyFrameRange;
|
||||
|
||||
public Track(BindingType bt, uint channelIndex)
|
||||
{
|
||||
keyFrameRange = 0;
|
||||
props = 0;
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
name = default;
|
||||
#endif
|
||||
|
||||
bindingType = bt;
|
||||
this.channelIndex = channelIndex;
|
||||
}
|
||||
|
||||
public BindingType bindingType
|
||||
{
|
||||
get => (BindingType)(props & 0xf);
|
||||
set => props = (uint)value | props & 0xfffffff0;
|
||||
}
|
||||
|
||||
public uint channelIndex
|
||||
{
|
||||
get => props >> 4 & 3;
|
||||
set => props = value << 4 | props & 0xffffffcf;
|
||||
}
|
||||
|
||||
// Zero out last 4 bits, to force Unknown binding type for such tracks
|
||||
public static uint CalculateHash(uint h) => h & 0xfffffff0;
|
||||
public static uint CalculateHash(in FixedStringName h) => CalculateHash(h.CalculateHash32());
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
public struct TrackGroupInfo
|
||||
{
|
||||
public FixedString128Bytes name;
|
||||
public uint hash;
|
||||
}
|
||||
#endif
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct TrackSet
|
||||
{
|
||||
public BlobArray<KeyFrame> keyframes;
|
||||
public BlobArray<Track> tracks;
|
||||
public BlobArray<int> trackGroups;
|
||||
public PerfectHashTableBlob trackGroupPHT;
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
public BlobArray<TrackGroupInfo> trackGroupDebugInfo;
|
||||
#endif
|
||||
|
||||
public int GetTrackGroupIndex(uint boneHash) => trackGroupPHT.Query(boneHash);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct AnimationClipBlob: GenericAssetBlob
|
||||
{
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
public BlobString name;
|
||||
public string Name() => name.ToString();
|
||||
public float bakingTime;
|
||||
public float BakingTime() => bakingTime;
|
||||
#endif
|
||||
public Hash128 hash;
|
||||
public Hash128 Hash() => hash;
|
||||
|
||||
public TrackSet clipTracks;
|
||||
public TrackSet additiveReferencePoseFrame;
|
||||
public BlobArray<AnimationEventBlob> events;
|
||||
|
||||
public uint flags;
|
||||
public float cycleOffset;
|
||||
public float length;
|
||||
public bool looped { get => GetFlag(1); set => SetFlag(1, value); }
|
||||
public bool loopPoseBlend { get => GetFlag(2); set => SetFlag(2, value); }
|
||||
public uint maxTrackKeyframeLength;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
void SetFlag(int index, bool value)
|
||||
{
|
||||
var v = 1u << index;
|
||||
var mask = ~v;
|
||||
var valueBits = math.select(0, v, value);
|
||||
flags = flags & mask | valueBits;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
bool GetFlag(int index)
|
||||
{
|
||||
var v = 1u << index;
|
||||
return (flags & v) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ee0714c0498362a4fb8592fc69214070
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/AnimationEngine/AnimationClipBlob.cs
|
||||
uploadId: 897522
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
using Unity.Entities;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
public struct RigDefinitionComponent: IComponentData, IEnableableComponent
|
||||
{
|
||||
public BlobAssetReference<RigDefinitionBlob> rigBlob;
|
||||
public bool applyRootMotion;
|
||||
|
||||
// Dynamic per-frame data
|
||||
internal DynamicFrameData dynamicFrameData;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct DynamicFrameData
|
||||
{
|
||||
public int bonePoseOffset;
|
||||
public int boneFlagsOffset;
|
||||
public int rigBoneCount;
|
||||
|
||||
public static DynamicFrameData MakeInvalid()
|
||||
{
|
||||
return new DynamicFrameData()
|
||||
{
|
||||
boneFlagsOffset = -1,
|
||||
bonePoseOffset = -1,
|
||||
rigBoneCount = -1,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 70db13716bccec641b9cf402fdbe0821
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/AnimationEngine/AnimationEngineComponents.cs
|
||||
uploadId: 897522
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
|
||||
using Rukhanka.WaybackMachine;
|
||||
using Unity.Burst;
|
||||
using Unity.Entities;
|
||||
using Unity.Jobs;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
|
||||
[DisableAutoCreation]
|
||||
[UpdateBefore(typeof(AnimationProcessSystem))]
|
||||
public partial struct AnimationEventEmitSystem: ISystem
|
||||
{
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
JobHandle EmitAnimationEvents(ref SystemState ss, JobHandle dependsOn)
|
||||
{
|
||||
var dt = SystemAPI.Time.DeltaTime;
|
||||
|
||||
var emitAnimationEventsJob = new EmitAnimationEventsJob()
|
||||
{
|
||||
deltaTime = dt
|
||||
};
|
||||
var jh = emitAnimationEventsJob.ScheduleParallel(dependsOn);
|
||||
return jh;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
JobHandle MakeProcessedAnimationsSnapshot(ref SystemState ss, JobHandle dependsOn)
|
||||
{
|
||||
var makeProcessedAnimationsSnapshotJob = new MakeProcessedAnimationsSnapshotJob() { };
|
||||
var jh = makeProcessedAnimationsSnapshotJob.ScheduleParallel(dependsOn);
|
||||
return jh;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
JobHandle CopyEventsForWaybackMachineDuringRecording(ref SystemState ss, JobHandle dependsOn)
|
||||
{
|
||||
if (!SystemAPI.TryGetSingletonRW<RecordComponent>(out var rcd))
|
||||
return dependsOn;
|
||||
|
||||
var copyEventsToWaybackMachineJob = new CopyAnimationEventsToWaybackMachineRecordingJob()
|
||||
{
|
||||
outEvents = rcd.ValueRW.wbData.Value.emittedAnimationEvents
|
||||
};
|
||||
var jh = copyEventsToWaybackMachineJob.Schedule(dependsOn);
|
||||
return jh;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[BurstCompile]
|
||||
public void OnUpdate(ref SystemState ss)
|
||||
{
|
||||
// Emit animation events based on current and previously processed animations
|
||||
var emitAnimationEventsJH = EmitAnimationEvents(ref ss, ss.Dependency);
|
||||
|
||||
// Make a snapshot of current frame animations, to use it in next frame as previously processed jobs
|
||||
var makeProcessedAnimationsSnapshotJH = MakeProcessedAnimationsSnapshot(ref ss, emitAnimationEventsJH);
|
||||
|
||||
// Copy animation events into shadow buffer for wayback machine if recording requested
|
||||
// Why to copy? Wayback machine recordings are performed in fixed-step update loop. If game frame time is more
|
||||
// then recording fixed time, then some events will be missed from recording because each simulation (game)
|
||||
// frame event buffer is cleared in AnimationEventEmitSystem
|
||||
var copyEventsToWaybackMachineBufJH = CopyEventsForWaybackMachineDuringRecording(ref ss, emitAnimationEventsJH);
|
||||
|
||||
var combinedJH = JobHandle.CombineDependencies(copyEventsToWaybackMachineBufJH, makeProcessedAnimationsSnapshotJH);
|
||||
ss.Dependency = combinedJH;
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c6f4459336e6b6a44bc192d28fceb8ce
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/AnimationEngine/AnimationEventEmitSystem.cs
|
||||
uploadId: 897522
|
||||
+195
@@ -0,0 +1,195 @@
|
||||
|
||||
using Rukhanka.Toolbox;
|
||||
using Rukhanka.WaybackMachine;
|
||||
using Unity;
|
||||
using Unity.Burst;
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using Unity.Entities;
|
||||
using Unity.Mathematics;
|
||||
|
||||
//=================================================================================================================//
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
public partial struct AnimationEventEmitSystem
|
||||
{
|
||||
|
||||
[BurstCompile]
|
||||
partial struct EmitAnimationEventsJob : IJobEntity
|
||||
{
|
||||
public float deltaTime;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Execute(Entity e, in DynamicBuffer<AnimationToProcessComponent> atp, in DynamicBuffer<PreviousProcessedAnimationComponent> ppa, ref DynamicBuffer<AnimationEventComponent> aec)
|
||||
{
|
||||
aec.Clear();
|
||||
|
||||
var ppaArr = ppa.AsNativeArray();
|
||||
var atpArr = atp.AsNativeArray();
|
||||
ulong prevAnimationsProcessedIndices = 0;
|
||||
|
||||
var maxBitsCount = UnsafeUtility.SizeOf<ulong>() * 8;
|
||||
BurstAssert.IsTrue(ppaArr.Length <= maxBitsCount, "Too many simultaneous animations! Change bits holder to the NativeBitArray if this error occurs.");
|
||||
|
||||
for (var i = 0; i < atpArr.Length; ++i)
|
||||
{
|
||||
var a = atpArr[i];
|
||||
if (a.animation == BlobAssetReference<AnimationClipBlob>.Null)
|
||||
continue;
|
||||
|
||||
var curTime = a.time;
|
||||
var prevBufferId = GetPreviousBufferAnimationIndex(a.motionId, i, ppaArr);
|
||||
var prevTime = 0.0f;
|
||||
if (prevBufferId < 0)
|
||||
{
|
||||
// There is no such animation in "previous buffer". Assume that this animation advances by dt already
|
||||
var animationAdjustedDeltaTime = deltaTime / a.animation.Value.length;
|
||||
prevTime = curTime - animationAdjustedDeltaTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
prevAnimationsProcessedIndices |= 1ul << prevBufferId;
|
||||
prevTime = ppaArr[prevBufferId].animationTime;
|
||||
}
|
||||
|
||||
if (prevTime == curTime)
|
||||
continue;
|
||||
|
||||
var negativeAnimationDT = prevTime > curTime;
|
||||
if (negativeAnimationDT)
|
||||
(prevTime, curTime) = (curTime, prevTime);
|
||||
|
||||
ref var aes = ref a.animation.Value.events;
|
||||
if (a.animation.Value.looped)
|
||||
{
|
||||
ProcessEventsForLoopedAnimation(e, ref aec, ref aes, prevTime, curTime, negativeAnimationDT);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (prevTime < 1 && curTime > 0)
|
||||
ProcessEventsForAnimation(e, ref aec, ref aes, prevTime, curTime, negativeAnimationDT);
|
||||
}
|
||||
}
|
||||
|
||||
// Now we need check for animations that missed now, but were there at previous frame
|
||||
// Animation can ending playing for at least one frame (dt)
|
||||
for (var i = 0; i < ppaArr.Length; ++i)
|
||||
{
|
||||
var bitMask = 1ul << i;
|
||||
if ((prevAnimationsProcessedIndices & bitMask) != 0)
|
||||
continue;
|
||||
|
||||
var p = ppaArr[i];
|
||||
ref var aes = ref p.animation.Value.events;
|
||||
ProcessEventsForAnimation(e, ref aec, ref aes, p.animationTime, p.animationTime + deltaTime, false);
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void ProcessEventsForLoopedAnimation(Entity e, ref DynamicBuffer<AnimationEventComponent> aec, ref BlobArray<AnimationEventBlob> aes, float prevTime, float curTime, bool negativeAnimationDT)
|
||||
{
|
||||
var t0 = prevTime;
|
||||
var dt = curTime - prevTime;
|
||||
if (t0 < 0)
|
||||
t0 = t0 - math.floor(t0);
|
||||
|
||||
var t1 = t0 + dt;
|
||||
var it0 = t0;
|
||||
|
||||
// Divide whole range to sections that fit in [0..1] range, and execute events calculation for them individually
|
||||
do
|
||||
{
|
||||
it0 = math.floor(it0);
|
||||
var tStart = math.max(it0, t0);
|
||||
var tEnd = math.min(it0 + 1, t1);
|
||||
tEnd -= tStart;
|
||||
tStart = math.frac(tStart);
|
||||
tEnd += tStart;
|
||||
ProcessEventsForAnimation(e, ref aec, ref aes, tStart, tEnd, negativeAnimationDT);
|
||||
it0 += 1;
|
||||
}
|
||||
while (it0 < t1);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void ProcessEventsForAnimation(Entity e, ref DynamicBuffer<AnimationEventComponent> outEvents, ref BlobArray<AnimationEventBlob> events, float fStart, float fEnd, bool reverseEventIteration)
|
||||
{
|
||||
for (var i = 0; i < events.Length; ++i)
|
||||
{
|
||||
// Reverse events iteration order to preserve events order in output buffer
|
||||
var idx = reverseEventIteration ? events.Length - i - 1 : i;
|
||||
ref var ae = ref events[idx];
|
||||
var eventTime = ae.time;
|
||||
|
||||
var emitEvent = eventTime >= fStart && eventTime <= fEnd;
|
||||
|
||||
if (emitEvent)
|
||||
{
|
||||
var evt = new AnimationEventComponent(ref ae);
|
||||
outEvents.Add(evt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int GetPreviousBufferAnimationIndex(uint motionId, int animationBufferIndex, NativeArray<PreviousProcessedAnimationComponent> ppa)
|
||||
{
|
||||
// Fast path
|
||||
if (animationBufferIndex < ppa.Length && ppa[animationBufferIndex].motionId == motionId)
|
||||
return animationBufferIndex;
|
||||
|
||||
// Full search
|
||||
for (var i = 0; i < ppa.Length; ++i)
|
||||
{
|
||||
if (motionId == ppa[i].motionId)
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
//=================================================================================================================//
|
||||
|
||||
[BurstCompile]
|
||||
partial struct MakeProcessedAnimationsSnapshotJob: IJobEntity
|
||||
{
|
||||
void Execute(in DynamicBuffer<AnimationToProcessComponent> atp, ref DynamicBuffer<PreviousProcessedAnimationComponent> ppa)
|
||||
{
|
||||
ppa.Resize(atp.Length, NativeArrayOptions.UninitializedMemory);
|
||||
for (var i = 0; i < atp.Length; ++i)
|
||||
{
|
||||
var a = atp[i];
|
||||
var p = new PreviousProcessedAnimationComponent()
|
||||
{
|
||||
animationTime = a.time,
|
||||
motionId = a.motionId,
|
||||
animation = a.animation
|
||||
};
|
||||
ppa[i] = p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//=================================================================================================================//
|
||||
|
||||
[BurstCompile]
|
||||
[WithAll(typeof(RecordComponent))]
|
||||
partial struct CopyAnimationEventsToWaybackMachineRecordingJob: IJobEntity
|
||||
{
|
||||
public NativeList<AnimationEventComponent> outEvents;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Execute(in DynamicBuffer<AnimationEventComponent> aec)
|
||||
{
|
||||
outEvents.AddRange(aec.AsNativeArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3350396351b897c49b7e5d5e4e629bcb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/AnimationEngine/AnimationEventEmitSystem_Jobs.cs
|
||||
uploadId: 897522
|
||||
+262
@@ -0,0 +1,262 @@
|
||||
|
||||
using Unity.Burst;
|
||||
using Unity.Collections;
|
||||
using Unity.Entities;
|
||||
using Unity.Jobs;
|
||||
using Unity.Mathematics;
|
||||
using Unity.Transforms;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
|
||||
[DisableAutoCreation]
|
||||
[UpdateAfter(typeof(AnimationCullingSystem))]
|
||||
public partial struct AnimationProcessSystem: ISystem
|
||||
{
|
||||
EntityQuery animatedObjectQuery;
|
||||
|
||||
NativeList<int2> bonePosesOffsetsArr;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[BurstCompile]
|
||||
public void OnCreate(ref SystemState ss)
|
||||
{
|
||||
InitializeRuntimeData(ref ss);
|
||||
|
||||
bonePosesOffsetsArr = new (Allocator.Persistent);
|
||||
|
||||
var eqb0 = new EntityQueryBuilder(Allocator.Temp)
|
||||
.WithAll<RigDefinitionComponent, AnimationToProcessComponent>()
|
||||
.WithDisabled<GPUAnimationEngineTag>();
|
||||
animatedObjectQuery = ss.GetEntityQuery(eqb0);
|
||||
|
||||
ss.RequireForUpdate(animatedObjectQuery);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[BurstCompile]
|
||||
public void OnDestroy(ref SystemState ss)
|
||||
{
|
||||
bonePosesOffsetsArr.Dispose();
|
||||
|
||||
if (SystemAPI.TryGetSingleton<RuntimeAnimationData>(out var rad))
|
||||
{
|
||||
rad.Dispose();
|
||||
ss.EntityManager.DestroyEntity(SystemAPI.GetSingletonEntity<RuntimeAnimationData>());
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void InitializeRuntimeData(ref SystemState ss)
|
||||
{
|
||||
var rad = RuntimeAnimationData.MakeDefault();
|
||||
ss.EntityManager.CreateSingleton(rad, "Rukhanka.RuntimeAnimationData");
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
JobHandle PrepareComputationData(ref SystemState ss, NativeArray<int> chunkBaseEntityIndices, ref RuntimeAnimationData runtimeData, NativeList<Entity> entitiesArr, JobHandle dependsOn)
|
||||
{
|
||||
var rigDefinitionTypeHandle = SystemAPI.GetComponentTypeHandle<RigDefinitionComponent>(true);
|
||||
var cullAnimationsTagComponentLookup = SystemAPI.GetComponentLookup<CullAnimationsTag>(true);
|
||||
var rigDefTypeHandle = SystemAPI.GetComponentTypeHandle<RigDefinitionComponent>();
|
||||
|
||||
// Calculate bone offsets per entity
|
||||
var calcBoneOffsetsJob = new CalculateBoneOffsetsJob()
|
||||
{
|
||||
chunkBaseEntityIndices = chunkBaseEntityIndices,
|
||||
bonePosesOffsets = bonePosesOffsetsArr,
|
||||
rigDefinitionTypeHandle = rigDefinitionTypeHandle,
|
||||
cullAnimationsTagLookup = cullAnimationsTagComponentLookup,
|
||||
entities = entitiesArr,
|
||||
};
|
||||
|
||||
var calcBoneOffsetsJobJH = calcBoneOffsetsJob.ScheduleParallel(animatedObjectQuery, dependsOn);
|
||||
|
||||
// Do prefix sum to calculate absolute offsets
|
||||
var prefixSumJob = new DoPrefixSumJob()
|
||||
{
|
||||
boneOffsets = bonePosesOffsetsArr
|
||||
};
|
||||
|
||||
var prefixSumJH = prefixSumJob.Schedule(calcBoneOffsetsJobJH);
|
||||
|
||||
// Resize data buffers depending on current workload
|
||||
var resizeDataBuffersJob = new ResizeDataBuffersJob()
|
||||
{
|
||||
boneOffsets = bonePosesOffsetsArr,
|
||||
runtimeData = runtimeData
|
||||
};
|
||||
|
||||
var resizeDataBuffersJH = resizeDataBuffersJob.Schedule(prefixSumJH);
|
||||
|
||||
// Fill boneToEntityArr with proper values
|
||||
var boneToEntityArrFillJob = new CalculatePerBoneInfoJob()
|
||||
{
|
||||
bonePosesOffsets = bonePosesOffsetsArr,
|
||||
boneToEntityIndices = runtimeData.boneToEntityArr,
|
||||
chunkBaseEntityIndices = chunkBaseEntityIndices,
|
||||
rigDefinitionTypeHandle = rigDefTypeHandle
|
||||
};
|
||||
|
||||
var boneToEntityJH = boneToEntityArrFillJob.ScheduleParallel(animatedObjectQuery, resizeDataBuffersJH);
|
||||
return boneToEntityJH;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
JobHandle AnimationCalculation(ref SystemState ss, NativeList<Entity> entitiesArr, in RuntimeAnimationData runtimeData, JobHandle dependsOn)
|
||||
{
|
||||
var animationToProcessBufferLookup = SystemAPI.GetBufferLookup<AnimationToProcessComponent>(true);
|
||||
var rootMotionAnimationStateBufferLookupRW = SystemAPI.GetBufferLookup<RootMotionAnimationStateComponent>();
|
||||
|
||||
var rigDefsArr = animatedObjectQuery.ToComponentDataListAsync<RigDefinitionComponent>(ss.WorldUpdateAllocator, dependsOn, out var rigDefsLookupJH);
|
||||
|
||||
var computeAnimationsJob = new ComputeBoneAnimationJob()
|
||||
{
|
||||
animationsToProcessLookup = animationToProcessBufferLookup,
|
||||
entityArr = entitiesArr,
|
||||
rigDefs = rigDefsArr,
|
||||
boneTransformFlagsArr = runtimeData.boneTransformFlagsHolderArr,
|
||||
animatedBonesBuffer = runtimeData.animatedBonesBuffer,
|
||||
boneToEntityArr = runtimeData.boneToEntityArr,
|
||||
rootMotionAnimStateBufferLookup = rootMotionAnimationStateBufferLookupRW,
|
||||
};
|
||||
|
||||
var jh = computeAnimationsJob.Schedule(runtimeData.animatedBonesBuffer, 16, rigDefsLookupJH);
|
||||
return jh;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
JobHandle ProcessAnimatorParameterCurves(ref SystemState ss, JobHandle dependsOn)
|
||||
{
|
||||
var genericCurveProcessJob = new ProcessAnimatorParameterCurveJob();
|
||||
var jh = genericCurveProcessJob.ScheduleParallel(dependsOn);
|
||||
return jh;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
JobHandle CopyEntityBonesToAnimationTransforms(ref SystemState ss, ref RuntimeAnimationData runtimeData, JobHandle dependsOn)
|
||||
{
|
||||
var rigDefinitionLookup = SystemAPI.GetComponentLookup<RigDefinitionComponent>(true);
|
||||
var gpuAnimationEngineTagLookup = SystemAPI.GetComponentLookup<GPUAnimationEngineTag>(true);
|
||||
var parentComponentLookup = SystemAPI.GetComponentLookup<Parent>();
|
||||
var ptmComponentLookup = SystemAPI.GetComponentLookup<PostTransformMatrix>(true);
|
||||
|
||||
// Now take available entity transforms as ref poses overrides
|
||||
var copyEntityBoneTransforms = new CopyEntityBoneTransformsToAnimationBuffer()
|
||||
{
|
||||
rigDefComponentLookup = rigDefinitionLookup,
|
||||
boneTransformFlags = runtimeData.boneTransformFlagsHolderArr,
|
||||
animatedBoneTransforms = runtimeData.animatedBonesBuffer,
|
||||
parentComponentLookup = parentComponentLookup,
|
||||
gpuAnimationEngineTagLookup = gpuAnimationEngineTagLookup,
|
||||
ptmComponentLookup = ptmComponentLookup,
|
||||
};
|
||||
|
||||
var jh = copyEntityBoneTransforms.ScheduleParallel(dependsOn);
|
||||
return jh;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
JobHandle MakeAbsoluteBoneTransforms(ref SystemState ss, in RuntimeAnimationData runtimeData, JobHandle dependsOn)
|
||||
{
|
||||
var rigDefTypeHandle = SystemAPI.GetComponentTypeHandle<RigDefinitionComponent>(true);
|
||||
|
||||
var makeAbsTransformsJob = new MakeAbsoluteTransformsJob()
|
||||
{
|
||||
localBoneTransforms = runtimeData.animatedBonesBuffer,
|
||||
worldBoneTransforms = runtimeData.worldSpaceBonesBuffer,
|
||||
boneTransformFlags = runtimeData.boneTransformFlagsHolderArr,
|
||||
rigDefTypeHandle = rigDefTypeHandle
|
||||
};
|
||||
|
||||
var query = SystemAPI.QueryBuilder()
|
||||
.WithAll<RigDefinitionComponent>()
|
||||
.WithNone<GPUAnimationEngineTag>()
|
||||
.Build();
|
||||
var jh = makeAbsTransformsJob.ScheduleParallel(query, dependsOn);
|
||||
return jh;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
JobHandle ComputeRootMotion(ref SystemState ss, in RuntimeAnimationData runtimeData, JobHandle dependsOn)
|
||||
{
|
||||
var computeRootMotionJob = new ComputeRootMotionJob()
|
||||
{
|
||||
animatedBonePoses = runtimeData.animatedBonesBuffer,
|
||||
deltaTime = SystemAPI.Time.DeltaTime,
|
||||
parentLookup = SystemAPI.GetComponentLookup<Parent>(true),
|
||||
ptmLookup = SystemAPI.GetComponentLookup<PostTransformMatrix>(true),
|
||||
localTransformLookup = SystemAPI.GetComponentLookup<LocalTransform>(true),
|
||||
};
|
||||
|
||||
var jh = computeRootMotionJob.ScheduleParallel(dependsOn);
|
||||
return jh;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
JobHandle AnimateBlendShapeWeights(ref SystemState ss, JobHandle dependsOn)
|
||||
{
|
||||
var animateBlendWeightsJob = new AnimateBlendShapeWeightsJob()
|
||||
{
|
||||
animationToProcessLookup = SystemAPI.GetBufferLookup<AnimationToProcessComponent>(true),
|
||||
cullAnimationsLookup = SystemAPI.GetComponentLookup<CullAnimationsTag>(true)
|
||||
};
|
||||
var jh = animateBlendWeightsJob.ScheduleParallel(dependsOn);
|
||||
return jh;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[BurstCompile]
|
||||
public void OnUpdate(ref SystemState ss)
|
||||
{
|
||||
ref var runtimeData = ref SystemAPI.GetSingletonRW<RuntimeAnimationData>().ValueRW;
|
||||
|
||||
var entityCount = animatedObjectQuery.CalculateEntityCount();
|
||||
if (entityCount == 0) return;
|
||||
|
||||
bonePosesOffsetsArr.Resize(entityCount + 1, NativeArrayOptions.UninitializedMemory);
|
||||
var chunkBaseEntityIndices = animatedObjectQuery.CalculateBaseEntityIndexArrayAsync(ss.WorldUpdateAllocator, ss.Dependency, out var baseIndexCalcJH);
|
||||
var entitiesArr = animatedObjectQuery.ToEntityListAsync(ss.WorldUpdateAllocator, ss.Dependency, out var entityArrJH);
|
||||
|
||||
var combinedJH = JobHandle.CombineDependencies(baseIndexCalcJH, entityArrJH);
|
||||
|
||||
// Define array with bone pose offsets for calculated bone poses
|
||||
var calcBoneOffsetsJH = PrepareComputationData(ref ss, chunkBaseEntityIndices, ref runtimeData, entitiesArr, combinedJH);
|
||||
|
||||
// Animate blend shape weights
|
||||
var animateBlendShapeWeights = AnimateBlendShapeWeights(ref ss, calcBoneOffsetsJH);
|
||||
|
||||
// Curves that control controller parameters
|
||||
var parameterControlByCurvesJob = ProcessAnimatorParameterCurves(ref ss, calcBoneOffsetsJH);
|
||||
|
||||
var combinedJH2 = JobHandle.CombineDependencies(parameterControlByCurvesJob, animateBlendShapeWeights);
|
||||
|
||||
// Spawn jobs for animation calculation
|
||||
var computeAnimationJH = AnimationCalculation(ref ss, entitiesArr, runtimeData, combinedJH2);
|
||||
|
||||
// Copy entities poses into animation buffer for non-animated parts
|
||||
var copyEntityTransformsIntoAnimationBufferJH = CopyEntityBonesToAnimationTransforms(ref ss, ref runtimeData, computeAnimationJH);
|
||||
|
||||
// Compute root motion
|
||||
var rootMotionJH = ComputeRootMotion(ref ss, runtimeData, copyEntityTransformsIntoAnimationBufferJH);
|
||||
|
||||
// Make world space transforms out of local bone poses
|
||||
var makeAbsTransformsJH = MakeAbsoluteBoneTransforms(ref ss, runtimeData, rootMotionJH);
|
||||
|
||||
ss.Dependency = makeAbsTransformsJH;
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d2a720ee00702074395f1ce17309a70f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/AnimationEngine/AnimationProcessSystem.cs
|
||||
uploadId: 897522
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
using Rukhanka.Toolbox;
|
||||
using Unity.Collections;
|
||||
using Unity.Entities;
|
||||
using Unity.Mathematics;
|
||||
#if RUKHANKA_WITH_NETCODE
|
||||
using Unity.NetCode;
|
||||
#endif
|
||||
using FixedStringName = Unity.Collections.FixedString512Bytes;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
[InternalBufferCapacity(4)]
|
||||
[ChunkSerializable]
|
||||
public struct AnimationToProcessComponent: IBufferElementData
|
||||
{
|
||||
public float weight;
|
||||
public float time;
|
||||
public BlobAssetReference<AnimationClipBlob> animation;
|
||||
public BlobAssetReference<AvatarMaskBlob> avatarMask;
|
||||
public AnimationBlendingMode blendMode;
|
||||
public float layerWeight;
|
||||
public int layerIndex;
|
||||
public uint motionId;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct AnimatorEntityRefComponent: IComponentData
|
||||
{
|
||||
public int boneIndexInAnimationRig;
|
||||
public Entity animatorEntity;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#if RUKHANKA_WITH_NETCODE
|
||||
[GhostComponent(PrefabType = GhostPrefabType.Client)]
|
||||
#endif
|
||||
public struct SkinnedMeshRendererComponent: IComponentData
|
||||
{
|
||||
public uint nameHash;
|
||||
public Entity animatedRigEntity;
|
||||
public int rootBoneIndexInRig;
|
||||
public BlobAssetReference<SkinnedMeshInfoBlob> smrInfoBlob;
|
||||
|
||||
public bool IsGPUAnimator(in ComponentLookup<GPUAnimationEngineTag> gpuAnimationEngineTagLookup)
|
||||
=> AnimationUtils.IsGPUAnimator(animatedRigEntity, gpuAnimationEngineTagLookup);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#if RUKHANKA_WITH_NETCODE
|
||||
[GhostComponent(PrefabType = GhostPrefabType.Client)]
|
||||
#endif
|
||||
public struct ShouldUpdateBoundingBoxTag: IComponentData, IEnableableComponent { }
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct RootMotionAnimationStateComponent: IBufferElementData, IEnableableComponent
|
||||
{
|
||||
public uint uniqueMotionId;
|
||||
public BoneTransform animationState;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct RootMotionVelocityComponent: IComponentData
|
||||
{
|
||||
public bool removeBuiltinEntityMovement;
|
||||
public float3 worldVelocity;
|
||||
public float3 deltaPos;
|
||||
public quaternion deltaRot;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[InternalBufferCapacity(4)]
|
||||
public struct AnimationEventComponent: IBufferElementData, IEnableableComponent
|
||||
{
|
||||
public AnimationEventComponent(ref AnimationEventBlob aeb)
|
||||
{
|
||||
nameHash = aeb.nameHash;
|
||||
floatParam = aeb.floatParam;
|
||||
intParam = aeb.intParam;
|
||||
stringParamHash = aeb.stringParamHash;
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
name = "";
|
||||
if (aeb.name.Length > 0)
|
||||
aeb.name.CopyToWithTruncate(ref name);
|
||||
stringParam = "";
|
||||
if (aeb.stringParam.Length > 0)
|
||||
aeb.stringParam.CopyToWithTruncate(ref stringParam);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
public FixedString32Bytes name;
|
||||
public FixedString32Bytes stringParam;
|
||||
#endif
|
||||
public uint nameHash;
|
||||
public float floatParam;
|
||||
public int intParam;
|
||||
public uint stringParamHash;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[ChunkSerializable]
|
||||
public struct PreviousProcessedAnimationComponent: IBufferElementData
|
||||
{
|
||||
public uint motionId;
|
||||
public float animationTime;
|
||||
public BlobAssetReference<AnimationClipBlob> animation;
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Define some special bone names
|
||||
public static class SpecialBones
|
||||
{
|
||||
// Special bone hashes need to be recalculated in case of hashing function change!
|
||||
// I cannot add hash computation code here, because burst will fail to compile
|
||||
public static readonly string UnnamedRootBoneName = "RUKHANKA_UnnamedRootBone";
|
||||
public static readonly string AnimatorTypeName = "RUKHANKA_Animator";
|
||||
public static readonly uint AnimatorTypeNameHash = 0xde00343e;
|
||||
}
|
||||
}
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bce484afb6b804048b94e9f522c6ca5c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/AnimationEngine/AnimationProcessSystemComponents.cs
|
||||
uploadId: 897522
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
|
||||
using System;
|
||||
using Unity.Burst.CompilerServices;
|
||||
using Unity.Collections;
|
||||
using Unity.Entities;
|
||||
using Unity.Mathematics;
|
||||
|
||||
//=================================================================================================================//
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
partial struct AnimationProcessSystem
|
||||
{
|
||||
struct LayerInfo
|
||||
{
|
||||
public int index;
|
||||
public float weight;
|
||||
public AnimationBlendingMode blendMode;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static float ComputeAnimatedProperty(float animatedValue, in NativeArray<AnimationToProcessComponent> animations, uint trackHash, uint propHash)
|
||||
{
|
||||
var layerValue = 0.0f;
|
||||
var rv = animatedValue;
|
||||
var layerHasAnimation = false;
|
||||
LayerInfo layerInfo = default;
|
||||
|
||||
for (int l = 0; l < animations.Length; ++l)
|
||||
{
|
||||
var atp = animations[l];
|
||||
if (Hint.Unlikely(atp.animation == BlobAssetReference<AnimationClipBlob>.Null || atp.weight == 0 || atp.layerWeight == 0))
|
||||
continue;
|
||||
|
||||
var curLayerInfo = GetLayerInfoFromAnimation(atp);
|
||||
|
||||
// Apply layer value
|
||||
if (layerInfo.index != curLayerInfo.index)
|
||||
{
|
||||
if (layerHasAnimation)
|
||||
rv = ApplyLayerValue(rv, layerValue, layerInfo);
|
||||
layerValue = 0;
|
||||
layerHasAnimation = false;
|
||||
}
|
||||
layerInfo = curLayerInfo;
|
||||
|
||||
ref var trackSet = ref atp.animation.Value.clipTracks;
|
||||
var trackGroupIndex = trackSet.trackGroupPHT.Query(trackHash);
|
||||
if (trackGroupIndex < 0)
|
||||
continue;
|
||||
|
||||
var animTime = ComputeBoneAnimationJob.NormalizeAnimationTime(atp.time, ref atp.animation.Value);
|
||||
|
||||
var trackRange = new int2(trackSet.trackGroups[trackGroupIndex], trackSet.trackGroups[trackGroupIndex + 1]);
|
||||
for (var k = trackRange.x; k < trackRange.y; ++k)
|
||||
{
|
||||
var track = trackSet.tracks[k];
|
||||
if (track.props == propHash)
|
||||
{
|
||||
var curveValue = SampleTrack(ref trackSet, k, atp, animTime.x);
|
||||
if (atp.animation.Value.loopPoseBlend)
|
||||
curveValue -= CalculateTrackLoopValue(ref trackSet, k, atp, animTime.y);
|
||||
layerValue += curveValue * atp.weight;
|
||||
layerHasAnimation = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (layerHasAnimation)
|
||||
rv = ApplyLayerValue(rv, layerValue, layerInfo);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static float ApplyLayerValue(float currentValue, float layerValue, in LayerInfo layerInfo)
|
||||
{
|
||||
var rv = currentValue;
|
||||
if (Hint.Likely(layerInfo.blendMode == AnimationBlendingMode.Override))
|
||||
{
|
||||
rv = math.lerp(rv, layerValue, layerInfo.weight);
|
||||
}
|
||||
else
|
||||
{
|
||||
rv += layerValue * layerInfo.weight;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static float SampleTrack(ref TrackSet trackSet, int trackIndex, in AnimationToProcessComponent atp, float animTime)
|
||||
{
|
||||
var curveValue = BlobCurve.SampleAnimationCurve(ref trackSet, trackIndex, animTime);
|
||||
// Make additive animation if requested
|
||||
if (atp.blendMode == AnimationBlendingMode.Additive)
|
||||
{
|
||||
var additiveValue = BlobCurve.SampleAnimationCurve(ref trackSet, trackIndex, 0);
|
||||
curveValue -= additiveValue;
|
||||
}
|
||||
return curveValue;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static float CalculateTrackLoopValue(ref TrackSet trackSet, int trackIndex, in AnimationToProcessComponent atp, float normalizedTime)
|
||||
{
|
||||
var startV = SampleTrack(ref trackSet, trackIndex, atp, 0);
|
||||
var endV = SampleTrack(ref trackSet, trackIndex, atp, atp.animation.Value.length);
|
||||
|
||||
var rv = (endV - startV) * normalizedTime;
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static LayerInfo GetLayerInfoFromAnimation(in AnimationToProcessComponent atp)
|
||||
{
|
||||
var rv = new LayerInfo()
|
||||
{
|
||||
weight = atp.layerWeight,
|
||||
blendMode = atp.blendMode,
|
||||
index = atp.layerIndex
|
||||
};
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4d9e19b3f73cd174b97a4a499d857d09
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/AnimationEngine/AnimationProcessSystem_Common.cs
|
||||
uploadId: 897522
|
||||
+1064
File diff suppressed because it is too large
Load Diff
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b0a1354f9dc3a6b48bfc81641c1a6e7f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/AnimationEngine/AnimationProcessSystem_Jobs.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,243 @@
|
||||
using System;
|
||||
using Unity.Collections;
|
||||
using Unity.Entities;
|
||||
using Unity.Mathematics;
|
||||
using Unity.Assertions;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
public struct AnimationStream: IDisposable
|
||||
{
|
||||
public DynamicFrameData rigFrameData;
|
||||
public RuntimeAnimationData runtimeData;
|
||||
public BlobAssetReference<RigDefinitionBlob> rigBlob;
|
||||
public NativeBitArray worldPoseDirtyFlags;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static AnimationStream Create(RuntimeAnimationData rd, in RigDefinitionComponent rdc)
|
||||
{
|
||||
var rv = new AnimationStream()
|
||||
{
|
||||
rigFrameData = rdc.dynamicFrameData,
|
||||
runtimeData = rd,
|
||||
rigBlob = rdc.rigBlob,
|
||||
worldPoseDirtyFlags = new NativeBitArray(rdc.dynamicFrameData.rigBoneCount, Allocator.Temp)
|
||||
};
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
RebuildOutdatedBonePoses(-1);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public BoneTransform GetLocalPose(int boneIndex)
|
||||
{
|
||||
if (boneIndex >= rigFrameData.rigBoneCount)
|
||||
return BoneTransform.Identity();
|
||||
|
||||
return runtimeData.animatedBonesBuffer[rigFrameData.bonePoseOffset + boneIndex];
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public float3 GetLocalPosition(int boneIndex) => GetLocalPose(boneIndex).pos;
|
||||
public quaternion GetLocalRotation(int boneIndex) => GetLocalPose(boneIndex).rot;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public BoneTransform GetWorldPose(int boneIndex)
|
||||
{
|
||||
if (boneIndex >= rigFrameData.rigBoneCount)
|
||||
return BoneTransform.Identity();
|
||||
|
||||
var isWorldPoseDirty = worldPoseDirtyFlags.IsSet(boneIndex);
|
||||
if (isWorldPoseDirty)
|
||||
RebuildOutdatedBonePoses(boneIndex);
|
||||
|
||||
return runtimeData.worldSpaceBonesBuffer[rigFrameData.bonePoseOffset + boneIndex];
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public float3 GetWorldPosition(int boneIndex) => GetWorldPose(boneIndex).pos;
|
||||
public quaternion GetWorldRotation(int boneIndex) => GetWorldPose(boneIndex).rot;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
BoneTransform GetParentBoneWorldPose(int boneIndex)
|
||||
{
|
||||
if (boneIndex >= rigFrameData.rigBoneCount)
|
||||
return BoneTransform.Identity();
|
||||
|
||||
var parentBoneIndex = rigBlob.Value.bones[boneIndex].parentBoneIndex;
|
||||
var parentWorldPose = BoneTransform.Identity();
|
||||
if (parentBoneIndex >= 0)
|
||||
{
|
||||
if (worldPoseDirtyFlags.IsSet(parentBoneIndex))
|
||||
RebuildOutdatedBonePoses(parentBoneIndex);
|
||||
parentWorldPose = runtimeData.worldSpaceBonesBuffer[parentBoneIndex + rigFrameData.bonePoseOffset];
|
||||
}
|
||||
|
||||
return parentWorldPose;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void SetWorldPose(int boneIndex, in BoneTransform bt)
|
||||
{
|
||||
if (boneIndex >= rigFrameData.rigBoneCount)
|
||||
return;
|
||||
|
||||
var absBoneIndex = rigFrameData.bonePoseOffset + boneIndex;
|
||||
runtimeData.worldSpaceBonesBuffer[absBoneIndex] = bt;
|
||||
|
||||
var parentWorldPose = GetParentBoneWorldPose(boneIndex);
|
||||
|
||||
ref var boneLocalPose = ref runtimeData.animatedBonesBuffer.ElementAt(absBoneIndex);
|
||||
boneLocalPose = BoneTransform.Multiply(BoneTransform.Inverse(parentWorldPose), bt);
|
||||
|
||||
MarkChildrenWorldPosesAsDirty(boneIndex);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void SetWorldPosition(int boneIndex, float3 pos)
|
||||
{
|
||||
if (boneIndex >= rigFrameData.rigBoneCount)
|
||||
return;
|
||||
|
||||
var curPose = GetWorldPose(boneIndex);
|
||||
curPose.pos = pos;
|
||||
SetWorldPose(boneIndex, curPose);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void SetWorldRotation(int boneIndex, quaternion rot)
|
||||
{
|
||||
if (boneIndex >= rigFrameData.rigBoneCount)
|
||||
return;
|
||||
|
||||
var absBoneIndex = rigFrameData.bonePoseOffset + boneIndex;
|
||||
ref var boneWorldPose = ref runtimeData.worldSpaceBonesBuffer.ElementAt(absBoneIndex);
|
||||
boneWorldPose.rot = rot;
|
||||
|
||||
var parentWorldRot = GetParentBoneWorldPose(boneIndex).rot;
|
||||
|
||||
ref var boneLocalPose = ref runtimeData.animatedBonesBuffer.ElementAt(absBoneIndex);
|
||||
boneLocalPose.rot = math.mul(math.inverse(parentWorldRot), boneWorldPose.rot);
|
||||
|
||||
MarkChildrenWorldPosesAsDirty(boneIndex);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void SetLocalPose(int boneIndex, in BoneTransform bt)
|
||||
{
|
||||
if (boneIndex >= rigFrameData.rigBoneCount)
|
||||
return;
|
||||
|
||||
var absBoneIndex = rigFrameData.bonePoseOffset + boneIndex;
|
||||
runtimeData.animatedBonesBuffer[absBoneIndex] = bt;
|
||||
MarkChildrenWorldPosesAsDirty(boneIndex);
|
||||
worldPoseDirtyFlags.Set(boneIndex, true);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void SetLocalPosition(int boneIndex, float3 pos)
|
||||
{
|
||||
if (boneIndex >= rigFrameData.rigBoneCount)
|
||||
return;
|
||||
|
||||
var absBoneIndex = rigFrameData.bonePoseOffset + boneIndex;
|
||||
ref var curPose = ref runtimeData.animatedBonesBuffer.ElementAt(absBoneIndex);
|
||||
curPose.pos = pos;
|
||||
MarkChildrenWorldPosesAsDirty(boneIndex);
|
||||
worldPoseDirtyFlags.Set(boneIndex, true);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void SetLocalRotation(int boneIndex, quaternion rot)
|
||||
{
|
||||
if (boneIndex >= rigFrameData.rigBoneCount)
|
||||
return;
|
||||
|
||||
var absBoneIndex = rigFrameData.bonePoseOffset + boneIndex;
|
||||
ref var curPose = ref runtimeData.animatedBonesBuffer.ElementAt(absBoneIndex);
|
||||
curPose.rot = rot;
|
||||
MarkChildrenWorldPosesAsDirty(boneIndex);
|
||||
worldPoseDirtyFlags.Set(boneIndex, true);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void MarkChildrenWorldPosesAsDirty(int rootBoneIndex)
|
||||
{
|
||||
for (var i = rootBoneIndex + 1; i < rigFrameData.rigBoneCount; ++i)
|
||||
{
|
||||
ref var bone = ref rigBlob.Value.bones[i];
|
||||
if (bone.parentBoneIndex == rootBoneIndex)
|
||||
{
|
||||
worldPoseDirtyFlags.Set(i, true);
|
||||
MarkChildrenWorldPosesAsDirty(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void RebuildOutdatedBonePoses(int interestedBoneIndex)
|
||||
{
|
||||
if (rigFrameData.rigBoneCount < 0)
|
||||
return;
|
||||
|
||||
var endBoneIndex = math.select(rigFrameData.rigBoneCount - 1, interestedBoneIndex, interestedBoneIndex >= 0);
|
||||
endBoneIndex = math.min(endBoneIndex, rigFrameData.rigBoneCount);
|
||||
for (var i = 0; i <= endBoneIndex; ++i)
|
||||
{
|
||||
var isWorldPoseDirty = worldPoseDirtyFlags.IsSet(i);
|
||||
if (!isWorldPoseDirty)
|
||||
continue;
|
||||
|
||||
var absBoneIndex = rigFrameData.bonePoseOffset + i;
|
||||
ref var rigBone = ref rigBlob.Value.bones[i];
|
||||
var boneLocalPose = runtimeData.animatedBonesBuffer[absBoneIndex];
|
||||
|
||||
var parentBoneWorldPose = BoneTransform.Identity();
|
||||
if (rigBone.parentBoneIndex >= 0)
|
||||
{
|
||||
parentBoneWorldPose = runtimeData.worldSpaceBonesBuffer[rigFrameData.bonePoseOffset + rigBone.parentBoneIndex];
|
||||
}
|
||||
|
||||
var worldPose = BoneTransform.Multiply(parentBoneWorldPose, boneLocalPose);
|
||||
runtimeData.worldSpaceBonesBuffer[absBoneIndex] = worldPose;
|
||||
}
|
||||
worldPoseDirtyFlags.SetBits(0, false, endBoneIndex + 1);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public AnimationTransformFlags GetAnimationTransformFlagsRO()
|
||||
{
|
||||
return AnimationTransformFlags.CreateFromBufferRO(runtimeData.boneTransformFlagsHolderArr, rigFrameData.boneFlagsOffset, rigFrameData.rigBoneCount);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public AnimationTransformFlags GetAnimationTransformFlagsRW()
|
||||
{
|
||||
return AnimationTransformFlags.CreateFromBufferRW(runtimeData.boneTransformFlagsHolderArr, rigFrameData.boneFlagsOffset, rigFrameData.rigBoneCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e30795c9f15acc849bbee0264be3ccf0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/AnimationEngine/AnimationStream.cs
|
||||
uploadId: 897522
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Rukhanka.Toolbox;
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
public struct AnimationTransformFlags
|
||||
{
|
||||
UnsafeBitArray transformFlags;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool IsTranslationSet(int index) => transformFlags.IsSet(index * 4 + 0);
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool IsRotationSet(int index) => transformFlags.IsSet(index * 4 + 1);
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool IsScaleSet(int index) => transformFlags.IsSet(index * 4 + 2);
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool IsAbsoluteTransform(int index) => transformFlags.IsSet(index * 4 + 3);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetTranslationFlag(int index) => transformFlags.SetBitThreadSafe(index * 4 + 0);
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetRotationFlag(int index) => transformFlags.SetBitThreadSafe(index * 4 + 1);
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetScaleFlag(int index) => transformFlags.SetBitThreadSafe(index * 4 + 2);
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetAbsoluteTransformFlag(int index) => transformFlags.SetBitThreadSafe(index * 4 + 3);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ResetAllFlags() => transformFlags.Clear();
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
unsafe static UnsafeBitArray GetRigTransformFlagsInternal(void* ptr, int bufLenInBytes, int ulongOffset, int boneCount)
|
||||
{
|
||||
// Each bone contains 4 bit flags - TRS and is bone already in absolute transform
|
||||
var sizeInUlongs = (boneCount * 4 >> 6) + 1;
|
||||
var sizeInBytes = sizeInUlongs * 8;
|
||||
var startPtr = (ulong*)ptr + ulongOffset;
|
||||
|
||||
#if ENABLE_UNITY_COLLECTIONS_CHECKS
|
||||
if ((byte*)startPtr + sizeInBytes > (byte*)ptr + bufLenInBytes)
|
||||
{
|
||||
throw new InvalidOperationException($"Buffer range error! Offset and/or count exceed buffer space!");
|
||||
}
|
||||
#endif
|
||||
|
||||
var rv = new UnsafeBitArray(startPtr, sizeInUlongs * 8, Allocator.None);
|
||||
return rv;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe AnimationTransformFlags CreateFromBufferRW(in NativeList<ulong> buf, int bufElementOffset, int boneCount)
|
||||
{
|
||||
var rwPtr = buf.GetUnsafePtr();
|
||||
var rv = new AnimationTransformFlags() { transformFlags = GetRigTransformFlagsInternal(rwPtr, buf.Length * sizeof(ulong), bufElementOffset, boneCount) };
|
||||
return rv;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe AnimationTransformFlags CreateFromBufferRO(in NativeList<ulong> buf, int bufElementOffset, int boneCount)
|
||||
{
|
||||
var roPtr = buf.GetUnsafeReadOnlyPtr();
|
||||
var rv = new AnimationTransformFlags() { transformFlags = GetRigTransformFlagsInternal(roPtr, buf.Length * sizeof(ulong), bufElementOffset, boneCount) };
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ad302fcd955fb504bb4d7a4fffbf5c22
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/AnimationEngine/AnimationTransformFlags.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,42 @@
|
||||
|
||||
using Unity.Entities;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
public struct AvatarMaskBlob: GenericAssetBlob
|
||||
{
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
public BlobString name;
|
||||
public string Name() => name.ToString();
|
||||
public BlobArray<BlobString> includedBoneNames;
|
||||
public float bakingTime;
|
||||
public float BakingTime() => bakingTime;
|
||||
#endif
|
||||
public Hash128 hash;
|
||||
public Hash128 Hash() => hash;
|
||||
|
||||
public BlobArray<uint> includedBoneMask;
|
||||
public uint humanBodyPartsAvatarMask;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static (int, uint) GetUintIndexAndMask(int boneIndex)
|
||||
{
|
||||
int uintIndex = boneIndex >> 5; // boneIndex / 32
|
||||
int byteOffsetInUint = boneIndex & 0x1f; // boneIndex % 32;
|
||||
uint boneMask = 1u << byteOffsetInUint;
|
||||
return (uintIndex, boneMask);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public bool IsBoneIncluded(int boneIndex)
|
||||
{
|
||||
var (uintIndex, mask) = GetUintIndexAndMask(boneIndex);
|
||||
var rv = (includedBoneMask[uintIndex] & mask) != 0;
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3e9e4993401af9e458a46c80dcacceaf
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/AnimationEngine/AvatarMaskBlob.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,109 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using Unity.Entities;
|
||||
using Unity.Mathematics;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
public static class BlobCurve
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
static public float EvaluateBezierCurve(KeyFrame f0, KeyFrame f1, float l)
|
||||
{
|
||||
float dt = f1.time - f0.time;
|
||||
float m0 = f0.outTan * dt;
|
||||
float m1 = f1.inTan * dt;
|
||||
|
||||
float t2 = l * l;
|
||||
float t3 = t2 * l;
|
||||
|
||||
float a = 2 * t3 - 3 * t2 + 1;
|
||||
float b = t3 - 2 * t2 + l;
|
||||
float c = t3 - t2;
|
||||
float d = -2 * t3 + 3 * t2;
|
||||
|
||||
float rv = a * f0.v + b * m0 + c * m1 + d * f1.v;
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe float SampleAnimationCurve(ref BlobArray<KeyFrame> kf, float time)
|
||||
{
|
||||
var arr = new ReadOnlySpan<KeyFrame>(kf.GetUnsafePtr(), kf.Length);
|
||||
return SampleAnimationCurveBinarySearch(arr, time);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe float SampleAnimationCurve(ref TrackSet ts, int trackIndex, float time)
|
||||
{
|
||||
var track = ts.tracks[trackIndex];
|
||||
var kfRange = new ReadOnlySpan<KeyFrame>((KeyFrame*)ts.keyframes.GetUnsafePtr() + track.keyFrameRange.x, track.keyFrameRange.y);
|
||||
return SampleAnimationCurveBinarySearch(kfRange, time);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float SampleAnimationCurveBinarySearch(in ReadOnlySpan<KeyFrame> kf, float time)
|
||||
{
|
||||
var startIndex = 0;
|
||||
var endIndex = kf.Length;
|
||||
var less = true;
|
||||
var greater = true;
|
||||
KeyFrame frame0 = default, frame1 = default;
|
||||
|
||||
if (kf.Length < 3)
|
||||
return SampleAnimationCurveLinearSearch(kf, time);
|
||||
|
||||
while(endIndex - startIndex >= 1 && (less || greater) && endIndex > 1)
|
||||
{
|
||||
var middleIndex = (endIndex + startIndex) / 2;
|
||||
frame1 = kf[middleIndex];
|
||||
frame0 = kf[middleIndex - 1];
|
||||
|
||||
less = time < frame0.time;
|
||||
greater = time > frame1.time;
|
||||
|
||||
startIndex = math.select(startIndex, middleIndex + 1, greater);
|
||||
endIndex = math.select(endIndex, middleIndex, less);
|
||||
}
|
||||
|
||||
if (less)
|
||||
return kf[0].v;
|
||||
|
||||
if (greater)
|
||||
return kf[^1].v;
|
||||
|
||||
float f = (time - frame0.time) / (frame1.time - frame0.time);
|
||||
return EvaluateBezierCurve(frame0, frame1, f);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static float SampleAnimationCurveLinearSearch(in ReadOnlySpan<KeyFrame> kf, float time)
|
||||
{
|
||||
for (int i = 0; i < kf.Length; ++i)
|
||||
{
|
||||
var frame1 = kf[i];
|
||||
if (frame1.time >= time)
|
||||
{
|
||||
if (i == 0)
|
||||
return kf[i].v;
|
||||
var frame0 = kf[i - 1];
|
||||
|
||||
float f = (time - frame0.time) / (frame1.time - frame0.time);
|
||||
return EvaluateBezierCurve(frame0, frame1, f);
|
||||
}
|
||||
}
|
||||
return kf[^1].v;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e1e6e7021f2c35f4c9c8257fbc6ae4f3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/AnimationEngine/BlobCurve.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,120 @@
|
||||
using Unity.Mathematics;
|
||||
using Unity.Transforms;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
public struct BoneTransform
|
||||
{
|
||||
public float3 pos;
|
||||
public quaternion rot;
|
||||
public float3 scale;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public BoneTransform(LocalTransform lt)
|
||||
{
|
||||
pos = lt.Position;
|
||||
rot = lt.Rotation;
|
||||
scale = lt.Scale;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public BoneTransform(LocalTransform lt, in PostTransformMatrix ptm)
|
||||
{
|
||||
pos = lt.Position;
|
||||
rot = lt.Rotation;
|
||||
scale = lt.Scale * new float3(ptm.Value[0][0], ptm.Value[1][1], ptm.Value[2][2]);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static BoneTransform Identity()
|
||||
{
|
||||
return new BoneTransform() { pos = 0, rot = quaternion.identity, scale = 1 };
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public float4x4 ToFloat4x4()
|
||||
{
|
||||
return float4x4.TRS(pos, rot, scale);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Multiply child with parent
|
||||
public static BoneTransform Multiply(in BoneTransform parent, in BoneTransform child)
|
||||
{
|
||||
var rv = new BoneTransform();
|
||||
rv.pos = math.mul(parent.rot, child.pos * parent.scale) + parent.pos;
|
||||
rv.rot = math.mul(parent.rot, child.rot);
|
||||
rv.scale = parent.scale * child.scale;
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static BoneTransform Inverse(in BoneTransform bt)
|
||||
{
|
||||
var rv = new BoneTransform();
|
||||
rv.rot = math.inverse(bt.rot);
|
||||
rv.scale = math.rcp(bt.scale);
|
||||
rv.pos = math.mul(rv.rot, -bt.pos * rv.scale);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static BoneTransform Scale(in BoneTransform bt, float3 scale)
|
||||
{
|
||||
var rv = new BoneTransform()
|
||||
{
|
||||
pos = bt.pos * scale.x,
|
||||
rot = bt.rot.value * scale.y,
|
||||
scale = bt.scale * scale.z,
|
||||
};
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static BoneTransform Add(in BoneTransform p0, in BoneTransform p1)
|
||||
{
|
||||
var rv = new BoneTransform()
|
||||
{
|
||||
pos = p0.pos + p1.pos,
|
||||
rot = p0.rot.value + p1.rot.value,
|
||||
scale = p0.scale + p1.scale
|
||||
};
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static BoneTransform Lerp(in BoneTransform bt0, in BoneTransform bt1, float f)
|
||||
{
|
||||
var rv = new BoneTransform()
|
||||
{
|
||||
pos = math.lerp(bt0.pos, bt1.pos, f),
|
||||
rot = math.slerp(bt0.rot.value, bt1.rot.value, f),
|
||||
scale = math.lerp(bt0.scale, bt1.scale, f)
|
||||
};
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static float3 TransformPoint(BoneTransform bt, float3 v)
|
||||
{
|
||||
float3 rv = math.rotate(bt.rot, v * bt.scale) + bt.pos;
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public LocalTransform ToLocalTransformComponent() => new LocalTransform() { Position = pos, Rotation = rot, Scale = scale.x };
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4d9a73be1128e564fa48a1770a1149bb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/AnimationEngine/BoneTransform.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,65 @@
|
||||
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
public static class HumanStaticData
|
||||
{
|
||||
static readonly float bodyDefaultMass = 82.5f;
|
||||
|
||||
public static readonly float[] bodyPartsMasses =
|
||||
{
|
||||
12 / bodyDefaultMass,
|
||||
10 / bodyDefaultMass,
|
||||
10 / bodyDefaultMass,
|
||||
4 / bodyDefaultMass,
|
||||
4 / bodyDefaultMass,
|
||||
0.8f / bodyDefaultMass,
|
||||
0.8f / bodyDefaultMass,
|
||||
2.5f / bodyDefaultMass,
|
||||
12 / bodyDefaultMass,
|
||||
12 / bodyDefaultMass,
|
||||
1 / bodyDefaultMass,
|
||||
4 / bodyDefaultMass,
|
||||
0.5f / bodyDefaultMass,
|
||||
0.5f / bodyDefaultMass,
|
||||
2 / bodyDefaultMass,
|
||||
2 / bodyDefaultMass,
|
||||
1.5f / bodyDefaultMass,
|
||||
1.5f / bodyDefaultMass,
|
||||
0.5f / bodyDefaultMass,
|
||||
0.5f / bodyDefaultMass,
|
||||
0.2f / bodyDefaultMass,
|
||||
0.2f / bodyDefaultMass,
|
||||
};
|
||||
|
||||
public static readonly int4[] massIndicesTable =
|
||||
{
|
||||
new ((int)HumanBodyBones.LeftUpperLeg, (int)HumanBodyBones.RightUpperLeg, (int)HumanBodyBones.Spine, -1),
|
||||
new ((int)HumanBodyBones.LeftUpperLeg, (int)HumanBodyBones.LeftLowerLeg, -1, -1),
|
||||
new ((int)HumanBodyBones.RightUpperLeg, (int)HumanBodyBones.RightLowerLeg, -1, -1),
|
||||
new ((int)HumanBodyBones.LeftLowerLeg, (int)HumanBodyBones.LeftFoot, -1, -1),
|
||||
new ((int)HumanBodyBones.RightLowerLeg, (int)HumanBodyBones.RightFoot, -1, -1),
|
||||
new ((int)HumanBodyBones.LeftFoot, -1, -1, -1),
|
||||
new ((int)HumanBodyBones.RightFoot, -1, -1, -1),
|
||||
new ((int)HumanBodyBones.Spine, (int)HumanBodyBones.Chest, -1, -1),
|
||||
new ((int)HumanBodyBones.UpperChest, (int)HumanBodyBones.Chest, -1, -1),
|
||||
new ((int)HumanBodyBones.UpperChest, (int)HumanBodyBones.Neck, (int)HumanBodyBones.LeftShoulder, (int)HumanBodyBones.RightShoulder),
|
||||
new ((int)HumanBodyBones.Neck, (int)HumanBodyBones.Head, -1, 1),
|
||||
new ((int)HumanBodyBones.Head, -1, -1, 1),
|
||||
new ((int)HumanBodyBones.LeftShoulder, (int)HumanBodyBones.LeftUpperArm, -1, 1),
|
||||
new ((int)HumanBodyBones.RightShoulder, (int)HumanBodyBones.RightUpperArm, -1, 1),
|
||||
new ((int)HumanBodyBones.LeftLowerArm, (int)HumanBodyBones.LeftUpperArm, -1, 1),
|
||||
new ((int)HumanBodyBones.RightLowerArm, (int)HumanBodyBones.RightUpperArm, -1, 1),
|
||||
new ((int)HumanBodyBones.LeftLowerArm, (int)HumanBodyBones.LeftHand, -1, 1),
|
||||
new ((int)HumanBodyBones.RightLowerArm, (int)HumanBodyBones.RightHand, -1, 1),
|
||||
new ((int)HumanBodyBones.LeftHand, -1, -1, 1),
|
||||
new ((int)HumanBodyBones.RightHand, -1, -1, 1),
|
||||
new ((int)HumanBodyBones.LeftToes, -1, -1, 1),
|
||||
new ((int)HumanBodyBones.RightToes, -1, -1, 1),
|
||||
};
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f04734850528aef44aaaff3219c1f72e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/AnimationEngine/HumanStaticData.cs
|
||||
uploadId: 897522
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Rukhanka.Toolbox;
|
||||
using Unity.Collections;
|
||||
using Unity.Entities;
|
||||
using Unity.Mathematics;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
public struct RuntimeAnimationData: IComponentData
|
||||
{
|
||||
internal NativeList<BoneTransform> animatedBonesBuffer;
|
||||
internal NativeList<BoneTransform> worldSpaceBonesBuffer;
|
||||
internal NativeList<int3> boneToEntityArr;
|
||||
internal NativeList<ulong> boneTransformFlagsHolderArr;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static RuntimeAnimationData MakeDefault()
|
||||
{
|
||||
var rv = new RuntimeAnimationData()
|
||||
{
|
||||
animatedBonesBuffer = new (Allocator.Persistent),
|
||||
worldSpaceBonesBuffer = new (Allocator.Persistent),
|
||||
boneToEntityArr = new (Allocator.Persistent),
|
||||
boneTransformFlagsHolderArr = new (Allocator.Persistent),
|
||||
};
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
animatedBonesBuffer.Dispose();
|
||||
worldSpaceBonesBuffer.Dispose();
|
||||
boneToEntityArr.Dispose();
|
||||
boneTransformFlagsHolderArr.Dispose();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ReadOnlySpan<BoneTransform> GetAnimationDataForRigRO(in NativeList<BoneTransform> animatedBonesBuffer, int offset, int length)
|
||||
{
|
||||
var rv = animatedBonesBuffer.GetReadOnlySpan(offset, length);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Span<BoneTransform> GetAnimationDataForRigRW(in NativeList<BoneTransform> animatedBonesBuffer, int offset, int length)
|
||||
{
|
||||
var rv = animatedBonesBuffer.GetSpan(offset, length);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ReadOnlySpan<BoneTransform> GetAnimationDataForRigRO
|
||||
(
|
||||
in NativeList<BoneTransform> animatedBonesBuffer,
|
||||
in RigDefinitionComponent rigDefinition
|
||||
)
|
||||
{
|
||||
return GetAnimationDataForRigRO(animatedBonesBuffer, rigDefinition.dynamicFrameData.bonePoseOffset, rigDefinition.dynamicFrameData.rigBoneCount);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Span<BoneTransform> GetAnimationDataForRigRW
|
||||
(
|
||||
in NativeList<BoneTransform> animatedBonesBuffer,
|
||||
in RigDefinitionComponent rigDefinition
|
||||
)
|
||||
{
|
||||
return GetAnimationDataForRigRW(animatedBonesBuffer, rigDefinition.dynamicFrameData.bonePoseOffset, rigDefinition.dynamicFrameData.rigBoneCount);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static AnimationTransformFlags GetAnimationTransformFlagsRO(in NativeList<int3> boneToEntityArr, in NativeList<ulong> boneTransformFlagsArr, int globalBoneIndex, int boneCount)
|
||||
{
|
||||
var boneInfo = boneToEntityArr[globalBoneIndex];
|
||||
var rv = AnimationTransformFlags.CreateFromBufferRO(boneTransformFlagsArr, boneInfo.z, boneCount);
|
||||
return rv;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static AnimationTransformFlags GetAnimationTransformFlagsRW(in NativeList<int3> boneToEntityArr, in NativeList<ulong> boneTransformFlagsArr, int globalBoneIndex, int boneCount)
|
||||
{
|
||||
var boneInfo = boneToEntityArr[globalBoneIndex];
|
||||
var rv = AnimationTransformFlags.CreateFromBufferRW(boneTransformFlagsArr, boneInfo.z, boneCount);
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 51c1d5baca6feef46ad93355850eb2e7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/AnimationEngine/RuntimeAnimationDataSingleton.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,58 @@
|
||||
using Hash128 = Unity.Entities.Hash128;
|
||||
using Unity.Mathematics;
|
||||
using Unity.Entities;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
public struct SkinnedMeshBoneInfo
|
||||
{
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
public BlobString name;
|
||||
#endif
|
||||
public uint hash;
|
||||
public float4x4 bindPose;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct BlendShapeInfo
|
||||
{
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
public BlobString name;
|
||||
#endif
|
||||
public uint hash;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct SkinnedMeshInfoBlob: GenericAssetBlob
|
||||
{
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
public BlobString skeletonName;
|
||||
public string Name() => skeletonName.ToString();
|
||||
public float bakingTime;
|
||||
public float BakingTime() => bakingTime;
|
||||
#endif
|
||||
public Hash128 hash;
|
||||
public Hash128 Hash() => hash;
|
||||
|
||||
public BlobArray<SkinnedMeshBoneInfo> bones;
|
||||
public BlobArray<BlendShapeInfo> blendShapes;
|
||||
// Each weight index is bone count for a vertex (lower 8 bits) and start bone weight data index (upper 24 bits)
|
||||
// Bone weights index need to be used to index into Mesh.GetAllBoneWeights().
|
||||
public BlobArray<uint> boneWeightsIndices;
|
||||
public int meshBoneWeightsCount;
|
||||
public int meshBlendShapesCount;
|
||||
public int meshVerticesCount;
|
||||
|
||||
public static uint GetBoneWeightCount(uint packedBoneWeightsValue) => packedBoneWeightsValue & 0xff;
|
||||
public static uint GetBoneWeightIndexOffset(uint packedBoneWeightsValue) => packedBoneWeightsValue >> 8;
|
||||
public static uint PackBoneCountAndOffset(byte boneCount, uint offset)
|
||||
{
|
||||
var rv = boneCount | (offset << 8);
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f390f1fc5933c314e8a444549220162a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/AnimationEngine/SkinnedMeshBlob.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,27 @@
|
||||
|
||||
using Unity.Entities;
|
||||
using Unity.Transforms;
|
||||
|
||||
#if RUKHANKA_WITH_NETCODE
|
||||
using Unity.NetCode;
|
||||
#endif
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
[UpdateBefore(typeof(TransformSystemGroup))]
|
||||
#if RUKHANKA_WITH_NETCODE
|
||||
[UpdateAfter(typeof(PredictedSimulationSystemGroup))]
|
||||
#else
|
||||
[WorldSystemFilter(WorldSystemFilterFlags.LocalSimulation | WorldSystemFilterFlags.ClientSimulation)]
|
||||
#endif
|
||||
public partial class RukhankaAnimationSystemGroup: ComponentSystemGroup { }
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#if RUKHANKA_WITH_NETCODE
|
||||
[UpdateInGroup(typeof(PredictedSimulationSystemGroup))]
|
||||
public partial class RukhankaPredictedAnimationSystemGroup: ComponentSystemGroup { }
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 27dcde57ce4d3c242be17a4d0ff56e71
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/AnimationSystemGroup.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,52 @@
|
||||
using Unity.Collections;
|
||||
using Unity.Entities;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
public static class AnimationUtils
|
||||
{
|
||||
public static Hash128 CalculateBoneRemapTableHash(in BlobAssetReference<SkinnedMeshInfoBlob> skinnedMesh, in BlobAssetReference<RigDefinitionBlob> rigDef)
|
||||
{
|
||||
var rv = new Hash128(skinnedMesh.Value.hash.Value.x, skinnedMesh.Value.hash.Value.y, rigDef.Value.hash.Value.x, rigDef.Value.hash.Value.y);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static BlobAssetReference<BoneRemapTableBlob> MakeSkinnedMeshToRigRemapTable(in SkinnedMeshRendererComponent sm, in RigDefinitionComponent rigDef, Allocator allocator)
|
||||
{
|
||||
var bb = new BlobBuilder(Allocator.Temp);
|
||||
ref var brt = ref bb.ConstructRoot<BoneRemapTableBlob>();
|
||||
|
||||
var bba = bb.Allocate(ref brt.remapIndices, sm.smrInfoBlob.Value.bones.Length);
|
||||
for (int i = 0; i < bba.Length; ++i)
|
||||
{
|
||||
bba[i] = -1;
|
||||
ref var rb = ref sm.smrInfoBlob.Value.bones[i];
|
||||
var rbHash = rb.hash;
|
||||
|
||||
for (int j = 0; j < rigDef.rigBlob.Value.bones.Length; ++j)
|
||||
{
|
||||
ref var bn = ref rigDef.rigBlob.Value.bones[j];
|
||||
var bnHash = bn.hash;
|
||||
|
||||
if (bnHash == rbHash)
|
||||
{
|
||||
bba[i] = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
var rv = bb.CreateBlobAssetReference<BoneRemapTableBlob>(allocator);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static bool IsGPUAnimator(Entity animatedRigEntity, in ComponentLookup<GPUAnimationEngineTag> gpuAnimationEngineTagLookup)
|
||||
{
|
||||
return animatedRigEntity != Entity.Null && gpuAnimationEngineTagLookup.IsComponentEnabled(animatedRigEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c733bc1447e67644bb4bb6324086e012
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/AnimationUtils.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c2eaa811ea5636741a61036f9d5d7dc8
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+217
@@ -0,0 +1,217 @@
|
||||
using Unity.Entities;
|
||||
using Unity.Mathematics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
public struct ControllerBlob: GenericAssetBlob
|
||||
{
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
public BlobString name;
|
||||
public string Name() => name.ToString();
|
||||
public float bakingTime;
|
||||
public float BakingTime() => bakingTime;
|
||||
#endif
|
||||
public Hash128 hash;
|
||||
public Hash128 Hash() => hash;
|
||||
|
||||
public BlobArray<LayerBlob> layers;
|
||||
public BlobArray<ParameterBlob> parameters;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public enum ControllerParameterType
|
||||
{
|
||||
Int,
|
||||
Float,
|
||||
Bool,
|
||||
Trigger
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct ParameterValue
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
public float floatValue;
|
||||
[FieldOffset(0)]
|
||||
public int intValue;
|
||||
[FieldOffset(0)]
|
||||
public bool boolValue;
|
||||
|
||||
public static implicit operator ParameterValue(float f) => new ParameterValue() { floatValue = f };
|
||||
public static implicit operator ParameterValue(int i) => new ParameterValue() { intValue = i };
|
||||
public static implicit operator ParameterValue(bool b) => new ParameterValue() { boolValue = b };
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct ParameterBlob
|
||||
{
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
public BlobString name;
|
||||
#endif
|
||||
public uint hash;
|
||||
public ParameterValue defaultValue;
|
||||
public ControllerParameterType type;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public enum AnimationBlendingMode
|
||||
{
|
||||
Override = 0,
|
||||
Additive = 1
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public enum AnimatorConditionMode
|
||||
{
|
||||
If = 1,
|
||||
IfNot = 2,
|
||||
Greater = 3,
|
||||
Less = 4,
|
||||
Equals = 6,
|
||||
NotEqual = 7
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct LayerBlob
|
||||
{
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
public BlobString name;
|
||||
#endif
|
||||
public int defaultStateIndex;
|
||||
public float initialWeight;
|
||||
public int syncedLayerIndex;
|
||||
// syncedTiming acts as a bool (v != 0) for layers that have syncedLayerIndex >= 0. Base layer state speed also
|
||||
// need to be adjusted, so syncedTiming acts as index to synced layer for weight lerp computation
|
||||
public int syncedTiming;
|
||||
public AnimationBlendingMode blendingMode;
|
||||
public BlobArray<StateBlob> states;
|
||||
public Hash128 avatarMaskBlobHash;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct TransitionBlob
|
||||
{
|
||||
// Just a copy of UnityEditor.Animations.TransitionInterruptionSource
|
||||
// I cannot use original enum because it is in editor assembly
|
||||
public enum InterruptionSource
|
||||
{
|
||||
None,
|
||||
Source,
|
||||
Destination,
|
||||
SourceThenDestination,
|
||||
DestinationThenSource,
|
||||
}
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
public BlobString name;
|
||||
#endif
|
||||
public uint hash;
|
||||
public BlobArray<ConditionBlob> conditions;
|
||||
public int targetStateId;
|
||||
public float duration;
|
||||
public float exitTime;
|
||||
public float offset;
|
||||
public bool hasExitTime;
|
||||
public bool hasFixedDuration;
|
||||
public InterruptionSource interruptionSource;
|
||||
public bool orderedInterruption;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct ConditionBlob
|
||||
{
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
public BlobString name;
|
||||
#endif
|
||||
public int paramIdx;
|
||||
public ParameterValue threshold;
|
||||
public AnimatorConditionMode conditionMode;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct MotionBlob
|
||||
{
|
||||
public enum Type
|
||||
{
|
||||
None,
|
||||
AnimationClip,
|
||||
BlendTree1D,
|
||||
BlendTree2DSimpleDirectional,
|
||||
BlendTree2DFreeformDirectional,
|
||||
BlendTree2DFreeformCartesian,
|
||||
BlendTreeDirect
|
||||
}
|
||||
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
public BlobString name;
|
||||
#endif
|
||||
public uint hash;
|
||||
public Type type;
|
||||
public int animationIndex;
|
||||
public BlendTreeBlob blendTree;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct ChildMotionBlob
|
||||
{
|
||||
public MotionBlob motion;
|
||||
public float threshold;
|
||||
public float timeScale;
|
||||
public bool mirror;
|
||||
public float2 position2D;
|
||||
public int directBlendParameterIndex;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct BlendTreeBlob
|
||||
{
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
public BlobString name;
|
||||
#endif
|
||||
public int blendParameterIndex;
|
||||
public int blendParameterYIndex;
|
||||
public bool normalizeBlendValues;
|
||||
public BlobArray<ChildMotionBlob> motions;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct StateBlob
|
||||
{
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
public BlobString name;
|
||||
public BlobString tag;
|
||||
#endif
|
||||
public uint hash;
|
||||
public uint tagHash;
|
||||
public float speed;
|
||||
public int speedMultiplierParameterIndex;
|
||||
public int timeParameterIndex;
|
||||
public float cycleOffset;
|
||||
public int cycleOffsetParameterIndex;
|
||||
public BlobArray<TransitionBlob> transitions;
|
||||
public MotionBlob motion;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct ControllerAnimationsBlob
|
||||
{
|
||||
public BlobArray<Hash128> animations;
|
||||
}
|
||||
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bb7e72e901a18aa4ea9c4806acac4167
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/AnimatorController/AnimatorControllerBlob.cs
|
||||
uploadId: 897522
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
using Rukhanka;
|
||||
using Unity.Burst;
|
||||
using Unity.Collections;
|
||||
using Unity.Entities;
|
||||
#if RUKHANKA_WITH_NETCODE
|
||||
using Unity.NetCode;
|
||||
#endif
|
||||
using UnityEngine;
|
||||
using static Rukhanka.AnimatorControllerSystemJobs;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[assembly: RegisterGenericSystemType(typeof(AnimatorControllerSystem<PredictedAnimatorControllerQuery>))]
|
||||
[assembly: RegisterGenericSystemType(typeof(AnimatorControllerSystem<AnimatorControllerQuery>))]
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
|
||||
[DisableAutoCreation]
|
||||
public partial struct AnimatorControllerSystem<T>: ISystem where T: AnimatorControllerQueryCreator, new()
|
||||
{
|
||||
EntityQuery animatorControllerQuery;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[BurstCompile]
|
||||
public void OnCreate(ref SystemState ss)
|
||||
{
|
||||
var queryCreator = new T();
|
||||
animatorControllerQuery = queryCreator.CreateQuery(ref ss);
|
||||
|
||||
ss.RequireForUpdate(animatorControllerQuery);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[BurstCompile]
|
||||
public void OnUpdate(ref SystemState ss)
|
||||
{
|
||||
var dt = SystemAPI.Time.DeltaTime;
|
||||
|
||||
var controllerLayersBufferHandle = SystemAPI.GetBufferTypeHandle<AnimatorControllerLayerComponent>();
|
||||
var controllerParametersBufferHandle = SystemAPI.GetBufferTypeHandle<AnimatorControllerParameterComponent>();
|
||||
var animatorOverrideAnimationsLookup = SystemAPI.GetComponentLookup<AnimatorOverrideAnimations>(true);
|
||||
var entityTypeHandle = SystemAPI.GetEntityTypeHandle();
|
||||
var controllerEventsBufferLookup = SystemAPI.GetBufferLookup<AnimatorControllerEventComponent>();
|
||||
var animDBSingleton = SystemAPI.GetSingleton<BlobDatabaseSingleton>();
|
||||
|
||||
var internalDataSingletonQuery = SystemAPI.QueryBuilder().WithAllRW<InternalAnimatorDataSingleton>().Build();
|
||||
var internalAnimatorData = FillAnimationsFromControllerSystem.GetInternalDataSingleton(internalDataSingletonQuery, ref ss);
|
||||
|
||||
var stateMachineProcessJob = new StateMachineProcessJob()
|
||||
{
|
||||
controllerLayersBufferHandle = controllerLayersBufferHandle,
|
||||
controllerParametersBufferHandle = controllerParametersBufferHandle,
|
||||
dt = dt,
|
||||
entityTypeHandle = entityTypeHandle,
|
||||
controllerEventsBufferLookup = controllerEventsBufferLookup,
|
||||
animationDatabase = animDBSingleton.animations,
|
||||
animatorOverrideAnimationLookup = animatorOverrideAnimationsLookup,
|
||||
animatorOverrideAnimationsMap = internalAnimatorData.animatorOverrideAnimationsMap.AsParallelWriter()
|
||||
|
||||
};
|
||||
|
||||
ss.Dependency = stateMachineProcessJob.ScheduleParallel(animatorControllerQuery, ss.Dependency);
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public interface AnimatorControllerQueryCreator
|
||||
{
|
||||
EntityQuery CreateQuery(ref SystemState ss);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct PredictedAnimatorControllerQuery: AnimatorControllerQueryCreator
|
||||
{
|
||||
public EntityQuery CreateQuery(ref SystemState ss)
|
||||
{
|
||||
var eqBuilder0 = new EntityQueryBuilder(Allocator.Temp)
|
||||
.WithAllRW<AnimatorControllerLayerComponent>()
|
||||
#if RUKHANKA_WITH_NETCODE
|
||||
.WithAll<Simulate, PredictedGhost>()
|
||||
#endif
|
||||
;
|
||||
var animatorControllerQuery = ss.GetEntityQuery(eqBuilder0);
|
||||
return animatorControllerQuery;
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct AnimatorControllerQuery: AnimatorControllerQueryCreator
|
||||
{
|
||||
public EntityQuery CreateQuery(ref SystemState ss)
|
||||
{
|
||||
var eqBuilder0 = new EntityQueryBuilder(Allocator.Temp)
|
||||
.WithAllRW<AnimatorControllerLayerComponent>()
|
||||
#if RUKHANKA_WITH_NETCODE
|
||||
.WithNone<GhostInstance>()
|
||||
#endif
|
||||
;
|
||||
var animatorControllerQuery = ss.GetEntityQuery(eqBuilder0);
|
||||
return animatorControllerQuery;
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ecdf4d0de0c58ed4489dca4b8c9ab490
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/AnimatorController/AnimatorControllerSystem.cs
|
||||
uploadId: 897522
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
using Unity.Collections;
|
||||
using Unity.Entities;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
|
||||
public struct AnimatorControllerLayerComponent: IBufferElementData, IEnableableComponent
|
||||
{
|
||||
public BlobAssetReference<ControllerBlob> controller;
|
||||
public BlobAssetReference<ControllerAnimationsBlob> animations;
|
||||
public int layerIndex;
|
||||
public float weight;
|
||||
public float speed;
|
||||
public RuntimeAnimatorData rtd;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct AnimatorControllerParameterComponent: IBufferElementData
|
||||
{
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
public FixedString64Bytes name;
|
||||
#endif
|
||||
public uint hash;
|
||||
public ControllerParameterType type;
|
||||
public ParameterValue value;
|
||||
|
||||
public float FloatValue
|
||||
{
|
||||
get => value.floatValue;
|
||||
set => this.value.floatValue = value;
|
||||
}
|
||||
|
||||
public int IntValue
|
||||
{
|
||||
get => value.intValue;
|
||||
set => this.value.intValue= value;
|
||||
}
|
||||
|
||||
public bool BoolValue
|
||||
{
|
||||
get => value.boolValue;
|
||||
set => this.value.boolValue = value;
|
||||
}
|
||||
|
||||
public void SetTrigger()
|
||||
{
|
||||
value.boolValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct AnimatorControllerParameterIndexTableComponent: IComponentData
|
||||
{
|
||||
public BlobAssetReference<PerfectHashTableBlob> value;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct AnimatorControllerEventComponent : IBufferElementData, IEnableableComponent
|
||||
{
|
||||
public enum EventType
|
||||
{
|
||||
StateEnter,
|
||||
StateExit,
|
||||
StateUpdate
|
||||
}
|
||||
|
||||
public EventType eventType;
|
||||
public int layerId;
|
||||
public int stateId;
|
||||
public float timeInState;
|
||||
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
public FixedString32Bytes stateName;
|
||||
#endif
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct AnimatorOverrideAnimations: IComponentData, IEnableableComponent
|
||||
{
|
||||
public BlobAssetReference<ControllerAnimationsBlob> value;
|
||||
}
|
||||
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1026c97737e562f4c816544af6ac420f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/AnimatorController/AnimatorControllerSystemComponents.cs
|
||||
uploadId: 897522
|
||||
+740
@@ -0,0 +1,740 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using Rukhanka.Toolbox;
|
||||
using Unity.Burst;
|
||||
using Unity.Burst.CompilerServices;
|
||||
using Unity.Burst.Intrinsics;
|
||||
using Unity.Collections;
|
||||
using Unity.Entities;
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
using Hash128 = Unity.Entities.Hash128;
|
||||
|
||||
[assembly: InternalsVisibleTo("Rukhanka.Tests")]
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
public struct AnimatorControllerSystemJobs
|
||||
{
|
||||
|
||||
[BurstCompile]
|
||||
public struct StateMachineProcessJob: IJobChunk
|
||||
{
|
||||
public float dt;
|
||||
public BufferTypeHandle<AnimatorControllerLayerComponent> controllerLayersBufferHandle;
|
||||
public BufferTypeHandle<AnimatorControllerParameterComponent> controllerParametersBufferHandle;
|
||||
public EntityTypeHandle entityTypeHandle;
|
||||
[NativeDisableParallelForRestriction]
|
||||
public BufferLookup<AnimatorControllerEventComponent> controllerEventsBufferLookup;
|
||||
[ReadOnly]
|
||||
public NativeHashMap<Hash128, BlobAssetReference<AnimationClipBlob>> animationDatabase;
|
||||
[ReadOnly]
|
||||
public ComponentLookup<AnimatorOverrideAnimations> animatorOverrideAnimationLookup;
|
||||
|
||||
public NativeParallelHashMap<int, BlobAssetReference<ControllerAnimationsBlob>>.ParallelWriter animatorOverrideAnimationsMap;
|
||||
|
||||
BlobAssetReference<ControllerAnimationsBlob> controllerAnimationsBlob;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask)
|
||||
{
|
||||
var layerBuffers = chunk.GetBufferAccessor(ref controllerLayersBufferHandle);
|
||||
var parameterBuffers = chunk.GetBufferAccessor(ref controllerParametersBufferHandle);
|
||||
var entities = chunk.GetNativeArray(entityTypeHandle);
|
||||
|
||||
var cee = new ChunkEntityEnumerator(useEnabledMask, chunkEnabledMask, chunk.Count);
|
||||
|
||||
while (cee.NextEntityIndex(out var i))
|
||||
{
|
||||
var layers = layerBuffers[i];
|
||||
var parameters = parameterBuffers.Length > 0 ? parameterBuffers[i].AsNativeArray() : default;
|
||||
var e = entities[i];
|
||||
|
||||
DynamicBuffer<AnimatorControllerEventComponent> controllerEventsBuffer = default;
|
||||
if (controllerEventsBufferLookup.HasBuffer(e) && controllerEventsBufferLookup.IsBufferEnabled(e))
|
||||
controllerEventsBuffer = controllerEventsBufferLookup[e];
|
||||
|
||||
ExecuteSingle(layers, parameters, ref controllerEventsBuffer, e);
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
unsafe void ExecuteSingle
|
||||
(
|
||||
in DynamicBuffer<AnimatorControllerLayerComponent> aclc,
|
||||
in NativeArray<AnimatorControllerParameterComponent> acpc,
|
||||
ref DynamicBuffer<AnimatorControllerEventComponent> events,
|
||||
Entity entity
|
||||
)
|
||||
{
|
||||
if (events.IsCreated)
|
||||
events.Clear();
|
||||
|
||||
var numIntsForBitMemory = BitFieldN.CalculateUIntsCountForGivenBitCount(acpc.Length);
|
||||
var triggersToResetMem = stackalloc uint[numIntsForBitMemory];
|
||||
var triggersToReset = new BitFieldN(triggersToResetMem, numIntsForBitMemory);
|
||||
|
||||
var startIndex = 0;
|
||||
for (int i = 0; i < aclc.Length; ++i)
|
||||
{
|
||||
ref var acc = ref aclc.ElementAt(i);
|
||||
|
||||
// Save controller animations blob asset reference in class variable, because passing it inside almost all functions will bloat signatures significantly
|
||||
controllerAnimationsBlob = FillAnimationsFromControllerSystem.GetControllerAnimationsBlob
|
||||
(entity, animatorOverrideAnimationLookup, acc.animations, animatorOverrideAnimationsMap);
|
||||
|
||||
ProcessLayer(ref acc.controller.Value, acc.layerIndex, acpc, aclc, ref events, triggersToReset);
|
||||
if (events.IsCreated)
|
||||
{
|
||||
EmitStateUpdateEvents(ref events, acc, startIndex);
|
||||
startIndex = events.Length;
|
||||
}
|
||||
}
|
||||
|
||||
// Reset affected triggers
|
||||
ResetTriggers(acpc, triggersToReset);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void ResetTriggers(NativeArray<AnimatorControllerParameterComponent> acpc, BitFieldN triggersToReset)
|
||||
{
|
||||
if (!triggersToReset.TestAny())
|
||||
return;
|
||||
|
||||
for (var i = 0; i < acpc.Length; ++i)
|
||||
{
|
||||
var p = acpc[i];
|
||||
if (p.type == ControllerParameterType.Trigger && triggersToReset.IsSet(i))
|
||||
{
|
||||
p.BoolValue = false;
|
||||
acpc[i] = p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
RuntimeAnimatorData.StateData InitControllerStateData(ref RuntimeAnimatorData rtd, int stateID)
|
||||
{
|
||||
var rv = rtd.MakeDefaultState();
|
||||
rv.id = stateID;
|
||||
rv.normalizedDuration = 0;
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void TryExitTransition(ref AnimatorControllerLayerComponent acc, ref DynamicBuffer<AnimatorControllerEventComponent> events)
|
||||
{
|
||||
if (acc.rtd.activeTransition.id < 0)
|
||||
return;
|
||||
|
||||
if (CheckTransitionExitConditions(acc.rtd.activeTransition))
|
||||
{
|
||||
// Add state exit event
|
||||
EmitEvent(ref events, AnimatorControllerEventComponent.EventType.StateExit, acc, acc.rtd.srcState.id, acc.rtd.srcState.normalizedDuration);
|
||||
|
||||
acc.rtd.srcState = acc.rtd.dstState;
|
||||
acc.rtd.ClearStateSnapshots();
|
||||
acc.rtd.dstState = acc.rtd.MakeDefaultState();
|
||||
acc.rtd.activeTransition = acc.rtd.MakeDefaultTransition();
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int GetValidTransitionForCurrentFrame
|
||||
(
|
||||
ref BlobArray<TransitionBlob> transitions,
|
||||
float normalizedStateDuration,
|
||||
float srcStateDurationFrameDelta,
|
||||
NativeArray<AnimatorControllerParameterComponent> runtimeParams,
|
||||
BitFieldN triggersToReset
|
||||
)
|
||||
{
|
||||
var isTransitionFits = false;
|
||||
int i = 0;
|
||||
for (; i < transitions.Length && !isTransitionFits; ++i)
|
||||
{
|
||||
ref var t = ref transitions[i];
|
||||
isTransitionFits =
|
||||
CheckTransitionEnterExitTimeCondition(ref t, normalizedStateDuration, srcStateDurationFrameDelta) &&
|
||||
CheckTransitionEnterConditions(ref t, runtimeParams, triggersToReset);
|
||||
}
|
||||
return isTransitionFits ? i - 1 : -1;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void TryEnterTransition
|
||||
(
|
||||
in DynamicBuffer<AnimatorControllerLayerComponent> aclc,
|
||||
ref ControllerBlob controllerBlob,
|
||||
int layerIndex,
|
||||
NativeArray<AnimatorControllerParameterComponent> runtimeParams,
|
||||
float srcStateDurationFrameDelta,
|
||||
float curStateDuration,
|
||||
ref DynamicBuffer<AnimatorControllerEventComponent> events,
|
||||
BitFieldN triggersToReset
|
||||
)
|
||||
{
|
||||
ref var acc = ref aclc.ElementAt(layerIndex);
|
||||
ref var layer = ref controllerBlob.layers[layerIndex];
|
||||
ref var currentState = ref layer.states[acc.rtd.srcState.id];
|
||||
|
||||
if (acc.rtd.activeTransition.id >= 0)
|
||||
{
|
||||
TryTransitionInterruption(ref layer, ref acc.rtd, srcStateDurationFrameDelta, runtimeParams, triggersToReset);
|
||||
return;
|
||||
}
|
||||
|
||||
var newTransitionIndex = GetValidTransitionForCurrentFrame(ref currentState.transitions, acc.rtd.srcState.normalizedDuration, srcStateDurationFrameDelta, runtimeParams, triggersToReset);
|
||||
if (newTransitionIndex < 0)
|
||||
return;
|
||||
|
||||
ref var t = ref currentState.transitions[newTransitionIndex];
|
||||
var timeShouldBeInTransition = GetTimeInSecondsShouldBeInTransition(ref t, acc.rtd.srcState.normalizedDuration, curStateDuration, srcStateDurationFrameDelta);
|
||||
acc.rtd.activeTransition.id = newTransitionIndex;
|
||||
acc.rtd.activeTransition.length = GetTransitionLength(ref t);
|
||||
acc.rtd.activeTransition.normalizedDuration = timeShouldBeInTransition / CalculateTransitionDuration(acc.rtd.activeTransition, curStateDuration);
|
||||
var dstStateDur = CalculateStateDuration(layerIndex, t.targetStateId, ref controllerBlob, aclc.AsNativeArray(), runtimeParams);
|
||||
acc.rtd.dstState = InitControllerStateData(ref acc.rtd, t.targetStateId);
|
||||
acc.rtd.dstState.normalizedDuration += timeShouldBeInTransition / dstStateDur + t.offset;
|
||||
|
||||
// Add state enter event
|
||||
EmitEvent(ref events, AnimatorControllerEventComponent.EventType.StateEnter, acc, acc.rtd.dstState.id, acc.rtd.dstState.normalizedDuration);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void EmitEvent
|
||||
(
|
||||
ref DynamicBuffer<AnimatorControllerEventComponent> events,
|
||||
AnimatorControllerEventComponent.EventType eventType,
|
||||
AnimatorControllerLayerComponent aclc,
|
||||
int stateId,
|
||||
float stateDuration
|
||||
)
|
||||
{
|
||||
if (!events.IsCreated)
|
||||
return;
|
||||
|
||||
var evt = new AnimatorControllerEventComponent()
|
||||
{
|
||||
eventType = eventType,
|
||||
stateId = stateId,
|
||||
layerId = aclc.layerIndex,
|
||||
timeInState = stateDuration,
|
||||
};
|
||||
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
aclc.controller.Value.layers[aclc.layerIndex].states[stateId].name.CopyToWithTruncate(ref evt.stateName);
|
||||
#endif
|
||||
|
||||
events.Add(evt);
|
||||
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void EmitStateUpdateEvents(ref DynamicBuffer<AnimatorControllerEventComponent> events, in AnimatorControllerLayerComponent acc, int startIndex)
|
||||
{
|
||||
if (!events.IsCreated)
|
||||
return;
|
||||
|
||||
var srcStateEnterExit = false;
|
||||
var dstStateEnterExit = false;
|
||||
for (var i = startIndex; i < events.Length; ++i)
|
||||
{
|
||||
var e = events[i];
|
||||
if (acc.rtd.srcState.id >= 0 && e.stateId == acc.rtd.srcState.id)
|
||||
srcStateEnterExit = true;
|
||||
if (acc.rtd.dstState.id >= 0 && e.stateId == acc.rtd.dstState.id)
|
||||
dstStateEnterExit = true;
|
||||
}
|
||||
|
||||
if (acc.rtd.srcState.id >= 0 && !srcStateEnterExit)
|
||||
EmitEvent(ref events, AnimatorControllerEventComponent.EventType.StateUpdate, acc, acc.rtd.srcState.id, acc.rtd.srcState.normalizedDuration);
|
||||
if (acc.rtd.dstState.id >= 0 && !dstStateEnterExit)
|
||||
EmitEvent(ref events, AnimatorControllerEventComponent.EventType.StateUpdate, acc, acc.rtd.dstState.id, acc.rtd.dstState.normalizedDuration);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void ProcessLayer
|
||||
(
|
||||
ref ControllerBlob c,
|
||||
int layerIndex,
|
||||
in NativeArray<AnimatorControllerParameterComponent> runtimeParams,
|
||||
DynamicBuffer<AnimatorControllerLayerComponent> aclc,
|
||||
ref DynamicBuffer<AnimatorControllerEventComponent> events,
|
||||
BitFieldN triggersToReset
|
||||
)
|
||||
{
|
||||
ref var layer = ref c.layers[layerIndex];
|
||||
ref var acc = ref aclc.ElementAt(layerIndex);
|
||||
|
||||
var currentStateID = acc.rtd.srcState.id;
|
||||
if (currentStateID < 0)
|
||||
currentStateID = layer.defaultStateIndex;
|
||||
|
||||
// Adjust delta time according to the layer animator speed
|
||||
var layerDeltaTime = dt * acc.speed;
|
||||
|
||||
var curStateDuration = CalculateStateDuration(layerIndex, currentStateID, ref c, aclc.AsNativeArray(), runtimeParams);
|
||||
|
||||
if (Hint.Unlikely(acc.rtd.srcState.id < 0))
|
||||
{
|
||||
acc.rtd.srcState = InitControllerStateData(ref acc.rtd, layer.defaultStateIndex);
|
||||
EmitEvent(ref events, AnimatorControllerEventComponent.EventType.StateEnter, acc, acc.rtd.srcState.id, acc.rtd.srcState.normalizedDuration);
|
||||
}
|
||||
|
||||
var srcStateDurationFrameDelta = CalculateStateFrameDeltaSafe(layerDeltaTime, curStateDuration);
|
||||
acc.rtd.srcState.normalizedDuration += srcStateDurationFrameDelta;
|
||||
|
||||
if (acc.rtd.dstState.id >= 0)
|
||||
{
|
||||
var dstStateDuration = CalculateStateDuration(layerIndex, acc.rtd.dstState.id, ref c, aclc.AsNativeArray(), runtimeParams);
|
||||
acc.rtd.dstState.normalizedDuration += CalculateStateFrameDeltaSafe(layerDeltaTime, dstStateDuration);
|
||||
}
|
||||
|
||||
if (acc.rtd.activeTransition.id >= 0)
|
||||
{
|
||||
var transitionDuration = CalculateTransitionDuration(acc.rtd.activeTransition, curStateDuration);
|
||||
acc.rtd.activeTransition.normalizedDuration += layerDeltaTime / transitionDuration;
|
||||
}
|
||||
|
||||
TryExitTransition(ref acc, ref events);
|
||||
TryEnterTransition(aclc, ref c, layerIndex, runtimeParams, srcStateDurationFrameDelta, curStateDuration, ref events, triggersToReset);
|
||||
// Check transition exit conditions one more time in case of Enter->Exit sequence appeared in single frame
|
||||
TryExitTransition(ref acc, ref events);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
float CalculateStateFrameDeltaSafe(float dt, float stateDuration)
|
||||
{
|
||||
var rv = dt / stateDuration;
|
||||
rv = math.select(0, rv, math.isfinite(rv));
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
float CalculateMotionDuration
|
||||
(
|
||||
ref MotionBlob mb,
|
||||
in NativeArray<AnimatorControllerParameterComponent> runtimeParams,
|
||||
float weight
|
||||
)
|
||||
{
|
||||
if (weight == 0) return 0;
|
||||
|
||||
switch (mb.type)
|
||||
{
|
||||
case MotionBlob.Type.None:
|
||||
return 1;
|
||||
case MotionBlob.Type.AnimationClip:
|
||||
var animationHash = controllerAnimationsBlob.Value.animations[mb.animationIndex];
|
||||
var animBlob = BlobDatabaseSingleton.GetBlobAsset(animationHash, animationDatabase);
|
||||
if (animBlob != BlobAssetReference<AnimationClipBlob>.Null)
|
||||
return animBlob.Value.length * weight;
|
||||
return 1;
|
||||
}
|
||||
|
||||
var childMotions = ScriptedAnimator.GetChildMotionsList(ref mb, runtimeParams);
|
||||
var rv = CalculateBlendTreeMotionDuration(childMotions, ref mb.blendTree.motions, runtimeParams, weight);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
float CalculateBlendTreeMotionDuration
|
||||
(
|
||||
NativeList<ScriptedAnimator.MotionIndexAndWeight> miwArr,
|
||||
ref BlobArray<ChildMotionBlob> motions,
|
||||
in NativeArray<AnimatorControllerParameterComponent> runtimeParams,
|
||||
float weight
|
||||
)
|
||||
{
|
||||
if (!miwArr.IsCreated || miwArr.IsEmpty)
|
||||
return 1;
|
||||
|
||||
var weightSum = 0.0f;
|
||||
for (int i = 0; i < miwArr.Length; ++i)
|
||||
weightSum += miwArr[i].weight;
|
||||
|
||||
// If total weight less then 1, normalize weights
|
||||
if (Hint.Unlikely(weightSum < 1))
|
||||
{
|
||||
for (int i = 0; i < miwArr.Length; ++i)
|
||||
{
|
||||
var miw = miwArr[i];
|
||||
miw.weight = miw.weight / weightSum;
|
||||
miwArr[i] = miw;
|
||||
}
|
||||
}
|
||||
|
||||
var rv = 0.0f;
|
||||
for (int i = 0; i < miwArr.Length; ++i)
|
||||
{
|
||||
var miw = miwArr[i];
|
||||
ref var m = ref motions[miw.motionIndex];
|
||||
rv += CalculateMotionDuration(ref m.motion, runtimeParams, weight * miw.weight) / m.timeScale;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Negative value means that length is a normalized value from source state length
|
||||
float GetTransitionLength(ref TransitionBlob tb)
|
||||
{
|
||||
return math.select(-tb.duration, tb.duration, tb.hasFixedDuration);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
float CalculateTransitionDuration(in RuntimeAnimatorData.TransitionData trd, float curStateDuration)
|
||||
{
|
||||
var rv = math.abs(trd.length);
|
||||
if (trd.length < 0)
|
||||
{
|
||||
rv *= curStateDuration;
|
||||
}
|
||||
return math.max(rv, 0.0001f);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
float CalculateStateDuration
|
||||
(
|
||||
int layerIndex,
|
||||
int stateId,
|
||||
ref ControllerBlob controllerBlob,
|
||||
in NativeArray<AnimatorControllerLayerComponent> aclc,
|
||||
in NativeArray<AnimatorControllerParameterComponent> runtimeParams
|
||||
)
|
||||
{
|
||||
ref var layer = ref controllerBlob.layers[layerIndex];
|
||||
ref var sb = ref layer.states[stateId];
|
||||
var motionDuration = CalculateMotionDuration(ref sb.motion, runtimeParams, 1);
|
||||
|
||||
// In case of layer sync option is enabled, adjust state duration with respect to "timing" property
|
||||
if (Hint.Unlikely(layer.syncedLayerIndex >= 0))
|
||||
{
|
||||
// Override controller must be exact copy of current
|
||||
ref var baseState = ref controllerBlob.layers[layer.syncedLayerIndex].states[stateId];
|
||||
var baseMotionDuration = CalculateMotionDuration(ref baseState.motion, runtimeParams, 1);
|
||||
var weightedMotionDuration = math.lerp(baseMotionDuration, motionDuration, aclc[layerIndex].weight);
|
||||
motionDuration = math.select(baseMotionDuration, weightedMotionDuration, layer.syncedTiming > 0);
|
||||
}
|
||||
else if (Hint.Unlikely(layer.syncedTiming >= 0))
|
||||
{
|
||||
ref var syncedState = ref controllerBlob.layers[layer.syncedTiming].states[stateId];
|
||||
var syncedStateDuration = CalculateMotionDuration(ref syncedState.motion, runtimeParams, 1);
|
||||
motionDuration = math.lerp(motionDuration, syncedStateDuration, aclc[layer.syncedTiming].weight);
|
||||
}
|
||||
|
||||
var speedMultiplier = 1.0f;
|
||||
if (sb.speedMultiplierParameterIndex >= 0)
|
||||
{
|
||||
speedMultiplier = runtimeParams[sb.speedMultiplierParameterIndex].FloatValue;
|
||||
}
|
||||
var rv = motionDuration / (sb.speed * speedMultiplier);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
internal static float GetLoopAwareTransitionExitTime(float exitTime, float normalizedDuration, float speedSign)
|
||||
{
|
||||
var rv = exitTime;
|
||||
if (exitTime <= 1.0f)
|
||||
{
|
||||
// Unity animator logic and documentation mismatch. Documentation says that exit time loop condition should be when transition exitTime less then 1, but in practice it will loop when exitTime is less or equal(!) to 1.
|
||||
exitTime = math.min(exitTime, 0.9999f);
|
||||
var snd = normalizedDuration * speedSign;
|
||||
|
||||
var f = math.frac(snd);
|
||||
rv += (int)snd;
|
||||
if (f > exitTime)
|
||||
rv += 1;
|
||||
}
|
||||
return rv * speedSign;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
float GetTimeInSecondsShouldBeInTransition(ref TransitionBlob tb, float normalizedStateDuration, float curStateDuration, float frameDT)
|
||||
{
|
||||
if (!tb.hasExitTime) return 0;
|
||||
|
||||
// This should be always less then curStateRTD.normalizedDuration
|
||||
var loopAwareExitTime = GetLoopAwareTransitionExitTime(tb.exitTime, normalizedStateDuration - frameDT, math.sign(frameDT));
|
||||
var loopDelta = normalizedStateDuration - loopAwareExitTime;
|
||||
var rv = loopDelta * curStateDuration;
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool CheckTransitionEnterExitTimeCondition(ref TransitionBlob tb, float normalizedStateDuration, float srcStateDurationFrameDelta)
|
||||
{
|
||||
var noNormalConditions = tb.conditions.Length == 0;
|
||||
if (!tb.hasExitTime) return !noNormalConditions;
|
||||
|
||||
var l0 = normalizedStateDuration - srcStateDurationFrameDelta;
|
||||
var l1 = normalizedStateDuration;
|
||||
var speedSign = math.select(-1, 1, l0 < l1);
|
||||
|
||||
var loopAwareExitTime = GetLoopAwareTransitionExitTime(tb.exitTime, l0, speedSign);
|
||||
|
||||
if (speedSign < 0)
|
||||
(l0, l1) = (l1, l0);
|
||||
|
||||
var rv = loopAwareExitTime > l0 && loopAwareExitTime <= l1;
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool CheckIntCondition(in AnimatorControllerParameterComponent param, ref ConditionBlob c)
|
||||
{
|
||||
var rv = true;
|
||||
switch (c.conditionMode)
|
||||
{
|
||||
case AnimatorConditionMode.Equals:
|
||||
if (param.IntValue != c.threshold.intValue) rv = false;
|
||||
break;
|
||||
case AnimatorConditionMode.Greater:
|
||||
if (param.IntValue <= c.threshold.intValue) rv = false;
|
||||
break;
|
||||
case AnimatorConditionMode.Less:
|
||||
if (param.IntValue >= c.threshold.intValue) rv = false;
|
||||
break;
|
||||
case AnimatorConditionMode.NotEqual:
|
||||
if (param.IntValue == c.threshold.intValue) rv = false;
|
||||
break;
|
||||
default:
|
||||
Debug.LogError($"Unsupported condition type for int parameter value!");
|
||||
break;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool CheckFloatCondition(in AnimatorControllerParameterComponent param, ref ConditionBlob c)
|
||||
{
|
||||
var rv = true;
|
||||
switch (c.conditionMode)
|
||||
{
|
||||
case AnimatorConditionMode.Greater:
|
||||
if (param.FloatValue <= c.threshold.floatValue) rv = false;
|
||||
break;
|
||||
case AnimatorConditionMode.Less:
|
||||
if (param.FloatValue >= c.threshold.floatValue) rv = false;
|
||||
break;
|
||||
default:
|
||||
Debug.LogError($"Unsupported condition type for int parameter value!");
|
||||
break;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool CheckBoolCondition(in AnimatorControllerParameterComponent param, ref ConditionBlob c)
|
||||
{
|
||||
var rv = true;
|
||||
switch (c.conditionMode)
|
||||
{
|
||||
case AnimatorConditionMode.If:
|
||||
rv = param.BoolValue;
|
||||
break;
|
||||
case AnimatorConditionMode.IfNot:
|
||||
rv = !param.BoolValue;
|
||||
break;
|
||||
default:
|
||||
Debug.LogError($"Unsupported condition type for int parameter value!");
|
||||
break;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void MarkTriggersToReset(ref TransitionBlob tb, BitFieldN triggersToReset)
|
||||
{
|
||||
for (int i = 0; i < tb.conditions.Length; ++i)
|
||||
{
|
||||
ref var c = ref tb.conditions[i];
|
||||
// Mark all transition parameters as "need to be reset". We will check actual parameter type later, after all layers processing
|
||||
triggersToReset.Set(c.paramIdx, true);
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool CheckTransitionEnterConditions(ref TransitionBlob tb, NativeArray<AnimatorControllerParameterComponent> runtimeParams, BitFieldN triggersToReset)
|
||||
{
|
||||
if (tb.conditions.Length == 0)
|
||||
return true;
|
||||
|
||||
var rv = true;
|
||||
var hasTriggers = false;
|
||||
for (int i = 0; i < tb.conditions.Length && rv; ++i)
|
||||
{
|
||||
ref var c = ref tb.conditions[i];
|
||||
var param = runtimeParams[c.paramIdx];
|
||||
|
||||
switch (param.type)
|
||||
{
|
||||
case ControllerParameterType.Float:
|
||||
rv = CheckFloatCondition(param, ref c);
|
||||
break;
|
||||
case ControllerParameterType.Int:
|
||||
rv = CheckIntCondition(param, ref c);
|
||||
break;
|
||||
case ControllerParameterType.Bool:
|
||||
rv = CheckBoolCondition(param, ref c);
|
||||
break;
|
||||
case ControllerParameterType.Trigger:
|
||||
rv = CheckBoolCondition(param, ref c);
|
||||
hasTriggers = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasTriggers && rv)
|
||||
MarkTriggersToReset(ref tb, triggersToReset);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool CheckTransitionExitConditions(RuntimeAnimatorData.TransitionData transitionRuntimeData)
|
||||
{
|
||||
return transitionRuntimeData.normalizedDuration >= 1;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
unsafe void TryTransitionInterruption
|
||||
(
|
||||
ref LayerBlob layerBlob,
|
||||
ref RuntimeAnimatorData rtd,
|
||||
float srcStateDurationFrameDelta,
|
||||
NativeArray<AnimatorControllerParameterComponent> runtimeParams,
|
||||
BitFieldN triggersToReset
|
||||
)
|
||||
{
|
||||
ref var srcStateTransitions = ref layerBlob.states[rtd.srcState.id].transitions;
|
||||
var transitionIndex = rtd.activeTransition.id;
|
||||
// Transition index could be greater than number of actual transitions when 'ScriptedAnimator.CrossFade' is used
|
||||
if (transitionIndex >= srcStateTransitions.Length)
|
||||
return;
|
||||
|
||||
// We are in transition right now. Perform transition interruption logic
|
||||
ref var activeTransition = ref srcStateTransitions[transitionIndex];
|
||||
// Can't interrupt, return
|
||||
if (Hint.Likely(activeTransition.interruptionSource == TransitionBlob.InterruptionSource.None))
|
||||
return;
|
||||
|
||||
// Make copy of triggers to reset flags because I don't know if there are valid interruption transitions yet
|
||||
var triggersToResetMem = stackalloc uint[triggersToReset.SizeInInts()];
|
||||
var triggersClone = triggersToReset.Clone(triggersToResetMem);
|
||||
|
||||
var transitionIndexCandidateSrc = -1;
|
||||
var transitionIndexCandidateDst = -1;
|
||||
var canBeInterrupted = false;
|
||||
|
||||
if
|
||||
(
|
||||
activeTransition.interruptionSource == TransitionBlob.InterruptionSource.Source ||
|
||||
activeTransition.interruptionSource == TransitionBlob.InterruptionSource.SourceThenDestination ||
|
||||
activeTransition.interruptionSource == TransitionBlob.InterruptionSource.DestinationThenSource
|
||||
)
|
||||
{
|
||||
transitionIndexCandidateSrc = GetValidTransitionForCurrentFrame(ref srcStateTransitions, rtd.srcState.normalizedDuration, srcStateDurationFrameDelta, runtimeParams, triggersClone);
|
||||
canBeInterrupted |=
|
||||
transitionIndexCandidateSrc >= 0 &&
|
||||
(activeTransition.orderedInterruption ? transitionIndexCandidateSrc < transitionIndex : transitionIndexCandidateSrc != transitionIndex);
|
||||
}
|
||||
|
||||
ref var dstStateTransitions = ref layerBlob.states[rtd.dstState.id].transitions;
|
||||
if
|
||||
(
|
||||
activeTransition.interruptionSource == TransitionBlob.InterruptionSource.Destination ||
|
||||
activeTransition.interruptionSource == TransitionBlob.InterruptionSource.SourceThenDestination ||
|
||||
activeTransition.interruptionSource == TransitionBlob.InterruptionSource.DestinationThenSource
|
||||
)
|
||||
{
|
||||
transitionIndexCandidateDst = GetValidTransitionForCurrentFrame(ref dstStateTransitions, rtd.dstState.normalizedDuration, srcStateDurationFrameDelta, runtimeParams, triggersClone);
|
||||
canBeInterrupted |= transitionIndexCandidateDst >= 0;
|
||||
}
|
||||
|
||||
if (!canBeInterrupted)
|
||||
return;
|
||||
|
||||
// There is valid interruption
|
||||
// Select interrupting transition
|
||||
var interruptingTransitionID = -1;
|
||||
TransitionBlob* transitionBlobsPtr = null;
|
||||
switch (activeTransition.interruptionSource)
|
||||
{
|
||||
case TransitionBlob.InterruptionSource.Source:
|
||||
interruptingTransitionID = transitionIndexCandidateSrc;
|
||||
transitionBlobsPtr = (TransitionBlob*)srcStateTransitions.GetUnsafePtr();
|
||||
break;
|
||||
case TransitionBlob.InterruptionSource.Destination:
|
||||
interruptingTransitionID = transitionIndexCandidateDst;
|
||||
transitionBlobsPtr = (TransitionBlob*)dstStateTransitions.GetUnsafePtr();
|
||||
break;
|
||||
case TransitionBlob.InterruptionSource.SourceThenDestination:
|
||||
interruptingTransitionID = math.select(transitionIndexCandidateDst, transitionIndexCandidateSrc, transitionIndexCandidateSrc >= 0);
|
||||
transitionBlobsPtr = transitionIndexCandidateSrc >= 0 ? (TransitionBlob*)srcStateTransitions.GetUnsafePtr() : (TransitionBlob*)dstStateTransitions.GetUnsafePtr();
|
||||
break;
|
||||
case TransitionBlob.InterruptionSource.DestinationThenSource:
|
||||
interruptingTransitionID = math.select(transitionIndexCandidateSrc, transitionIndexCandidateDst, transitionIndexCandidateDst >= 0);
|
||||
transitionBlobsPtr = transitionIndexCandidateDst >= 0 ? (TransitionBlob*)dstStateTransitions.GetUnsafePtr() : (TransitionBlob*)srcStateTransitions.GetUnsafePtr();
|
||||
break;
|
||||
default:
|
||||
// This should not happen
|
||||
BurstAssert.IsTrue(false, "Transition interruption invalid code path");
|
||||
return;
|
||||
}
|
||||
ref var newTransition = ref transitionBlobsPtr[interruptingTransitionID];
|
||||
|
||||
// Copy triggers back to original bitset
|
||||
triggersToReset.CopyFrom(triggersClone);
|
||||
|
||||
// If state snapshot array is empty, then push both destination state and source state
|
||||
// If there are some state snapshots in array, the these snapshots are our "source state" already, so we need to add only destination state
|
||||
if (rtd.srcStateSnapshots.IsEmpty)
|
||||
rtd.PushStateSnapshot(rtd.srcState.id, 1, rtd.srcState.normalizedDuration, rtd.srcState.motionId);
|
||||
rtd.PushStateSnapshot(rtd.dstState.id, rtd.activeTransition.normalizedDuration, rtd.dstState.normalizedDuration, rtd.dstState.motionId);
|
||||
|
||||
rtd.srcState.normalizedDuration = 0;
|
||||
// If interrupting transition belongs to next state, configure srcState accordingly
|
||||
if (transitionBlobsPtr == dstStateTransitions.GetUnsafePtr())
|
||||
rtd.srcState.id = rtd.dstState.id;
|
||||
|
||||
rtd.activeTransition.id = interruptingTransitionID;
|
||||
rtd.activeTransition.length = GetTransitionLength(ref newTransition);
|
||||
rtd.activeTransition.normalizedDuration = 0;
|
||||
|
||||
rtd.dstState = InitControllerStateData(ref rtd, newTransition.targetStateId);
|
||||
rtd.dstState.normalizedDuration = newTransition.offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 66f7113131a9adf41bd19f34c3f9434c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/AnimatorController/AnimatorControllerSystem_Job.cs
|
||||
uploadId: 897522
|
||||
+182
@@ -0,0 +1,182 @@
|
||||
using Unity.Entities;
|
||||
using FixedStringName = Unity.Collections.FixedString512Bytes;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
public struct AnimatorParametersAspect
|
||||
{
|
||||
public AnimatorControllerParameterIndexTableComponent indexTable;
|
||||
public DynamicBuffer<AnimatorControllerParameterComponent> parametersArr;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public AnimatorParametersAspect(DynamicBuffer<AnimatorControllerParameterComponent> parametersArr, AnimatorControllerParameterIndexTableComponent indexTable)
|
||||
{
|
||||
this.parametersArr = parametersArr;
|
||||
this.indexTable = indexTable;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public float GetFloatParameter(FastAnimatorParameter fp) => GetParameterValue(fp).floatValue;
|
||||
public int GetIntParameter(FastAnimatorParameter fp) => GetParameterValue(fp).intValue;
|
||||
public bool GetBoolParameter(FastAnimatorParameter fp) => GetParameterValue(fp).boolValue;
|
||||
public float GetFloatParameter(uint h) => GetParameterValue(h).floatValue;
|
||||
public int GetIntParameter(uint h) => GetParameterValue(h).intValue;
|
||||
public bool GetBoolParameter(uint h) => GetParameterValue(h).boolValue;
|
||||
public float GetFloatParameter(FixedStringName n) => GetParameterValue(n).floatValue;
|
||||
public int GetIntParameter(FixedStringName n) => GetParameterValue(n).intValue;
|
||||
public bool GetBoolParameter(FixedStringName n) => GetParameterValue(n).boolValue;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public ParameterValue GetParameterValue(FastAnimatorParameter fp)
|
||||
{
|
||||
ParameterValue rv;
|
||||
if (indexTable.value.IsCreated)
|
||||
fp.GetRuntimeParameterData(indexTable.value, parametersArr, out rv);
|
||||
else
|
||||
fp.GetRuntimeParameterData(parametersArr, out rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public ParameterValue GetParameterValue(uint parameterHash)
|
||||
{
|
||||
var fp = new FastAnimatorParameter()
|
||||
{
|
||||
hash = parameterHash,
|
||||
};
|
||||
return GetParameterValue(fp);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public ParameterValue GetParameterValue(FixedStringName parameterName)
|
||||
{
|
||||
var fp = new FastAnimatorParameter(parameterName);
|
||||
return GetParameterValue(fp);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public ParameterValue GetParameterValueByIndex(int paramIndex)
|
||||
{
|
||||
return parametersArr.ElementAt(paramIndex).value;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void SetParameterValue(FastAnimatorParameter fp, ParameterValue value)
|
||||
{
|
||||
if (indexTable.value.IsCreated)
|
||||
fp.SetRuntimeParameterData(indexTable.value, parametersArr, value);
|
||||
else
|
||||
fp.SetRuntimeParameterData(parametersArr, value);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void SetTrigger(FastAnimatorParameter fp)
|
||||
{
|
||||
SetParameterValue(fp, new ParameterValue() { boolValue = true });
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void ResetTrigger(FastAnimatorParameter fp)
|
||||
{
|
||||
SetParameterValue(fp, new ParameterValue() { boolValue = false });
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void SetIntParameter(FastAnimatorParameter fp, int v)
|
||||
{
|
||||
SetParameterValue(fp, new ParameterValue() { intValue = v });
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void SetFloatParameter(FastAnimatorParameter fp, float v)
|
||||
{
|
||||
SetParameterValue(fp, new ParameterValue() { floatValue = v });
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void SetBoolParameter(FastAnimatorParameter fp, bool v)
|
||||
{
|
||||
SetParameterValue(fp, new ParameterValue() { boolValue = v });
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void SetParameterValueByIndex(int paramIndex, ParameterValue value)
|
||||
{
|
||||
parametersArr.ElementAt(paramIndex).value = value;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void SetParameterValue(uint parameterHash, ParameterValue value)
|
||||
{
|
||||
var fp = new FastAnimatorParameter()
|
||||
{
|
||||
hash = parameterHash,
|
||||
};
|
||||
SetParameterValue(fp, value);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void SetParameterValue(FixedStringName parameterName, ParameterValue value)
|
||||
{
|
||||
var fp = new FastAnimatorParameter(parameterName);
|
||||
SetParameterValue(fp, value);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public int GetParameterIndex(FastAnimatorParameter fp)
|
||||
{
|
||||
var index = indexTable.value.IsCreated ?
|
||||
fp.GetRuntimeParameterIndex(indexTable.value, parametersArr) :
|
||||
fp.GetRuntimeParameterIndex(parametersArr);
|
||||
|
||||
return index;
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public bool HasParameter(FastAnimatorParameter fp)
|
||||
{
|
||||
return GetParameterIndex(fp) != -1;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public bool HasParameter(uint parameterHash)
|
||||
{
|
||||
var fp = new FastAnimatorParameter()
|
||||
{
|
||||
hash = parameterHash,
|
||||
};
|
||||
return HasParameter(fp);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public bool HasParameter(FixedStringName parameterName)
|
||||
{
|
||||
var fp = new FastAnimatorParameter(parameterName);
|
||||
return HasParameter(fp);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public int ParametersCount() => parametersArr.Length;
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 20aa9d2bf9a03ba45be985d8bc55cdd7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/AnimatorController/AnimatorParameterAspect.cs
|
||||
uploadId: 897522
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
using Rukhanka.Toolbox;
|
||||
using Unity.Entities;
|
||||
using FixedStringName = Unity.Collections.FixedString512Bytes;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
public readonly struct AnimatorStateQueryAspect
|
||||
{
|
||||
readonly DynamicBuffer<AnimatorControllerLayerComponent> layersArr;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public AnimatorStateQueryAspect(DynamicBuffer<AnimatorControllerLayerComponent> layers)
|
||||
{
|
||||
layersArr = layers;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct RuntimeStateInfo
|
||||
{
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
public FixedStringName name;
|
||||
#endif
|
||||
public uint hash;
|
||||
public float normalizedTime;
|
||||
}
|
||||
|
||||
public struct RuntimeTransitionInfo
|
||||
{
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
public FixedStringName name;
|
||||
#endif
|
||||
public uint hash;
|
||||
public float normalizedTime;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public RuntimeStateInfo GetLayerCurrentStateInfo(int layerIndex)
|
||||
{
|
||||
if (layersArr.Length <= layerIndex)
|
||||
return default;
|
||||
|
||||
var layerRuntimeData = layersArr[layerIndex];
|
||||
ref var layerBlob = ref layerRuntimeData.controller.Value.layers[layerIndex];
|
||||
var curStateID = layerRuntimeData.rtd.srcState.id;
|
||||
|
||||
if (curStateID < 0 || curStateID >= layerBlob.states.Length)
|
||||
return default;
|
||||
|
||||
var rv = new RuntimeStateInfo()
|
||||
{
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
name = layerBlob.states[curStateID].name.ToFixedString(),
|
||||
#endif
|
||||
hash = layerBlob.states[curStateID].hash,
|
||||
normalizedTime = layerRuntimeData.rtd.srcState.normalizedDuration,
|
||||
};
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public RuntimeTransitionInfo GetLayerCurrentTransitionInfo(int layerIndex)
|
||||
{
|
||||
if (layersArr.Length <= layerIndex)
|
||||
return default;
|
||||
|
||||
var layerRuntimeData = layersArr[layerIndex];
|
||||
ref var layerBlob = ref layerRuntimeData.controller.Value.layers[layerIndex];
|
||||
var curTransitionID = layerRuntimeData.rtd.activeTransition.id;
|
||||
var curStateID = layerRuntimeData.rtd.srcState.id;
|
||||
|
||||
if (curTransitionID < 0 || curStateID < 0 || curStateID >= layerBlob.states.Length)
|
||||
return default;
|
||||
|
||||
var rv = new RuntimeTransitionInfo()
|
||||
{
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
name = layerBlob.states[curStateID].transitions[curTransitionID].name.ToFixedString(),
|
||||
#endif
|
||||
hash = layerBlob.states[curStateID].transitions[curTransitionID].hash,
|
||||
normalizedTime = layerRuntimeData.rtd.activeTransition.normalizedDuration
|
||||
};
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public bool IsInTransition(int layerIndex)
|
||||
{
|
||||
if (layersArr.Length <= layerIndex)
|
||||
return default;
|
||||
|
||||
var layerRuntimeData = layersArr[layerIndex];
|
||||
var rv = layerRuntimeData.rtd.activeTransition.id >= 0;
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e4d517a04d48e334796e296fb7c2d66e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/AnimatorController/AnimatorStateQueryAspect.cs
|
||||
uploadId: 897522
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
using Unity.Entities;
|
||||
using FixedStringName = Unity.Collections.FixedString512Bytes;
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using Rukhanka.Toolbox;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
|
||||
public struct FastAnimatorParameter
|
||||
{
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
public FixedStringName paramName;
|
||||
#endif
|
||||
public uint hash;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public FastAnimatorParameter(FixedStringName name)
|
||||
{
|
||||
hash = name.CalculateHash32();
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
paramName = name;
|
||||
#endif
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public FastAnimatorParameter(uint hash)
|
||||
{
|
||||
this.hash = hash;
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
paramName = default;
|
||||
#endif
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool GetRuntimeParameterDataInternal(int paramIdx, DynamicBuffer<AnimatorControllerParameterComponent> runtimeParameters, out ParameterValue outData)
|
||||
{
|
||||
bool isValid = paramIdx >= 0;
|
||||
|
||||
if (isValid)
|
||||
{
|
||||
outData = runtimeParameters[paramIdx].value;
|
||||
}
|
||||
else
|
||||
{
|
||||
outData = default;
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
Debug.LogError($"Could find animator parameter with name {paramName} in hash table! Returning default value!");
|
||||
#endif
|
||||
}
|
||||
return isValid;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public bool GetRuntimeParameterData(BlobAssetReference<PerfectHashTableBlob> pt, DynamicBuffer<AnimatorControllerParameterComponent> runtimeParameters, out ParameterValue outData)
|
||||
{
|
||||
var paramIdx = GetRuntimeParameterIndex(pt, runtimeParameters);
|
||||
return GetRuntimeParameterDataInternal(paramIdx, runtimeParameters, out outData);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public bool GetRuntimeParameterData(DynamicBuffer<AnimatorControllerParameterComponent> runtimeParameters, out ParameterValue outData)
|
||||
{
|
||||
var paramIdx = GetRuntimeParameterIndex(runtimeParameters);
|
||||
return GetRuntimeParameterDataInternal(paramIdx, runtimeParameters, out outData);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool SetRuntimeParameterDataInternal(int paramIdx, DynamicBuffer<AnimatorControllerParameterComponent> runtimeParameters, in ParameterValue paramData)
|
||||
{
|
||||
bool isValid = paramIdx >= 0;
|
||||
|
||||
if (isValid)
|
||||
{
|
||||
var p = runtimeParameters[paramIdx];
|
||||
p.value = paramData;
|
||||
runtimeParameters[paramIdx] = p;
|
||||
}
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
else
|
||||
{
|
||||
Debug.LogError($"Could find animator parameter with name {paramName} in hash table! Setting value is failed!");
|
||||
}
|
||||
#endif
|
||||
return isValid;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public bool SetRuntimeParameterData(BlobAssetReference<PerfectHashTableBlob> pt, DynamicBuffer<AnimatorControllerParameterComponent> runtimeParameters, in ParameterValue paramData)
|
||||
{
|
||||
var paramIdx = GetRuntimeParameterIndex(pt, runtimeParameters);
|
||||
return SetRuntimeParameterDataInternal(paramIdx, runtimeParameters, paramData);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public bool SetRuntimeParameterData(DynamicBuffer<AnimatorControllerParameterComponent> runtimeParameters, in ParameterValue paramData)
|
||||
{
|
||||
var paramIdx = GetRuntimeParameterIndex(runtimeParameters);
|
||||
return SetRuntimeParameterDataInternal(paramIdx, runtimeParameters, paramData);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public bool SetTrigger(BlobAssetReference<PerfectHashTableBlob> pt, DynamicBuffer<AnimatorControllerParameterComponent> runtimeParameters) => SetRuntimeParameterData(pt, runtimeParameters, new ParameterValue() { boolValue = true });
|
||||
public bool SetTrigger(DynamicBuffer<AnimatorControllerParameterComponent> runtimeParameters) => SetRuntimeParameterData(runtimeParameters, new ParameterValue() { boolValue = true });
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Linear search variant
|
||||
public static int GetRuntimeParameterIndex(uint hash, in ReadOnlySpan<AnimatorControllerParameterComponent> parameters)
|
||||
{
|
||||
for (int i = 0; i < parameters.Length; ++i)
|
||||
{
|
||||
var p = parameters[i];
|
||||
if (p.hash == hash)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Perfect hash table variant
|
||||
public static int GetRuntimeParameterIndex(uint hash, in BlobAssetReference<PerfectHashTableBlob> pt, in ReadOnlySpan<AnimatorControllerParameterComponent> parameters)
|
||||
{
|
||||
var paramIdx = pt.Value.Query(hash);
|
||||
if (paramIdx < 0)
|
||||
return -1;
|
||||
|
||||
return paramIdx;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public unsafe int GetRuntimeParameterIndex(in BlobAssetReference<PerfectHashTableBlob> pt, in DynamicBuffer<AnimatorControllerParameterComponent> acpc)
|
||||
{
|
||||
var span = new ReadOnlySpan<AnimatorControllerParameterComponent>(acpc.GetUnsafeReadOnlyPtr(), acpc.Length);
|
||||
return GetRuntimeParameterIndex(hash, pt, span);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public unsafe int GetRuntimeParameterIndex(in DynamicBuffer<AnimatorControllerParameterComponent> acpc)
|
||||
{
|
||||
var span = new ReadOnlySpan<AnimatorControllerParameterComponent>(acpc.GetUnsafeReadOnlyPtr(), acpc.Length);
|
||||
return GetRuntimeParameterIndex(hash, span);
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fa18070f79c037d47b59593d7d6cc720
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/AnimatorController/FastAnimatorParameter.cs
|
||||
uploadId: 897522
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
using Rukhanka.WaybackMachine;
|
||||
using Unity.Burst;
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using Unity.Entities;
|
||||
using Unity.Jobs;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
|
||||
[DisableAutoCreation]
|
||||
[BurstCompile]
|
||||
[UpdateAfter(typeof(AnimatorControllerSystem<AnimatorControllerQuery>))]
|
||||
public partial struct FillAnimationsFromControllerSystem: ISystem
|
||||
{
|
||||
EntityQuery fillAnimationsBufferQuery;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[BurstCompile]
|
||||
public void OnCreate(ref SystemState ss)
|
||||
{
|
||||
fillAnimationsBufferQuery = SystemAPI.QueryBuilder()
|
||||
.WithAll<AnimatorControllerLayerComponent, AnimationToProcessComponent>()
|
||||
.Build();
|
||||
|
||||
ss.RequireForUpdate(fillAnimationsBufferQuery);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[BurstCompile]
|
||||
public void OnDestroy(ref SystemState ss)
|
||||
{
|
||||
if (SystemAPI.TryGetSingletonRW<InternalAnimatorDataSingleton>(out var internalDataSingleton))
|
||||
internalDataSingleton.ValueRW.Dispose();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[BurstCompile]
|
||||
public void OnUpdate(ref SystemState ss)
|
||||
{
|
||||
var entityTypeHandle = SystemAPI.GetEntityTypeHandle();
|
||||
var controllerLayersBufferHandleRO = SystemAPI.GetBufferTypeHandle<AnimatorControllerLayerComponent>(true);
|
||||
var controllerParametersBufferHandleRO = SystemAPI.GetBufferTypeHandle<AnimatorControllerParameterComponent>(true);
|
||||
var animatorOverrideAnimationsLookup = SystemAPI.GetComponentLookup<AnimatorOverrideAnimations>(true);
|
||||
var animationToProcessBufferHandle = SystemAPI.GetBufferTypeHandle<AnimationToProcessComponent>();
|
||||
var animDBSingleton = SystemAPI.GetSingleton<BlobDatabaseSingleton>();
|
||||
|
||||
var internalDataSingletonQuery = SystemAPI.QueryBuilder().WithAllRW<InternalAnimatorDataSingleton>().Build();
|
||||
var internalAnimatorData = GetInternalDataSingleton(internalDataSingletonQuery, ref ss);
|
||||
|
||||
var fillAnimationsBufferJob = new FillAnimationsBufferJob()
|
||||
{
|
||||
controllerLayersBufferHandle = controllerLayersBufferHandleRO,
|
||||
controllerParametersBufferHandle = controllerParametersBufferHandleRO,
|
||||
animationToProcessBufferHandle = animationToProcessBufferHandle,
|
||||
animatorOverrideAnimationLookup = animatorOverrideAnimationsLookup,
|
||||
entityTypeHandle = entityTypeHandle,
|
||||
animationDatabase = animDBSingleton.animations,
|
||||
avatarMaskDatabase = animDBSingleton.avatarMasks,
|
||||
animatorOverrideAnimationsMap = internalAnimatorData.animatorOverrideAnimationsMap.AsParallelWriter()
|
||||
};
|
||||
|
||||
ss.Dependency = fillAnimationsBufferJob.ScheduleParallel(fillAnimationsBufferQuery, ss.Dependency);
|
||||
ss.Dependency = CopyEventsForWaybackMachineDuringRecording(ref ss, ss.Dependency);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
internal static InternalAnimatorDataSingleton GetInternalDataSingleton(EntityQuery singletonQuery, ref SystemState ss)
|
||||
{
|
||||
if (singletonQuery.TryGetSingletonRW<InternalAnimatorDataSingleton>(out var internalAnimatorData))
|
||||
return internalAnimatorData.ValueRW;
|
||||
|
||||
var iads = InternalAnimatorDataSingleton.MakeDefault();
|
||||
ss.EntityManager.CreateSingleton(iads, "Rukhanka.InternalAnimatorDataSingleton");
|
||||
return iads;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
internal static unsafe BlobAssetReference<ControllerAnimationsBlob> GetControllerAnimationsBlob
|
||||
(
|
||||
Entity e,
|
||||
ComponentLookup<AnimatorOverrideAnimations> animatorOverrideAnimationLookup,
|
||||
BlobAssetReference<ControllerAnimationsBlob> cab,
|
||||
NativeParallelHashMap<int, BlobAssetReference<ControllerAnimationsBlob>>.ParallelWriter animatorOverrideAnimationsMap
|
||||
)
|
||||
{
|
||||
if (animatorOverrideAnimationLookup.TryGetComponent(e, out var animationOverrides) && animatorOverrideAnimationLookup.IsComponentEnabled(e))
|
||||
{
|
||||
// Try cache first
|
||||
var combinedHash = animationOverrides.value.GetHashCode() ^ cab.GetHashCode();
|
||||
if (UnsafeParallelHashMapBase<int, BlobAssetReference<ControllerAnimationsBlob>>
|
||||
.TryGetFirstValueAtomic(animatorOverrideAnimationsMap.m_Writer.m_Buffer, combinedHash, out var rv, out _))
|
||||
return rv;
|
||||
|
||||
// Merge controller animations and override animations
|
||||
var bb = new BlobBuilder(Allocator.Temp);
|
||||
ref var mergedBlobAsset = ref bb.ConstructRoot<ControllerAnimationsBlob>();
|
||||
var animsArr = bb.Allocate(ref mergedBlobAsset.animations, cab.Value.animations.Length);
|
||||
for (var i = 0; i < animsArr.Length; ++i)
|
||||
{
|
||||
var overrideAnim = animationOverrides.value.Value.animations[i];
|
||||
animsArr[i] = overrideAnim.IsValid ? overrideAnim : cab.Value.animations[i];
|
||||
}
|
||||
rv = bb.CreateBlobAssetReference<ControllerAnimationsBlob>(Allocator.Persistent);
|
||||
animatorOverrideAnimationsMap.TryAdd(combinedHash, rv);
|
||||
return rv;
|
||||
}
|
||||
return cab;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
JobHandle CopyEventsForWaybackMachineDuringRecording(ref SystemState ss, JobHandle dependsOn)
|
||||
{
|
||||
if (!SystemAPI.TryGetSingletonRW<RecordComponent>(out var rcd))
|
||||
return dependsOn;
|
||||
|
||||
var copyEventsToWaybackMachineJob = new CopyAnimatorEventsToWaybackMachineRecordingJob()
|
||||
{
|
||||
outEvents = rcd.ValueRW.wbData.Value.emittedAnimatorEvents
|
||||
};
|
||||
var jh = copyEventsToWaybackMachineJob.Schedule(dependsOn);
|
||||
return jh;
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8142c7ce247b2d4449bacfa4ece227cc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/AnimatorController/FillAnimationsFromControllerSystem.cs
|
||||
uploadId: 897522
|
||||
+271
@@ -0,0 +1,271 @@
|
||||
using System;
|
||||
using Unity.Burst;
|
||||
using Unity.Burst.CompilerServices;
|
||||
using Unity.Burst.Intrinsics;
|
||||
using Unity.Collections;
|
||||
using Unity.Entities;
|
||||
using Unity.Mathematics;
|
||||
using static Rukhanka.AnimatorControllerSystemJobs;
|
||||
using Rukhanka.WaybackMachine;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
public partial struct FillAnimationsFromControllerSystem
|
||||
{
|
||||
|
||||
[BurstCompile]
|
||||
struct FillAnimationsBufferJob: IJobChunk
|
||||
{
|
||||
[ReadOnly]
|
||||
public BufferTypeHandle<AnimatorControllerLayerComponent> controllerLayersBufferHandle;
|
||||
[ReadOnly]
|
||||
public BufferTypeHandle<AnimatorControllerParameterComponent> controllerParametersBufferHandle;
|
||||
[ReadOnly]
|
||||
public ComponentLookup<AnimatorOverrideAnimations> animatorOverrideAnimationLookup;
|
||||
[ReadOnly]
|
||||
public EntityTypeHandle entityTypeHandle;
|
||||
[ReadOnly]
|
||||
public NativeHashMap<Hash128, BlobAssetReference<AnimationClipBlob>> animationDatabase;
|
||||
[ReadOnly]
|
||||
public NativeHashMap<Hash128, BlobAssetReference<AvatarMaskBlob>> avatarMaskDatabase;
|
||||
|
||||
public BufferTypeHandle<AnimationToProcessComponent> animationToProcessBufferHandle;
|
||||
public NativeParallelHashMap<int, BlobAssetReference<ControllerAnimationsBlob>>.ParallelWriter animatorOverrideAnimationsMap;
|
||||
|
||||
BlobAssetReference<ControllerAnimationsBlob> controllerAnimationsBlob;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask)
|
||||
{
|
||||
var layerBuffers = chunk.GetBufferAccessor(ref controllerLayersBufferHandle);
|
||||
var parameterBuffers = chunk.GetBufferAccessor(ref controllerParametersBufferHandle);
|
||||
var animationsToProcessBuffers = chunk.GetBufferAccessor(ref animationToProcessBufferHandle);
|
||||
var entities = chunk.GetNativeArray(entityTypeHandle);
|
||||
|
||||
var cee = new ChunkEntityEnumerator(useEnabledMask, chunkEnabledMask, chunk.Count);
|
||||
|
||||
while (cee.NextEntityIndex(out var i))
|
||||
{
|
||||
var layers = layerBuffers[i].AsNativeArray();
|
||||
var parameters = parameterBuffers.Length > 0 ? parameterBuffers[i].AsNativeArray() : default;
|
||||
var e = entities[i];
|
||||
|
||||
var animsBuf = animationsToProcessBuffers[i];
|
||||
|
||||
AddAnimationsForEntity(ref animsBuf, layers, parameters, e);
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void AnimationsPostSetup(Span<AnimationToProcessComponent> animations, ref LayerBlob lb, int layerIndex, float weightMultiplier, float layerWeight)
|
||||
{
|
||||
// Set blending mode and adjust animations weight according to layer weight
|
||||
for (int k = 0; k < animations.Length; ++k)
|
||||
{
|
||||
var a = animations[k];
|
||||
a.blendMode = lb.blendingMode;
|
||||
a.layerWeight = layerWeight;
|
||||
a.layerIndex = layerIndex;
|
||||
a.weight *= weightMultiplier;
|
||||
a.avatarMask = BlobDatabaseSingleton.GetBlobAsset(lb.avatarMaskBlobHash, avatarMaskDatabase);
|
||||
animations[k] = a;
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
unsafe void AddAnimationsForEntity
|
||||
(
|
||||
ref DynamicBuffer<AnimationToProcessComponent> animations,
|
||||
in NativeArray<AnimatorControllerLayerComponent> aclc,
|
||||
in NativeArray<AnimatorControllerParameterComponent> runtimeParams,
|
||||
Entity entity
|
||||
)
|
||||
{
|
||||
if (entity == Entity.Null)
|
||||
return;
|
||||
|
||||
animations.Clear();
|
||||
|
||||
// Need to skip zero weight layers
|
||||
for (int i = 0; i < aclc.Length; ++i)
|
||||
{
|
||||
var animationCurIndex = animations.Length;
|
||||
|
||||
var l = aclc[i];
|
||||
controllerAnimationsBlob = GetControllerAnimationsBlob(entity, animatorOverrideAnimationLookup, l.animations, animatorOverrideAnimationsMap);
|
||||
|
||||
var cb = l.controller;
|
||||
ref var lb = ref cb.Value.layers[i];
|
||||
if (l.weight == 0 || l.rtd.srcState.id < 0)
|
||||
continue;
|
||||
|
||||
ref var srcState0Blob = ref lb.states[l.rtd.srcState.id];
|
||||
|
||||
var srcStateWeight = 1.0f;
|
||||
var dstStateWeight = 0.0f;
|
||||
|
||||
if (l.rtd.activeTransition.id >= 0)
|
||||
{
|
||||
dstStateWeight = l.rtd.activeTransition.normalizedDuration;
|
||||
srcStateWeight = (1 - dstStateWeight);
|
||||
}
|
||||
|
||||
var srcStateTime = GetDurationTime(ref srcState0Blob, runtimeParams, l.rtd.srcState.normalizedDuration);
|
||||
|
||||
var dstStateAnimCount = 0;
|
||||
if (l.rtd.dstState.id >= 0)
|
||||
{
|
||||
ref var dstStateBlob = ref lb.states[l.rtd.dstState.id];
|
||||
var dstStateTime = GetDurationTime(ref dstStateBlob, runtimeParams, l.rtd.dstState.normalizedDuration);
|
||||
dstStateAnimCount = AddMotionForEntity(ref animations, ref dstStateBlob.motion, runtimeParams, 1, dstStateTime, l.rtd.dstState.motionId);
|
||||
}
|
||||
|
||||
var srcStateAnimCount = 0;
|
||||
|
||||
// No state snapshots - no transition interruption process
|
||||
// Default state motion processing
|
||||
if (Hint.Likely(l.rtd.srcStateSnapshots.Length == 0))
|
||||
{
|
||||
ref var srcStateBlob = ref lb.states[l.rtd.srcState.id];
|
||||
srcStateAnimCount += AddMotionForEntity(ref animations, ref srcStateBlob.motion, runtimeParams, 1, srcStateTime, l.rtd.srcState.motionId);
|
||||
}
|
||||
|
||||
// Transition interruption motions from state snapshots
|
||||
for (var k = l.rtd.srcStateSnapshots.length - 1; k >= 0; --k)
|
||||
{
|
||||
var stateData = l.rtd.srcStateSnapshots[k];
|
||||
ref var srcStateBlob = ref lb.states[stateData.id];
|
||||
srcStateAnimCount += AddMotionForEntity(ref animations, ref srcStateBlob.motion, runtimeParams, stateData.weight, stateData.normalizedTime, stateData.motionId);
|
||||
}
|
||||
|
||||
var animStartPtr = (AnimationToProcessComponent*)animations.GetUnsafePtr() + animationCurIndex;
|
||||
var dstAnimsSpan = new Span<AnimationToProcessComponent>(animStartPtr, dstStateAnimCount);
|
||||
var srcAnimsSpan = new Span<AnimationToProcessComponent>(animStartPtr + dstStateAnimCount, srcStateAnimCount);
|
||||
|
||||
var dstLayerMultiplier = math.select(dstStateWeight, 1, srcStateAnimCount > 0);
|
||||
var srcLayerMultiplier = math.select(srcStateWeight, 1, dstStateAnimCount > 0);
|
||||
dstStateWeight = math.select(1, dstStateWeight, srcStateAnimCount > 0);
|
||||
srcStateWeight = math.select(1, srcStateWeight, dstStateAnimCount > 0);
|
||||
|
||||
AnimationsPostSetup(dstAnimsSpan, ref lb, i, dstStateWeight, dstLayerMultiplier * l.weight);
|
||||
AnimationsPostSetup(srcAnimsSpan, ref lb, i, srcStateWeight, srcLayerMultiplier * l.weight);
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void AddAnimationForEntity
|
||||
(
|
||||
ref DynamicBuffer<AnimationToProcessComponent> outAnims,
|
||||
ref MotionBlob mb,
|
||||
float weight,
|
||||
float normalizedStateTime,
|
||||
uint motionId
|
||||
)
|
||||
{
|
||||
var atp = new AnimationToProcessComponent();
|
||||
|
||||
var animationHash = controllerAnimationsBlob.Value.animations[mb.animationIndex];
|
||||
atp.animation = BlobDatabaseSingleton.GetBlobAsset(animationHash, animationDatabase);
|
||||
atp.weight = weight;
|
||||
atp.time = normalizedStateTime;
|
||||
atp.motionId = mb.hash + motionId;
|
||||
outAnims.Add(atp);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void AddMotionsFromBlendtree
|
||||
(
|
||||
in NativeList<ScriptedAnimator.MotionIndexAndWeight> miws,
|
||||
ref DynamicBuffer<AnimationToProcessComponent> outAnims,
|
||||
in NativeArray<AnimatorControllerParameterComponent> runtimeParams,
|
||||
ref BlobArray<ChildMotionBlob> motions,
|
||||
float weight,
|
||||
float normalizedStateTime,
|
||||
uint motionId
|
||||
)
|
||||
{
|
||||
for (int i = 0; i < miws.Length; ++i)
|
||||
{
|
||||
var miw = miws[i];
|
||||
ref var m = ref motions[miw.motionIndex];
|
||||
var finalWeight = weight * miw.weight;
|
||||
if (finalWeight > 0)
|
||||
AddMotionForEntity(ref outAnims, ref m.motion, runtimeParams, finalWeight, normalizedStateTime, (uint)(i + motionId));
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int AddMotionForEntity
|
||||
(
|
||||
ref DynamicBuffer<AnimationToProcessComponent> outAnims,
|
||||
ref MotionBlob mb,
|
||||
in NativeArray<AnimatorControllerParameterComponent> runtimeParams,
|
||||
float weight,
|
||||
float normalizedStateTime,
|
||||
uint motionId
|
||||
)
|
||||
{
|
||||
var startLen = outAnims.Length;
|
||||
|
||||
switch (mb.type)
|
||||
{
|
||||
case MotionBlob.Type.None:
|
||||
break;
|
||||
case MotionBlob.Type.AnimationClip:
|
||||
AddAnimationForEntity(ref outAnims, ref mb, weight, normalizedStateTime, motionId);
|
||||
break;
|
||||
}
|
||||
|
||||
var childMotions = ScriptedAnimator.GetChildMotionsList(ref mb, runtimeParams);
|
||||
if (childMotions.IsCreated)
|
||||
{
|
||||
AddMotionsFromBlendtree(childMotions, ref outAnims, runtimeParams, ref mb.blendTree.motions, weight, normalizedStateTime, motionId);
|
||||
}
|
||||
|
||||
return outAnims.Length - startLen;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
float GetDurationTime(ref StateBlob sb, in NativeArray<AnimatorControllerParameterComponent> runtimeParams, float normalizedDuration)
|
||||
{
|
||||
var timeDuration = normalizedDuration;
|
||||
if (sb.timeParameterIndex >= 0)
|
||||
{
|
||||
timeDuration = runtimeParams[sb.timeParameterIndex].FloatValue;
|
||||
}
|
||||
var stateCycleOffset = sb.cycleOffset;
|
||||
if (sb.cycleOffsetParameterIndex >= 0)
|
||||
{
|
||||
stateCycleOffset = runtimeParams[sb.cycleOffsetParameterIndex].FloatValue;
|
||||
}
|
||||
timeDuration += stateCycleOffset;
|
||||
return timeDuration;
|
||||
}
|
||||
}
|
||||
|
||||
//=================================================================================================================//
|
||||
|
||||
[BurstCompile]
|
||||
[WithAll(typeof(RecordComponent))]
|
||||
partial struct CopyAnimatorEventsToWaybackMachineRecordingJob: IJobEntity
|
||||
{
|
||||
public NativeList<AnimatorControllerEventComponent> outEvents;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Execute(in DynamicBuffer<AnimatorControllerEventComponent> acec)
|
||||
{
|
||||
outEvents.AddRange(acec.AsNativeArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f9daf9e1f7afc80459aad91d4b092a7e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/AnimatorController/FillAnimationsFromControllerSystem_Jobs.cs
|
||||
uploadId: 897522
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
|
||||
using Unity.Collections;
|
||||
using Unity.Entities;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
public struct InternalAnimatorDataSingleton: IComponentData
|
||||
{
|
||||
internal NativeParallelHashMap<int, BlobAssetReference<ControllerAnimationsBlob>> animatorOverrideAnimationsMap;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static InternalAnimatorDataSingleton MakeDefault()
|
||||
{
|
||||
var rv = new InternalAnimatorDataSingleton()
|
||||
{
|
||||
animatorOverrideAnimationsMap = new (0xff, Allocator.Persistent)
|
||||
};
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var kv in animatorOverrideAnimationsMap)
|
||||
kv.Value.Dispose();
|
||||
animatorOverrideAnimationsMap.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c7c1e038272b2944aab7454e9960315e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/AnimatorController/InternalAnimatorDataSingleton.cs
|
||||
uploadId: 897522
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
using Unity.Collections;
|
||||
#if RUKHANKA_WITH_NETCODE
|
||||
using Unity.NetCode;
|
||||
#endif
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
|
||||
public struct RuntimeAnimatorData
|
||||
{
|
||||
public struct StateSnapshot
|
||||
{
|
||||
public int id;
|
||||
public uint motionId;
|
||||
public float weight;
|
||||
public float normalizedTime;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------//
|
||||
|
||||
public struct StateData
|
||||
{
|
||||
public int id;
|
||||
public uint motionId;
|
||||
public float normalizedDuration;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------------------//
|
||||
|
||||
public struct TransitionData
|
||||
{
|
||||
public int id;
|
||||
public float normalizedDuration;
|
||||
public float length;
|
||||
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public StateData MakeDefaultState() => new StateData() { id = -1, normalizedDuration = 0, motionId = GetNextMotionID() };
|
||||
public TransitionData MakeDefaultTransition() => new TransitionData() { id = -1, length = 0, normalizedDuration = 0 };
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
uint GetNextMotionID()
|
||||
{
|
||||
motionIdCounter += 0xff;
|
||||
return motionIdCounter;
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public uint motionIdCounter;
|
||||
public StateData srcState;
|
||||
public StateData dstState;
|
||||
public TransitionData activeTransition;
|
||||
#if RUKHANKA_WITH_NETCODE
|
||||
[GhostField(SendData = false)]
|
||||
#endif
|
||||
public FixedList64Bytes<StateSnapshot> srcStateSnapshots;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void ClearStateSnapshots() { srcStateSnapshots.Clear(); }
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void PushStateSnapshot(int stateID, float weight, float normalizedTime, uint motionId)
|
||||
{
|
||||
// If we are out of free space prune snapshot with the lowest weight
|
||||
if (srcStateSnapshots.length == srcStateSnapshots.Capacity)
|
||||
{
|
||||
var minWeight = 1.0f;
|
||||
var minWeightIndex = 0;
|
||||
for (var i = 0; i < srcStateSnapshots.length; ++i)
|
||||
{
|
||||
var w = srcStateSnapshots[i].weight;
|
||||
if (minWeight > w)
|
||||
{
|
||||
minWeight = w;
|
||||
minWeightIndex = i;
|
||||
}
|
||||
}
|
||||
if (minWeightIndex != srcStateSnapshots.length - 1)
|
||||
srcStateSnapshots[minWeightIndex] = srcStateSnapshots[^1];
|
||||
srcStateSnapshots.Length -= 1;
|
||||
}
|
||||
|
||||
// Scale existing weights
|
||||
for (var i = 0; i < srcStateSnapshots.length; ++i)
|
||||
{
|
||||
ref var sn = ref srcStateSnapshots.ElementAt(i);
|
||||
sn.weight *= 1 - weight;
|
||||
}
|
||||
var ss = new StateSnapshot() { id = stateID, weight = weight, normalizedTime = normalizedTime, motionId = motionId };
|
||||
srcStateSnapshots.Add(ss);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static RuntimeAnimatorData MakeDefault()
|
||||
{
|
||||
var rv = new RuntimeAnimatorData();
|
||||
rv.srcState = rv.MakeDefaultState();
|
||||
rv.dstState = rv.MakeDefaultState();
|
||||
rv.activeTransition = rv.MakeDefaultTransition();
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: da31f172d48d3614dac5465f62e8efe0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/AnimatorController/RuntimeAnimatorData.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c8738974cb5aaa14ba8720e321ce7b49
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using Unity.Collections;
|
||||
using Unity.Entities;
|
||||
using Hash128 = Unity.Entities.Hash128;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[assembly: RegisterGenericComponentType(typeof(Rukhanka.NewBlobAssetDatabaseRecord<Rukhanka.AnimationClipBlob>))]
|
||||
[assembly: RegisterGenericComponentType(typeof(Rukhanka.NewBlobAssetDatabaseRecord<Rukhanka.AvatarMaskBlob>))]
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
|
||||
public struct BlobDatabaseSingleton: IComponentData
|
||||
{
|
||||
public NativeHashMap<Hash128, BlobAssetReference<AnimationClipBlob>> animations;
|
||||
public NativeHashMap<Hash128, BlobAssetReference<AvatarMaskBlob>> avatarMasks;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static BlobAssetReference<T> GetBlobAsset<T>(Hash128 blobHash, NativeHashMap<Hash128, BlobAssetReference<T>> blobDatabase) where T: unmanaged
|
||||
{
|
||||
if (!blobDatabase.TryGetValue(blobHash, out var bar))
|
||||
return default;
|
||||
|
||||
return bar;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public BlobAssetReference<AvatarMaskBlob> GetAvatarMaskBlob(Hash128 blobHash) => GetBlobAsset(blobHash, avatarMasks);
|
||||
public BlobAssetReference<AnimationClipBlob> GetAnimationClipBlob(Hash128 blobHash) => GetBlobAsset(blobHash, animations);
|
||||
}
|
||||
|
||||
//=================================================================================================================//
|
||||
|
||||
public struct NewBlobAssetDatabaseRecord<T>: IBufferElementData where T: unmanaged
|
||||
{
|
||||
public Hash128 hash;
|
||||
public BlobAssetReference<T> value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3af0196747a230b45a6e72e1dbec5523
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/BlobDatabase/BlobDatabase.cs
|
||||
uploadId: 897522
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
using Unity.Burst;
|
||||
using Unity.Collections;
|
||||
using Unity.Entities;
|
||||
using Hash128 = Unity.Entities.Hash128;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
[UpdateInGroup(typeof(InitializationSystemGroup), OrderLast = true)]
|
||||
public partial struct BlobDatabaseUpdateSystem: ISystem
|
||||
{
|
||||
EntityQuery newBlobAssetsQuery;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[BurstCompile]
|
||||
public void OnCreate(ref SystemState ss)
|
||||
{
|
||||
CreateDBSingleton(ref ss);
|
||||
|
||||
newBlobAssetsQuery = SystemAPI.QueryBuilder()
|
||||
.WithAny
|
||||
<
|
||||
NewBlobAssetDatabaseRecord<AnimationClipBlob>,
|
||||
NewBlobAssetDatabaseRecord<AvatarMaskBlob>
|
||||
>()
|
||||
.WithOptions(EntityQueryOptions.IncludePrefab)
|
||||
.Build();
|
||||
|
||||
ss.RequireForUpdate(newBlobAssetsQuery);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void CreateDBSingleton(ref SystemState ss)
|
||||
{
|
||||
var blobDB = new BlobDatabaseSingleton()
|
||||
{
|
||||
animations = new (128, Allocator.Persistent),
|
||||
avatarMasks = new (128, Allocator.Persistent),
|
||||
};
|
||||
ss.EntityManager.CreateSingleton(blobDB, "Rukhanka.BlobDatabaseSingleton");
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[BurstCompile]
|
||||
public void OnUpdate(ref SystemState ss)
|
||||
{
|
||||
if (!SystemAPI.TryGetSingletonRW<BlobDatabaseSingleton>(out var db))
|
||||
return;
|
||||
|
||||
var ecbs = SystemAPI.GetSingleton<BeginSimulationEntityCommandBufferSystem.Singleton>();
|
||||
var ecb = ecbs.CreateCommandBuffer(ss.WorldUnmanaged);
|
||||
|
||||
ProcessNewBlobs(ref ss, ref db.ValueRW.avatarMasks, ecb);
|
||||
ProcessNewBlobs(ref ss, ref db.ValueRW.animations, ecb);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void ProcessNewBlobs<T>(ref SystemState ss, ref NativeHashMap<Hash128, BlobAssetReference<T>> db, EntityCommandBuffer ecb) where T: unmanaged
|
||||
{
|
||||
var componentTypeHandle = ss.EntityManager.GetBufferTypeHandle<NewBlobAssetDatabaseRecord<T>>(true);
|
||||
var processNewBlobsJob = new ProcessNewBlobsJob<T>()
|
||||
{
|
||||
db = db,
|
||||
componentTypeHandle = componentTypeHandle
|
||||
};
|
||||
ss.Dependency = processNewBlobsJob.Schedule(newBlobAssetsQuery, ss.Dependency);
|
||||
ecb.RemoveComponent<NewBlobAssetDatabaseRecord<T>>(newBlobAssetsQuery, EntityQueryCaptureMode.AtPlayback);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[BurstCompile]
|
||||
public void OnDestroy(ref SystemState ss)
|
||||
{
|
||||
if (SystemAPI.TryGetSingletonRW<BlobDatabaseSingleton>(out var animDB))
|
||||
{
|
||||
animDB.ValueRW.animations.Dispose();
|
||||
animDB.ValueRW.avatarMasks.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1b5bab6a76d92554cba1f6cafa248b48
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/BlobDatabase/BlobDatabaseUpdateSystem.cs
|
||||
uploadId: 897522
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
using Unity.Burst.Intrinsics;
|
||||
using Unity.Collections;
|
||||
using Unity.Entities;
|
||||
using Unity.Jobs;
|
||||
using Hash128 = Unity.Entities.Hash128;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[assembly: RegisterGenericJobType(typeof(Rukhanka.BlobDatabaseUpdateSystem.ProcessNewBlobsJob<Rukhanka.AvatarMaskBlob>))]
|
||||
[assembly: RegisterGenericJobType(typeof(Rukhanka.BlobDatabaseUpdateSystem.ProcessNewBlobsJob<Rukhanka.AnimationClipBlob>))]
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
public partial struct BlobDatabaseUpdateSystem
|
||||
{
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
internal struct ProcessNewBlobsJob<T>: IJobChunk where T: unmanaged
|
||||
{
|
||||
public BufferTypeHandle<NewBlobAssetDatabaseRecord<T>> componentTypeHandle;
|
||||
public NativeHashMap<Hash128, BlobAssetReference<T>> db;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask)
|
||||
{
|
||||
var ba = chunk.GetBufferAccessor(ref componentTypeHandle);
|
||||
for (var i = 0; i < chunk.Count && ba.Length > 0; ++i)
|
||||
{
|
||||
var newBlobsArr = ba[i];
|
||||
for (int j = 0; j < newBlobsArr.Length; ++j)
|
||||
{
|
||||
var a = newBlobsArr[j];
|
||||
db[a.hash] = a.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b900a60865904dc429164cdcb5c6f9dc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/BlobDatabase/BlobDatabaseUpdateSystem_Jobs.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9f666a7d3068fc54c958786db43c176d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f49e1b09fc72bc744991b3bc2ff4e89f
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,49 @@
|
||||
#ifndef DEBUG_HLSL_
|
||||
#define DEBUG_HLSL_
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "Packages/com.rukhanka.animation/Rukhanka.Runtime/Common/Shaders/DebugMarkers.cs.hlsl"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifdef RUKHANKA_SHADER_DEBUG
|
||||
#define CHECK_MIN_MAX_RANGE(debugMarker, v, minValue, maxValue) if ((v) < (minValue) || (v) >= (maxValue)) InterlockedAdd(debugLoggerCB[debugMarker], 1);
|
||||
|
||||
// Vulkan reports size in elements (ints) rather then bytes as in D3D11/12
|
||||
#ifdef SHADER_API_VULKAN
|
||||
#define CHECK_RAW_BUFFER_OUT_OF_BOUNDS(debugMarker, index, count, buffer) \
|
||||
{ \
|
||||
uint bufferSize = 0; \
|
||||
buffer.GetDimensions(bufferSize); \
|
||||
bufferSize *= 4; \
|
||||
if ((int)(index) < 0 || (int)((index) + (count)) > (int)bufferSize) InterlockedAdd(debugLoggerCB[debugMarker], 1); \
|
||||
}
|
||||
#else
|
||||
#define CHECK_RAW_BUFFER_OUT_OF_BOUNDS(debugMarker, index, count, buffer) \
|
||||
{ \
|
||||
uint bufferSize = 0; \
|
||||
buffer.GetDimensions(bufferSize); \
|
||||
if ((int)(index) < 0 || (int)((index) + (count)) > (int)bufferSize) InterlockedAdd(debugLoggerCB[debugMarker], 1); \
|
||||
}
|
||||
#endif
|
||||
|
||||
#define CHECK_STRUCTURED_BUFFER_OUT_OF_BOUNDS(debugMarker, index, buffer) \
|
||||
{ \
|
||||
uint numStructs = 0; \
|
||||
uint stride = 0; \
|
||||
buffer.GetDimensions(numStructs, stride); \
|
||||
if ((int)(index) < 0 || (int)(index) >= (int)numStructs) InterlockedAdd(debugLoggerCB[debugMarker], 1); \
|
||||
}
|
||||
|
||||
RWStructuredBuffer<uint> debugLoggerCB;
|
||||
#else
|
||||
#define CHECK_MIN_MAX_RANGE(debugMarker, v, minValue, maxValue)
|
||||
#define CHECK_RAW_BUFFER_OUT_OF_BOUNDS(debugMarker, index, count, buffer)
|
||||
#define CHECK_STRUCTURED_BUFFER_OUT_OF_BOUNDS(debugMarker, index, buffer)
|
||||
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ba72f5fdfaa93d349a06294d14840691
|
||||
ShaderIncludeImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/Common/Shaders/Debug.hlsl
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,48 @@
|
||||
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
[GenerateHLSL]
|
||||
public enum RukhankaDebugMarkers
|
||||
{
|
||||
Deformation_CopyMeshData,
|
||||
Deformation_CopyBlendShapes,
|
||||
Deformation_CreatePerVertexDeformationWorkload,
|
||||
Deformation_SkinnedMeshVertex_Write,
|
||||
Deformation_SkinnedMeshVertex_Read,
|
||||
Deformation_DeformedVertex_Read,
|
||||
Deformation_BoneInfluence_Read,
|
||||
Deformation_DeformedVertex_Write,
|
||||
Deformation_FrameSkinMatrices_Read,
|
||||
Deformation_PerVertexWorkload_Read,
|
||||
Deformation_FrameDeformedVertex_Read,
|
||||
GPUAnimator_MakeRigSpaceBoneTransforms_AnimatedBoneWorkload_Read,
|
||||
GPUAnimator_MakeRigSpaceBoneTransforms_AnimationJobs_Read,
|
||||
GPUAnimator_MakeRigSpaceBoneTransforms_BoneLocalTransforms_Read0,
|
||||
GPUAnimator_MakeRigSpaceBoneTransforms_BoneLocalTransforms_Read1,
|
||||
GPUAnimator_MakeRigSpaceBoneTransforms_OutBoneTransforms_Write,
|
||||
GPUAnimator_ComputeSkinMatrices_SkinMatrixWorkload_Read,
|
||||
GPUAnimator_ComputeSkinMatrices_RigSpaceBoneTransforms_Read,
|
||||
GPUAnimator_ComputeSkinMatrices_OutSkinMatrices_Write,
|
||||
GPUAnimator_ProcessAnimations_AnimatedBoneWorkload_Read,
|
||||
GPUAnimator_ProcessAnimations_AnimationJobs_Read,
|
||||
GPUAnimator_ProcessAnimations_OutAnimatedBones_Write,
|
||||
GPUAnimator_GenericAvatar_AvatarMaskBuffer_Read,
|
||||
GPUAnimator_HumanoidAvatar_AvatarMaskBuffer_Read,
|
||||
GPUAnimator_HumanRotationData_Read,
|
||||
GPUAnimator_QueryPerfectHashTable_AnimationClips_Read0,
|
||||
GPUAnimator_QueryPerfectHashTable_AnimationClips_Read1,
|
||||
GPUAnimator_KeyFrame_Read,
|
||||
GPUAnimator_Track_Read,
|
||||
GPUAnimator_TrackSet_Read,
|
||||
GPUAnimator_AnimationClip_Read,
|
||||
GPUAnimator_SkinnedMeshBone_Read,
|
||||
GPUAnimator_RigDefinition_Read,
|
||||
GPUAnimator_RigBone_Read,
|
||||
GPUAnimator_GPUAttachment_RigSpaceBoneTransforms_Read,
|
||||
Total
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// This file was automatically generated. Please don't edit by hand. Execute Editor command [ Edit > Rendering > Generate Shader Includes ] instead
|
||||
//
|
||||
|
||||
#ifndef DEBUGMARKERS_CS_HLSL
|
||||
#define DEBUGMARKERS_CS_HLSL
|
||||
//
|
||||
// Rukhanka.RukhankaDebugMarkers: static fields
|
||||
//
|
||||
#define RUKHANKADEBUGMARKERS_DEFORMATION_COPY_MESH_DATA (0)
|
||||
#define RUKHANKADEBUGMARKERS_DEFORMATION_COPY_BLEND_SHAPES (1)
|
||||
#define RUKHANKADEBUGMARKERS_DEFORMATION_CREATE_PER_VERTEX_DEFORMATION_WORKLOAD (2)
|
||||
#define RUKHANKADEBUGMARKERS_DEFORMATION_SKINNED_MESH_VERTEX_WRITE (3)
|
||||
#define RUKHANKADEBUGMARKERS_DEFORMATION_SKINNED_MESH_VERTEX_READ (4)
|
||||
#define RUKHANKADEBUGMARKERS_DEFORMATION_DEFORMED_VERTEX_READ (5)
|
||||
#define RUKHANKADEBUGMARKERS_DEFORMATION_BONE_INFLUENCE_READ (6)
|
||||
#define RUKHANKADEBUGMARKERS_DEFORMATION_DEFORMED_VERTEX_WRITE (7)
|
||||
#define RUKHANKADEBUGMARKERS_DEFORMATION_FRAME_SKIN_MATRICES_READ (8)
|
||||
#define RUKHANKADEBUGMARKERS_DEFORMATION_PER_VERTEX_WORKLOAD_READ (9)
|
||||
#define RUKHANKADEBUGMARKERS_DEFORMATION_FRAME_DEFORMED_VERTEX_READ (10)
|
||||
#define RUKHANKADEBUGMARKERS_GPUANIMATOR_MAKE_RIG_SPACE_BONE_TRANSFORMS_ANIMATED_BONE_WORKLOAD_READ (11)
|
||||
#define RUKHANKADEBUGMARKERS_GPUANIMATOR_MAKE_RIG_SPACE_BONE_TRANSFORMS_ANIMATION_JOBS_READ (12)
|
||||
#define RUKHANKADEBUGMARKERS_GPUANIMATOR_MAKE_RIG_SPACE_BONE_TRANSFORMS_BONE_LOCAL_TRANSFORMS_READ0 (13)
|
||||
#define RUKHANKADEBUGMARKERS_GPUANIMATOR_MAKE_RIG_SPACE_BONE_TRANSFORMS_BONE_LOCAL_TRANSFORMS_READ1 (14)
|
||||
#define RUKHANKADEBUGMARKERS_GPUANIMATOR_MAKE_RIG_SPACE_BONE_TRANSFORMS_OUT_BONE_TRANSFORMS_WRITE (15)
|
||||
#define RUKHANKADEBUGMARKERS_GPUANIMATOR_COMPUTE_SKIN_MATRICES_SKIN_MATRIX_WORKLOAD_READ (16)
|
||||
#define RUKHANKADEBUGMARKERS_GPUANIMATOR_COMPUTE_SKIN_MATRICES_RIG_SPACE_BONE_TRANSFORMS_READ (17)
|
||||
#define RUKHANKADEBUGMARKERS_GPUANIMATOR_COMPUTE_SKIN_MATRICES_OUT_SKIN_MATRICES_WRITE (18)
|
||||
#define RUKHANKADEBUGMARKERS_GPUANIMATOR_PROCESS_ANIMATIONS_ANIMATED_BONE_WORKLOAD_READ (19)
|
||||
#define RUKHANKADEBUGMARKERS_GPUANIMATOR_PROCESS_ANIMATIONS_ANIMATION_JOBS_READ (20)
|
||||
#define RUKHANKADEBUGMARKERS_GPUANIMATOR_PROCESS_ANIMATIONS_OUT_ANIMATED_BONES_WRITE (21)
|
||||
#define RUKHANKADEBUGMARKERS_GPUANIMATOR_GENERIC_AVATAR_AVATAR_MASK_BUFFER_READ (22)
|
||||
#define RUKHANKADEBUGMARKERS_GPUANIMATOR_HUMANOID_AVATAR_AVATAR_MASK_BUFFER_READ (23)
|
||||
#define RUKHANKADEBUGMARKERS_GPUANIMATOR_HUMAN_ROTATION_DATA_READ (24)
|
||||
#define RUKHANKADEBUGMARKERS_GPUANIMATOR_QUERY_PERFECT_HASH_TABLE_ANIMATION_CLIPS_READ0 (25)
|
||||
#define RUKHANKADEBUGMARKERS_GPUANIMATOR_QUERY_PERFECT_HASH_TABLE_ANIMATION_CLIPS_READ1 (26)
|
||||
#define RUKHANKADEBUGMARKERS_GPUANIMATOR_KEY_FRAME_READ (27)
|
||||
#define RUKHANKADEBUGMARKERS_GPUANIMATOR_TRACK_READ (28)
|
||||
#define RUKHANKADEBUGMARKERS_GPUANIMATOR_TRACK_SET_READ (29)
|
||||
#define RUKHANKADEBUGMARKERS_GPUANIMATOR_ANIMATION_CLIP_READ (30)
|
||||
#define RUKHANKADEBUGMARKERS_GPUANIMATOR_SKINNED_MESH_BONE_READ (31)
|
||||
#define RUKHANKADEBUGMARKERS_GPUANIMATOR_RIG_DEFINITION_READ (32)
|
||||
#define RUKHANKADEBUGMARKERS_GPUANIMATOR_RIG_BONE_READ (33)
|
||||
#define RUKHANKADEBUGMARKERS_GPUANIMATOR_GPUATTACHMENT_RIG_SPACE_BONE_TRANSFORMS_READ (34)
|
||||
#define RUKHANKADEBUGMARKERS_TOTAL (35)
|
||||
|
||||
|
||||
#endif
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8835b540c29a9e24780f45cd69428b92
|
||||
ShaderIncludeImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/Common/Shaders/DebugMarkers.cs.hlsl
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cdbd14dcc81008542b7c6fca79ba7111
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/Common/Shaders/DebugMarkers.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5b2404e6da098614aa9939f5120e09eb
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
#ifndef ANIMATION_CLIP_HLSL_
|
||||
#define ANIMATION_CLIP_HLSL_
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "TrackSet.hlsl"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct AnimationClip
|
||||
{
|
||||
uint4 hash;
|
||||
TrackSet clipTracks;
|
||||
TrackSet additiveReferencePoseTracks;
|
||||
uint flags;
|
||||
float cycleOffset;
|
||||
float length;
|
||||
|
||||
static const uint size = (4 + 1 + 1 + 1) * 4 + TrackSet::size * 2;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool IsLooped() { return GetFlag(1); }
|
||||
bool LoopPoseBlend() { return GetFlag(2); }
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool GetFlag(int index)
|
||||
{
|
||||
uint v = 1u << index;
|
||||
return (flags & v) != 0;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static AnimationClip ReadFromRawBuffer(ByteAddressBuffer b, int byteAddress)
|
||||
{
|
||||
AnimationClip rv = (AnimationClip)0;
|
||||
rv.hash = b.Load4(byteAddress);
|
||||
rv.clipTracks = TrackSet::ReadFromRawBuffer(b, byteAddress + 16);
|
||||
rv.additiveReferencePoseTracks = TrackSet::ReadFromRawBuffer(b, byteAddress + 16 + TrackSet::size);
|
||||
rv.flags = b.Load(byteAddress + 16 + TrackSet::size * 2);
|
||||
rv.cycleOffset = asfloat(b.Load(byteAddress + 20 + TrackSet::size * 2));
|
||||
rv.length = asfloat(b.Load(byteAddress + 24 + TrackSet::size * 2));
|
||||
|
||||
CHECK_RAW_BUFFER_OUT_OF_BOUNDS(RUKHANKADEBUGMARKERS_GPUANIMATOR_ANIMATION_CLIP_READ, byteAddress, size, b);
|
||||
|
||||
return rv;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 73e65cfe5708b1346962e42ce655aad5
|
||||
ShaderIncludeImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/Common/Shaders/GPUStructures/AnimationClip.hlsl
|
||||
uploadId: 897522
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
#ifndef ANIMATION_TO_PROCESS_HLSL_
|
||||
#define ANIMATION_TO_PROCESS_HLSL_
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define BLEND_MODE_OVERRIDE 0
|
||||
#define BLEND_MODE_ADDITIVE 1
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct AnimationToProcess
|
||||
{
|
||||
int animationClipAddress;
|
||||
float weight;
|
||||
float time;
|
||||
int blendMode;
|
||||
float layerWeight;
|
||||
int layerIndex;
|
||||
int avatarMaskDataOffset;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ff3f32e2710712542bbe95b14f0535c3
|
||||
ShaderIncludeImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/Common/Shaders/GPUStructures/AnimationToProcess.hlsl
|
||||
uploadId: 897522
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
#ifndef AVATAR_MASK_HLSL_
|
||||
#define AVATAR_MASK_HLSL_
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// uint
|
||||
ByteAddressBuffer avatarMasksBuffer;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool IsBoneInGenericAvatarMask(int avatarMaskDataOffset, int boneIndex)
|
||||
{
|
||||
int uintIndex = boneIndex >> 5; // boneIndex / 32
|
||||
int byteOffsetInUint = boneIndex & 0x1f; // boneIndex % 32
|
||||
uint boneMask = 1u << byteOffsetInUint;
|
||||
|
||||
// First uint in mask is human body parts mask, skip it
|
||||
uint absMaskDataIndex = avatarMaskDataOffset + 1 + uintIndex;
|
||||
uint avatarMask = avatarMasksBuffer.Load(absMaskDataIndex * 4);
|
||||
|
||||
CHECK_RAW_BUFFER_OUT_OF_BOUNDS(RUKHANKADEBUGMARKERS_GPUANIMATOR_GENERIC_AVATAR_AVATAR_MASK_BUFFER_READ, absMaskDataIndex * 4, 4, avatarMasksBuffer);
|
||||
|
||||
return (avatarMask & boneMask) != 0;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool IsBoneInHumanAvatarMask(int avatarMaskDataOffset, int humanAvatarMaskBodyPart)
|
||||
{
|
||||
uint absMaskDataIndex = avatarMaskDataOffset;
|
||||
uint humanAvatarMask = avatarMasksBuffer.Load(absMaskDataIndex * 4);
|
||||
|
||||
CHECK_RAW_BUFFER_OUT_OF_BOUNDS(RUKHANKADEBUGMARKERS_GPUANIMATOR_HUMANOID_AVATAR_AVATAR_MASK_BUFFER_READ, absMaskDataIndex * 4, 4, avatarMasksBuffer);
|
||||
|
||||
return (humanAvatarMask & 1 << humanAvatarMaskBodyPart) != 0;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool IsBoneInAvatarMask(int avatarMaskDataOffset, int humanAvatarMaskBodyPart, int boneIndex)
|
||||
{
|
||||
if (avatarMaskDataOffset < 0)
|
||||
return true;
|
||||
|
||||
bool rv = humanAvatarMaskBodyPart < 0 ?
|
||||
IsBoneInGenericAvatarMask(avatarMaskDataOffset, boneIndex) :
|
||||
IsBoneInHumanAvatarMask(avatarMaskDataOffset, humanAvatarMaskBodyPart);
|
||||
return rv;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6c18bdd7b329d1b40889497acd238107
|
||||
ShaderIncludeImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/Common/Shaders/GPUStructures/AvatarMask.hlsl
|
||||
uploadId: 897522
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
#ifndef BONE_TRANFORM_HLSL_
|
||||
#define BONE_TRANFORM_HLSL_
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "Quaternion.hlsl"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct BoneTransform
|
||||
{
|
||||
float3 pos;
|
||||
Quaternion rot;
|
||||
float3 scale;
|
||||
|
||||
static const uint size = (3 + 4 + 3) * 4;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static BoneTransform Identity()
|
||||
{
|
||||
BoneTransform rv;
|
||||
rv.pos = 0;
|
||||
rv.rot.value = float4(0, 0, 0, 1);
|
||||
rv.scale = 1;
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static BoneTransform Zero()
|
||||
{
|
||||
BoneTransform rv = (BoneTransform)0;
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static BoneTransform FromMatrix(float3x4 m)
|
||||
{
|
||||
BoneTransform rv;
|
||||
rv.rot = Quaternion::FromMatrix((float3x3)m);
|
||||
rv.pos = m._m03_m13_m23;
|
||||
|
||||
float3 u = m._m00_m10_m20;
|
||||
float3 v = m._m01_m11_m21;
|
||||
float3 w = m._m02_m12_m22;
|
||||
rv.scale = float3(length(u), length(v), length(w));
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static BoneTransform Multiply(BoneTransform parent, BoneTransform child)
|
||||
{
|
||||
BoneTransform rv;
|
||||
rv.pos = Quaternion::Rotate(parent.rot, child.pos * parent.scale) + parent.pos;
|
||||
rv.rot = Quaternion::Multiply(parent.rot, child.rot);
|
||||
rv.scale = parent.scale * child.scale;
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static BoneTransform Scale(BoneTransform v, float3 scale)
|
||||
{
|
||||
v.pos *= scale.x;
|
||||
v.rot.value *= scale.y;
|
||||
v.scale *= scale.z;
|
||||
return v;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
float4x4 ToFloat4x4()
|
||||
{
|
||||
float3x3 rotMat = rot.ToRotationMatrix();
|
||||
float4 c0 = float4(rotMat._11_21_31 * scale, pos.x);
|
||||
float4 c1 = float4(rotMat._12_22_32 * scale, pos.y);
|
||||
float4 c2 = float4(rotMat._13_23_33 * scale, pos.z);
|
||||
float4 c3 = float4(0, 0, 0, 1);
|
||||
|
||||
float4x4 rv = float4x4(c0, c1, c2, c3);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static BoneTransform Inverse(BoneTransform v)
|
||||
{
|
||||
BoneTransform rv;
|
||||
rv.rot = Quaternion::Inverse(v.rot);
|
||||
rv.scale = rcp(v.scale);
|
||||
rv.pos = Quaternion::Rotate(rv.rot, -v.pos * rv.scale);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static float3 TransformPoint(BoneTransform bt, float3 v)
|
||||
{
|
||||
float3 rv = Quaternion::Rotate(bt.rot, v * bt.scale) + bt.pos;
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static float3 TransformDirection(BoneTransform bt, float3 v)
|
||||
{
|
||||
float3 rv = Quaternion::Rotate(bt.rot, v);
|
||||
return rv;
|
||||
}
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3d24b7cf754800d4f80fd374dc9639ac
|
||||
ShaderIncludeImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/Common/Shaders/GPUStructures/BoneTransform.hlsl
|
||||
uploadId: 897522
|
||||
+150
@@ -0,0 +1,150 @@
|
||||
#ifndef DUAL_QUATERNION_HLSL_
|
||||
#define DUAL_QUATERBION_HLSL_
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "Quaternion.hlsl"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct DualQuaternion
|
||||
{
|
||||
Quaternion qReal, qDual;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static DualQuaternion Identity()
|
||||
{
|
||||
DualQuaternion rv;
|
||||
rv.qReal.value = float4(0, 0, 0, 1);
|
||||
rv.qDual.value = float4(0, 0, 0, 0);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static DualQuaternion Construct(Quaternion r, Quaternion d)
|
||||
{
|
||||
DualQuaternion rv;
|
||||
rv.qReal = Quaternion::Normalize(r);
|
||||
rv.qDual = d;
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static DualQuaternion Construct(float3 t, Quaternion r)
|
||||
{
|
||||
DualQuaternion rv;
|
||||
rv.qReal = Quaternion::Normalize(r);
|
||||
|
||||
Quaternion qt;
|
||||
qt.value = float4(t, 0);
|
||||
rv.qDual = Quaternion::Scale(Quaternion::Multiply(qt, rv.qReal), 0.5f);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static DualQuaternion Normalize(DualQuaternion q)
|
||||
{
|
||||
DualQuaternion rv = q;
|
||||
float m = length(q.qReal.value);
|
||||
float rcpM = rcp(m);
|
||||
rv.qReal = Quaternion::Scale(q.qReal, rcpM);
|
||||
rv.qDual = Quaternion::Scale(q.qDual, rcpM);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static DualQuaternion Multiply(DualQuaternion l, DualQuaternion r)
|
||||
{
|
||||
DualQuaternion rv;
|
||||
rv.qReal = Quaternion::Multiply(l.qReal, r.qReal);
|
||||
Quaternion qDual0 = Quaternion::Multiply(l.qDual, r.qReal);
|
||||
Quaternion qDual1 = Quaternion::Multiply(l.qReal, r.qDual);
|
||||
rv.qDual.value = qDual0.value + qDual1.value;
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static DualQuaternion Add(DualQuaternion l, DualQuaternion r)
|
||||
{
|
||||
DualQuaternion rv;
|
||||
rv.qReal.value = l.qReal.value + r.qReal.value;
|
||||
rv.qDual.value = l.qDual.value + r.qDual.value;
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static DualQuaternion Scale(DualQuaternion q, float s)
|
||||
{
|
||||
DualQuaternion rv;
|
||||
rv.qReal = Quaternion::Scale(q.qReal, s);
|
||||
rv.qDual = Quaternion::Scale(q.qDual, s);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
BoneTransform GetBoneTransform()
|
||||
{
|
||||
BoneTransform rv;
|
||||
Quaternion t0 = Quaternion::Scale(qDual, 2);
|
||||
Quaternion t1 = Quaternion::Conjugate(qReal);
|
||||
Quaternion t = Quaternion::Multiply(t0, t1);
|
||||
rv.pos = t.value.xyz;
|
||||
|
||||
rv.rot = qReal;
|
||||
rv.scale = 1;
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
float4x4 ToTransformMatrix()
|
||||
{
|
||||
DualQuaternion q = DualQuaternion::Normalize(this);
|
||||
float4x4 m = float4x4
|
||||
(
|
||||
1, 0, 0, 0,
|
||||
0, 1, 0, 0,
|
||||
0, 0, 1, 0,
|
||||
0, 0, 0, 1
|
||||
);
|
||||
|
||||
float4 r = q.qReal.value;
|
||||
|
||||
// Extract rotation
|
||||
m._11 = r.w * r.w + r.x * r.x - r.y * r.y - r.z * r.z;
|
||||
m._12 = 2 * r.x * r.y + 2 * r.w * r.z;
|
||||
m._13 = 2 * r.x * r.z - 2 * r.w * r.y;
|
||||
|
||||
m._21 = 2 * r.x * r.y - 2 * r.w * r.z;
|
||||
m._22 = r.w * r.w + r.y * r.y - r.x * r.x - r.z * r.z;
|
||||
m._23 = 2 * r.y * r.z + 2 * r.w * r.x;
|
||||
|
||||
m._31 = 2 * r.x * r.z + 2 * r.w * r.y;
|
||||
m._32 = 2 * r.y * r.z - 2 * r.w * r.x;
|
||||
m._33 = r.w * r.w + r.z * r.z - r.x * r.x - r.y * r.y;
|
||||
|
||||
// Extract translation
|
||||
Quaternion t0 = Quaternion::Scale(q.qDual, 2);
|
||||
Quaternion t = Quaternion::Multiply(t0, Quaternion::Conjugate(q.qReal));
|
||||
m._41 = t.value.x;
|
||||
m._42 = t.value.y;
|
||||
m._43 = t.value.z;
|
||||
|
||||
return m;
|
||||
}
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8edf7d0e28b62ca4094ee053fe36a5b0
|
||||
ShaderIncludeImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/Common/Shaders/GPUStructures/DualQuaternion.hlsl
|
||||
uploadId: 897522
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
#ifndef HUMAN_ROTATION_DATA_HLSL_
|
||||
#define HUMAN_ROTATION_DATA_HLSL_
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct HumanRotationData
|
||||
{
|
||||
float3 minMuscleAngles, maxMuscleAngles;
|
||||
Quaternion preRot, postRot;
|
||||
float3 sign;
|
||||
|
||||
static const uint size = (3 + 3 + 4 + 4 + 3) * 4;
|
||||
|
||||
static HumanRotationData ReadFromRawBuffer(ByteAddressBuffer b, int index)
|
||||
{
|
||||
int byteAddress = index * size;
|
||||
|
||||
HumanRotationData rv = (HumanRotationData)0;
|
||||
rv.minMuscleAngles = asfloat(b.Load3(byteAddress + 0));
|
||||
rv.maxMuscleAngles = asfloat(b.Load3(byteAddress + 12));
|
||||
rv.preRot.value = asfloat(b.Load4(byteAddress + 24));
|
||||
rv.postRot.value = asfloat(b.Load4(byteAddress + 40));
|
||||
rv.sign = asfloat(b.Load3(byteAddress + 56));
|
||||
|
||||
CHECK_RAW_BUFFER_OUT_OF_BOUNDS(RUKHANKADEBUGMARKERS_GPUANIMATOR_HUMAN_ROTATION_DATA_READ, byteAddress, size, b);
|
||||
|
||||
return rv;
|
||||
}
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9d46c9cd9b4c75d4188bf5e6914efc46
|
||||
ShaderIncludeImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/Common/Shaders/GPUStructures/HumanRotationData.hlsl
|
||||
uploadId: 897522
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
#ifndef KEYFRAME_HLSL_
|
||||
#define KEYFRAME_HLSL_
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct KeyFrame
|
||||
{
|
||||
float v;
|
||||
float inTan;
|
||||
float outTan;
|
||||
float time;
|
||||
|
||||
static const uint size = 4 * 4;
|
||||
|
||||
static KeyFrame ReadFromRawBuffer(ByteAddressBuffer b, int baseAddress, int index)
|
||||
{
|
||||
int addr = baseAddress + index * size;
|
||||
float4 v = asfloat(b.Load4(addr));
|
||||
|
||||
KeyFrame rv = (KeyFrame)0;
|
||||
rv.v = v.x;
|
||||
rv.inTan = v.y;
|
||||
rv.outTan = v.z;
|
||||
rv.time = v.w;
|
||||
|
||||
//CHECK_RAW_BUFFER_OUT_OF_BOUNDS(RUKHANKADEBUGMARKERS_GPUANIMATOR_KEY_FRAME_READ, addr, size, b);
|
||||
|
||||
return rv;
|
||||
}
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ecfd91f2fbf55b847880b642d5da4cc8
|
||||
ShaderIncludeImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/Common/Shaders/GPUStructures/KeyFrame.hlsl
|
||||
uploadId: 897522
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
#ifndef PERFECT_HASH_TABLE_HLSL_
|
||||
#define PERFECT_HASH_TABLE_HLSL_
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// High-Order Half of 64-Bit Product
|
||||
// Ref "Hackers Delight" 8-2
|
||||
uint MultiplyHighUnsigned(uint u, uint v)
|
||||
{
|
||||
uint u0 = u & 0xffff;
|
||||
uint u1 = u >> 16;
|
||||
uint v0 = v & 0xffff;
|
||||
uint v1 = v >> 16;
|
||||
uint w0 = u0 * v0;
|
||||
uint t = u1 * v0 + (w0 >> 16);
|
||||
uint w1 = t & 0xFFFF;
|
||||
uint w2 = t >> 16;
|
||||
w1 = u0 * v1 + w1;
|
||||
return u1 * v1 + w2 + (w1 >> 16);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
uint HashLemer(uint v, uint seed)
|
||||
{
|
||||
seed = v ^ seed;
|
||||
uint tmpH = MultiplyHighUnsigned(seed, 0x4a39b70d);
|
||||
uint tmpL = seed * 0x4a39b70d;
|
||||
uint m1 = tmpH ^ tmpL;
|
||||
tmpH = MultiplyHighUnsigned(m1, 0x12fad5c9);
|
||||
tmpL = m1 * 0x12fad5c9;
|
||||
uint m2 = tmpH ^ tmpL;
|
||||
return m2;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
uint GetValueIndex(uint v, uint seed, uint modMask)
|
||||
{
|
||||
uint hv = HashLemer(v, seed);
|
||||
uint rv = hv & modMask;
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int QueryPerfectHashTable(uint v, uint seed, uint phtAddress, uint sizeMask)
|
||||
{
|
||||
uint index = GetValueIndex(v, seed, sizeMask);
|
||||
uint byteAddress = phtAddress + index * 4 * 2;
|
||||
CHECK_RAW_BUFFER_OUT_OF_BOUNDS(RUKHANKADEBUGMARKERS_GPUANIMATOR_QUERY_PERFECT_HASH_TABLE_ANIMATION_CLIPS_READ0, byteAddress, 8, animationClips);
|
||||
uint2 phtv = animationClips.Load2(byteAddress);
|
||||
if (phtv.x == v)
|
||||
return (int)(phtv.y & 0xffff);
|
||||
uint nextIndex = phtv.y >> 16;
|
||||
uint byteAddress2 = phtAddress + nextIndex * 4 * 2;
|
||||
CHECK_RAW_BUFFER_OUT_OF_BOUNDS(RUKHANKADEBUGMARKERS_GPUANIMATOR_QUERY_PERFECT_HASH_TABLE_ANIMATION_CLIPS_READ1, byteAddress2, 8, animationClips);
|
||||
uint2 phtv2 = animationClips.Load2(byteAddress2);
|
||||
if (phtv2.x == v)
|
||||
return (int)(phtv2.y & 0xffff);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#endif
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cd4cb6713dc57034da2b5a2cda9cf73f
|
||||
ShaderIncludeImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/Common/Shaders/GPUStructures/PerfectHashTable.hlsl
|
||||
uploadId: 897522
|
||||
+216
@@ -0,0 +1,216 @@
|
||||
#ifndef QUATERNION_HLSL_
|
||||
#define QUATERNION_HLSL_
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct Quaternion
|
||||
{
|
||||
float4 value;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static Quaternion Identity()
|
||||
{
|
||||
Quaternion rv;
|
||||
rv.value = float4(0, 0, 0, 1);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static float3 Rotate(Quaternion q, float3 v)
|
||||
{
|
||||
float3 t = 2 * cross(q.value.xyz, v);
|
||||
float3 rv = v + q.value.w * t + cross(q.value.xyz, t);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static Quaternion Multiply(Quaternion a, Quaternion b)
|
||||
{
|
||||
Quaternion rv;
|
||||
rv.value = float4(a.value.wwww * b.value + (a.value.xyzx * b.value.wwwx + a.value.yzxy * b.value.zxyy) * float4(1.0f, 1.0f, 1.0f, -1.0f) - a.value.zxyz * b.value.yzxz);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static Quaternion ShortestRotation(Quaternion from, Quaternion to)
|
||||
{
|
||||
Quaternion rv;
|
||||
uint4 sign = asuint(dot(from.value, to.value)) & 0x80000000;
|
||||
rv.value = asfloat(sign ^ asuint(to.value));
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static Quaternion AxisAngle(float3 axis, float angle)
|
||||
{
|
||||
float sina, cosa;
|
||||
sincos(0.5f * angle, sina, cosa);
|
||||
Quaternion rv;
|
||||
rv.value = float4(axis * sina, cosa);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static Quaternion Normalize(Quaternion q)
|
||||
{
|
||||
float4 x = q.value;
|
||||
Quaternion rv;
|
||||
rv.value = rsqrt(dot(x, x)) * x;
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static Quaternion Inverse(Quaternion q)
|
||||
{
|
||||
float4 a = q.value;
|
||||
Quaternion rv;
|
||||
rv.value = rcp(dot(a, a)) * a * float4(-1.0f, -1.0f, -1.0f, 1.0f);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static Quaternion Conjugate(Quaternion q)
|
||||
{
|
||||
Quaternion rv;
|
||||
rv.value = q.value * float4(-1, -1, -1, 1);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static Quaternion NormalizeSafe(Quaternion q)
|
||||
{
|
||||
float FLT_MIN_NORMAL = 1.175494351e-38F;
|
||||
float len = dot(q.value, q.value);
|
||||
Quaternion rv;
|
||||
rv.value = float4(0, 0, 0, 1);
|
||||
|
||||
if (len > FLT_MIN_NORMAL)
|
||||
{
|
||||
rv.value = q.value * rsqrt(len);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static Quaternion FromMatrix(float3x3 m)
|
||||
{
|
||||
Quaternion rv;
|
||||
|
||||
float3 u = m._m00_m10_m20;
|
||||
float3 v = m._m01_m11_m21;
|
||||
float3 w = m._m02_m12_m22;
|
||||
|
||||
uint u_sign = asuint(u.x) & 0x80000000;
|
||||
float t = v.y + asfloat(asuint(w.z) ^ u_sign);
|
||||
uint1 u_mask1 = (int)u_sign >> 31;
|
||||
uint1 t_mask1 = asint(t) >> 31;
|
||||
uint4 u_mask = u_mask1.xxxx;
|
||||
uint4 t_mask = t_mask1.xxxx;
|
||||
uint4 u_mask_inv = uint4(~u_mask1.x, ~u_mask1.x, ~u_mask1.x, ~u_mask1.x);
|
||||
uint4 t_mask_inv = uint4(~t_mask1.x, ~t_mask1.x, ~t_mask1.x, ~t_mask1.x);
|
||||
|
||||
float tr = 1.0f + abs(u.x);
|
||||
|
||||
uint4 sign_flips = uint4(0x00000000, 0x80000000, 0x80000000, 0x80000000) ^ (u_mask & uint4(0x00000000, 0x80000000, 0x00000000, 0x80000000)) ^ (t_mask & uint4(0x80000000, 0x80000000, 0x80000000, 0x00000000));
|
||||
|
||||
rv.value = float4(tr, u.y, w.x, v.z) + asfloat(asuint(float4(t, v.x, u.z, w.y)) ^ sign_flips); // +---, +++-, ++-+, +-++
|
||||
|
||||
rv.value = asfloat((asuint(rv.value) & u_mask_inv) | (asuint(rv.value.zwxy) & u_mask));
|
||||
rv.value = asfloat((asuint(rv.value.wzyx) & t_mask_inv) | (asuint(rv.value) & t_mask));
|
||||
rv.value = normalize(rv.value);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
float3x3 ToRotationMatrix()
|
||||
{
|
||||
float4 value2 = value * 2;
|
||||
uint3 npn = uint3(0x80000000, 0x00000000, 0x80000000);
|
||||
uint3 nnp = uint3(0x80000000, 0x80000000, 0x00000000);
|
||||
uint3 pnn = uint3(0x00000000, 0x80000000, 0x80000000);
|
||||
float3 c0 = value2.y * asfloat(asuint(value.yxw) ^ npn) - value2.z * asfloat(asuint(value.zwx) ^ pnn) + float3(1, 0, 0);
|
||||
float3 c1 = value2.z * asfloat(asuint(value.wzy) ^ nnp) - value2.x * asfloat(asuint(value.yxw) ^ npn) + float3(0, 1, 0);
|
||||
float3 c2 = value2.x * asfloat(asuint(value.zwx) ^ pnn) - value2.y * asfloat(asuint(value.wzy) ^ nnp) + float3(0, 0, 1);
|
||||
|
||||
float3x3 rv = float3x3(c0, c1, c2);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static Quaternion EulerXYZ(float3 eulerAngles)
|
||||
{
|
||||
float3 sinV, cosV;
|
||||
sincos(0.5f * eulerAngles, sinV, cosV);
|
||||
Quaternion rv;
|
||||
rv.value = float4(sinV.xyz, cosV.x) * cosV.yxxy * cosV.zzyz + sinV.yxxy * sinV.zzyz * float4(cosV.xyz, sinV.x) * float4(-1.0f, 1.0f, -1.0f, 1.0f);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static float4 ChangeSign(float4 a, float4 b)
|
||||
{
|
||||
return asfloat(asuint(a) ^ asuint(b) & 0x80000000);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static Quaternion Scale(Quaternion q, float s)
|
||||
{
|
||||
Quaternion rv;
|
||||
rv.value = q.value * s;
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static Quaternion Nlerp(Quaternion a, Quaternion b, float t)
|
||||
{
|
||||
Quaternion rv;
|
||||
rv.value = normalize(a.value + t * (ChangeSign(b.value, dot(a.value, b.value)) - a.value));
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static Quaternion Slerp(Quaternion a, Quaternion b, float t)
|
||||
{
|
||||
float dt = dot(a.value, b.value);
|
||||
if (dt < 0)
|
||||
{
|
||||
dt = -dt;
|
||||
b.value = -b.value;
|
||||
}
|
||||
|
||||
if (dt < 0.9995f)
|
||||
{
|
||||
float angle = acos(dt);
|
||||
float s = rsqrt(1 - dt * dt);
|
||||
float w1 = sin(angle * (1 - t)) * s;
|
||||
float w2 = sin(angle * t) * s;
|
||||
Quaternion rv;
|
||||
rv.value = a.value * w1 + b.value * w2;
|
||||
return rv;
|
||||
}
|
||||
|
||||
return Nlerp(a, b, t);
|
||||
}
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4e63b0f0c621a4b47ab8c2ddfa5439b5
|
||||
ShaderIncludeImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/Common/Shaders/GPUStructures/Quaternion.hlsl
|
||||
uploadId: 897522
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
#ifndef RIG_BONE_HLSL_
|
||||
#define RIG_BONE_HLSL_
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct RigBone
|
||||
{
|
||||
uint hash;
|
||||
int parentBoneIndex;
|
||||
BoneTransform refPose;
|
||||
int humanBodyPart;
|
||||
|
||||
static const uint size = (1 + 1 + 1) * 4 + BoneTransform::size;
|
||||
|
||||
static RigBone ReadFromRawBuffer(ByteAddressBuffer b, int index)
|
||||
{
|
||||
int byteAddress = index * size;
|
||||
|
||||
RigBone rv = (RigBone)0;
|
||||
rv.hash = b.Load(byteAddress);
|
||||
rv.parentBoneIndex = b.Load(byteAddress + 4);
|
||||
rv.refPose.pos = asfloat(b.Load3(byteAddress + 8));
|
||||
rv.refPose.rot.value = asfloat(b.Load4(byteAddress + 20));
|
||||
rv.refPose.scale = asfloat(b.Load3(byteAddress + 36));
|
||||
rv.humanBodyPart = b.Load(byteAddress + 48);
|
||||
|
||||
CHECK_RAW_BUFFER_OUT_OF_BOUNDS(RUKHANKADEBUGMARKERS_GPUANIMATOR_RIG_BONE_READ, byteAddress, size, b);
|
||||
|
||||
return rv;
|
||||
}
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a4b682d628d7a5f4ab07b948d0360648
|
||||
ShaderIncludeImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/Common/Shaders/GPUStructures/RigBone.hlsl
|
||||
uploadId: 897522
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user