Netcode Bootstrap
This commit is contained in:
+61
@@ -0,0 +1,61 @@
|
||||
|
||||
using Unity.Entities;
|
||||
using Unity.Mathematics;
|
||||
using Unity.Rendering;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
// If present and enabled, then GPU animation engine is used
|
||||
public struct GPUAnimationEngineTag: IComponentData, IEnableableComponent { }
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct GPUAttachmentComponent: IComponentData
|
||||
{
|
||||
public int attachedBoneIndex;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Used to move attachments using GPU computed bone poses in shader
|
||||
[MaterialProperty("_RukhankaGPUBoneIndex")]
|
||||
public struct GPUAttachmentBoneIndexMPComponent: IComponentData
|
||||
{
|
||||
public int boneIndex;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[MaterialProperty("_RukhankaAttachmentToBoneTransform")]
|
||||
public struct GPUAttachmentToBoneTransformMPComponent: IComponentData
|
||||
{
|
||||
public float4x4 value;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[MaterialProperty("_RukhankaAnimatedEntityLocalToWorld")]
|
||||
public struct GPURigEntityLocalToWorldMPComponent: IComponentData
|
||||
{
|
||||
public float4x4 value;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public struct GPURigFrameOffsetsComponent: IComponentData
|
||||
{
|
||||
public int boneIndex;
|
||||
public int rigIndex;
|
||||
public int animationToProcessIndex;
|
||||
|
||||
public void AddOffsets(GPURigFrameOffsetsComponent o)
|
||||
{
|
||||
boneIndex += o.boneIndex;
|
||||
rigIndex += o.rigIndex;
|
||||
animationToProcessIndex += o.animationToProcessIndex;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 041a1589732bfa541891d94c0997f0fa
|
||||
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/GPUAnimationEngine/GPUAnimationComponents.cs
|
||||
uploadId: 897522
|
||||
+255
@@ -0,0 +1,255 @@
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using Unity.Entities;
|
||||
using Unity.Jobs;
|
||||
using Unity.Mathematics;
|
||||
using Unity.Burst;
|
||||
using Unity.Rendering;
|
||||
using Hash128 = Unity.Entities.Hash128;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
|
||||
[UpdateInGroup(typeof(RukhankaDeformationSystemGroup))]
|
||||
[UpdateBefore(typeof(GPUAnimationSystem))]
|
||||
public partial struct GPUAnimationPreparationSystem: ISystem
|
||||
{
|
||||
NativeParallelHashMap<Hash128, BlobAssetReference<AnimationClipBlob>> newAnimationsMap;
|
||||
NativeParallelHashMap<Hash128, BlobAssetReference<RigDefinitionBlob>> newRigsMap;
|
||||
NativeParallelHashMap<Hash128, GPUSkinnedMeshPlacementData> newSkinnedMeshesDataMap;
|
||||
NativeParallelHashMap<Hash128, BlobAssetReference<AvatarMaskBlob>> newAvatarMaskMap;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void OnCreate(ref SystemState ss)
|
||||
{
|
||||
#if !HYBRID_RENDERER_DISABLED
|
||||
if (!EntitiesGraphicsUtils.IsEntitiesGraphicsSupportedOnSystem())
|
||||
#endif
|
||||
{
|
||||
ss.Enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
var runtimeAnimationDataSingleton = GPURuntimeAnimationData.Construct();
|
||||
ss.EntityManager.CreateSingleton(runtimeAnimationDataSingleton, "Rukhanka GPU Animation Data");
|
||||
|
||||
var initialCapacity = 0x1000;
|
||||
newAnimationsMap = new (initialCapacity, Allocator.Persistent);
|
||||
newRigsMap = new (initialCapacity, Allocator.Persistent);
|
||||
newSkinnedMeshesDataMap = new (initialCapacity, Allocator.Persistent);
|
||||
newAvatarMaskMap = new (initialCapacity, Allocator.Persistent);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[BurstCompile]
|
||||
public void OnDestroy(ref SystemState ss)
|
||||
{
|
||||
if (SystemAPI.TryGetSingletonRW<GPURuntimeAnimationData>(out var radRW))
|
||||
{
|
||||
ref var rad = ref radRW.ValueRW;
|
||||
rad.Dispose();
|
||||
ss.EntityManager.DestroyEntity(SystemAPI.GetSingletonEntity<GPURuntimeAnimationData>());
|
||||
}
|
||||
|
||||
newAnimationsMap.Dispose();
|
||||
newRigsMap.Dispose();
|
||||
newSkinnedMeshesDataMap.Dispose();
|
||||
newAvatarMaskMap.Dispose();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[BurstCompile]
|
||||
public void OnUpdate(ref SystemState ss)
|
||||
{
|
||||
ref var runtimeAnimationData = ref SystemAPI.GetSingletonRW<GPURuntimeAnimationData>().ValueRW;
|
||||
|
||||
var resetFrameDataJH = ResetFrameData(ref ss, ref runtimeAnimationData, ss.Dependency);
|
||||
|
||||
// Prepare new resources that appear this frame
|
||||
var newAnimationResourcesJH = GatherNewRigsAndAnimations(ref ss, ref runtimeAnimationData, resetFrameDataJH);
|
||||
var newRigRemapTablesJH = GatherNewRigRemapTables(ref ss, ref runtimeAnimationData, resetFrameDataJH);
|
||||
var newResourcesJH = JobHandle.CombineDependencies(newAnimationResourcesJH, newRigRemapTablesJH);
|
||||
|
||||
// Register new resources in persistent db
|
||||
var registerNewResourcesJH = RegisterNewResources(ref ss, ref runtimeAnimationData, newResourcesJH);
|
||||
|
||||
ss.Dependency = registerNewResourcesJH;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
unsafe JobHandle ResetFrameData(ref SystemState ss, ref GPURuntimeAnimationData rad, JobHandle dependsOn)
|
||||
{
|
||||
var resetFrameDataJob = new ResetFrameDataJob()
|
||||
{
|
||||
newAnimationsMap = newAnimationsMap,
|
||||
newRigsMap = newRigsMap,
|
||||
newAnimationsList = rad.newAnimationsList,
|
||||
newRigsList = rad.newRigsList,
|
||||
newSkinnedMeshDataMap = newSkinnedMeshesDataMap,
|
||||
newSkinnedMeshDataList = rad.newSkinnedMeshesDataList,
|
||||
newAvatarMasksList = rad.newAvatarMasksList,
|
||||
newAvatarMasksMap = newAvatarMaskMap,
|
||||
totalGPURigsCount = (uint2*)UnsafeUtility.AddressOf(ref rad.totalGPURigsCount),
|
||||
totalGPUHumanRotationDataEntriesCount = (uint2*)UnsafeUtility.AddressOf(ref rad.totalGPUHumanRotationDataEntriesCount),
|
||||
totalGPURigBonesCount = (uint2*)UnsafeUtility.AddressOf(ref rad.totalGPURigBonesCount),
|
||||
totalGPUAnimationClipsDataSize = (uint2*)UnsafeUtility.AddressOf(ref rad.totalGPUAnimationClipsSize),
|
||||
totalGPUBoneRemapIndicesCount = (uint2*)UnsafeUtility.AddressOf(ref rad.totalGPUSkinnedMeshBonesCount),
|
||||
totalGPUBoneRemapTablesCount = (uint2*)UnsafeUtility.AddressOf(ref rad.totalGPUSkinnedMeshesCount),
|
||||
totalGPUAvatarMasksDataCount = (uint2*)UnsafeUtility.AddressOf(ref rad.totalGPUAvatarMasksDataCount),
|
||||
};
|
||||
var rv = resetFrameDataJob.Schedule(dependsOn);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
unsafe JobHandle CreateNewRigsData(ref SystemState ss, ref GPURuntimeAnimationData rad, JobHandle dependsOn)
|
||||
{
|
||||
// Cannot process hash map in a parallel manner, so copy data to the list
|
||||
var copyNewRigsToListJob = new CreateNewRigsListJob()
|
||||
{
|
||||
outList = rad.newRigsList,
|
||||
newBlobAssetsMap = newRigsMap
|
||||
};
|
||||
var copyNewRigsToListJH = copyNewRigsToListJob.Schedule(dependsOn);
|
||||
|
||||
// Calculate new data capacity. Increased capacity is needed for GPU buffer resize
|
||||
var calculateNewRigsOffsetsJob = new CalculateNewRigsOffsets()
|
||||
{
|
||||
newRigs = rad.newRigsList,
|
||||
totalGPURigsCount = new UnsafeAtomicCounter32(UnsafeUtility.AddressOf(ref rad.totalGPURigsCount.x)),
|
||||
totalGPURigBonesCount = new UnsafeAtomicCounter32(UnsafeUtility.AddressOf(ref rad.totalGPURigBonesCount.x)),
|
||||
totalGPUHumanRotationDataEntriesCount = new UnsafeAtomicCounter32(UnsafeUtility.AddressOf(ref rad.totalGPUHumanRotationDataEntriesCount.x))
|
||||
};
|
||||
var calculateNewRigsOffsetsJH = calculateNewRigsOffsetsJob.Schedule(rad.newRigsList, 1, copyNewRigsToListJH);
|
||||
|
||||
return calculateNewRigsOffsetsJH;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
unsafe JobHandle CreateNewAvatarMasksData(ref SystemState ss, ref GPURuntimeAnimationData rad, JobHandle dependsOn)
|
||||
{
|
||||
var copyNewAvatarMasksToListJob = new CreateNewAvatarMasksListJob()
|
||||
{
|
||||
outList = rad.newAvatarMasksList,
|
||||
newBlobAssetsMap = newAvatarMaskMap
|
||||
};
|
||||
var copyNewAvatarMasksToListJH = copyNewAvatarMasksToListJob.Schedule(dependsOn);
|
||||
|
||||
// Calculate new data capacity. Increased capacity is needed for GPU buffer resize
|
||||
var calculateNewAvatarMaskOffsetsJob = new CalculateNewAvatarMasksOffsetsJob()
|
||||
{
|
||||
newAvatarMasks = rad.newAvatarMasksList,
|
||||
totalAvatarMasksCounter = new UnsafeAtomicCounter32(UnsafeUtility.AddressOf(ref rad.totalGPUAvatarMasksDataCount.x)),
|
||||
};
|
||||
var calculateNewAvatarMaskOffsetsJH = calculateNewAvatarMaskOffsetsJob.Schedule(rad.newAvatarMasksList, 1, copyNewAvatarMasksToListJH);
|
||||
|
||||
return calculateNewAvatarMaskOffsetsJH;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
unsafe JobHandle CreateNewAnimationsData(ref SystemState ss, ref GPURuntimeAnimationData rad, JobHandle dependsOn)
|
||||
{
|
||||
// Cannot process hash map in a parallel manner, so copy data to the list
|
||||
var copyNewAnimationsToListJob = new CreateNewAnimationsListJob()
|
||||
{
|
||||
outList = rad.newAnimationsList,
|
||||
newBlobAssetsMap = newAnimationsMap
|
||||
};
|
||||
var copyNewAnimationsToListJH = copyNewAnimationsToListJob.Schedule(dependsOn);
|
||||
|
||||
// Calculate new data capacity. Increased capacity is needed for GPU buffer resize
|
||||
var calculateNewAnimationsOffsetsJob = new CalculateNewAnimationOffsets()
|
||||
{
|
||||
newAnimationClips = rad.newAnimationsList,
|
||||
totalAnimationClipsOffsetCounter = new UnsafeAtomicCounter32(UnsafeUtility.AddressOf(ref rad.totalGPUAnimationClipsSize.x)),
|
||||
};
|
||||
var calculateNewAnimationsOffsetsJH = calculateNewAnimationsOffsetsJob.Schedule(rad.newAnimationsList, 1, copyNewAnimationsToListJH);
|
||||
|
||||
return calculateNewAnimationsOffsetsJH;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
JobHandle GatherNewRigsAndAnimations(ref SystemState ss, ref GPURuntimeAnimationData rad, JobHandle dependsOn)
|
||||
{
|
||||
var newFrameResourcesJob = new GatherNewRigsAndAnimationsJob()
|
||||
{
|
||||
existingAnimations = rad.animationClipsMap,
|
||||
existingRigs = rad.rigDefinitionsMap,
|
||||
existingAvatarMasks = rad.avatarMasksDataMap,
|
||||
newFrameAnimations = newAnimationsMap.AsParallelWriter(),
|
||||
newRigDefinitions = newRigsMap.AsParallelWriter(),
|
||||
newFrameAvatarMasks = newAvatarMaskMap.AsParallelWriter()
|
||||
};
|
||||
var newFrameResourcesJH = newFrameResourcesJob.ScheduleParallel(dependsOn);
|
||||
|
||||
var createNewAnimationsJH = CreateNewAnimationsData(ref ss, ref rad, newFrameResourcesJH);
|
||||
var createNewAvatarMasksJH = CreateNewAvatarMasksData(ref ss, ref rad, newFrameResourcesJH);
|
||||
var createNewRigsJH = CreateNewRigsData(ref ss, ref rad, newFrameResourcesJH);
|
||||
|
||||
var rv = JobHandle.CombineDependencies(createNewAnimationsJH, createNewAvatarMasksJH, createNewRigsJH);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
unsafe JobHandle RegisterNewResources(ref SystemState ss, ref GPURuntimeAnimationData rad, JobHandle dependsOn)
|
||||
{
|
||||
var registerNewResourcesJob = new RegisterNewResourcesJob()
|
||||
{
|
||||
newAnimations = rad.newAnimationsList,
|
||||
animationClipsOffsets = rad.animationClipsMap,
|
||||
newRigDefs = rad.newRigsList,
|
||||
rigDefinitionOffsets = rad.rigDefinitionsMap,
|
||||
newSkinnedMeshesDatas = rad.newSkinnedMeshesDataList,
|
||||
skinnedMeshDataOffsets = rad.skinnedMeshesDataMap,
|
||||
newAvatarMasks = rad.newAvatarMasksList,
|
||||
avatarMasksDataOffsets = rad.avatarMasksDataMap,
|
||||
maximumKeyFrameArrayLength = (uint*)UnsafeUtility.AddressOf(ref rad.maxTrackKeyframesCount)
|
||||
};
|
||||
|
||||
var registerNewResourcesJH = registerNewResourcesJob.Schedule(dependsOn);
|
||||
return registerNewResourcesJH;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
unsafe JobHandle GatherNewRigRemapTables(ref SystemState ss, ref GPURuntimeAnimationData rad, JobHandle dependsOn)
|
||||
{
|
||||
var newBoneRemapTablesJob = new NewFrameRigToSkinnedMeshRemapTablesJob()
|
||||
{
|
||||
skinnedMeshesDataMap = rad.skinnedMeshesDataMap,
|
||||
rigDefComponentLookup = SystemAPI.GetComponentLookup<RigDefinitionComponent>(true),
|
||||
gpuAnimationEngineComponentLookup = SystemAPI.GetComponentLookup<GPUAnimationEngineTag>(true),
|
||||
newFrameSkinnedMeshData = newSkinnedMeshesDataMap.AsParallelWriter()
|
||||
};
|
||||
var newBoneRemapTablesJH = newBoneRemapTablesJob.ScheduleParallel(dependsOn);
|
||||
|
||||
var copyNewRemapTablesToListJob = new CreateNewSkinnedMeshesDataListJob()
|
||||
{
|
||||
outList = rad.newSkinnedMeshesDataList,
|
||||
newSkinnedMeshesDataMap = newSkinnedMeshesDataMap
|
||||
};
|
||||
var copyNewRemapTablesToListJH = copyNewRemapTablesToListJob.Schedule(newBoneRemapTablesJH);
|
||||
|
||||
var calculateNewBoneRemapTablesOffsetsJob = new CalculateNewBoneRemapTablesOffsetsJob()
|
||||
{
|
||||
newBoneRemapTables = rad.newSkinnedMeshesDataList,
|
||||
totalGPUBoneRemapIndicesCount = new UnsafeAtomicCounter32(UnsafeUtility.AddressOf(ref rad.totalGPUSkinnedMeshBonesCount)),
|
||||
totalGPUBoneRemapTablesCount = new UnsafeAtomicCounter32(UnsafeUtility.AddressOf(ref rad.totalGPUSkinnedMeshesCount)),
|
||||
};
|
||||
var calculateNewBoneRemapTablesOffsetsJH = calculateNewBoneRemapTablesOffsetsJob.Schedule(rad.newSkinnedMeshesDataList, 1, copyNewRemapTablesToListJH);
|
||||
|
||||
return calculateNewBoneRemapTablesOffsetsJH;
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c706bbc6cab420d45ad653e7118bfdfd
|
||||
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/GPUAnimationEngine/GPUAnimationPreparationSystem.cs
|
||||
uploadId: 897522
|
||||
+456
@@ -0,0 +1,456 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Unity.Assertions;
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using Unity.Entities;
|
||||
using Unity.Jobs;
|
||||
using Unity.Mathematics;
|
||||
using Unity.Rendering;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using Rukhanka.Toolbox;
|
||||
using Unity.Burst;
|
||||
using Hash128 = Unity.Entities.Hash128;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
|
||||
public partial struct GPUAnimationPreparationSystem
|
||||
{
|
||||
[BurstCompile]
|
||||
unsafe struct ResetFrameDataJob: IJob
|
||||
{
|
||||
public NativeParallelHashMap<Hash128, BlobAssetReference<AnimationClipBlob>> newAnimationsMap;
|
||||
public NativeParallelHashMap<Hash128, BlobAssetReference<RigDefinitionBlob>> newRigsMap;
|
||||
public NativeParallelHashMap<Hash128, BlobAssetReference<AvatarMaskBlob>> newAvatarMasksMap;
|
||||
|
||||
public NativeList<GPURigDefinitionPlacementData> newRigsList;
|
||||
public NativeList<GPUAnimationClipPlacementData> newAnimationsList;
|
||||
public NativeList<GPUAvatarMaskPlacementData> newAvatarMasksList;
|
||||
|
||||
public NativeParallelHashMap<Hash128, GPUSkinnedMeshPlacementData> newSkinnedMeshDataMap;
|
||||
public NativeList<GPUSkinnedMeshPlacementData> newSkinnedMeshDataList;
|
||||
|
||||
[NativeDisableUnsafePtrRestriction]
|
||||
public uint2 *totalGPUAnimationClipsDataSize;
|
||||
[NativeDisableUnsafePtrRestriction]
|
||||
public uint2 *totalGPURigsCount;
|
||||
[NativeDisableUnsafePtrRestriction]
|
||||
public uint2 *totalGPURigBonesCount;
|
||||
[NativeDisableUnsafePtrRestriction]
|
||||
public uint2 *totalGPUBoneRemapIndicesCount;
|
||||
[NativeDisableUnsafePtrRestriction]
|
||||
public uint2 *totalGPUBoneRemapTablesCount;
|
||||
[NativeDisableUnsafePtrRestriction]
|
||||
public uint2 *totalGPUHumanRotationDataEntriesCount;
|
||||
[NativeDisableUnsafePtrRestriction]
|
||||
public uint2 *totalGPUAvatarMasksDataCount;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void Execute()
|
||||
{
|
||||
newAnimationsMap.Clear();
|
||||
newRigsMap.Clear();
|
||||
newAvatarMasksMap.Clear();
|
||||
|
||||
newRigsList.Clear();
|
||||
newAnimationsList.Clear();
|
||||
newAvatarMasksList.Clear();
|
||||
|
||||
newSkinnedMeshDataMap.Clear();
|
||||
foreach (var nbrt in newSkinnedMeshDataList)
|
||||
{
|
||||
nbrt.boneRemapTableBlob.Dispose();
|
||||
}
|
||||
newSkinnedMeshDataList.Clear();
|
||||
|
||||
totalGPUAnimationClipsDataSize->y = totalGPUAnimationClipsDataSize->x;
|
||||
totalGPURigsCount->y = totalGPURigsCount->x;
|
||||
totalGPURigBonesCount->y = totalGPURigBonesCount->x;
|
||||
totalGPUBoneRemapIndicesCount->y = totalGPUBoneRemapIndicesCount->x;
|
||||
totalGPUBoneRemapTablesCount->y = totalGPUBoneRemapTablesCount->x;
|
||||
totalGPUHumanRotationDataEntriesCount->y = totalGPUHumanRotationDataEntriesCount->x;
|
||||
totalGPUAvatarMasksDataCount->y = totalGPUAvatarMasksDataCount->x;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------------//
|
||||
|
||||
[BurstCompile]
|
||||
[WithAll(typeof(GPUAnimationEngineTag))]
|
||||
partial struct GatherNewRigsAndAnimationsJob: IJobEntity
|
||||
{
|
||||
[ReadOnly]
|
||||
public NativeParallelHashMap<Hash128, int> existingAnimations;
|
||||
[ReadOnly]
|
||||
public NativeParallelHashMap<Hash128, int> existingRigs;
|
||||
[ReadOnly]
|
||||
public NativeParallelHashMap<Hash128, int> existingAvatarMasks;
|
||||
|
||||
[NativeDisableContainerSafetyRestriction]
|
||||
public NativeParallelHashMap<Hash128, BlobAssetReference<AnimationClipBlob>>.ParallelWriter newFrameAnimations;
|
||||
[NativeDisableContainerSafetyRestriction]
|
||||
public NativeParallelHashMap<Hash128, BlobAssetReference<RigDefinitionBlob>>.ParallelWriter newRigDefinitions;
|
||||
[NativeDisableContainerSafetyRestriction]
|
||||
public NativeParallelHashMap<Hash128, BlobAssetReference<AvatarMaskBlob>>.ParallelWriter newFrameAvatarMasks;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Execute(in RigDefinitionComponent rdc, in DynamicBuffer<AnimationToProcessComponent> atps)
|
||||
{
|
||||
var rigHash = rdc.rigBlob.Value.hash;
|
||||
if (!existingRigs.TryGetValue(rigHash, out _))
|
||||
{
|
||||
newRigDefinitions.TryAdd(rigHash, rdc.rigBlob);
|
||||
}
|
||||
|
||||
for (var i = 0; i < atps.Length; ++i)
|
||||
{
|
||||
var atp = atps[i];
|
||||
if (!atp.animation.IsCreated)
|
||||
continue;
|
||||
|
||||
// Add animation
|
||||
var animationHash = atp.animation.Value.hash;
|
||||
if (!existingAnimations.TryGetValue(animationHash, out _))
|
||||
{
|
||||
newFrameAnimations.TryAdd(animationHash, atp.animation);
|
||||
}
|
||||
|
||||
// Add avatar mask
|
||||
if (atp.avatarMask.IsCreated)
|
||||
{
|
||||
var avatarMaskHash = atp.avatarMask.Value.hash;
|
||||
if (!existingAvatarMasks.TryGetValue(avatarMaskHash, out _))
|
||||
{
|
||||
newFrameAvatarMasks.TryAdd(avatarMaskHash, atp.avatarMask);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------------//
|
||||
|
||||
[BurstCompile]
|
||||
struct CreateNewAnimationsListJob: IJob
|
||||
{
|
||||
[ReadOnly]
|
||||
public NativeParallelHashMap<Hash128, BlobAssetReference<AnimationClipBlob>> newBlobAssetsMap;
|
||||
public NativeList<GPUAnimationClipPlacementData> outList;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void Execute()
|
||||
{
|
||||
outList.Capacity = math.max(newBlobAssetsMap.Count(), outList.Capacity);
|
||||
foreach (var nba in newBlobAssetsMap)
|
||||
{
|
||||
var acp = new GPUAnimationClipPlacementData()
|
||||
{
|
||||
animationClipBlob = nba.Value
|
||||
};
|
||||
outList.Add(acp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------------//
|
||||
|
||||
[BurstCompile]
|
||||
struct CreateNewAvatarMasksListJob: IJob
|
||||
{
|
||||
[ReadOnly]
|
||||
public NativeParallelHashMap<Hash128, BlobAssetReference<AvatarMaskBlob>> newBlobAssetsMap;
|
||||
public NativeList<GPUAvatarMaskPlacementData> outList;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void Execute()
|
||||
{
|
||||
outList.Capacity = math.max(newBlobAssetsMap.Count(), outList.Capacity);
|
||||
foreach (var nba in newBlobAssetsMap)
|
||||
{
|
||||
var acp = new GPUAvatarMaskPlacementData()
|
||||
{
|
||||
avatarMaskBlob = nba.Value
|
||||
};
|
||||
outList.Add(acp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------------//
|
||||
|
||||
[BurstCompile]
|
||||
struct CreateNewRigsListJob: IJob
|
||||
{
|
||||
[ReadOnly]
|
||||
public NativeParallelHashMap<Hash128, BlobAssetReference<RigDefinitionBlob>> newBlobAssetsMap;
|
||||
public NativeList<GPURigDefinitionPlacementData> outList;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void Execute()
|
||||
{
|
||||
outList.Capacity = math.max(newBlobAssetsMap.Count(), outList.Capacity);
|
||||
foreach (var nba in newBlobAssetsMap)
|
||||
{
|
||||
var rdp = new GPURigDefinitionPlacementData()
|
||||
{
|
||||
rigBlob = nba.Value,
|
||||
};
|
||||
outList.Add(rdp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------------//
|
||||
|
||||
[BurstCompile]
|
||||
struct CreateNewSkinnedMeshesDataListJob: IJob
|
||||
{
|
||||
[ReadOnly]
|
||||
public NativeParallelHashMap<Hash128, GPUSkinnedMeshPlacementData> newSkinnedMeshesDataMap;
|
||||
public NativeList<GPUSkinnedMeshPlacementData> outList;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void Execute()
|
||||
{
|
||||
outList.Capacity = math.max(newSkinnedMeshesDataMap.Count(), outList.Capacity);
|
||||
foreach (var nba in newSkinnedMeshesDataMap)
|
||||
{
|
||||
outList.Add(nba.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------------//
|
||||
|
||||
[BurstCompile]
|
||||
partial struct NewFrameRigToSkinnedMeshRemapTablesJob: IJobEntity
|
||||
{
|
||||
[ReadOnly]
|
||||
public NativeParallelHashMap<Hash128, int> skinnedMeshesDataMap;
|
||||
[ReadOnly]
|
||||
public ComponentLookup<GPUAnimationEngineTag> gpuAnimationEngineComponentLookup;
|
||||
[ReadOnly]
|
||||
public ComponentLookup<RigDefinitionComponent> rigDefComponentLookup;
|
||||
|
||||
[WriteOnly, NativeDisableContainerSafetyRestriction]
|
||||
public NativeParallelHashMap<Hash128, GPUSkinnedMeshPlacementData>.ParallelWriter newFrameSkinnedMeshData;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Execute(in SkinnedMeshRendererComponent asmc)
|
||||
{
|
||||
if (!asmc.IsGPUAnimator(gpuAnimationEngineComponentLookup))
|
||||
return;
|
||||
|
||||
rigDefComponentLookup.TryGetComponent(asmc.animatedRigEntity, out var rigDef);
|
||||
var hash = AnimationUtils.CalculateBoneRemapTableHash(asmc.smrInfoBlob, rigDef.rigBlob);
|
||||
|
||||
if (skinnedMeshesDataMap.TryGetValue(hash, out _))
|
||||
return;
|
||||
|
||||
var remapTableBlob = AnimationUtils.MakeSkinnedMeshToRigRemapTable(asmc, rigDef, Allocator.TempJob);
|
||||
var skinnedMeshInfo = new GPUSkinnedMeshPlacementData()
|
||||
{
|
||||
hash = hash,
|
||||
dataOffset = -1,
|
||||
skinnedMeshInfo = asmc.smrInfoBlob,
|
||||
boneRemapTableBlob = remapTableBlob
|
||||
};
|
||||
if (!newFrameSkinnedMeshData.TryAdd(hash, skinnedMeshInfo))
|
||||
remapTableBlob.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------------//
|
||||
|
||||
[BurstCompile]
|
||||
struct CalculateNewAnimationOffsets: IJobParallelForDefer
|
||||
{
|
||||
[NativeDisableParallelForRestriction]
|
||||
public NativeList<GPUAnimationClipPlacementData> newAnimationClips;
|
||||
|
||||
[NativeDisableUnsafePtrRestriction]
|
||||
public UnsafeAtomicCounter32 totalAnimationClipsOffsetCounter;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void Execute(int index)
|
||||
{
|
||||
ref var ac = ref newAnimationClips.ElementAt(index);
|
||||
ref var acb = ref ac.animationClipBlob.Value;
|
||||
var clipSize = CalculateGPUAnimationClipPlacementData(ref acb, ref ac);
|
||||
ac.animationClipDataOffset = totalAnimationClipsOffsetCounter.Add(clipSize);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int CalculateGPUAnimationClipPlacementData(ref AnimationClipBlob acb, ref GPUAnimationClipPlacementData acpd)
|
||||
{
|
||||
var totalBytes = 0;
|
||||
totalBytes += UnsafeUtility.SizeOf<GPUStructures.AnimationClip>();
|
||||
totalBytes = CalculateGPUTrackSetSize(totalBytes, ref acb.clipTracks, ref acpd.clipTracks);
|
||||
totalBytes = CalculateGPUTrackSetSize(totalBytes, ref acb.additiveReferencePoseFrame, ref acpd.additiveRefPoseTracks);
|
||||
return totalBytes;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int CalculateGPUTrackSetSize(int currentOffset, ref TrackSet ts, ref GPUTrackSetPlacementData tpd)
|
||||
{
|
||||
var rv = currentOffset;
|
||||
if (ts.keyframes.Length == 0)
|
||||
{
|
||||
tpd = GPUTrackSetPlacementData.Empty();
|
||||
return rv;
|
||||
}
|
||||
|
||||
tpd.keyFramesOffset = rv;
|
||||
rv += ts.keyframes.Length * UnsafeUtility.SizeOf<GPUStructures.KeyFrame>();
|
||||
|
||||
tpd.tracksOffset = rv;
|
||||
rv += ts.tracks.Length * UnsafeUtility.SizeOf<GPUStructures.Track>();
|
||||
|
||||
tpd.trackGroupsOffset = rv;
|
||||
rv += ts.trackGroups.Length * UnsafeUtility.SizeOf<int>();
|
||||
|
||||
tpd.hashTableOffset = rv;
|
||||
rv += ts.trackGroupPHT.pht.Length * UnsafeUtility.SizeOf<int2>();
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------------//
|
||||
|
||||
[BurstCompile]
|
||||
struct CalculateNewAvatarMasksOffsetsJob: IJobParallelForDefer
|
||||
{
|
||||
[NativeDisableParallelForRestriction]
|
||||
public NativeList<GPUAvatarMaskPlacementData> newAvatarMasks;
|
||||
|
||||
[NativeDisableUnsafePtrRestriction]
|
||||
public UnsafeAtomicCounter32 totalAvatarMasksCounter;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void Execute(int index)
|
||||
{
|
||||
ref var am = ref newAvatarMasks.ElementAt(index);
|
||||
ref var amb = ref am.avatarMaskBlob.Value;
|
||||
// Plus one because we need space for human body parts mask
|
||||
am.dataOffset = totalAvatarMasksCounter.Add(amb.includedBoneMask.Length + 1);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------------//
|
||||
|
||||
[BurstCompile]
|
||||
struct CalculateNewRigsOffsets: IJobParallelForDefer
|
||||
{
|
||||
[NativeDisableParallelForRestriction]
|
||||
public NativeList<GPURigDefinitionPlacementData> newRigs;
|
||||
|
||||
[NativeDisableUnsafePtrRestriction]
|
||||
public UnsafeAtomicCounter32 totalGPURigsCount;
|
||||
[NativeDisableUnsafePtrRestriction]
|
||||
public UnsafeAtomicCounter32 totalGPURigBonesCount;
|
||||
[NativeDisableUnsafePtrRestriction]
|
||||
public UnsafeAtomicCounter32 totalGPUHumanRotationDataEntriesCount;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void Execute(int index)
|
||||
{
|
||||
ref var rig = ref newRigs.ElementAt(index);
|
||||
rig.rigDefinitionIndex = totalGPURigsCount.Add(1);
|
||||
ref var rb = ref rig.rigBlob.Value;
|
||||
rig.rigBonesOffset = totalGPURigBonesCount.Add(rb.bones.Length);
|
||||
rig.humanRotationDataOffset = -1;
|
||||
if (rb.humanData.IsValid)
|
||||
rig.humanRotationDataOffset = totalGPUHumanRotationDataEntriesCount.Add(rb.humanData.Value.humanRotData.Length);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------------//
|
||||
|
||||
[BurstCompile]
|
||||
struct CalculateNewBoneRemapTablesOffsetsJob: IJobParallelForDefer
|
||||
{
|
||||
[NativeDisableParallelForRestriction]
|
||||
public NativeList<GPUSkinnedMeshPlacementData> newBoneRemapTables;
|
||||
|
||||
[NativeDisableUnsafePtrRestriction]
|
||||
public UnsafeAtomicCounter32 totalGPUBoneRemapTablesCount;
|
||||
[NativeDisableUnsafePtrRestriction]
|
||||
public UnsafeAtomicCounter32 totalGPUBoneRemapIndicesCount;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void Execute(int index)
|
||||
{
|
||||
ref var rig = ref newBoneRemapTables.ElementAt(index);
|
||||
rig.dataOffset = totalGPUBoneRemapIndicesCount.Add(rig.boneRemapTableBlob.Value.remapIndices.Length);
|
||||
totalGPUBoneRemapTablesCount.Add(1);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------------//
|
||||
|
||||
[BurstCompile]
|
||||
unsafe struct RegisterNewResourcesJob: IJob
|
||||
{
|
||||
[ReadOnly]
|
||||
public NativeList<GPUAnimationClipPlacementData> newAnimations;
|
||||
[ReadOnly]
|
||||
public NativeList<GPURigDefinitionPlacementData> newRigDefs;
|
||||
[ReadOnly]
|
||||
public NativeList<GPUSkinnedMeshPlacementData> newSkinnedMeshesDatas;
|
||||
[ReadOnly]
|
||||
public NativeList<GPUAvatarMaskPlacementData> newAvatarMasks;
|
||||
|
||||
public NativeParallelHashMap<Hash128, int> animationClipsOffsets;
|
||||
public NativeParallelHashMap<Hash128, int> rigDefinitionOffsets;
|
||||
public NativeParallelHashMap<Hash128, int> skinnedMeshDataOffsets;
|
||||
public NativeParallelHashMap<Hash128, int> avatarMasksDataOffsets;
|
||||
|
||||
[NativeDisableUnsafePtrRestriction]
|
||||
public uint *maximumKeyFrameArrayLength;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void Execute()
|
||||
{
|
||||
foreach (var a in newAnimations)
|
||||
{
|
||||
animationClipsOffsets.Add(a.animationClipBlob.Value.hash, a.animationClipDataOffset);
|
||||
*maximumKeyFrameArrayLength = math.max(*maximumKeyFrameArrayLength, a.animationClipBlob.Value.maxTrackKeyframeLength);
|
||||
}
|
||||
|
||||
foreach (var a in newRigDefs)
|
||||
{
|
||||
rigDefinitionOffsets.Add(a.rigBlob.Value.hash, a.rigDefinitionIndex);
|
||||
}
|
||||
|
||||
foreach (var a in newSkinnedMeshesDatas)
|
||||
{
|
||||
skinnedMeshDataOffsets.Add(a.hash, a.dataOffset);
|
||||
}
|
||||
|
||||
foreach (var a in newAvatarMasks)
|
||||
{
|
||||
avatarMasksDataOffsets.Add(a.avatarMaskBlob.Value.hash, a.dataOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 936431d056ec6a84a9b63f42a8f8e087
|
||||
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/GPUAnimationEngine/GPUAnimationPreparationSystem_Jobs.cs
|
||||
uploadId: 897522
|
||||
+580
@@ -0,0 +1,580 @@
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using Unity.Entities;
|
||||
using Unity.Jobs;
|
||||
using UnityEngine;
|
||||
using Rukhanka.Toolbox;
|
||||
using Unity.Assertions;
|
||||
using Unity.Rendering;
|
||||
using Unity.Transforms;
|
||||
using Hash128 = Unity.Entities.Hash128;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
|
||||
[UpdateInGroup(typeof(RukhankaDeformationSystemGroup))]
|
||||
[UpdateAfter(typeof(SkinnedMeshPreparationSystem))]
|
||||
[UpdateBefore(typeof(MeshDeformationSystem))]
|
||||
public partial class GPUAnimationSystem: SystemBase
|
||||
{
|
||||
ComputeShader animationEngineCS;
|
||||
// Buffers for persistent GPU data. Equivalent for blob asset store
|
||||
GraphicsBuffer animationClipsGB;
|
||||
GraphicsBuffer avatarMasksGB;
|
||||
GraphicsBuffer rigDefinitionGB;
|
||||
GraphicsBuffer rigHumanAvatarRotationDataGB;
|
||||
GraphicsBuffer rigBonesGB;
|
||||
GraphicsBuffer skinnedMeshBoneDataGB;
|
||||
|
||||
SparseUploaderPool sparseUploaderPool;
|
||||
|
||||
// Buffers for per frame animation data (workload, animation to process). Equivalent for animation components
|
||||
FrameFencedGPUBufferPool<GPUStructures.AnimationToProcess> frameAnimationToProcessGB;
|
||||
FrameFencedGPUBufferPool<GPUStructures.AnimatedBoneWorkload> framePerBoneAnimationWorkloadGB;
|
||||
FrameFencedGPUBufferPool<GPUStructures.AnimationJob> frameRigAnimationJobsGB;
|
||||
FrameFencedGPUBufferPool<GPUStructures.SkinnedMeshWorkload> frameSkinMatrixWorkloadGB;
|
||||
|
||||
// Buffers filled by compute shaders
|
||||
GraphicsBuffer animatedBonesGB;
|
||||
GraphicsBuffer rigSpaceAnimatedBonesGB;
|
||||
|
||||
ComputeKernel processAnimationKernel;
|
||||
ComputeKernel makeRigSpaceBoneTransformsKernel;
|
||||
ComputeKernel makeSkinMatricesKernel;
|
||||
ComputeKernel copyBufferKernel;
|
||||
ComputeKernel copyMeshDataKernel;
|
||||
|
||||
NativeParallelHashMap<Hash128, BlobAssetReference<BoneRemapTableBlob>> skinnedMeshToRigBoneRemapTables;
|
||||
EntityQuery gpuAnimatedRigQuery, gpuAnimatedRigNoChunkComponentsQuery, smrQuery;
|
||||
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
BoneVisualizationSystem boneVisualizationSystem;
|
||||
#endif
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
protected override void OnCreate()
|
||||
{
|
||||
if (!SystemInfo.supportsComputeShaders)
|
||||
{
|
||||
Debug.LogWarning("Compute shaders is not supported on current platform. GPU animation engine is disabled.");
|
||||
Enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
#if !HYBRID_RENDERER_DISABLED
|
||||
if (!EntitiesGraphicsUtils.IsEntitiesGraphicsSupportedOnSystem())
|
||||
#endif
|
||||
{
|
||||
Enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
skinnedMeshToRigBoneRemapTables = new (0xff, Allocator.Persistent);
|
||||
|
||||
CreateComputeBuffers();
|
||||
|
||||
animationEngineCS = Resources.Load<ComputeShader>("GPUAnimationEngine");
|
||||
processAnimationKernel = new ComputeKernel(animationEngineCS, "ProcessAnimations");
|
||||
makeRigSpaceBoneTransformsKernel = new ComputeKernel(animationEngineCS, "MakeRigSpaceBoneTransforms");
|
||||
makeSkinMatricesKernel = new ComputeKernel(animationEngineCS, "ComputeSkinMatrices");
|
||||
|
||||
gpuAnimatedRigQuery = new EntityQueryBuilder(Allocator.Temp)
|
||||
.WithAll<RigDefinitionComponent, AnimationToProcessComponent, GPUAnimationEngineTag>()
|
||||
.WithAllChunkComponent<GPURigFrameOffsetsComponent>()
|
||||
.WithNone<CullAnimationsTag>()
|
||||
.Build(EntityManager);
|
||||
|
||||
gpuAnimatedRigNoChunkComponentsQuery = new EntityQueryBuilder(Allocator.Temp)
|
||||
.WithAll<RigDefinitionComponent, AnimationToProcessComponent, GPUAnimationEngineTag>()
|
||||
.WithNoneChunkComponent<GPURigFrameOffsetsComponent>()
|
||||
.WithNone<CullAnimationsTag>()
|
||||
.Build(EntityManager);
|
||||
|
||||
smrQuery = new EntityQueryBuilder(Allocator.Temp)
|
||||
.WithAll<SkinnedMeshRendererComponent, LocalToWorld>()
|
||||
.Build(EntityManager);
|
||||
|
||||
Assert.IsTrue(UnsafeUtility.SizeOf<GPUStructures.KeyFrame>() == UnsafeUtility.SizeOf<KeyFrame>());
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
protected override void OnUpdate()
|
||||
{
|
||||
ref var runtimeAnimationData = ref SystemAPI.GetSingletonRW<GPURuntimeAnimationData>().ValueRW;
|
||||
|
||||
EntityManager.AddChunkComponentData<GPURigFrameOffsetsComponent>(gpuAnimatedRigNoChunkComponentsQuery, default);
|
||||
|
||||
PrepareFrameAnimationData(ref runtimeAnimationData, Dependency);
|
||||
DispatchAnimationCalculation(ref runtimeAnimationData);
|
||||
SetBuffersForBoneRenderer(ref runtimeAnimationData);
|
||||
GPUBuffersEndFrame();
|
||||
sparseUploaderPool.FrameCleanup();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
protected override void OnDestroy()
|
||||
{
|
||||
animationClipsGB?.Release();
|
||||
avatarMasksGB?.Release();
|
||||
rigDefinitionGB?.Release();
|
||||
rigHumanAvatarRotationDataGB?.Release();
|
||||
rigBonesGB?.Release();
|
||||
skinnedMeshBoneDataGB?.Release();
|
||||
|
||||
sparseUploaderPool?.Dispose();
|
||||
|
||||
frameAnimationToProcessGB?.Dispose();
|
||||
framePerBoneAnimationWorkloadGB?.Dispose();
|
||||
frameRigAnimationJobsGB?.Dispose();
|
||||
frameSkinMatrixWorkloadGB?.Dispose();
|
||||
|
||||
animatedBonesGB?.Dispose();
|
||||
rigSpaceAnimatedBonesGB?.Dispose();
|
||||
|
||||
if (skinnedMeshToRigBoneRemapTables.IsCreated)
|
||||
{
|
||||
foreach (var remapTable in skinnedMeshToRigBoneRemapTables)
|
||||
{
|
||||
remapTable.Value.Dispose();
|
||||
}
|
||||
}
|
||||
skinnedMeshToRigBoneRemapTables.Dispose();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void CreateComputeBuffers()
|
||||
{
|
||||
var bufferInitialCapacity = 0x4;
|
||||
frameAnimationToProcessGB = new (bufferInitialCapacity, GraphicsBuffer.Target.Structured, GraphicsBuffer.UsageFlags.LockBufferForWrite);
|
||||
framePerBoneAnimationWorkloadGB = new (bufferInitialCapacity, GraphicsBuffer.Target.Structured, GraphicsBuffer.UsageFlags.LockBufferForWrite);
|
||||
frameRigAnimationJobsGB = new (bufferInitialCapacity, GraphicsBuffer.Target.Structured, GraphicsBuffer.UsageFlags.LockBufferForWrite);
|
||||
frameSkinMatrixWorkloadGB = new (bufferInitialCapacity, GraphicsBuffer.Target.Structured, GraphicsBuffer.UsageFlags.LockBufferForWrite);
|
||||
|
||||
animatedBonesGB = new (GraphicsBuffer.Target.Structured, GraphicsBuffer.UsageFlags.None, bufferInitialCapacity, UnsafeUtility.SizeOf<GPUStructures.BoneTransform>());
|
||||
rigSpaceAnimatedBonesGB = new (GraphicsBuffer.Target.Structured, GraphicsBuffer.UsageFlags.None, bufferInitialCapacity, UnsafeUtility.SizeOf<GPUStructures.BoneTransform>());
|
||||
|
||||
// Persistent data buffers
|
||||
var persistentBuffersInitialCapacity = 0x4;
|
||||
animationClipsGB = new GraphicsBuffer(GraphicsBuffer.Target.Raw, GraphicsBuffer.UsageFlags.None, persistentBuffersInitialCapacity, UnsafeUtility.SizeOf<uint>());
|
||||
avatarMasksGB = new GraphicsBuffer(GraphicsBuffer.Target.Raw, GraphicsBuffer.UsageFlags.None, persistentBuffersInitialCapacity, UnsafeUtility.SizeOf<uint>());
|
||||
rigDefinitionGB = new GraphicsBuffer(GraphicsBuffer.Target.Raw, GraphicsBuffer.UsageFlags.None, persistentBuffersInitialCapacity, UnsafeUtility.SizeOf<GPUStructures.RigDefinition>());
|
||||
rigHumanAvatarRotationDataGB = new GraphicsBuffer(GraphicsBuffer.Target.Raw, GraphicsBuffer.UsageFlags.None, persistentBuffersInitialCapacity, UnsafeUtility.SizeOf<GPUStructures.HumanRotationData>());
|
||||
rigBonesGB = new GraphicsBuffer(GraphicsBuffer.Target.Raw, GraphicsBuffer.UsageFlags.None, persistentBuffersInitialCapacity, UnsafeUtility.SizeOf<GPUStructures.RigBone>());
|
||||
skinnedMeshBoneDataGB = new GraphicsBuffer(GraphicsBuffer.Target.Raw, GraphicsBuffer.UsageFlags.None, persistentBuffersInitialCapacity, UnsafeUtility.SizeOf<GPUStructures.SkinnedMeshBoneData>());
|
||||
|
||||
sparseUploaderPool = new SparseUploaderPool();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
unsafe JobHandle CalculateFrameWorksetData(ref GPURuntimeAnimationData rad, JobHandle dependsOn)
|
||||
{
|
||||
// All following jobs should be sequential. Interlocked math for a few counters are significantly slower than
|
||||
// sequential execution
|
||||
|
||||
var gpuRigFrameOffsetsTypeHandle = SystemAPI.GetComponentTypeHandle<GPURigFrameOffsetsComponent>();
|
||||
var rigDefTypeHandle = SystemAPI.GetComponentTypeHandle<RigDefinitionComponent>(true);
|
||||
var atpBufHandle = SystemAPI.GetBufferTypeHandle<AnimationToProcessComponent>(true);
|
||||
|
||||
// Workload sizes for animation calculation per chunk
|
||||
var computeRigsWorkloadSizesJob = new ComputeFrameRigWorkloadSizesPerChunkJob()
|
||||
{
|
||||
frameOffsetsTypeHandle = gpuRigFrameOffsetsTypeHandle,
|
||||
atpBufTypeHandle = atpBufHandle,
|
||||
rigDefComponentTypeHandle = rigDefTypeHandle,
|
||||
};
|
||||
var computeRigsWorkloadSizesJH = computeRigsWorkloadSizesJob.ScheduleParallel(gpuAnimatedRigQuery, dependsOn);
|
||||
|
||||
// Make absolute chunk offsets
|
||||
var computeAbsChunkOffsetsJob = new ComputeFrameRigWorkloadSizesAbsChunkOffsetsJob()
|
||||
{
|
||||
frameAnimatedBonesCounter = (uint*)UnsafeUtility.AddressOf(ref rad.frameAnimatedBonesCounter),
|
||||
frameAnimatedRigsCounter = (uint*)UnsafeUtility.AddressOf(ref rad.frameAnimatedRigsCounter),
|
||||
frameAnimationToProcessCounter = (uint*)UnsafeUtility.AddressOf(ref rad.frameAnimationToProcessCounter),
|
||||
gpuRigChunkDataTypeHandle = gpuRigFrameOffsetsTypeHandle
|
||||
};
|
||||
var computeAbsChunkOffsetsJH = computeAbsChunkOffsetsJob.Schedule(gpuAnimatedRigQuery, computeRigsWorkloadSizesJH);
|
||||
|
||||
// Workload sizes for skin matrices calculation
|
||||
// To avoid InterlockedAdd (it is slow when invocation count is really big), and single thread job I use 2-step
|
||||
// process here: first job increments per-thread counters, and another single thread job do final accumulate.
|
||||
var computeSkinnedMeshCountJob = new ComputeFrameSkinnedMeshWorkloadSizesJob()
|
||||
{
|
||||
frameSkinnedMeshesPerThreadCounters = rad.frameSkinnedMeshesPerThreadCounters,
|
||||
gpuAnimationEngineTagLookup = SystemAPI.GetComponentLookup<GPUAnimationEngineTag>(true)
|
||||
};
|
||||
var computeSkinnedMeshCountTotalJob = new ComputeFrameSkinnedMeshWorkloadSizesTotalJob()
|
||||
{
|
||||
frameSkinnedMeshesPerThreadCounters = rad.frameSkinnedMeshesPerThreadCounters,
|
||||
frameSkinnedMeshesCounter = (uint*)UnsafeUtility.AddressOf(ref rad.frameSkinnedMeshesCounter)
|
||||
};
|
||||
|
||||
var computeSkinnedMeshCountJH = computeSkinnedMeshCountJob.ScheduleParallel(dependsOn);
|
||||
var computeSkinnedMeshCountTotalJH = computeSkinnedMeshCountTotalJob.Schedule(computeSkinnedMeshCountJH);
|
||||
|
||||
var rv = JobHandle.CombineDependencies(computeAbsChunkOffsetsJH, computeSkinnedMeshCountTotalJH);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void GPUBuffersBeginFrame()
|
||||
{
|
||||
frameAnimationToProcessGB.BeginFrame();
|
||||
framePerBoneAnimationWorkloadGB.BeginFrame();
|
||||
frameRigAnimationJobsGB.BeginFrame();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void GPUBuffersEndFrame()
|
||||
{
|
||||
frameAnimationToProcessGB.EndFrame();
|
||||
framePerBoneAnimationWorkloadGB.EndFrame();
|
||||
frameRigAnimationJobsGB.EndFrame();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
unsafe void FillFrameSkinMatrixCalculationWorkloadGPUBuffers
|
||||
(
|
||||
ref GPURuntimeAnimationData rad,
|
||||
in NativeParallelHashMap<Entity, SkinnedMeshRendererFrameDeformationData> entityToSMRFrameDataMap
|
||||
)
|
||||
{
|
||||
var frameSkinMatrixWorkloadBuf = frameSkinMatrixWorkloadGB.LockBufferForWrite(0, (int)rad.frameSkinnedMeshesCounter);
|
||||
rad.frameSkinnedMeshesCounter = 0;
|
||||
var frameSkinnedMeshesAtomicCounter = new UnsafeAtomicCounter32(UnsafeUtility.AddressOf(ref rad.frameSkinnedMeshesCounter));
|
||||
|
||||
var fillFrameSkinMatrixWorkloadBuffersJob = new FillFrameSkinMatrixWorkloadBuffersJob()
|
||||
{
|
||||
frameSkinnedMeshesAtomicCounter = frameSkinnedMeshesAtomicCounter,
|
||||
frameSkinMatrixWorkloadBuf = frameSkinMatrixWorkloadBuf,
|
||||
skinnedMeshDataMap = rad.skinnedMeshesDataMap,
|
||||
rigDefLookup = SystemAPI.GetComponentLookup<RigDefinitionComponent>(true),
|
||||
gpuAnimationEngineTagLookup = SystemAPI.GetComponentLookup<GPUAnimationEngineTag>(true),
|
||||
localToWorldLookup = SystemAPI.GetComponentLookup<LocalToWorld>(true),
|
||||
entityToSMRFrameDataMap = entityToSMRFrameDataMap,
|
||||
gpuRigChunkDataTypeHandle = SystemAPI.GetComponentTypeHandle<GPURigFrameOffsetsComponent>(true),
|
||||
smrTypeHandle = SystemAPI.GetComponentTypeHandle<SkinnedMeshRendererComponent>(true),
|
||||
l2wTypeHandle = SystemAPI.GetComponentTypeHandle<LocalToWorld>(true),
|
||||
entityTypeHandle = SystemAPI.GetEntityTypeHandle(),
|
||||
frameOffsetsLookup = SystemAPI.GetComponentLookup<GPURigFrameOffsetsComponent>(true)
|
||||
};
|
||||
fillFrameSkinMatrixWorkloadBuffersJob.ScheduleParallel(smrQuery, default).Complete();
|
||||
|
||||
frameSkinMatrixWorkloadGB.UnlockBufferAfterWrite((int)rad.frameSkinnedMeshesCounter);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void FillFrameAnimationCalculationWorkloadGPUBuffers(ref GPURuntimeAnimationData rad)
|
||||
{
|
||||
var animatedBonesWorkloadBuf = framePerBoneAnimationWorkloadGB.LockBufferForWrite(0, (int)rad.frameAnimatedBonesCounter);
|
||||
var animationToProcessBuf = frameAnimationToProcessGB.LockBufferForWrite(0, (int)rad.frameAnimationToProcessCounter);
|
||||
var rigAnimationJobsBuf = frameRigAnimationJobsGB.LockBufferForWrite(0, (int)rad.frameAnimatedRigsCounter);
|
||||
|
||||
// Fill GPU buffer data for current frame animation calculation
|
||||
var fillFrameAnimatedRigWorkloadDataJob = new FillFrameAnimatedRigWorkloadBuffersJob()
|
||||
{
|
||||
animatedBonesWorkloadBuf = animatedBonesWorkloadBuf,
|
||||
animationToProcessBuf = animationToProcessBuf,
|
||||
frameRigAnimationJobs = rigAnimationJobsBuf,
|
||||
animationClipsOffsets = rad.animationClipsMap,
|
||||
rigDefinitionOffsets = rad.rigDefinitionsMap,
|
||||
avatarMasksOffsets = rad.avatarMasksDataMap,
|
||||
frameOffsetsTypeHandle = SystemAPI.GetComponentTypeHandle<GPURigFrameOffsetsComponent>(true),
|
||||
atpBufTypeHandle = SystemAPI.GetBufferTypeHandle<AnimationToProcessComponent>(true),
|
||||
rigDefComponentTypeHandle = SystemAPI.GetComponentTypeHandle<RigDefinitionComponent>(true),
|
||||
entityTypeHandle = SystemAPI.GetEntityTypeHandle()
|
||||
};
|
||||
fillFrameAnimatedRigWorkloadDataJob.ScheduleParallel(gpuAnimatedRigQuery, new JobHandle()).Complete();
|
||||
|
||||
framePerBoneAnimationWorkloadGB.UnlockBufferAfterWrite((int)rad.frameAnimatedBonesCounter);
|
||||
frameAnimationToProcessGB.UnlockBufferAfterWrite((int)rad.frameAnimationToProcessCounter);
|
||||
frameRigAnimationJobsGB.UnlockBufferAfterWrite((int)rad.frameAnimatedRigsCounter);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
unsafe JobHandle ResetFrameData(ref GPURuntimeAnimationData rad, JobHandle dependsOn)
|
||||
{
|
||||
var resetFrameDataJob = new ResetFrameDataJob()
|
||||
{
|
||||
frameAnimatedBonesCounter = new UnsafeAtomicCounter32(UnsafeUtility.AddressOf(ref rad.frameAnimatedBonesCounter)),
|
||||
frameAnimatedRigsCounter = new UnsafeAtomicCounter32(UnsafeUtility.AddressOf(ref rad.frameAnimatedRigsCounter)),
|
||||
frameSkinnedMeshesCounter = new UnsafeAtomicCounter32(UnsafeUtility.AddressOf(ref rad.frameSkinnedMeshesCounter)),
|
||||
frameAnimationToProcessCounter = new UnsafeAtomicCounter32(UnsafeUtility.AddressOf(ref rad.frameAnimationToProcessCounter)),
|
||||
frameSkinnedMeshesPerThreadCounters = rad.frameSkinnedMeshesPerThreadCounters
|
||||
};
|
||||
var rv = resetFrameDataJob.Schedule(dependsOn);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void PrepareFrameAnimationData(ref GPURuntimeAnimationData runtimeAnimationData, JobHandle dependsOn)
|
||||
{
|
||||
var resetFrameDataJH = ResetFrameData(ref runtimeAnimationData, dependsOn);
|
||||
var calculateFrameWorksetDataJH = CalculateFrameWorksetData(ref runtimeAnimationData, resetFrameDataJH);
|
||||
|
||||
// Complete dependency because need to resize GPU buffers with actualized frame counts
|
||||
calculateFrameWorksetDataJH.Complete();
|
||||
|
||||
ResizeGPUBuffers(ref runtimeAnimationData);
|
||||
CopyPersistentAnimationDataToGPUBuffers(ref runtimeAnimationData);
|
||||
GPUBuffersBeginFrame();
|
||||
FillFrameAnimationCalculationWorkloadGPUBuffers(ref runtimeAnimationData);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void CopyPersistentAnimationDataToGPUBuffers(ref GPURuntimeAnimationData rad)
|
||||
{
|
||||
CopyNewAnimationsToGPUBuffers(ref rad);
|
||||
CopyNewRigsToGPUBuffer(ref rad);
|
||||
CopyNewBoneRemapTablesToGPUBuffers(ref rad);
|
||||
CopyNewAvatarMasksToGPUBuffer(ref rad);
|
||||
//DebugBufRead(ref rad);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
GraphicsBuffer GrowPersistentDataComputeBuffer(GraphicsBuffer gb, uint newElementCount)
|
||||
{
|
||||
if (gb == null || gb.count >= newElementCount)
|
||||
return gb;
|
||||
|
||||
var newBuf = new GraphicsBuffer(gb.target, gb.usageFlags, (int)newElementCount, gb.stride);
|
||||
ComputeBufferTools.Copy(gb, newBuf, 0, 0, (uint)(newElementCount * gb.stride));
|
||||
gb.Dispose();
|
||||
return newBuf;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void ResizeGPUBuffers(ref GPURuntimeAnimationData rad)
|
||||
{
|
||||
// Persistent data buffers
|
||||
animationClipsGB = GrowPersistentDataComputeBuffer(animationClipsGB, rad.totalGPUAnimationClipsSize.x / 4);
|
||||
avatarMasksGB = GrowPersistentDataComputeBuffer(avatarMasksGB, rad.totalGPUAvatarMasksDataCount.x);
|
||||
rigDefinitionGB = GrowPersistentDataComputeBuffer(rigDefinitionGB, rad.totalGPURigsCount.x);
|
||||
rigHumanAvatarRotationDataGB = GrowPersistentDataComputeBuffer(rigHumanAvatarRotationDataGB, rad.totalGPUHumanRotationDataEntriesCount.x);
|
||||
rigBonesGB = GrowPersistentDataComputeBuffer(rigBonesGB, rad.totalGPURigBonesCount.x);
|
||||
skinnedMeshBoneDataGB = GrowPersistentDataComputeBuffer(skinnedMeshBoneDataGB, rad.totalGPUSkinnedMeshBonesCount.x);
|
||||
|
||||
// Everyframe updated buffers
|
||||
frameAnimationToProcessGB.Grow((int)rad.frameAnimationToProcessCounter);
|
||||
framePerBoneAnimationWorkloadGB.Grow((int)rad.frameAnimatedBonesCounter);
|
||||
frameRigAnimationJobsGB.Grow((int)rad.frameAnimatedRigsCounter);
|
||||
frameSkinMatrixWorkloadGB.Grow((int)rad.frameSkinnedMeshesCounter);
|
||||
|
||||
animatedBonesGB = ComputeBufferTools.GrowNoCopy(animatedBonesGB, (int)rad.frameAnimatedBonesCounter);
|
||||
rigSpaceAnimatedBonesGB = ComputeBufferTools.GrowNoCopy(rigSpaceAnimatedBonesGB, (int)rad.frameAnimatedBonesCounter);
|
||||
Shader.SetGlobalBuffer(ShaderID_rigSpaceBoneTransformsBuf, rigSpaceAnimatedBonesGB);
|
||||
Shader.SetGlobalBuffer(ShaderID_boneLocalTransforms, animatedBonesGB);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void CopyNewBoneRemapTablesToGPUBuffers(ref GPURuntimeAnimationData rad)
|
||||
{
|
||||
if (rad.newSkinnedMeshesDataList.IsEmpty)
|
||||
return;
|
||||
|
||||
var newSkinnedMeshBonesCount = (int)(rad.totalGPUSkinnedMeshBonesCount.x - rad.totalGPUSkinnedMeshBonesCount.y);
|
||||
|
||||
var skinnedMeshBonesUploader = sparseUploaderPool.GetUploader(skinnedMeshBoneDataGB);
|
||||
var skinnedMeshBonesUploadSizeInBytes = newSkinnedMeshBonesCount * UnsafeUtility.SizeOf<GPUStructures.SkinnedMeshBoneData>();
|
||||
var skinnedMeshBonesThreadedUploader = skinnedMeshBonesUploader.Begin(skinnedMeshBonesUploadSizeInBytes, skinnedMeshBonesUploadSizeInBytes, newSkinnedMeshBonesCount);
|
||||
|
||||
var copyNewBoneRemapTables = new CopyNewSkinnedMeshesDataJob()
|
||||
{
|
||||
newSkinnedMeshData = rad.newSkinnedMeshesDataList,
|
||||
gpuSkinnedMeshBonesData = skinnedMeshBonesThreadedUploader,
|
||||
};
|
||||
|
||||
copyNewBoneRemapTables.ScheduleParallel(rad.newSkinnedMeshesDataList.Length, 1, default).Complete();
|
||||
|
||||
skinnedMeshBonesUploader.EndAndCommit(skinnedMeshBonesThreadedUploader);
|
||||
sparseUploaderPool.PutUploader(skinnedMeshBonesUploader);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void CopyNewAvatarMasksToGPUBuffer(ref GPURuntimeAnimationData rad)
|
||||
{
|
||||
if (rad.newAvatarMasksList.IsEmpty)
|
||||
return;
|
||||
|
||||
var newAvatarMasksEntriesCount = (int)(rad.totalGPUAvatarMasksDataCount.x - rad.totalGPUAvatarMasksDataCount.y);
|
||||
|
||||
var avatarMaskUploader = sparseUploaderPool.GetUploader(avatarMasksGB);
|
||||
var avatarMaskUploadSizeInBytes = newAvatarMasksEntriesCount * UnsafeUtility.SizeOf<uint>();
|
||||
var avatarMaskThreadedUploader = avatarMaskUploader.Begin(avatarMaskUploadSizeInBytes, avatarMaskUploadSizeInBytes, newAvatarMasksEntriesCount);
|
||||
|
||||
var copyNewAvatarMasksToGPUJob = new CopyNewAvatarMasksToGPUJob()
|
||||
{
|
||||
gpuAvatarMasks = avatarMaskThreadedUploader,
|
||||
newAvatarMasks = rad.newAvatarMasksList
|
||||
};
|
||||
|
||||
copyNewAvatarMasksToGPUJob.ScheduleParallel(rad.newAvatarMasksList.Length, 1, default).Complete();
|
||||
|
||||
avatarMaskUploader.EndAndCommit(avatarMaskThreadedUploader);
|
||||
sparseUploaderPool.PutUploader(avatarMaskUploader);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void CopyNewRigsToGPUBuffer(ref GPURuntimeAnimationData rad)
|
||||
{
|
||||
if (rad.newRigsList.IsEmpty)
|
||||
return;
|
||||
|
||||
var newRigBonesCount = (int)(rad.totalGPURigBonesCount.x - rad.totalGPURigBonesCount.y);
|
||||
var newRigsCount = (int)(rad.totalGPURigsCount.x - rad.totalGPURigsCount.y);
|
||||
var newHumanRotationDataEntriesCount = (int)(rad.totalGPUHumanRotationDataEntriesCount.x - rad.totalGPUHumanRotationDataEntriesCount.y);
|
||||
|
||||
var rigBonesUploader = sparseUploaderPool.GetUploader(rigBonesGB);
|
||||
var rigBonesUploadSizeInBytes = newRigBonesCount * UnsafeUtility.SizeOf<GPUStructures.RigBone>();
|
||||
var rigBonesThreadedUploader = rigBonesUploader.Begin(rigBonesUploadSizeInBytes, rigBonesUploadSizeInBytes, newRigBonesCount);
|
||||
|
||||
var rigDefinitionUploader = sparseUploaderPool.GetUploader(rigDefinitionGB);
|
||||
var rigDefinitionsUploadSizeInBytes = newRigsCount * UnsafeUtility.SizeOf<GPUStructures.RigDefinition>();
|
||||
var rigDefinitionsThreadedUploader = rigDefinitionUploader.Begin(rigDefinitionsUploadSizeInBytes, rigDefinitionsUploadSizeInBytes, newRigsCount);
|
||||
|
||||
var rigHumanRotationDataUploader = sparseUploaderPool.GetUploader(rigHumanAvatarRotationDataGB);
|
||||
var rigHumanRotationDataUploadSizeInBytes = newHumanRotationDataEntriesCount * UnsafeUtility.SizeOf<GPUStructures.HumanRotationData>();
|
||||
var rigHumanRotationDataThreadedUploader = rigHumanRotationDataUploader.Begin(rigHumanRotationDataUploadSizeInBytes, rigHumanRotationDataUploadSizeInBytes, newRigsCount);
|
||||
|
||||
var copyNewRigsToGPUJob = new CopyNewRigsToGPUJob()
|
||||
{
|
||||
newRigs = rad.newRigsList,
|
||||
gpuRigBones = rigBonesThreadedUploader,
|
||||
gpuRigDefs = rigDefinitionsThreadedUploader,
|
||||
gpuHumanRotationData = rigHumanRotationDataThreadedUploader
|
||||
};
|
||||
|
||||
copyNewRigsToGPUJob.ScheduleParallel(rad.newRigsList.Length, 1, default).Complete();
|
||||
|
||||
rigDefinitionUploader.EndAndCommit(rigDefinitionsThreadedUploader);
|
||||
rigBonesUploader.EndAndCommit(rigBonesThreadedUploader);
|
||||
rigHumanRotationDataUploader.EndAndCommit(rigHumanRotationDataThreadedUploader);
|
||||
|
||||
sparseUploaderPool.PutUploader(rigDefinitionUploader);
|
||||
sparseUploaderPool.PutUploader(rigBonesUploader);
|
||||
sparseUploaderPool.PutUploader(rigHumanRotationDataUploader);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void CopyNewAnimationsToGPUBuffers(ref GPURuntimeAnimationData rad)
|
||||
{
|
||||
if (rad.newAnimationsList.IsEmpty)
|
||||
return;
|
||||
|
||||
var newAnimationClipsCount = rad.newAnimationsList.Length;
|
||||
// Very conservative value
|
||||
var maxNumUploadsPerClip = 0xf;
|
||||
|
||||
var animationClipsUploader = sparseUploaderPool.GetUploader(animationClipsGB);
|
||||
var maxAnimationClipSingleUploadSize = rad.totalGPUAnimationClipsSize.x - rad.totalGPUAnimationClipsSize.y;
|
||||
var animationClipsThreadedUploader = animationClipsUploader.Begin((int)maxAnimationClipSingleUploadSize, (int)maxAnimationClipSingleUploadSize, newAnimationClipsCount * maxNumUploadsPerClip);
|
||||
|
||||
var createGPUAnimationClip = new CopyNewAnimationsToGPUJob()
|
||||
{
|
||||
newAnimationClips = rad.newAnimationsList,
|
||||
gpuAnimationClips = animationClipsThreadedUploader,
|
||||
};
|
||||
|
||||
createGPUAnimationClip.ScheduleParallel(rad.newAnimationsList.Length, 1, default).Complete();
|
||||
|
||||
animationClipsUploader.EndAndCommit(animationClipsThreadedUploader);
|
||||
sparseUploaderPool.PutUploader(animationClipsUploader);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
internal void BuildSkinMatrices(in NativeParallelHashMap<Entity, SkinnedMeshRendererFrameDeformationData> entityToSMRFrameDataMap, FrameFencedGPUBufferPool<SkinMatrix> outSkinMatrixGB)
|
||||
{
|
||||
if (!SystemAPI.TryGetSingletonRW<GPURuntimeAnimationData>(out var radRW))
|
||||
return;
|
||||
|
||||
ref var runtimeAnimationData = ref radRW.ValueRW;
|
||||
|
||||
frameSkinMatrixWorkloadGB.BeginFrame();
|
||||
|
||||
FillFrameSkinMatrixCalculationWorkloadGPUBuffers(ref runtimeAnimationData, entityToSMRFrameDataMap);
|
||||
|
||||
if (runtimeAnimationData.frameSkinnedMeshesCounter > 0)
|
||||
{
|
||||
animationEngineCS.SetBuffer(makeSkinMatricesKernel, ShaderID_skinMatrixWorkloadBuf, frameSkinMatrixWorkloadGB);
|
||||
animationEngineCS.SetBuffer(makeSkinMatricesKernel, ShaderID_rigSpaceBoneTransformsBuf, rigSpaceAnimatedBonesGB);
|
||||
animationEngineCS.SetBuffer(makeSkinMatricesKernel, ShaderID_outSkinMatrices, outSkinMatrixGB);
|
||||
animationEngineCS.SetBuffer(makeSkinMatricesKernel, ShaderID_skinnedMeshBoneData, skinnedMeshBoneDataGB);
|
||||
animationEngineCS.SetInt(ShaderID_totalSkinnedMeshes, (int)runtimeAnimationData.frameSkinnedMeshesCounter);
|
||||
|
||||
makeSkinMatricesKernel.Dispatch(runtimeAnimationData.frameSkinnedMeshesCounter, 1, 1);
|
||||
}
|
||||
|
||||
frameSkinMatrixWorkloadGB.EndFrame();
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void DispatchAnimationCalculation(ref GPURuntimeAnimationData rad)
|
||||
{
|
||||
if (rad.frameAnimatedBonesCounter == 0)
|
||||
return;
|
||||
|
||||
// Animation computation
|
||||
animationEngineCS.SetBuffer(processAnimationKernel, ShaderID_outAnimatedBones, animatedBonesGB);
|
||||
animationEngineCS.SetBuffer(processAnimationKernel, ShaderID_animatedBoneWorkload, framePerBoneAnimationWorkloadGB);
|
||||
animationEngineCS.SetBuffer(processAnimationKernel, ShaderID_animationJobs, frameRigAnimationJobsGB);
|
||||
animationEngineCS.SetBuffer(processAnimationKernel, ShaderID_animationsToProcess, frameAnimationToProcessGB);
|
||||
animationEngineCS.SetBuffer(processAnimationKernel, ShaderID_rigDefinitions, rigDefinitionGB);
|
||||
animationEngineCS.SetBuffer(processAnimationKernel, ShaderID_rigBones, rigBonesGB);
|
||||
animationEngineCS.SetBuffer(processAnimationKernel, ShaderID_animationClips, animationClipsGB);
|
||||
animationEngineCS.SetBuffer(processAnimationKernel, ShaderID_humanRotationDataBuffer, rigHumanAvatarRotationDataGB);
|
||||
animationEngineCS.SetBuffer(processAnimationKernel, ShaderID_avatarMasksBuffer, avatarMasksGB);
|
||||
animationEngineCS.SetInt(ShaderID_animatedBonesCount, (int)rad.frameAnimatedBonesCounter);
|
||||
|
||||
processAnimationKernel.Dispatch(rad.frameAnimatedBonesCounter, 1, 1);
|
||||
|
||||
// Compute rig space bone transforms
|
||||
animationEngineCS.SetBuffer(makeRigSpaceBoneTransformsKernel, ShaderID_boneLocalTransforms, animatedBonesGB);
|
||||
animationEngineCS.SetBuffer(makeRigSpaceBoneTransformsKernel, ShaderID_animatedBoneWorkload, framePerBoneAnimationWorkloadGB);
|
||||
animationEngineCS.SetBuffer(makeRigSpaceBoneTransformsKernel, ShaderID_animationJobs, frameRigAnimationJobsGB);
|
||||
animationEngineCS.SetBuffer(makeRigSpaceBoneTransformsKernel, ShaderID_rigDefinitions, rigDefinitionGB);
|
||||
animationEngineCS.SetBuffer(makeRigSpaceBoneTransformsKernel, ShaderID_rigBones, rigBonesGB);
|
||||
animationEngineCS.SetBuffer(makeRigSpaceBoneTransformsKernel, ShaderID_outBoneTransforms, rigSpaceAnimatedBonesGB);
|
||||
makeRigSpaceBoneTransformsKernel.Dispatch(rad.frameAnimatedBonesCounter, 1, 1);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void SetBuffersForBoneRenderer(ref GPURuntimeAnimationData rad)
|
||||
{
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
boneVisualizationSystem ??= World.GetExistingSystemManaged<BoneVisualizationSystem>();
|
||||
boneVisualizationSystem.gpuBoneRenderer.SetGPUBuffersForFrame
|
||||
(
|
||||
framePerBoneAnimationWorkloadGB,
|
||||
frameRigAnimationJobsGB,
|
||||
rigDefinitionGB,
|
||||
rigBonesGB,
|
||||
rigSpaceAnimatedBonesGB,
|
||||
(int)rad.frameAnimatedBonesCounter
|
||||
);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ed0a503b97e8daf44baa4bcda24bfc15
|
||||
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/GPUAnimationEngine/GPUAnimationSystem.cs
|
||||
uploadId: 897522
|
||||
+569
@@ -0,0 +1,569 @@
|
||||
using Rukhanka.Toolbox;
|
||||
using Unity.Burst;
|
||||
using Unity.Burst.Intrinsics;
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using Unity.Entities;
|
||||
using Unity.Jobs;
|
||||
using Unity.Jobs.LowLevel.Unsafe;
|
||||
using Unity.Mathematics;
|
||||
using Unity.Rendering;
|
||||
using Unity.Transforms;
|
||||
using Hash128 = Unity.Entities.Hash128;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
public partial class GPUAnimationSystem
|
||||
{
|
||||
[BurstCompile]
|
||||
struct CopyNewAnimationsToGPUJob: IJobFor
|
||||
{
|
||||
[ReadOnly]
|
||||
public NativeList<GPUAnimationClipPlacementData> newAnimationClips;
|
||||
public ThreadedSparseUploader gpuAnimationClips;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void Execute(int index)
|
||||
{
|
||||
var animationClip = newAnimationClips[index];
|
||||
ref var ac = ref animationClip.animationClipBlob.Value;
|
||||
|
||||
var gpuAnimClipData = new GPUStructures.AnimationClip()
|
||||
{
|
||||
flags = ac.flags,
|
||||
hash = ac.hash.Value,
|
||||
length = ac.length,
|
||||
looped = ac.looped,
|
||||
cycleOffset = ac.cycleOffset,
|
||||
};
|
||||
|
||||
SetPerfectHashTableData(ref animationClip.animationClipBlob.Value.clipTracks, animationClip.clipTracks, ref gpuAnimClipData.clipTracks);
|
||||
SetPerfectHashTableData(ref animationClip.animationClipBlob.Value.additiveReferencePoseFrame, animationClip.additiveRefPoseTracks, ref gpuAnimClipData.additiveReferencePoseTracks);
|
||||
|
||||
var absOffset = animationClip.animationClipDataOffset;
|
||||
gpuAnimationClips.AddUpload(gpuAnimClipData, absOffset);
|
||||
|
||||
UploadTrackSet(absOffset, animationClip.clipTracks, ref animationClip.animationClipBlob.Value.clipTracks);
|
||||
UploadTrackSet(absOffset, animationClip.additiveRefPoseTracks, ref animationClip.animationClipBlob.Value.additiveReferencePoseFrame);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void SetPerfectHashTableData(ref TrackSet ts, in GPUTrackSetPlacementData tspd, ref GPUStructures.TrackSet gpuTrackSet)
|
||||
{
|
||||
gpuTrackSet = tspd.ToGPUTrackSet();
|
||||
gpuTrackSet.trackGroupPHTSeed = ts.trackGroupPHT.seed;
|
||||
gpuTrackSet.trackGroupPHTSizeMask = (uint)ts.trackGroupPHT.pht.Length - 1;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
unsafe void UploadTrackSet(int baseOffset, in GPUTrackSetPlacementData tspd, ref TrackSet ts)
|
||||
{
|
||||
if (ts.keyframes.Length == 0)
|
||||
return;
|
||||
|
||||
gpuAnimationClips.AddUpload(ts.keyframes.GetUnsafePtr(), ts.keyframes.Length * UnsafeUtility.SizeOf<GPUStructures.KeyFrame>(), baseOffset + tspd.keyFramesOffset);
|
||||
gpuAnimationClips.AddUpload(ts.trackGroups.GetUnsafePtr(), ts.trackGroups.Length * UnsafeUtility.SizeOf<int>(), baseOffset + tspd.trackGroupsOffset);
|
||||
gpuAnimationClips.AddUpload(ts.trackGroupPHT.pht.GetUnsafePtr(), ts.trackGroupPHT.pht.Length * UnsafeUtility.SizeOf<int2>(), baseOffset + tspd.hashTableOffset);
|
||||
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
// Must copy because debug tracks contain name field
|
||||
var tmpArr = new NativeArray<GPUStructures.Track>(ts.tracks.Length, Allocator.Temp, NativeArrayOptions.UninitializedMemory);
|
||||
for (var i = 0; i < tmpArr.Length; ++i)
|
||||
{
|
||||
var it = ts.tracks[i];
|
||||
var t = new GPUStructures.Track()
|
||||
{
|
||||
props = it.props,
|
||||
keyFrameRange = it.keyFrameRange
|
||||
};
|
||||
tmpArr[i] = t;
|
||||
}
|
||||
gpuAnimationClips.AddUpload(tmpArr, baseOffset + tspd.tracksOffset);
|
||||
#else
|
||||
gpuAnimationClips.AddUpload(ts.tracks.GetUnsafePtr(), ts.tracks.Length * UnsafeUtility.SizeOf<GPUStructures.Track>(), baseOffset + tspd.tracksOffset);
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------------//
|
||||
|
||||
[BurstCompile]
|
||||
struct CopyNewAvatarMasksToGPUJob: IJobFor
|
||||
{
|
||||
[ReadOnly]
|
||||
public NativeList<GPUAvatarMaskPlacementData> newAvatarMasks;
|
||||
public ThreadedSparseUploader gpuAvatarMasks;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public unsafe void Execute(int index)
|
||||
{
|
||||
var am = newAvatarMasks[index];
|
||||
|
||||
var dstOffset = am.dataOffset * UnsafeUtility.SizeOf<uint>();
|
||||
gpuAvatarMasks.AddUpload(am.avatarMaskBlob.Value.humanBodyPartsAvatarMask, dstOffset);
|
||||
|
||||
ref var bm = ref am.avatarMaskBlob.Value.includedBoneMask;
|
||||
var srcPtr = bm.GetUnsafePtr();
|
||||
var srcSize = bm.Length * UnsafeUtility.SizeOf<uint>();
|
||||
gpuAvatarMasks.AddUpload(srcPtr, srcSize, dstOffset + 4);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------------//
|
||||
|
||||
[BurstCompile]
|
||||
struct CopyNewRigsToGPUJob: IJobFor
|
||||
{
|
||||
[ReadOnly]
|
||||
public NativeList<GPURigDefinitionPlacementData> newRigs;
|
||||
public ThreadedSparseUploader gpuRigDefs;
|
||||
public ThreadedSparseUploader gpuRigBones;
|
||||
public ThreadedSparseUploader gpuHumanRotationData;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public unsafe void Execute(int index)
|
||||
{
|
||||
var rig = newRigs[index];
|
||||
ref var rdb = ref rig.rigBlob.Value;
|
||||
|
||||
#if RUKHANKA_DEBUG_INFO
|
||||
for (var i = 0; i < rdb.bones.Length; ++i)
|
||||
{
|
||||
ref var b = ref rdb.bones[i];
|
||||
var grp = new GPUStructures.BoneTransform()
|
||||
{
|
||||
pos = b.refPose.pos,
|
||||
rot = b.refPose.rot.value,
|
||||
scale = b.refPose.scale
|
||||
};
|
||||
|
||||
var rb = new GPUStructures.RigBone()
|
||||
{
|
||||
hash = b.hash,
|
||||
refPose = grp,
|
||||
humanBodyPart = (int)b.humanBodyPart,
|
||||
parentBoneIndex = b.parentBoneIndex
|
||||
};
|
||||
var rigBoneIndex = rig.rigBonesOffset + i;
|
||||
var dstRigBoneOffsetInBytes = rigBoneIndex * UnsafeUtility.SizeOf<GPUStructures.RigBone>();
|
||||
gpuRigBones.AddUpload(rb, dstRigBoneOffsetInBytes);
|
||||
}
|
||||
#else
|
||||
var srcPtr = rdb.bones.GetUnsafePtr();
|
||||
var srcOffset = rig.rigBonesOffset * UnsafeUtility.SizeOf<GPUStructures.RigBone>();
|
||||
var srcSize = rdb.bones.Length * UnsafeUtility.SizeOf<GPUStructures.RigBone>();
|
||||
gpuRigBones.AddUpload(srcPtr, srcSize, srcOffset);
|
||||
#endif
|
||||
|
||||
if (rig.humanRotationDataOffset >= 0)
|
||||
{
|
||||
BurstAssert.IsTrue(UnsafeUtility.SizeOf<GPUStructures.HumanRotationData>() == UnsafeUtility.SizeOf<HumanRotationData>(), "Size must match");
|
||||
BurstAssert.IsTrue(rig.rigBlob.Value.bones.Length == rig.rigBlob.Value.humanData.Value.humanRotData.Length, "Count must match");
|
||||
var srcHRDPtr = rdb.humanData.Value.humanRotData.GetUnsafePtr();
|
||||
var srcHRDOffset = rig.humanRotationDataOffset * UnsafeUtility.SizeOf<GPUStructures.HumanRotationData>();
|
||||
var srcHRDSize = rdb.humanData.Value.humanRotData.Length * UnsafeUtility.SizeOf<GPUStructures.HumanRotationData>();
|
||||
gpuHumanRotationData.AddUpload(srcHRDPtr, srcHRDSize, srcHRDOffset);
|
||||
}
|
||||
|
||||
var gpuRig = new GPUStructures.RigDefinition()
|
||||
{
|
||||
hash = rdb.hash.Value,
|
||||
rigBonesRange = new int2(rig.rigBonesOffset, rdb.bones.Length),
|
||||
rootBoneIndex = rdb.rootBoneIndex,
|
||||
humanRotationDataOffset = rig.humanRotationDataOffset
|
||||
};
|
||||
var dstRigDefOffsetInBytes = rig.rigDefinitionIndex * UnsafeUtility.SizeOf<GPUStructures.RigDefinition>();
|
||||
gpuRigDefs.AddUpload(gpuRig, dstRigDefOffsetInBytes);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------------//
|
||||
|
||||
[BurstCompile]
|
||||
struct CopyNewSkinnedMeshesDataJob: IJobFor
|
||||
{
|
||||
[ReadOnly]
|
||||
public NativeList<GPUSkinnedMeshPlacementData> newSkinnedMeshData;
|
||||
public ThreadedSparseUploader gpuSkinnedMeshBonesData;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void Execute(int index)
|
||||
{
|
||||
var brt = newSkinnedMeshData[index];
|
||||
BurstAssert.IsTrue
|
||||
(
|
||||
brt.skinnedMeshInfo.Value.bones.Length == brt.boneRemapTableBlob.Value.remapIndices.Length,
|
||||
"Skinned mesh bones count should be equal as remap indices count for this mesh"
|
||||
);
|
||||
|
||||
for (var i = 0; i < brt.skinnedMeshInfo.Value.bones.Length; ++i)
|
||||
{
|
||||
ref var bi = ref brt.skinnedMeshInfo.Value.bones[i];
|
||||
var smbd = new GPUStructures.SkinnedMeshBoneData()
|
||||
{
|
||||
boneRemapIndex = brt.boneRemapTableBlob.Value.remapIndices[i]
|
||||
};
|
||||
// Last column of bind pose is (0, 0, 0, 1). Do not store it
|
||||
smbd.bindPose.c0 = bi.bindPose.c0.xyz;
|
||||
smbd.bindPose.c1 = bi.bindPose.c1.xyz;
|
||||
smbd.bindPose.c2 = bi.bindPose.c2.xyz;
|
||||
smbd.bindPose.c3 = bi.bindPose.c3.xyz;
|
||||
|
||||
var dstOffsetInBytes = (brt.dataOffset + i) * UnsafeUtility.SizeOf<GPUStructures.SkinnedMeshBoneData>();
|
||||
gpuSkinnedMeshBonesData.AddUpload(smbd, dstOffsetInBytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------------//
|
||||
|
||||
[BurstCompile]
|
||||
struct FillFrameAnimatedRigWorkloadBuffersJob: IJobChunk
|
||||
{
|
||||
[NativeDisableParallelForRestriction]
|
||||
public NativeArray<GPUStructures.AnimatedBoneWorkload> animatedBonesWorkloadBuf;
|
||||
[NativeDisableParallelForRestriction]
|
||||
public NativeArray<GPUStructures.AnimationJob> frameRigAnimationJobs;
|
||||
[NativeDisableParallelForRestriction]
|
||||
public NativeArray<GPUStructures.AnimationToProcess> animationToProcessBuf;
|
||||
|
||||
[ReadOnly]
|
||||
public NativeParallelHashMap<Hash128, int> animationClipsOffsets;
|
||||
[ReadOnly]
|
||||
public NativeParallelHashMap<Hash128, int> avatarMasksOffsets;
|
||||
[ReadOnly]
|
||||
public NativeParallelHashMap<Hash128, int> rigDefinitionOffsets;
|
||||
|
||||
[ReadOnly]
|
||||
public ComponentTypeHandle<RigDefinitionComponent> rigDefComponentTypeHandle;
|
||||
[ReadOnly]
|
||||
public BufferTypeHandle<AnimationToProcessComponent> atpBufTypeHandle;
|
||||
[ReadOnly]
|
||||
public EntityTypeHandle entityTypeHandle;
|
||||
[ReadOnly]
|
||||
public ComponentTypeHandle<GPURigFrameOffsetsComponent> frameOffsetsTypeHandle;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask)
|
||||
{
|
||||
var rigDefs = chunk.GetNativeArray(ref rigDefComponentTypeHandle);
|
||||
var atpBufs = chunk.GetBufferAccessorRO(ref atpBufTypeHandle);
|
||||
var frameOffsets = chunk.GetNativeArray(ref frameOffsetsTypeHandle);
|
||||
var entities = chunk.GetNativeArray(entityTypeHandle);
|
||||
var frameOffsetsInChunk = chunk.GetChunkComponentData(ref frameOffsetsTypeHandle);
|
||||
|
||||
var cee = new ChunkEntityEnumerator(useEnabledMask, chunkEnabledMask, chunk.Count);
|
||||
while (cee.NextEntityIndex(out var i))
|
||||
{
|
||||
var rdc = rigDefs[i];
|
||||
var atps = atpBufs[i];
|
||||
var e = entities[i];
|
||||
var frameOffset = frameOffsets[i];
|
||||
frameOffset.AddOffsets(frameOffsetsInChunk);
|
||||
Execute(e, rdc, atps, frameOffset);
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Execute(Entity e, in RigDefinitionComponent rdc, in DynamicBuffer<AnimationToProcessComponent> atps, in GPURigFrameOffsetsComponent frameOffsets)
|
||||
{
|
||||
var abCount = rdc.rigBlob.Value.bones.Length;
|
||||
var atpCount = atps.Length;
|
||||
|
||||
for (var i = 0; i < atpCount; ++i)
|
||||
{
|
||||
var atp = atps[i];
|
||||
if (!atp.animation.IsCreated)
|
||||
continue;
|
||||
|
||||
var gpuAtp = new GPUStructures.AnimationToProcess()
|
||||
{
|
||||
time = atp.time,
|
||||
weight = atp.weight,
|
||||
animationClipAddress = animationClipsOffsets[atp.animation.Value.hash],
|
||||
blendMode = (int)atp.blendMode,
|
||||
layerIndex = atp.layerIndex,
|
||||
layerWeight = atp.layerWeight,
|
||||
avatarMaskDataOffset = atp.avatarMask.IsCreated ? avatarMasksOffsets[atp.avatarMask.Value.hash] : -1
|
||||
};
|
||||
|
||||
var idx = i + frameOffsets.animationToProcessIndex;
|
||||
animationToProcessBuf[idx] = gpuAtp;
|
||||
}
|
||||
|
||||
if (!rigDefinitionOffsets.TryGetValue(rdc.rigBlob.Value.hash, out var rigDefIndex))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var faj = new GPUStructures.AnimationJob()
|
||||
{
|
||||
rigDefinitionIndex = rigDefIndex,
|
||||
animatedBoneIndexOffset = frameOffsets.boneIndex,
|
||||
animationsToProcessRange = new int2(frameOffsets.animationToProcessIndex, atpCount)
|
||||
};
|
||||
frameRigAnimationJobs[frameOffsets.rigIndex] = faj;
|
||||
|
||||
var abw = new GPUStructures.AnimatedBoneWorkload()
|
||||
{
|
||||
animationJobIndex = frameOffsets.rigIndex,
|
||||
};
|
||||
|
||||
for (var i = 0; i < abCount; ++i)
|
||||
{
|
||||
abw.boneIndexInRig = i;
|
||||
animatedBonesWorkloadBuf[frameOffsets.boneIndex + i] = abw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------------//
|
||||
|
||||
[BurstCompile]
|
||||
struct FillFrameSkinMatrixWorkloadBuffersJob: IJobChunk
|
||||
{
|
||||
[ReadOnly]
|
||||
public ComponentLookup<GPUAnimationEngineTag> gpuAnimationEngineTagLookup;
|
||||
[ReadOnly]
|
||||
public ComponentLookup<RigDefinitionComponent> rigDefLookup;
|
||||
[ReadOnly]
|
||||
public ComponentLookup<GPURigFrameOffsetsComponent> frameOffsetsLookup;
|
||||
[ReadOnly]
|
||||
public ComponentLookup<LocalToWorld> localToWorldLookup;
|
||||
[ReadOnly]
|
||||
public NativeParallelHashMap<Hash128, int> skinnedMeshDataMap;
|
||||
[ReadOnly]
|
||||
public NativeParallelHashMap<Entity, SkinnedMeshRendererFrameDeformationData> entityToSMRFrameDataMap;
|
||||
|
||||
[NativeDisableParallelForRestriction]
|
||||
public NativeArray<GPUStructures.SkinnedMeshWorkload> frameSkinMatrixWorkloadBuf;
|
||||
[NativeDisableUnsafePtrRestriction]
|
||||
public UnsafeAtomicCounter32 frameSkinnedMeshesAtomicCounter;
|
||||
|
||||
[ReadOnly]
|
||||
public ComponentTypeHandle<SkinnedMeshRendererComponent> smrTypeHandle;
|
||||
[ReadOnly]
|
||||
public ComponentTypeHandle<LocalToWorld> l2wTypeHandle;
|
||||
[ReadOnly]
|
||||
public EntityTypeHandle entityTypeHandle;
|
||||
[ReadOnly]
|
||||
public ComponentTypeHandle<GPURigFrameOffsetsComponent> gpuRigChunkDataTypeHandle;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public unsafe void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask)
|
||||
{
|
||||
var smrs = chunk.GetNativeArray(ref smrTypeHandle);
|
||||
var l2ws = chunk.GetNativeArray(ref l2wTypeHandle);
|
||||
var entities = chunk.GetNativeArray(entityTypeHandle);
|
||||
|
||||
var cee = new ChunkEntityEnumerator(useEnabledMask, chunkEnabledMask, chunk.Count);
|
||||
while (cee.NextEntityIndex(out var i))
|
||||
{
|
||||
var smr = smrs[i];
|
||||
var l2w = l2ws[i];
|
||||
var e = entities[i];
|
||||
var rigChunkIndex = chunk.m_EntityComponentStore->GetChunk(smr.animatedRigEntity);
|
||||
var rigChunk = new ArchetypeChunk(rigChunkIndex, chunk.m_EntityComponentStore);
|
||||
Execute(e, smr, l2w, rigChunk);
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Execute(Entity e, in SkinnedMeshRendererComponent smr, in LocalToWorld l2w, in ArchetypeChunk rigChunk)
|
||||
{
|
||||
if (!smr.IsGPUAnimator(gpuAnimationEngineTagLookup))
|
||||
return;
|
||||
|
||||
if (!entityToSMRFrameDataMap.TryGetValue(e, out var skinMatrixData))
|
||||
return;
|
||||
|
||||
if (!rigDefLookup.TryGetComponent(smr.animatedRigEntity, out var rigDef))
|
||||
return;
|
||||
|
||||
var hash = AnimationUtils.CalculateBoneRemapTableHash(smr.smrInfoBlob, rigDef.rigBlob);
|
||||
|
||||
if (!skinnedMeshDataMap.TryGetValue(hash, out var remapTableOffset))
|
||||
{
|
||||
BurstAssert.IsTrue(false, "Skinned mesh to rig remap table is not found");
|
||||
return;
|
||||
}
|
||||
|
||||
var gpuRigFrameOffsets = rigChunk.GetChunkComponentData(ref gpuRigChunkDataTypeHandle);
|
||||
frameOffsetsLookup.TryGetComponent(smr.animatedRigEntity, out var frameOffsets);
|
||||
frameOffsets.AddOffsets(gpuRigFrameOffsets);
|
||||
|
||||
var animatedEntityL2W = localToWorldLookup[smr.animatedRigEntity];
|
||||
var invAnimatedEntityPose = math.inverse(animatedEntityL2W.Value);
|
||||
var smrPose = l2w.Value;
|
||||
var rootBoneToEntityTransform = math.mul(invAnimatedEntityPose, smrPose);
|
||||
|
||||
var smw = new GPUStructures.SkinnedMeshWorkload()
|
||||
{
|
||||
skinMatricesCount = smr.smrInfoBlob.Value.bones.Length,
|
||||
boneRemapTableIndex = remapTableOffset,
|
||||
skinMatrixBaseOutIndex = skinMatrixData.skinMatrixIndex,
|
||||
rootBoneIndex = smr.rootBoneIndexInRig,
|
||||
animatedBoneIndexOffset = frameOffsets.boneIndex,
|
||||
skinnedMeshInverseTransform = math.inverse(rootBoneToEntityTransform)
|
||||
};
|
||||
var index = frameSkinnedMeshesAtomicCounter.Add(1);
|
||||
frameSkinMatrixWorkloadBuf[index] = smw;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------------//
|
||||
|
||||
[BurstCompile]
|
||||
struct ComputeFrameRigWorkloadSizesPerChunkJob: IJobChunk
|
||||
{
|
||||
[ReadOnly]
|
||||
public ComponentTypeHandle<RigDefinitionComponent> rigDefComponentTypeHandle;
|
||||
[ReadOnly]
|
||||
public BufferTypeHandle<AnimationToProcessComponent> atpBufTypeHandle;
|
||||
|
||||
public ComponentTypeHandle<GPURigFrameOffsetsComponent> frameOffsetsTypeHandle;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask)
|
||||
{
|
||||
var rigDefs = chunk.GetNativeArray(ref rigDefComponentTypeHandle);
|
||||
var atpBufs = chunk.GetBufferAccessorRO(ref atpBufTypeHandle);
|
||||
var frameOffsets = chunk.GetNativeArray(ref frameOffsetsTypeHandle);
|
||||
|
||||
var gpuRigFrameOffsets = new GPURigFrameOffsetsComponent();
|
||||
|
||||
var cee = new ChunkEntityEnumerator(useEnabledMask, chunkEnabledMask, chunk.Count);
|
||||
while (cee.NextEntityIndex(out var i))
|
||||
{
|
||||
var rdc = rigDefs[i];
|
||||
var atps = atpBufs[i];
|
||||
frameOffsets[i] = gpuRigFrameOffsets;
|
||||
|
||||
gpuRigFrameOffsets.boneIndex += rdc.rigBlob.Value.bones.Length;
|
||||
gpuRigFrameOffsets.rigIndex += 1;
|
||||
gpuRigFrameOffsets.animationToProcessIndex += atps.Length;
|
||||
}
|
||||
chunk.SetChunkComponentData(ref frameOffsetsTypeHandle, gpuRigFrameOffsets);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------------//
|
||||
|
||||
[BurstCompile]
|
||||
unsafe struct ComputeFrameRigWorkloadSizesAbsChunkOffsetsJob: IJobChunk
|
||||
{
|
||||
[NativeDisableUnsafePtrRestriction]
|
||||
public uint* frameAnimatedBonesCounter;
|
||||
[NativeDisableUnsafePtrRestriction]
|
||||
public uint* frameAnimationToProcessCounter;
|
||||
[NativeDisableUnsafePtrRestriction]
|
||||
public uint* frameAnimatedRigsCounter;
|
||||
|
||||
public ComponentTypeHandle<GPURigFrameOffsetsComponent> gpuRigChunkDataTypeHandle;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask)
|
||||
{
|
||||
var gpuRigChunkData = chunk.GetChunkComponentData(ref gpuRigChunkDataTypeHandle);
|
||||
var gcd = gpuRigChunkData;
|
||||
|
||||
gpuRigChunkData.boneIndex = (int)*frameAnimatedBonesCounter;
|
||||
gpuRigChunkData.animationToProcessIndex = (int)*frameAnimationToProcessCounter;
|
||||
gpuRigChunkData.rigIndex = (int)*frameAnimatedRigsCounter;
|
||||
|
||||
*frameAnimatedBonesCounter += (uint)gcd.boneIndex;
|
||||
*frameAnimationToProcessCounter += (uint)gcd.animationToProcessIndex;;
|
||||
*frameAnimatedRigsCounter += (uint)gcd.rigIndex;
|
||||
|
||||
chunk.SetChunkComponentData(ref gpuRigChunkDataTypeHandle, gpuRigChunkData);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------------//
|
||||
|
||||
[BurstCompile]
|
||||
partial struct ComputeFrameSkinnedMeshWorkloadSizesJob: IJobEntity
|
||||
{
|
||||
[ReadOnly]
|
||||
public ComponentLookup<GPUAnimationEngineTag> gpuAnimationEngineTagLookup;
|
||||
|
||||
[NativeDisableParallelForRestriction]
|
||||
public NativeList<uint> frameSkinnedMeshesPerThreadCounters;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Execute(in SkinnedMeshRendererComponent asmc)
|
||||
{
|
||||
if (!asmc.IsGPUAnimator(gpuAnimationEngineTagLookup))
|
||||
return;
|
||||
|
||||
frameSkinnedMeshesPerThreadCounters[JobsUtility.ThreadIndex] += 1;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------------//
|
||||
|
||||
[BurstCompile]
|
||||
unsafe struct ComputeFrameSkinnedMeshWorkloadSizesTotalJob: IJob
|
||||
{
|
||||
[ReadOnly]
|
||||
public NativeList<uint> frameSkinnedMeshesPerThreadCounters;
|
||||
|
||||
[NativeDisableUnsafePtrRestriction]
|
||||
public uint* frameSkinnedMeshesCounter;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void Execute()
|
||||
{
|
||||
foreach (var c in frameSkinnedMeshesPerThreadCounters)
|
||||
{
|
||||
*frameSkinnedMeshesCounter += c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------------//
|
||||
|
||||
[BurstCompile]
|
||||
struct ResetFrameDataJob: IJob
|
||||
{
|
||||
[NativeDisableUnsafePtrRestriction]
|
||||
public UnsafeAtomicCounter32 frameAnimatedBonesCounter;
|
||||
[NativeDisableUnsafePtrRestriction]
|
||||
public UnsafeAtomicCounter32 frameAnimatedRigsCounter;
|
||||
[NativeDisableUnsafePtrRestriction]
|
||||
public UnsafeAtomicCounter32 frameAnimationToProcessCounter;
|
||||
[NativeDisableUnsafePtrRestriction]
|
||||
public UnsafeAtomicCounter32 frameSkinnedMeshesCounter;
|
||||
public NativeList<uint> frameSkinnedMeshesPerThreadCounters;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void Execute()
|
||||
{
|
||||
frameAnimatedBonesCounter.Reset(0);
|
||||
frameAnimatedRigsCounter.Reset(0);
|
||||
frameSkinnedMeshesCounter.Reset(0);
|
||||
frameAnimationToProcessCounter.Reset(0);
|
||||
frameSkinnedMeshesPerThreadCounters.Clear();
|
||||
frameSkinnedMeshesPerThreadCounters.Resize(JobsUtility.ThreadIndexCount, NativeArrayOptions.ClearMemory);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ff409b692d92c6c42961eb3c0f78059a
|
||||
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/GPUAnimationEngine/GPUAnimationSystem_Jobs.cs
|
||||
uploadId: 897522
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
using UnityEngine;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
public partial class GPUAnimationSystem
|
||||
{
|
||||
readonly int ShaderID_outAnimatedBones = Shader.PropertyToID("outAnimatedBones");
|
||||
readonly int ShaderID_animatedBoneWorkload = Shader.PropertyToID("animatedBoneWorkload");
|
||||
readonly int ShaderID_animationJobs = Shader.PropertyToID("animationJobs");
|
||||
readonly int ShaderID_animationsToProcess = Shader.PropertyToID("animationsToProcess");
|
||||
readonly int ShaderID_rigDefinitions = Shader.PropertyToID("rigDefinitions");
|
||||
readonly int ShaderID_rigBones = Shader.PropertyToID("rigBones");
|
||||
readonly int ShaderID_animationClips = Shader.PropertyToID("animationClips");
|
||||
readonly int ShaderID_humanRotationDataBuffer = Shader.PropertyToID("humanRotationDataBuffer");
|
||||
readonly int ShaderID_animatedBonesCount = Shader.PropertyToID("animatedBonesCount");
|
||||
readonly int ShaderID_outBoneTransforms = Shader.PropertyToID("outBoneTransforms");
|
||||
readonly int ShaderID_avatarMasksBuffer = Shader.PropertyToID("avatarMasksBuffer");
|
||||
readonly int ShaderID_skinMatrixWorkloadBuf = Shader.PropertyToID("skinMatrixWorkloadBuf");
|
||||
readonly int ShaderID_outSkinMatrices = Shader.PropertyToID("outSkinMatrices");
|
||||
readonly int ShaderID_skinnedMeshBoneData = Shader.PropertyToID("skinnedMeshBoneData");
|
||||
readonly int ShaderID_totalSkinnedMeshes = Shader.PropertyToID("totalSkinnedMeshes");
|
||||
|
||||
public static readonly int ShaderID_boneLocalTransforms = Shader.PropertyToID("boneLocalTransforms");
|
||||
public static readonly int ShaderID_rigSpaceBoneTransformsBuf = Shader.PropertyToID("rigSpaceBoneTransformsBuf");
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2b6a8234a3200384f94471df88fb843b
|
||||
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/GPUAnimationEngine/GPUAnimationSystem_ShaderVars.cs
|
||||
uploadId: 897522
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
using Unity.Entities;
|
||||
using Unity.Transforms;
|
||||
using UnityEngine;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
|
||||
[WorldSystemFilter(WorldSystemFilterFlags.Editor | WorldSystemFilterFlags.Default)]
|
||||
[UpdateInGroup(typeof(RukhankaDeformationSystemGroup))]
|
||||
[UpdateAfter(typeof(MeshDeformationSystem))]
|
||||
partial class GPUAttachmentsUpdateSystem: SystemBase
|
||||
{
|
||||
GraphicsBuffer dummyGB;
|
||||
EntityQuery gpuAttachmentsQuery;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
protected override void OnCreate()
|
||||
{
|
||||
gpuAttachmentsQuery = SystemAPI.QueryBuilder()
|
||||
.WithAll<GPUAttachmentBoneIndexMPComponent, GPUAttachmentComponent>()
|
||||
.Build();
|
||||
RequireForUpdate(gpuAttachmentsQuery);
|
||||
|
||||
// Make small dummy bone transform buffer to prevent "attempted to draw with missing bindings" warnings and missed meshes for GPU attachments in edit mode
|
||||
dummyGB = new (GraphicsBuffer.Target.Structured, GraphicsBuffer.UsageFlags.None, 1, 4);
|
||||
Shader.SetGlobalBuffer(GPUAnimationSystem.ShaderID_rigSpaceBoneTransformsBuf, dummyGB);
|
||||
Shader.SetGlobalBuffer(GPUAnimationSystem.ShaderID_boneLocalTransforms, dummyGB);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
protected override void OnDestroy()
|
||||
{
|
||||
dummyGB.Dispose();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
protected override void OnUpdate()
|
||||
{
|
||||
var attachmentBoneIndexUpdateJob = new UpdateGPUAttachmentBoneIndexJob()
|
||||
{
|
||||
frameOffsetsLookup = SystemAPI.GetComponentLookup<GPURigFrameOffsetsComponent>(true),
|
||||
localTransformLookup = SystemAPI.GetComponentLookup<LocalTransform>(true),
|
||||
parentLookup = SystemAPI.GetComponentLookup<Parent>(true),
|
||||
postTransformMatrixLookup = SystemAPI.GetComponentLookup<PostTransformMatrix>(true),
|
||||
rigBoneRefLookup = SystemAPI.GetComponentLookup<AnimatorEntityRefComponent>(true),
|
||||
gpuAttachmentTypeHandle = SystemAPI.GetComponentTypeHandle<GPUAttachmentComponent>(true),
|
||||
gpuAttachmentBoneIndexTypeHandle = SystemAPI.GetComponentTypeHandle<GPUAttachmentBoneIndexMPComponent>(),
|
||||
gpuAttachmentToBoneTransformTypeHandle = SystemAPI.GetComponentTypeHandle<GPUAttachmentToBoneTransformMPComponent>(),
|
||||
gpuRigEntityLocalToWorldTypeHandle = SystemAPI.GetComponentTypeHandle<GPURigEntityLocalToWorldMPComponent>(),
|
||||
gpuRigFrameOffsetsTypeHandle = SystemAPI.GetComponentTypeHandle<GPURigFrameOffsetsComponent>(true),
|
||||
gpuEngineTagLookup = SystemAPI.GetComponentLookup<GPUAnimationEngineTag>(true),
|
||||
entityTypeHandle = SystemAPI.GetEntityTypeHandle()
|
||||
};
|
||||
var attachmentBoneIndexUpdateJH = attachmentBoneIndexUpdateJob.ScheduleParallel(gpuAttachmentsQuery, Dependency);
|
||||
Dependency = attachmentBoneIndexUpdateJH;
|
||||
Dependency.Complete();
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0dce0f4078c7add4fa8632fffc9dc9e7
|
||||
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/GPUAnimationEngine/GPUAttachementsUpdateSystem.cs
|
||||
uploadId: 897522
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
using Rukhanka.Toolbox;
|
||||
using Unity.Entities;
|
||||
using Unity.Burst;
|
||||
using Unity.Burst.CompilerServices;
|
||||
using Unity.Burst.Intrinsics;
|
||||
using Unity.Collections;
|
||||
using Unity.Mathematics;
|
||||
using Unity.Transforms;
|
||||
using TransformHelpers = Unity.Transforms.TransformHelpers;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
partial class GPUAttachmentsUpdateSystem
|
||||
{
|
||||
[BurstCompile]
|
||||
struct UpdateGPUAttachmentBoneIndexJob: IJobChunk
|
||||
{
|
||||
struct ParentBoneInfo
|
||||
{
|
||||
public Entity boneEntity;
|
||||
public Entity rigEntity;
|
||||
public int boneIndexInRig;
|
||||
public float4x4 attachmentToBoneTransform;
|
||||
}
|
||||
|
||||
[ReadOnly]
|
||||
public ComponentLookup<GPURigFrameOffsetsComponent> frameOffsetsLookup;
|
||||
[ReadOnly]
|
||||
public ComponentLookup<LocalTransform> localTransformLookup;
|
||||
[ReadOnly]
|
||||
public ComponentLookup<PostTransformMatrix> postTransformMatrixLookup;
|
||||
[ReadOnly]
|
||||
public ComponentLookup<AnimatorEntityRefComponent> rigBoneRefLookup;
|
||||
[ReadOnly]
|
||||
public ComponentLookup<Parent> parentLookup;
|
||||
[ReadOnly]
|
||||
public ComponentTypeHandle<GPUAttachmentComponent> gpuAttachmentTypeHandle;
|
||||
[ReadOnly]
|
||||
public ComponentTypeHandle<GPURigFrameOffsetsComponent> gpuRigFrameOffsetsTypeHandle;
|
||||
[ReadOnly]
|
||||
public ComponentLookup<GPUAnimationEngineTag> gpuEngineTagLookup;
|
||||
[ReadOnly]
|
||||
public EntityTypeHandle entityTypeHandle;
|
||||
|
||||
public ComponentTypeHandle<GPUAttachmentBoneIndexMPComponent> gpuAttachmentBoneIndexTypeHandle;
|
||||
public ComponentTypeHandle<GPUAttachmentToBoneTransformMPComponent> gpuAttachmentToBoneTransformTypeHandle;
|
||||
public ComponentTypeHandle<GPURigEntityLocalToWorldMPComponent> gpuRigEntityLocalToWorldTypeHandle;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask)
|
||||
{
|
||||
var gpuAttachments = chunk.GetNativeArray(ref gpuAttachmentTypeHandle);
|
||||
var gpuAttachmentBoneIndices = chunk.GetNativeArray(ref gpuAttachmentBoneIndexTypeHandle);
|
||||
var gpuAttachmentToBoneTransforms = chunk.GetNativeArray(ref gpuAttachmentToBoneTransformTypeHandle);
|
||||
var gpuRigEntityLocalToWorlds = chunk.GetNativeArray(ref gpuRigEntityLocalToWorldTypeHandle);
|
||||
var entities = chunk.GetNativeArray(entityTypeHandle);
|
||||
|
||||
var cee = new ChunkEntityEnumerator(useEnabledMask, chunkEnabledMask, chunk.Count);
|
||||
while (cee.NextEntityIndex(out var i))
|
||||
{
|
||||
var gpuAttachment = gpuAttachments[i];
|
||||
var gpuAttachmentBoneIndex = gpuAttachmentBoneIndices[i];
|
||||
var gpuAttachmentToBoneTransform = gpuAttachmentToBoneTransforms[i];
|
||||
var gpuRigEntityToL2W = gpuRigEntityLocalToWorlds[i];
|
||||
var e = entities[i];
|
||||
|
||||
Execute(e, ref gpuAttachmentBoneIndex, ref gpuAttachmentToBoneTransform, ref gpuRigEntityToL2W, gpuAttachment, chunk);
|
||||
|
||||
gpuAttachmentBoneIndices[i] = gpuAttachmentBoneIndex;
|
||||
gpuAttachmentToBoneTransforms[i] = gpuAttachmentToBoneTransform;
|
||||
gpuRigEntityLocalToWorlds[i] = gpuRigEntityToL2W;
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Execute
|
||||
(
|
||||
Entity e,
|
||||
ref GPUAttachmentBoneIndexMPComponent abi,
|
||||
ref GPUAttachmentToBoneTransformMPComponent atbt,
|
||||
ref GPURigEntityLocalToWorldMPComponent rltw,
|
||||
in GPUAttachmentComponent ac,
|
||||
in ArchetypeChunk chunk
|
||||
)
|
||||
{
|
||||
abi.boneIndex = -1;
|
||||
|
||||
var pbi = GetParentRigBoneEntity(e, ac);
|
||||
if (pbi.boneEntity == Entity.Null)
|
||||
return;
|
||||
|
||||
// Act as ordinary attachment in case of CPU animation engine
|
||||
if (!AnimationUtils.IsGPUAnimator(pbi.rigEntity, gpuEngineTagLookup))
|
||||
return;
|
||||
|
||||
atbt.value = pbi.attachmentToBoneTransform;
|
||||
TransformHelpers.ComputeWorldTransformMatrix(pbi.rigEntity, out rltw.value, ref localTransformLookup, ref parentLookup, ref postTransformMatrixLookup);
|
||||
|
||||
if (frameOffsetsLookup.TryGetComponent(pbi.rigEntity, out var boneOffset) &&
|
||||
EntityTools.TryGetChunkComponentData(chunk, pbi.rigEntity, ref gpuRigFrameOffsetsTypeHandle, out var chunkFrameOffsets))
|
||||
{
|
||||
boneOffset.AddOffsets(chunkFrameOffsets);
|
||||
abi.boneIndex = boneOffset.boneIndex + pbi.boneIndexInRig;
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ParentBoneInfo GetParentRigBoneEntity(Entity e, GPUAttachmentComponent ac)
|
||||
{
|
||||
AnimatorEntityRefComponent rbr = default;
|
||||
float4x4 entityToBoneTransform = float4x4.identity;
|
||||
var parent = new Parent() { Value = e};
|
||||
|
||||
do
|
||||
{
|
||||
if (!localTransformLookup.TryGetComponent(parent.Value, out var plt))
|
||||
break;
|
||||
if (rigBoneRefLookup.TryGetComponent(parent.Value, out rbr))
|
||||
break;
|
||||
|
||||
var pltMat = plt.ToMatrix();
|
||||
if (Hint.Unlikely(postTransformMatrixLookup.TryGetComponent(parent.Value, out var postTransformMatrixComponent)))
|
||||
{
|
||||
pltMat = math.mul(pltMat, postTransformMatrixComponent.Value);
|
||||
}
|
||||
|
||||
entityToBoneTransform = math.mul(pltMat, entityToBoneTransform);
|
||||
}
|
||||
while (parentLookup.TryGetComponent(parent.Value, out parent));
|
||||
|
||||
var rv = new ParentBoneInfo();
|
||||
if (rbr.animatorEntity != Entity.Null)
|
||||
{
|
||||
rv.boneEntity = parent.Value;
|
||||
rv.rigEntity = rbr.animatorEntity;
|
||||
rv.boneIndexInRig = math.select(rbr.boneIndexInAnimationRig, ac.attachedBoneIndex, ac.attachedBoneIndex >= 0);
|
||||
rv.attachmentToBoneTransform = entityToBoneTransform;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c162722eee64c5047bbb4a2d7c06ae69
|
||||
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/GPUAnimationEngine/GPUAttachementsUpdateSystem_Jobs.cs
|
||||
uploadId: 897522
|
||||
+162
@@ -0,0 +1,162 @@
|
||||
|
||||
using System;
|
||||
using Unity.Collections;
|
||||
using Unity.Entities;
|
||||
using Unity.Jobs.LowLevel.Unsafe;
|
||||
using Unity.Mathematics;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka
|
||||
{
|
||||
public struct GPUTrackSetPlacementData
|
||||
{
|
||||
public int keyFramesOffset;
|
||||
public int tracksOffset;
|
||||
public int trackGroupsOffset;
|
||||
public int hashTableOffset;
|
||||
|
||||
internal GPUStructures.TrackSet ToGPUTrackSet()
|
||||
{
|
||||
var rv = new GPUStructures.TrackSet()
|
||||
{
|
||||
tracksOffset = tracksOffset,
|
||||
keyFramesOffset = keyFramesOffset,
|
||||
trackGroupsOffset = trackGroupsOffset,
|
||||
trackGroupPHTOffset = hashTableOffset,
|
||||
};
|
||||
return rv;
|
||||
}
|
||||
|
||||
internal static GPUTrackSetPlacementData Empty()
|
||||
{
|
||||
var rv = new GPUTrackSetPlacementData()
|
||||
{
|
||||
tracksOffset = -1,
|
||||
keyFramesOffset = -1,
|
||||
trackGroupsOffset = -1,
|
||||
hashTableOffset = -1,
|
||||
};
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------------//
|
||||
|
||||
public struct GPUAnimationClipPlacementData
|
||||
{
|
||||
public BlobAssetReference<AnimationClipBlob> animationClipBlob;
|
||||
public int animationClipDataOffset;
|
||||
public GPUTrackSetPlacementData clipTracks;
|
||||
public GPUTrackSetPlacementData additiveRefPoseTracks;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------------//
|
||||
|
||||
public struct GPURigDefinitionPlacementData
|
||||
{
|
||||
public BlobAssetReference<RigDefinitionBlob> rigBlob;
|
||||
public int rigDefinitionIndex;
|
||||
public int rigBonesOffset;
|
||||
public int humanRotationDataOffset;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------------//
|
||||
|
||||
public struct GPUSkinnedMeshPlacementData
|
||||
{
|
||||
public BlobAssetReference<BoneRemapTableBlob> boneRemapTableBlob;
|
||||
public BlobAssetReference<SkinnedMeshInfoBlob> skinnedMeshInfo;
|
||||
public Hash128 hash;
|
||||
public int dataOffset;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------------//
|
||||
|
||||
public struct GPUAvatarMaskPlacementData
|
||||
{
|
||||
public BlobAssetReference<AvatarMaskBlob> avatarMaskBlob;
|
||||
public int dataOffset;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------------//
|
||||
|
||||
public struct GPURuntimeAnimationData: IComponentData, IDisposable
|
||||
{
|
||||
public NativeList<GPUAnimationClipPlacementData> newAnimationsList;
|
||||
public NativeList<GPURigDefinitionPlacementData> newRigsList;
|
||||
public NativeList<GPUSkinnedMeshPlacementData> newSkinnedMeshesDataList;
|
||||
public NativeList<GPUAvatarMaskPlacementData> newAvatarMasksList;
|
||||
|
||||
public NativeParallelHashMap<Hash128, int> animationClipsMap;
|
||||
public uint2 totalGPUAnimationClipsSize;
|
||||
public uint maxTrackKeyframesCount;
|
||||
|
||||
public NativeParallelHashMap<Hash128, int> rigDefinitionsMap;
|
||||
public uint2 totalGPURigsCount;
|
||||
public uint2 totalGPUHumanRotationDataEntriesCount;
|
||||
public uint2 totalGPURigBonesCount;
|
||||
|
||||
public NativeParallelHashMap<Hash128, int> skinnedMeshesDataMap;
|
||||
public uint2 totalGPUSkinnedMeshBonesCount;
|
||||
public uint2 totalGPUSkinnedMeshesCount;
|
||||
|
||||
public NativeParallelHashMap<Hash128, int> avatarMasksDataMap;
|
||||
public uint2 totalGPUAvatarMasksDataCount;
|
||||
|
||||
public uint frameAnimatedBonesCounter;
|
||||
public uint frameAnimatedRigsCounter;
|
||||
public uint frameAnimationToProcessCounter;
|
||||
public NativeList<uint> frameSkinnedMeshesPerThreadCounters;
|
||||
public uint frameSkinnedMeshesCounter;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static GPURuntimeAnimationData Construct()
|
||||
{
|
||||
var initialCapacity = 0x1000;
|
||||
var rv = new GPURuntimeAnimationData();
|
||||
rv.newAnimationsList = new (initialCapacity, Allocator.Persistent);
|
||||
rv.newRigsList = new (initialCapacity, Allocator.Persistent);
|
||||
rv.newSkinnedMeshesDataList = new (initialCapacity, Allocator.Persistent);
|
||||
rv.newAvatarMasksList = new (initialCapacity, Allocator.Persistent);
|
||||
rv.animationClipsMap = new (initialCapacity, Allocator.Persistent);
|
||||
rv.rigDefinitionsMap = new (initialCapacity, Allocator.Persistent);
|
||||
rv.skinnedMeshesDataMap = new (initialCapacity, Allocator.Persistent);
|
||||
rv.avatarMasksDataMap = new (initialCapacity, Allocator.Persistent);
|
||||
|
||||
rv.totalGPUAnimationClipsSize = 0;
|
||||
rv.totalGPURigsCount = 0;
|
||||
rv.totalGPUHumanRotationDataEntriesCount = 0;
|
||||
rv.totalGPURigBonesCount = 0;
|
||||
rv.totalGPUSkinnedMeshBonesCount = 0;
|
||||
rv.totalGPUSkinnedMeshesCount = 0;
|
||||
rv.totalGPUAvatarMasksDataCount = 0;
|
||||
rv.maxTrackKeyframesCount = 0;
|
||||
|
||||
rv.frameAnimatedBonesCounter = 0;
|
||||
rv.frameAnimatedRigsCounter = 0;
|
||||
rv.frameAnimationToProcessCounter = 0;
|
||||
rv.frameSkinnedMeshesPerThreadCounters = new (JobsUtility.MaxJobThreadCount, Allocator.Persistent);
|
||||
rv.frameSkinnedMeshesCounter = 0;
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
newAnimationsList.Dispose();
|
||||
newRigsList.Dispose();
|
||||
newSkinnedMeshesDataList.Dispose();
|
||||
newAvatarMasksList.Dispose();
|
||||
animationClipsMap.Dispose();
|
||||
rigDefinitionsMap.Dispose();
|
||||
skinnedMeshesDataMap.Dispose();
|
||||
frameSkinnedMeshesPerThreadCounters.Dispose();
|
||||
avatarMasksDataMap.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 637b05431b241324098e50f624437bcb
|
||||
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/GPUAnimationEngine/GPURuntimeAnimationData.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,184 @@
|
||||
|
||||
using Unity.Collections;
|
||||
using Unity.Mathematics;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Rukhanka.GPUStructures
|
||||
{
|
||||
internal struct KeyFrame
|
||||
{
|
||||
public float v;
|
||||
public float inTan, outTan;
|
||||
public float time;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
internal struct Track
|
||||
{
|
||||
public uint props;
|
||||
public int2 keyFrameRange;
|
||||
|
||||
public uint bindingType
|
||||
{
|
||||
get => props & 0xf;
|
||||
set => props = value | props & 0xfffffff0;
|
||||
}
|
||||
|
||||
public uint channelIndex
|
||||
{
|
||||
get => props >> 4 & 3;
|
||||
set => props = value << 4 | props & 0xffffffcf;
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
internal struct TrackSet
|
||||
{
|
||||
public int keyFramesOffset;
|
||||
public int tracksOffset;
|
||||
public int trackGroupsOffset;
|
||||
public int trackGroupPHTOffset;
|
||||
public uint trackGroupPHTSeed;
|
||||
public uint trackGroupPHTSizeMask;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
internal struct AnimationClip
|
||||
{
|
||||
public uint4 hash;
|
||||
public TrackSet clipTracks;
|
||||
public TrackSet additiveReferencePoseTracks;
|
||||
public uint flags;
|
||||
public float cycleOffset;
|
||||
public float length;
|
||||
|
||||
public bool looped
|
||||
{
|
||||
get => new BitField32(flags).IsSet(1);
|
||||
set
|
||||
{
|
||||
var k = new BitField32(flags);
|
||||
k.SetBits(1, value);
|
||||
flags = k.Value;
|
||||
}
|
||||
}
|
||||
public bool loopPoseBlend
|
||||
{
|
||||
get => new BitField32(flags).IsSet(2);
|
||||
set
|
||||
{
|
||||
var k = new BitField32(flags);
|
||||
k.SetBits(2, value);
|
||||
flags = k.Value;
|
||||
}
|
||||
}
|
||||
public bool hasRootMotionCurves
|
||||
{
|
||||
get => new BitField32(flags).IsSet(3);
|
||||
set
|
||||
{
|
||||
var k = new BitField32(flags);
|
||||
k.SetBits(3, value);
|
||||
flags = k.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
internal struct BoneTransform
|
||||
{
|
||||
public float3 pos;
|
||||
public float4 rot;
|
||||
public float3 scale;
|
||||
|
||||
public BoneTransform(Rukhanka.BoneTransform bt)
|
||||
{
|
||||
pos = bt.pos;
|
||||
rot = bt.rot.value;
|
||||
scale = bt.scale;
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
internal struct RigBone
|
||||
{
|
||||
public uint hash;
|
||||
public int parentBoneIndex;
|
||||
public BoneTransform refPose;
|
||||
public int humanBodyPart;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
internal struct RigDefinition
|
||||
{
|
||||
public uint4 hash;
|
||||
public int2 rigBonesRange;
|
||||
public int rootBoneIndex;
|
||||
public int humanRotationDataOffset;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
internal struct AnimationJob
|
||||
{
|
||||
public int rigDefinitionIndex;
|
||||
public int animatedBoneIndexOffset;
|
||||
public int2 animationsToProcessRange;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
internal struct AnimationToProcess
|
||||
{
|
||||
public int animationClipAddress;
|
||||
public float weight;
|
||||
public float time;
|
||||
public int blendMode;
|
||||
public float layerWeight;
|
||||
public int layerIndex;
|
||||
public int avatarMaskDataOffset;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
internal struct AnimatedBoneWorkload
|
||||
{
|
||||
public int boneIndexInRig;
|
||||
public int animationJobIndex;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
internal struct SkinnedMeshWorkload
|
||||
{
|
||||
public int skinMatrixBaseOutIndex;
|
||||
public int boneRemapTableIndex;
|
||||
public int skinMatricesCount;
|
||||
public int rootBoneIndex;
|
||||
public int animatedBoneIndexOffset;
|
||||
public float4x4 skinnedMeshInverseTransform;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
internal struct SkinnedMeshBoneData
|
||||
{
|
||||
public int boneRemapIndex;
|
||||
public float3x4 bindPose;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
internal struct HumanRotationData
|
||||
{
|
||||
public float3 minMuscleAngles, maxMuscleAngles;
|
||||
public float4 preRot, postRot;
|
||||
public float3 sign;
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b91a58e950e22e9418767c351497b667
|
||||
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/GPUAnimationEngine/GPUStructures.cs
|
||||
uploadId: 897522
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e8c4107a6c99fbf4198757fac93c97cc
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
|
||||
#pragma kernel ProcessAnimations
|
||||
#pragma kernel MakeRigSpaceBoneTransforms
|
||||
#pragma kernel ComputeSkinMatrices
|
||||
|
||||
//#pragma enable_d3d11_debug_symbols
|
||||
//#pragma use_dxc
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "Packages/com.rukhanka.animation/Rukhanka.Runtime/Common/Shaders/ShaderConf.hlsl"
|
||||
#include "Packages/com.rukhanka.animation/Rukhanka.Runtime/Common/Shaders/Debug.hlsl"
|
||||
#include "Packages/com.rukhanka.animation/Rukhanka.Runtime/GPUAnimationEngine/Resources/GPUStructures.hlsl"
|
||||
#include "Packages/com.rukhanka.animation/Rukhanka.Runtime/GPUAnimationEngine/Resources/MakeSkinMatrices.hlsl"
|
||||
#include "Packages/com.rukhanka.animation/Rukhanka.Runtime/GPUAnimationEngine/Resources/ProcessAnimations.hlsl"
|
||||
#include "Packages/com.rukhanka.animation/Rukhanka.Runtime/GPUAnimationEngine/Resources/MakeRigSpaceBoneTransforms.hlsl"
|
||||
#include "Packages/com.rukhanka.animation/Rukhanka.Runtime/GPUAnimationEngine/Resources/MakeSkinMatrices.hlsl"
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7fb582dc2671eaa40869fc5ceea5f2c4
|
||||
ComputeShaderImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/GPUAnimationEngine/Resources/GPUAnimationEngine.compute
|
||||
uploadId: 897522
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fb0379f8129846944bf9d56dca4b60fc
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
#pragma once
|
||||
|
||||
//#pragma enable_d3d11_debug_symbols
|
||||
|
||||
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/UnityInstancing.hlsl"
|
||||
#include "Packages/com.rukhanka.animation/Rukhanka.Runtime/GPUAnimationEngine/Resources/GPUStructures.hlsl"
|
||||
#include "Packages/com.rukhanka.animation/Rukhanka.Runtime/Common/Shaders/ShaderConf.hlsl"
|
||||
#include "Packages/com.rukhanka.animation/Rukhanka.Runtime/Common/Shaders/Debug.hlsl"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#if defined(DOTS_INSTANCING_ON)
|
||||
|
||||
UNITY_DOTS_INSTANCING_START(MaterialPropertyMetadata)
|
||||
UNITY_DOTS_INSTANCED_PROP(int, _RukhankaGPUBoneIndex)
|
||||
UNITY_DOTS_INSTANCED_PROP(float4x4, _RukhankaAttachmentToBoneTransform)
|
||||
UNITY_DOTS_INSTANCED_PROP(float4x4, _RukhankaAnimatedEntityLocalToWorld)
|
||||
UNITY_DOTS_INSTANCING_END(MaterialPropertyMetadata)
|
||||
|
||||
StructuredBuffer<BoneTransform> boneLocalTransforms;
|
||||
StructuredBuffer<BoneTransform> rigSpaceBoneTransformsBuf;
|
||||
|
||||
#endif
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void GPUAttachmentMeshMover_float(in float3 vertex, in float3 normal, in float3 tangent, out float3 animatedVertex, out float3 animatedNormal, out float3 animatedTangent)
|
||||
{
|
||||
#if defined(DOTS_INSTANCING_ON)
|
||||
int gpuBoneIndex = UNITY_ACCESS_DOTS_INSTANCED_PROP(int, _RukhankaGPUBoneIndex);
|
||||
if (gpuBoneIndex < 0)
|
||||
{
|
||||
animatedVertex = vertex;
|
||||
animatedNormal = normal;
|
||||
animatedTangent = tangent;
|
||||
return;
|
||||
}
|
||||
|
||||
float4x4 attachmentToBoneTransform = UNITY_ACCESS_DOTS_INSTANCED_PROP(float4x4, _RukhankaAttachmentToBoneTransform);
|
||||
float4x4 entityRootLocalToWorld = UNITY_ACCESS_DOTS_INSTANCED_PROP(float4x4, _RukhankaAnimatedEntityLocalToWorld);
|
||||
|
||||
animatedVertex = mul(attachmentToBoneTransform, float4(vertex, 1)).xyz;
|
||||
CHECK_STRUCTURED_BUFFER_OUT_OF_BOUNDS(RUKHANKADEBUGMARKERS_GPUANIMATOR_GPUATTACHMENT_RIG_SPACE_BONE_TRANSFORMS_READ, gpuBoneIndex, rigSpaceBoneTransformsBuf)
|
||||
BoneTransform bt = rigSpaceBoneTransformsBuf[gpuBoneIndex];
|
||||
|
||||
float3 transformedPos = BoneTransform::TransformPoint(bt, animatedVertex);
|
||||
animatedVertex = transformedPos;
|
||||
animatedVertex = mul(entityRootLocalToWorld, float4(animatedVertex, 1)).xyz;
|
||||
animatedVertex = GetCameraRelativePositionWS(animatedVertex);
|
||||
animatedVertex = TransformWorldToObject(animatedVertex);
|
||||
|
||||
animatedNormal = mul((float3x3)attachmentToBoneTransform, normal);
|
||||
animatedNormal = BoneTransform::TransformDirection(bt, animatedNormal);
|
||||
animatedNormal = mul((float3x3)entityRootLocalToWorld, animatedNormal);
|
||||
animatedNormal = TransformWorldToObjectDir(animatedNormal);
|
||||
|
||||
animatedTangent = mul((float3x3)attachmentToBoneTransform, tangent);
|
||||
animatedTangent = BoneTransform::TransformDirection(bt, animatedTangent);
|
||||
animatedTangent = mul((float3x3)entityRootLocalToWorld, animatedTangent);
|
||||
animatedTangent = TransformWorldToObjectDir(animatedTangent);
|
||||
#else
|
||||
animatedVertex = vertex;
|
||||
animatedNormal = normal;
|
||||
animatedTangent = tangent;
|
||||
#endif
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 72cf2095e0e922349b94b239dcf25a14
|
||||
ShaderIncludeImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/GPUAnimationEngine/Resources/GPUAttachment/GPUAttachmentShaderNode.hlsl
|
||||
uploadId: 897522
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
|
||||
#ifndef GPU_STRUCTURES_HLSL_
|
||||
#define GPU_STRUCTURES_HLSL_
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// struct RigDefinition
|
||||
ByteAddressBuffer rigDefinitions;
|
||||
// struct RigBone
|
||||
ByteAddressBuffer rigBones;
|
||||
// struct AnimationClip
|
||||
ByteAddressBuffer animationClips;
|
||||
// struct HumanRotationData
|
||||
ByteAddressBuffer humanRotationDataBuffer;
|
||||
int animatedBonesCount;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "Packages/com.rukhanka.animation/Rukhanka.Runtime/Common/Shaders/Debug.hlsl"
|
||||
#include "Packages/com.rukhanka.animation/Rukhanka.Runtime/Common/Shaders/GPUStructures/BoneTransform.hlsl"
|
||||
#include "Packages/com.rukhanka.animation/Rukhanka.Runtime/Common/Shaders/GPUStructures/AnimationClip.hlsl"
|
||||
#include "Packages/com.rukhanka.animation/Rukhanka.Runtime/Common/Shaders/GPUStructures/Track.hlsl"
|
||||
#include "Packages/com.rukhanka.animation/Rukhanka.Runtime/Common/Shaders/GPUStructures/SkinnedMeshBone.hlsl"
|
||||
#include "Packages/com.rukhanka.animation/Rukhanka.Runtime/Common/Shaders/GPUStructures/RigDefinition.hlsl"
|
||||
#include "Packages/com.rukhanka.animation/Rukhanka.Runtime/Common/Shaders/GPUStructures/RigBone.hlsl"
|
||||
#include "Packages/com.rukhanka.animation/Rukhanka.Runtime/Common/Shaders/GPUStructures/HumanRotationData.hlsl"
|
||||
#include "Packages/com.rukhanka.animation/Rukhanka.Runtime/Common/Shaders/GPUStructures/AnimationToProcess.hlsl"
|
||||
#include "Packages/com.rukhanka.animation/Rukhanka.Runtime/Common/Shaders/GPUStructures/AvatarMask.hlsl"
|
||||
#include "Packages/com.rukhanka.animation/Rukhanka.Runtime/Common/Shaders/GPUStructures/PerfectHashTable.hlsl"
|
||||
#include "Packages/com.rukhanka.animation/Rukhanka.Runtime/Common/Shaders/GPUStructures/SkinMatrix.hlsl"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct AnimationJob
|
||||
{
|
||||
int rigDefinitionIndex;
|
||||
int animatedBoneIndexOffset;
|
||||
int2 animationsToProcessRange;
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct SkinnedMeshWorkload
|
||||
{
|
||||
int skinMatrixBaseOutIndex;
|
||||
int boneRemapTableIndex;
|
||||
int skinMatricesCount;
|
||||
int rootBoneIndex;
|
||||
int animatedBoneIndexOffset;
|
||||
float4x4 skinnedRootBoneToEntityTransform;
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct AnimatedBoneWorkload
|
||||
{
|
||||
int boneIndexInRig;
|
||||
int animationJobIndex;
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
StructuredBuffer<AnimatedBoneWorkload> animatedBoneWorkload;
|
||||
StructuredBuffer<AnimationJob> animationJobs;
|
||||
StructuredBuffer<AnimationToProcess> animationsToProcess;
|
||||
|
||||
#endif
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 73a44b85a6e0ce04896ab06edf8b01f5
|
||||
ShaderIncludeImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/GPUAnimationEngine/Resources/GPUStructures.hlsl
|
||||
uploadId: 897522
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
#ifndef MAKE_WORLD_SPACE_BONE_TRANSFORMS_HLSL_
|
||||
#define MAKE_WORLD_SPACE_BONE_TRANSFORMS_HLSL_
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
RWStructuredBuffer<BoneTransform> outBoneTransforms;
|
||||
StructuredBuffer<BoneTransform> boneLocalTransforms;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[numthreads(256, 1, 1)]
|
||||
void MakeRigSpaceBoneTransforms(uint tid: SV_DispatchThreadID)
|
||||
{
|
||||
if (tid >= (uint)animatedBonesCount)
|
||||
return;
|
||||
|
||||
CHECK_STRUCTURED_BUFFER_OUT_OF_BOUNDS(RUKHANKADEBUGMARKERS_GPUANIMATOR_MAKE_RIG_SPACE_BONE_TRANSFORMS_ANIMATED_BONE_WORKLOAD_READ, tid, animatedBoneWorkload);
|
||||
AnimatedBoneWorkload boneWorkload = animatedBoneWorkload[tid];
|
||||
|
||||
CHECK_STRUCTURED_BUFFER_OUT_OF_BOUNDS(RUKHANKADEBUGMARKERS_GPUANIMATOR_MAKE_RIG_SPACE_BONE_TRANSFORMS_ANIMATION_JOBS_READ, boneWorkload.animationJobIndex, animationJobs);
|
||||
AnimationJob animationJob = animationJobs[boneWorkload.animationJobIndex];
|
||||
RigDefinition rigDef = RigDefinition::ReadFromRawBuffer(rigDefinitions, animationJob.rigDefinitionIndex);
|
||||
RigBone rigBone = RigBone::ReadFromRawBuffer(rigBones, rigDef.rigBonesRange.x + boneWorkload.boneIndexInRig);
|
||||
|
||||
int absoluteBoneIndex = animationJob.animatedBoneIndexOffset + boneWorkload.boneIndexInRig;
|
||||
CHECK_STRUCTURED_BUFFER_OUT_OF_BOUNDS(RUKHANKADEBUGMARKERS_GPUANIMATOR_MAKE_RIG_SPACE_BONE_TRANSFORMS_BONE_LOCAL_TRANSFORMS_READ0, absoluteBoneIndex, boneLocalTransforms);
|
||||
BoneTransform bt = boneLocalTransforms[absoluteBoneIndex];
|
||||
int parentBoneIndex = rigBone.parentBoneIndex;
|
||||
while (parentBoneIndex > 0)
|
||||
{
|
||||
RigBone parentBoneData = RigBone::ReadFromRawBuffer(rigBones, rigDef.rigBonesRange.x + parentBoneIndex);
|
||||
int absoluteParentBoneIndex = animationJob.animatedBoneIndexOffset + parentBoneIndex;
|
||||
CHECK_STRUCTURED_BUFFER_OUT_OF_BOUNDS(RUKHANKADEBUGMARKERS_GPUANIMATOR_MAKE_RIG_SPACE_BONE_TRANSFORMS_BONE_LOCAL_TRANSFORMS_READ1, absoluteParentBoneIndex, boneLocalTransforms);
|
||||
BoneTransform parentBoneTransform = boneLocalTransforms[absoluteParentBoneIndex];
|
||||
bt = BoneTransform::Multiply(parentBoneTransform, bt);
|
||||
parentBoneIndex = parentBoneData.parentBoneIndex;
|
||||
}
|
||||
CHECK_STRUCTURED_BUFFER_OUT_OF_BOUNDS(RUKHANKADEBUGMARKERS_GPUANIMATOR_MAKE_RIG_SPACE_BONE_TRANSFORMS_OUT_BONE_TRANSFORMS_WRITE, absoluteBoneIndex, outBoneTransforms);
|
||||
outBoneTransforms[absoluteBoneIndex] = bt;
|
||||
}
|
||||
|
||||
#endif // MAKE_WORLD_SPACE_BONE_TRANSFORMS_HLSL_
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a93c55826c7c8b44c9a655d386904e4a
|
||||
ShaderIncludeImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/GPUAnimationEngine/Resources/MakeRigSpaceBoneTransforms.hlsl
|
||||
uploadId: 897522
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
#ifndef MAKE_SKIN_MATRICES_HLSL_
|
||||
#define MAKE_SKIN_MATRICES_HLSL_
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
RWByteAddressBuffer outSkinMatrices;
|
||||
StructuredBuffer<BoneTransform> rigSpaceBoneTransformsBuf;
|
||||
StructuredBuffer<SkinnedMeshWorkload> skinMatrixWorkloadBuf;
|
||||
ByteAddressBuffer skinnedMeshBoneData;
|
||||
uint totalSkinnedMeshes;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[numthreads(128, 1, 1)]
|
||||
void ComputeSkinMatrices(uint tid: SV_DispatchThreadID)
|
||||
{
|
||||
if (tid >= totalSkinnedMeshes)
|
||||
return;
|
||||
|
||||
CHECK_STRUCTURED_BUFFER_OUT_OF_BOUNDS(RUKHANKADEBUGMARKERS_GPUANIMATOR_COMPUTE_SKIN_MATRICES_SKIN_MATRIX_WORKLOAD_READ, tid, skinMatrixWorkloadBuf);
|
||||
SkinnedMeshWorkload smw = skinMatrixWorkloadBuf[tid];
|
||||
float4x4 w2l = smw.skinnedRootBoneToEntityTransform;
|
||||
|
||||
for (int i = 0; i < smw.skinMatricesCount; ++i)
|
||||
{
|
||||
SkinnedMeshBone smb = SkinnedMeshBone::ReadFromRawBuffer(skinnedMeshBoneData, smw.boneRemapTableIndex + i);
|
||||
|
||||
int boneTransformIndex = smb.boneRemapIndex + smw.animatedBoneIndexOffset;
|
||||
CHECK_STRUCTURED_BUFFER_OUT_OF_BOUNDS(RUKHANKADEBUGMARKERS_GPUANIMATOR_COMPUTE_SKIN_MATRICES_RIG_SPACE_BONE_TRANSFORMS_READ, boneTransformIndex, rigSpaceBoneTransformsBuf);
|
||||
BoneTransform bt = rigSpaceBoneTransformsBuf[boneTransformIndex];
|
||||
|
||||
float4x4 skinMatrix = bt.ToFloat4x4();
|
||||
skinMatrix = mul(w2l, skinMatrix);
|
||||
float4x4 outSkinMatrix = mul(skinMatrix, smb.bindPose);
|
||||
|
||||
int skinMatrixOutIndex = smw.skinMatrixBaseOutIndex + i;
|
||||
SkinMatrix::WriteToRawBuffer(outSkinMatrices, (float3x4)outSkinMatrix, skinMatrixOutIndex);
|
||||
}
|
||||
}
|
||||
|
||||
#endif //MAKE_SKIN_MATRICES_HLSL_
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f0c8e199e88b9894b92573b0636c8e6b
|
||||
ShaderIncludeImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/GPUAnimationEngine/Resources/MakeSkinMatrices.hlsl
|
||||
uploadId: 897522
|
||||
+246
@@ -0,0 +1,246 @@
|
||||
#ifndef PROCESS_ANIMATIONS_HLSL_
|
||||
#define PROCESS_ANIMATIONS_HLSL_
|
||||
|
||||
#include "Packages/com.rukhanka.animation/Rukhanka.Runtime/GPUAnimationEngine/Resources/TrackGroupSampler.hlsl"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define NUM_MAXIMUM_LAYER_WEIGHTS 16
|
||||
RWStructuredBuffer<BoneTransform> outAnimatedBones;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct LayerInfo
|
||||
{
|
||||
int index;
|
||||
float weight;
|
||||
int blendMode;
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
float2 NormalizeAnimationTime(float at, AnimationClip ac)
|
||||
{
|
||||
at += ac.cycleOffset;
|
||||
if (at < 0) at = 1 + at;
|
||||
float normalizedTime = ac.IsLooped() ? frac(at) : saturate(at);
|
||||
float timeInSeconds = normalizedTime * ac.length;
|
||||
float2 rv = float2(timeInSeconds, normalizedTime);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
BoneTransform MakeAdditiveAnimation(BoneTransform bonePose, BoneTransform zeroFramePose)
|
||||
{
|
||||
BoneTransform rv;
|
||||
rv.pos = bonePose.pos - zeroFramePose.pos;
|
||||
Quaternion conjugateZFRot = Quaternion::NormalizeSafe(Quaternion::Conjugate(zeroFramePose.rot));
|
||||
conjugateZFRot = Quaternion::ShortestRotation(bonePose.rot, conjugateZFRot);
|
||||
rv.rot = Quaternion::Multiply(conjugateZFRot, Quaternion::Normalize(bonePose.rot));
|
||||
rv.scale = bonePose.scale / zeroFramePose.scale;
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
BoneTransform CalculateLoopPose(BoneTransform bonePose, TrackSet ts, HumanRotationData hrd, float normalizedTime)
|
||||
{
|
||||
float lerpFactor = normalizedTime;
|
||||
|
||||
TrackSampler ffSampler = CreateFirstFrameTrackSampler();
|
||||
BoneTransformAndFlags rootPoseStart = SampleTrackGroup(ts, ffSampler, hrd);
|
||||
TrackSampler lfSampler = CreateLastFrameTrackSampler();
|
||||
BoneTransformAndFlags rootPoseEnd = SampleTrackGroup(ts, lfSampler, hrd);
|
||||
|
||||
float3 dPos = rootPoseEnd.bt.pos - rootPoseStart.bt.pos;
|
||||
Quaternion dRot = Quaternion::Multiply(Quaternion::Conjugate(rootPoseEnd.bt.rot), rootPoseStart.bt.rot);
|
||||
|
||||
BoneTransform rv;
|
||||
rv.pos = bonePose.pos - dPos * lerpFactor;
|
||||
rv.rot = Quaternion::Multiply(bonePose.rot, Quaternion::Slerp(Quaternion::Identity(), dRot, lerpFactor));
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool SampleAnimation(AnimationClip ac, uint baseAddress, float2 animTime, int rigBoneIndex, uint rigBoneHash, int blendMode, HumanRotationData hrd, out BoneTransformAndFlags btf)
|
||||
{
|
||||
btf = BoneTransformAndFlags::Identity();
|
||||
|
||||
TrackSet tsClip = ac.clipTracks;
|
||||
tsClip.OffsetByAddress(baseAddress);
|
||||
|
||||
int trackGroupIndex = tsClip.GetTrackGroupIndex(rigBoneHash);
|
||||
if (trackGroupIndex < 0)
|
||||
return false;
|
||||
|
||||
tsClip.trackGroupsOffset += trackGroupIndex * 4;
|
||||
|
||||
float timeInSeconds = animTime.x;
|
||||
TrackSampler tSampler = CreateDefaultTrackSampler(timeInSeconds);
|
||||
btf = SampleTrackGroup(tsClip, tSampler, hrd);
|
||||
|
||||
if (blendMode == BLEND_MODE_ADDITIVE)
|
||||
{
|
||||
TrackSet additiveTrackSet = ac.clipTracks;
|
||||
if (ac.additiveReferencePoseTracks.keyFramesOffset >= 0)
|
||||
additiveTrackSet = ac.additiveReferencePoseTracks;
|
||||
|
||||
additiveTrackSet.OffsetByAddress(baseAddress);
|
||||
|
||||
int additiveTrackGroupIndex = QueryPerfectHashTable(rigBoneHash, additiveTrackSet.trackGroupPHTSeed, additiveTrackSet.trackGroupPHTOffset, additiveTrackSet.trackGroupPHTSizeMask);
|
||||
if (additiveTrackGroupIndex >= 0)
|
||||
{
|
||||
TrackSampler ffSampler = CreateFirstFrameTrackSampler();
|
||||
additiveTrackSet.trackGroupsOffset += additiveTrackGroupIndex * 4;
|
||||
BoneTransformAndFlags additiveFramePose = SampleTrackGroup(additiveTrackSet, ffSampler, hrd);
|
||||
btf.bt = MakeAdditiveAnimation(btf.bt, additiveFramePose.bt);
|
||||
}
|
||||
}
|
||||
|
||||
bool calculateLoopPose = ac.LoopPoseBlend() & rigBoneIndex != 0;
|
||||
if (calculateLoopPose)
|
||||
{
|
||||
btf.bt = CalculateLoopPose(btf.bt, tsClip, hrd, animTime.y);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
BoneTransform AppendScaledPose(BoneTransform curPose, BoneTransform addedPose, float weight)
|
||||
{
|
||||
BoneTransform rv;
|
||||
rv.pos = curPose.pos + addedPose.pos * weight;
|
||||
rv.rot = Quaternion::Nlerp(curPose.rot, addedPose.rot, weight);
|
||||
rv.scale = curPose.scale + addedPose.scale * weight;
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
BoneTransform BlendLayerPose(BoneTransform curPose, BoneTransform layerPose, BoneTransform refPose, LayerInfo layerInfo, float weightSum, float3 layerFlags)
|
||||
{
|
||||
BoneTransform rv = curPose;
|
||||
|
||||
// Override
|
||||
if (layerInfo.blendMode == BLEND_MODE_OVERRIDE)
|
||||
{
|
||||
if (weightSum < 1)
|
||||
layerPose = AppendScaledPose(layerPose, refPose, max(0, 1 - weightSum));
|
||||
|
||||
if (layerFlags.x > 0)
|
||||
rv.pos = lerp(curPose.pos, layerPose.pos, layerInfo.weight);
|
||||
|
||||
if (layerFlags.y > 0)
|
||||
{
|
||||
layerPose.rot = Quaternion::ShortestRotation(curPose.rot, layerPose.rot);
|
||||
rv.rot = Quaternion::Nlerp(curPose.rot, layerPose.rot, layerInfo.weight);
|
||||
}
|
||||
|
||||
if (layerFlags.z > 0)
|
||||
rv.scale = lerp(curPose.scale, layerPose.scale, layerInfo.weight);
|
||||
}
|
||||
// Additive
|
||||
else
|
||||
{
|
||||
if (layerFlags.x > 0)
|
||||
rv.pos = curPose.pos + layerPose.pos * layerInfo.weight;
|
||||
|
||||
if (layerFlags.y > 0)
|
||||
{
|
||||
Quaternion layerRot;
|
||||
layerRot.value = float4(layerPose.rot.value.xyz * layerInfo.weight, layerPose.rot.value.w);
|
||||
layerRot = Quaternion::NormalizeSafe(layerRot);
|
||||
layerRot = Quaternion::ShortestRotation(curPose.rot, layerRot);
|
||||
rv.rot = Quaternion::Multiply(curPose.rot, layerRot);
|
||||
}
|
||||
|
||||
if (layerFlags.z > 0)
|
||||
rv.scale = curPose.scale * lerp(1, layerPose.scale, layerInfo.weight);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
LayerInfo GetLayerInfoFromAnimation(AnimationToProcess atp)
|
||||
{
|
||||
LayerInfo rv;
|
||||
rv.weight = atp.layerWeight;
|
||||
rv.blendMode = atp.blendMode;
|
||||
rv.index = atp.layerIndex;
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[numthreads(128, 1, 1)]
|
||||
void ProcessAnimations(uint tid: SV_DispatchThreadID)
|
||||
{
|
||||
if (tid >= (uint)animatedBonesCount)
|
||||
return;
|
||||
|
||||
CHECK_STRUCTURED_BUFFER_OUT_OF_BOUNDS(RUKHANKADEBUGMARKERS_GPUANIMATOR_PROCESS_ANIMATIONS_ANIMATED_BONE_WORKLOAD_READ, tid, animatedBoneWorkload);
|
||||
AnimatedBoneWorkload boneWorkload = animatedBoneWorkload[tid];
|
||||
|
||||
CHECK_STRUCTURED_BUFFER_OUT_OF_BOUNDS(RUKHANKADEBUGMARKERS_GPUANIMATOR_PROCESS_ANIMATIONS_ANIMATION_JOBS_READ, boneWorkload.animationJobIndex, animationJobs);
|
||||
AnimationJob animationJob = animationJobs[boneWorkload.animationJobIndex];
|
||||
|
||||
RigDefinition rigDef = RigDefinition::ReadFromRawBuffer(rigDefinitions, animationJob.rigDefinitionIndex);
|
||||
RigBone rigBone = RigBone::ReadFromRawBuffer(rigBones, rigDef.rigBonesRange.x + boneWorkload.boneIndexInRig);
|
||||
|
||||
HumanRotationData hrd = (HumanRotationData)0;
|
||||
if (rigDef.humanRotationDataRange.x >= 0)
|
||||
hrd = HumanRotationData::ReadFromRawBuffer(humanRotationDataBuffer, rigDef.humanRotationDataRange.x + boneWorkload.boneIndexInRig);
|
||||
|
||||
BoneTransform blendedBonePose = rigBone.refPose;
|
||||
BoneTransform layerPose = BoneTransform::Zero();
|
||||
float weightSum = 0;
|
||||
float3 layerFlags = 0;
|
||||
LayerInfo layerInfo = (LayerInfo)0;
|
||||
|
||||
int atpIndexStart = animationJob.animationsToProcessRange.x;
|
||||
int atpIndexEnd = animationJob.animationsToProcessRange.x + animationJob.animationsToProcessRange.y;
|
||||
for (int i = atpIndexStart; i < atpIndexEnd; ++i)
|
||||
{
|
||||
AnimationToProcess atp = animationsToProcess[i];
|
||||
bool inAvatarMask = IsBoneInAvatarMask(atp.avatarMaskDataOffset, rigBone.humanBodyPart, boneWorkload.boneIndexInRig);
|
||||
|
||||
if (atp.animationClipAddress < 0 || atp.weight == 0 || atp.layerWeight == 0 || !inAvatarMask)
|
||||
continue;
|
||||
|
||||
LayerInfo curLayerInfo = GetLayerInfoFromAnimation(atp);
|
||||
if (layerInfo.index != curLayerInfo.index)
|
||||
{
|
||||
blendedBonePose = BlendLayerPose(blendedBonePose, layerPose, rigBone.refPose, layerInfo, weightSum, layerFlags);
|
||||
weightSum = 0;
|
||||
layerFlags = 0;
|
||||
layerPose = BoneTransform::Zero();
|
||||
}
|
||||
layerInfo = curLayerInfo;
|
||||
|
||||
int baseAddress = atp.animationClipAddress;
|
||||
AnimationClip ac = AnimationClip::ReadFromRawBuffer(animationClips, baseAddress);
|
||||
float2 animTime = NormalizeAnimationTime(atp.time, ac);
|
||||
|
||||
BoneTransformAndFlags btf;
|
||||
if (SampleAnimation(ac, baseAddress, animTime, boneWorkload.boneIndexInRig, rigBone.hash, atp.blendMode, hrd, btf))
|
||||
{
|
||||
weightSum += atp.weight;
|
||||
layerFlags += btf.flags;
|
||||
layerPose = AppendScaledPose(layerPose, btf.bt, atp.weight);
|
||||
}
|
||||
}
|
||||
|
||||
blendedBonePose = BlendLayerPose(blendedBonePose, layerPose, rigBone.refPose, layerInfo, weightSum, layerFlags);
|
||||
|
||||
int outIndex = animationJob.animatedBoneIndexOffset + boneWorkload.boneIndexInRig;
|
||||
|
||||
CHECK_STRUCTURED_BUFFER_OUT_OF_BOUNDS(RUKHANKADEBUGMARKERS_GPUANIMATOR_PROCESS_ANIMATIONS_OUT_ANIMATED_BONES_WRITE, outIndex, outAnimatedBones);
|
||||
outAnimatedBones[outIndex] = blendedBonePose;
|
||||
}
|
||||
|
||||
#endif // PROCESS_ANIMATIONS_HLSL_
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 82f7bcf8fbdb6044181d80fc661b5392
|
||||
ShaderIncludeImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/GPUAnimationEngine/Resources/ProcessAnimations.hlsl
|
||||
uploadId: 897522
|
||||
+146
@@ -0,0 +1,146 @@
|
||||
#ifndef TRACK_GROUP_SAMPLER_HLSL_
|
||||
#define TRACK_GROUP_SAMPLER_HLSL_
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "Packages/com.rukhanka.animation/Rukhanka.Runtime/GPUAnimationEngine/Resources/TrackSampler.hlsl"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct BoneTransformAndFlags
|
||||
{
|
||||
BoneTransform bt;
|
||||
float3 flags;
|
||||
|
||||
static BoneTransformAndFlags Identity()
|
||||
{
|
||||
BoneTransformAndFlags rv;
|
||||
rv.bt = BoneTransform::Identity();
|
||||
rv.flags = 0;
|
||||
return rv;
|
||||
}
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Quaternion ApplyHumanoidPostTransform(HumanRotationData hrd, Quaternion q)
|
||||
{
|
||||
Quaternion rv = Quaternion::Multiply(Quaternion::Multiply(hrd.preRot, q), hrd.postRot);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
float3 MuscleRangeToRadians(float3 minA, float3 maxA, float3 muscle)
|
||||
{
|
||||
float3 negativeRange = min(muscle, 0);
|
||||
float3 positiveRange = max(0, muscle);
|
||||
float3 negativeRot = lerp(0, minA, -negativeRange);
|
||||
float3 positiveRot = lerp(0, maxA, +positiveRange);
|
||||
|
||||
float3 rv = negativeRot + positiveRot;
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Quaternion MuscleValuesToQuaternion(HumanRotationData humanBoneInfo, float3 muscleValues)
|
||||
{
|
||||
float3 r = MuscleRangeToRadians(humanBoneInfo.minMuscleAngles, humanBoneInfo.maxMuscleAngles, muscleValues);
|
||||
r *= humanBoneInfo.sign;
|
||||
|
||||
float3 rightVec = float3(1, 0, 0);
|
||||
float3 upVec = float3(0, 1, 0);
|
||||
float3 forwardVec = float3(0, 0, 1);
|
||||
|
||||
Quaternion qx = Quaternion::AxisAngle(rightVec, r.x);
|
||||
Quaternion qy = Quaternion::AxisAngle(upVec, r.y);
|
||||
Quaternion qz = Quaternion::AxisAngle(forwardVec, r.z);
|
||||
Quaternion qzy = Quaternion::Multiply(qz, qy);
|
||||
qzy.value.x = 0;
|
||||
Quaternion rv = Quaternion::Multiply(Quaternion::Normalize(qzy), qx);
|
||||
|
||||
rv = ApplyHumanoidPostTransform(humanBoneInfo, rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
BoneTransformAndFlags SampleTrackGroup(TrackSet ts, TrackSampler trackSampler, HumanRotationData hrd)
|
||||
{
|
||||
int trackStartIndex = animationClips.Load(ts.trackGroupsOffset);
|
||||
int trackEndIndex = animationClips.Load(ts.trackGroupsOffset + 4);
|
||||
|
||||
float pos[3] = {0, 0, 0};
|
||||
float rot[4] = {0, 0, 0, 1};
|
||||
float scale[3] = {1, 1, 1};
|
||||
float3 flags = 0;
|
||||
bool eulerToQuaternion = false;
|
||||
bool isHumanMuscle = false;
|
||||
|
||||
for (int i = trackStartIndex; i < trackEndIndex; ++i)
|
||||
{
|
||||
Track tk = Track::ReadFromRawBuffer(animationClips, ts.tracksOffset, i);
|
||||
int channelIndex = tk.GetChannelIndex();
|
||||
float interpolatedCurveValue = trackSampler.Sample(tk, ts.keyFramesOffset);
|
||||
|
||||
switch (tk.GetBindingType())
|
||||
{
|
||||
case BINDING_TYPE_TRANSLATION:
|
||||
pos[channelIndex] = interpolatedCurveValue;
|
||||
flags.x = 1;
|
||||
break;
|
||||
case BINDING_TYPE_QUATERNION:
|
||||
rot[channelIndex] = interpolatedCurveValue;
|
||||
flags.y = 1;
|
||||
break;
|
||||
case BINDING_TYPE_SCALE:
|
||||
scale[channelIndex] = interpolatedCurveValue;
|
||||
flags.z = 1;
|
||||
break;
|
||||
case BINDING_TYPE_EULER_ANGLES:
|
||||
eulerToQuaternion = true;
|
||||
rot[channelIndex] = interpolatedCurveValue;
|
||||
flags.y = 1;
|
||||
break;
|
||||
case BINDING_TYPE_HUMAN_MUSCLE:
|
||||
rot[channelIndex] = interpolatedCurveValue;
|
||||
isHumanMuscle = true;
|
||||
flags.y = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (eulerToQuaternion)
|
||||
{
|
||||
float3 eulerAnglesInDegrees = float3(rot[0], rot[1], rot[2]);
|
||||
Quaternion q = Quaternion::EulerXYZ(eulerAnglesInDegrees * (1 / 180.0f * 3.1415926f));
|
||||
rot[0] = q.value.x;
|
||||
rot[1] = q.value.y;
|
||||
rot[2] = q.value.z;
|
||||
rot[3] = q.value.w;
|
||||
}
|
||||
|
||||
if (isHumanMuscle)
|
||||
{
|
||||
float3 muscleValues = float3(rot[0], rot[1], rot[2]);
|
||||
Quaternion q = MuscleValuesToQuaternion(hrd, muscleValues);
|
||||
rot[0] = q.value.x;
|
||||
rot[1] = q.value.y;
|
||||
rot[2] = q.value.z;
|
||||
rot[3] = q.value.w;
|
||||
}
|
||||
|
||||
BoneTransform bt = BoneTransform::Identity();
|
||||
bt.pos = float3(pos[0], pos[1], pos[2]);
|
||||
bt.rot.value = float4(rot[0], rot[1], rot[2], rot[3]);
|
||||
bt.scale = float3(scale[0], scale[1], scale[2]);
|
||||
|
||||
BoneTransformAndFlags rv;
|
||||
rv.bt = bt;
|
||||
rv.flags = flags;
|
||||
|
||||
return rv;
|
||||
};
|
||||
|
||||
#endif
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 17fe18272a1400f40bd8ff451251ad4f
|
||||
ShaderIncludeImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/GPUAnimationEngine/Resources/TrackGroupSampler.hlsl
|
||||
uploadId: 897522
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
#ifndef TRACK_SAMPLER_HLSL_
|
||||
#define TRACK_SAMPLER_HLSL_
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define TRACK_SAMPLER_TYPE_DEFAULT 0
|
||||
#define TRACK_SAMPLER_TYPE_FIRST_FRAME 1
|
||||
#define TRACK_SAMPLER_TYPE_LAST_FRAME 2
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct TrackSampler
|
||||
{
|
||||
float time;
|
||||
int samplerType;
|
||||
|
||||
float Sample(Track tk, int keyFrameBaseAddress)
|
||||
{
|
||||
// With absence of interfaces in DXC and templates (hello HLSL 2021) need to invent such apprach
|
||||
switch (samplerType)
|
||||
{
|
||||
case TRACK_SAMPLER_TYPE_DEFAULT:
|
||||
return tk.SampleByBinarySearch(time, keyFrameBaseAddress);
|
||||
break;
|
||||
case TRACK_SAMPLER_TYPE_FIRST_FRAME:
|
||||
return tk.GetFirstFrameValue(keyFrameBaseAddress);
|
||||
break;
|
||||
case TRACK_SAMPLER_TYPE_LAST_FRAME:
|
||||
return tk.GetLastFrameValue(keyFrameBaseAddress);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------------------//
|
||||
// Helper functions to create typed samplers
|
||||
//-----------------------------------------------------------------------------------------//
|
||||
|
||||
TrackSampler CreateDefaultTrackSampler(float time)
|
||||
{
|
||||
TrackSampler rv = {time, TRACK_SAMPLER_TYPE_DEFAULT};
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
TrackSampler CreateFirstFrameTrackSampler()
|
||||
{
|
||||
TrackSampler rv = {0, TRACK_SAMPLER_TYPE_FIRST_FRAME};
|
||||
return rv;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
TrackSampler CreateLastFrameTrackSampler()
|
||||
{
|
||||
TrackSampler rv = {0, TRACK_SAMPLER_TYPE_LAST_FRAME};
|
||||
return rv;
|
||||
}
|
||||
|
||||
#endif
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4000a1fb1ac3eec4fb7dfdac3670dafb
|
||||
ShaderIncludeImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 298480
|
||||
packageName: Rukhanka Animation System 2
|
||||
packageVersion: 2.9.0
|
||||
assetPath: Packages/com.rukhanka.animation/Rukhanka.Runtime/GPUAnimationEngine/Resources/TrackSampler.hlsl
|
||||
uploadId: 897522
|
||||
Reference in New Issue
Block a user