Netcode Bootstrap
This commit is contained in:
+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
|
||||
Reference in New Issue
Block a user