555 lines
25 KiB
C#
555 lines
25 KiB
C#
using Rukhanka.Toolbox;
|
|
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;
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
namespace Rukhanka
|
|
{
|
|
|
|
[WorldSystemFilter(WorldSystemFilterFlags.Default | WorldSystemFilterFlags.Editor)]
|
|
[UpdateInGroup(typeof(RukhankaDeformationSystemGroup))]
|
|
[UpdateAfter(typeof(SkinnedMeshPreparationSystem))]
|
|
public partial class MeshDeformationSystem: SystemBase
|
|
{
|
|
GraphicsBuffer meshVertexDataCB;
|
|
GraphicsBuffer meshBoneWeightDataCB;
|
|
GraphicsBuffer meshBlendShapesDataCB;
|
|
GraphicsBuffer newMeshBonesPerVertexCB;
|
|
GraphicsBuffer frameVertexSkinningWorkloadCB;
|
|
|
|
GraphicsBuffer finalDeformedVerticesCB;
|
|
#if RUKHANKA_ENABLE_DEFORMATION_MOTION_VECTORS
|
|
// Double buffer scheme. Current frame will read previous frame data to calculate motion delta
|
|
GraphicsBuffer finalDeformedVerticesCB1;
|
|
uint bufferSwapCounter = 0;
|
|
#endif
|
|
|
|
SparseUploader sparseUploader;
|
|
// Small dummy raw compute buffer to calm down SparseUploader initialization
|
|
GraphicsBuffer dummyRawGB;
|
|
FrameFencedGPUBufferPool<SkinMatrix> frameSkinMatricesBuffer;
|
|
FrameFencedGPUBufferPool<float> frameBlendShapeWeightsBuffer;
|
|
FrameFencedGPUBufferPool<MeshFrameDeformationDescription> frameMeshDeformationDescriptionBuffer;
|
|
|
|
ComputeShader meshDeformationSystemCS;
|
|
ComputeKernel fillInitialMeshDataKernel;
|
|
ComputeKernel fillInitialMeshBlendShapesKernel;
|
|
ComputeKernel createPerVertexDeformationWorkloadKernel;
|
|
ComputeKernel skinningKernel;
|
|
|
|
EntitiesGraphicsSystem entitiesGraphicsSystem;
|
|
GPUAnimationSystem gpuAnimationSystem;
|
|
|
|
SharedComponentTypeHandle<RenderMeshArray> renderMeshArrayTypeHandle;
|
|
|
|
struct InputMeshVertexDesc
|
|
{
|
|
public VertexAttribute vertexAttribute;
|
|
public VertexAttributeFormat vertexAttributeFormat;
|
|
public int streamIndex;
|
|
public int dimension;
|
|
}
|
|
|
|
static readonly InputMeshVertexDesc[] inputMeshVertexDesc =
|
|
{
|
|
new () {vertexAttribute = VertexAttribute.Position, vertexAttributeFormat = VertexAttributeFormat.Float32, streamIndex = 0, dimension = 3 },
|
|
new () {vertexAttribute = VertexAttribute.Normal, vertexAttributeFormat = VertexAttributeFormat.Float32, streamIndex = 0, dimension = 3 },
|
|
new () {vertexAttribute = VertexAttribute.Tangent, vertexAttributeFormat = VertexAttributeFormat.Float32, streamIndex = 0, dimension = 4 },
|
|
};
|
|
|
|
EntityQuery activeDeformedEntitiesQuery, activeDeformedMeshesQuery;
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
protected override void OnCreate()
|
|
{
|
|
#if !HYBRID_RENDERER_DISABLED
|
|
if (!EntitiesGraphicsUtils.IsEntitiesGraphicsSupportedOnSystem())
|
|
#endif
|
|
{
|
|
Enabled = false;
|
|
return;
|
|
}
|
|
|
|
renderMeshArrayTypeHandle = GetSharedComponentTypeHandle<RenderMeshArray>();
|
|
entitiesGraphicsSystem = World.GetExistingSystemManaged<EntitiesGraphicsSystem>();
|
|
gpuAnimationSystem = World.GetExistingSystemManaged<GPUAnimationSystem>();
|
|
frameSkinMatricesBuffer = new (0xffff, GraphicsBuffer.Target.Raw, GraphicsBuffer.UsageFlags.None);
|
|
frameBlendShapeWeightsBuffer = new (0xffff, GraphicsBuffer.Target.Structured, GraphicsBuffer.UsageFlags.LockBufferForWrite);
|
|
frameMeshDeformationDescriptionBuffer = new (0xffff, GraphicsBuffer.Target.Structured, GraphicsBuffer.UsageFlags.LockBufferForWrite);
|
|
|
|
dummyRawGB = new (GraphicsBuffer.Target.Raw, GraphicsBuffer.UsageFlags.None, 1, 4);
|
|
sparseUploader = new (dummyRawGB);
|
|
|
|
activeDeformedEntitiesQuery = SystemAPI.QueryBuilder()
|
|
.WithAll<SkinnedMeshRendererComponent>()
|
|
.Build();
|
|
|
|
activeDeformedMeshesQuery = SystemAPI.QueryBuilder()
|
|
.WithAll<SkinnedMeshRendererComponent>()
|
|
.Build();
|
|
|
|
RequireForUpdate(activeDeformedEntitiesQuery);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
protected override void OnDestroy()
|
|
{
|
|
#if !HYBRID_RENDERER_DISABLED
|
|
if (!EntitiesGraphicsUtils.IsEntitiesGraphicsSupportedOnSystem())
|
|
#endif
|
|
{ return; }
|
|
|
|
meshVertexDataCB?.Dispose();
|
|
meshBoneWeightDataCB?.Dispose();
|
|
meshBlendShapesDataCB?.Dispose();
|
|
newMeshBonesPerVertexCB?.Dispose();
|
|
frameVertexSkinningWorkloadCB?.Dispose();
|
|
|
|
finalDeformedVerticesCB?.Dispose();
|
|
#if RUKHANKA_ENABLE_DEFORMATION_MOTION_VECTORS
|
|
finalDeformedVerticesCB1?.Dispose();
|
|
#endif
|
|
|
|
frameMeshDeformationDescriptionBuffer?.Dispose();
|
|
frameSkinMatricesBuffer?.Dispose();
|
|
frameBlendShapeWeightsBuffer?.Dispose();
|
|
sparseUploader.Dispose();
|
|
dummyRawGB.Dispose();
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
protected override void OnUpdate()
|
|
{
|
|
renderMeshArrayTypeHandle.Update(this);
|
|
|
|
ref var deformationRuntimeData = ref SystemAPI.GetSingletonRW<DeformationRuntimeData>().ValueRW;
|
|
|
|
var resetFrameCountersJH = ResetFrameCounters(ref deformationRuntimeData, Dependency);
|
|
var prepareSkinningDataJH = PrepareMeshGPUSkinningData(ref deformationRuntimeData, resetFrameCountersJH);
|
|
|
|
// Complete previous jobs here, because we need to know skin matrix buffer and blend shape weights sizes here to resize GPU buffers
|
|
prepareSkinningDataJH.Complete();
|
|
|
|
var (copySkinMatricesToGPUBufferJH, skinMatrixThreadedUploader) = CopySkinMatricesToGPUBuffer(deformationRuntimeData, default);
|
|
var copyBlendShapeWeightsToGPUBufferJH = CopyBlendShapeWeightsToGPUBuffer(deformationRuntimeData, default);
|
|
var copyFrameDeformationDataToGPUBuffersJH = JobHandle.CombineDependencies(copySkinMatricesToGPUBufferJH, copyBlendShapeWeightsToGPUBufferJH);
|
|
|
|
// Complete dependency second time before compute shader execution. Need to make sure that SkinMatrix GPU buffer data writes
|
|
// is complete.
|
|
copyFrameDeformationDataToGPUBuffersJH.Complete();
|
|
|
|
sparseUploader.EndAndCommit(skinMatrixThreadedUploader);
|
|
frameBlendShapeWeightsBuffer.UnlockBufferAfterWrite(deformationRuntimeData.frameBlendShapeWeightsCount);
|
|
frameMeshDeformationDescriptionBuffer.UnlockBufferAfterWrite(deformationRuntimeData.frameActiveDeformedMeshesCount);
|
|
|
|
CopyNewMeshesToInitialMeshDataBuffer(deformationRuntimeData);
|
|
|
|
// Need to inject GPU animation system here, for GPU animated entities skin matrices computation directly to skin matrix frame GPU buffer
|
|
// It is not pretty approach, I know
|
|
gpuAnimationSystem?.BuildSkinMatrices(deformationRuntimeData.entityToSMRFrameDataMap, frameSkinMatricesBuffer);
|
|
|
|
#if RUKHANKA_INPLACE_SKINNING
|
|
SetInplaceSkinningGlobalBuffers();
|
|
#else
|
|
ScheduleSkinningDispatch(deformationRuntimeData);
|
|
#endif
|
|
|
|
frameSkinMatricesBuffer.EndFrame();
|
|
sparseUploader.FrameCleanup();
|
|
frameBlendShapeWeightsBuffer.EndFrame();
|
|
frameMeshDeformationDescriptionBuffer.EndFrame();
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
unsafe JobHandle ResetFrameCounters(ref DeformationRuntimeData drd, JobHandle dependsOn)
|
|
{
|
|
var resetFrameCountersJob = new ResetFrameCountersJob()
|
|
{
|
|
frameActiveDeformedMeshesCounter = new UnsafeAtomicCounter32(UnsafeUtility.AddressOf(ref drd.frameActiveDeformedMeshesCount))
|
|
};
|
|
|
|
var rv = resetFrameCountersJob.Schedule(dependsOn);
|
|
return rv;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void SetInplaceSkinningGlobalBuffers()
|
|
{
|
|
Shader.SetGlobalBuffer(ShaderID_frameDeformedMeshes, frameMeshDeformationDescriptionBuffer);
|
|
Shader.SetGlobalBuffer(ShaderID_frameSkinMatrices, frameSkinMatricesBuffer);
|
|
Shader.SetGlobalBuffer(ShaderID_frameBlendShapeWeights, frameBlendShapeWeightsBuffer);
|
|
Shader.SetGlobalBuffer(ShaderID_inputBlendShapes, meshBlendShapesDataCB);
|
|
Shader.SetGlobalBuffer(ShaderID_inputBoneInfluences, meshBoneWeightDataCB);
|
|
Shader.SetGlobalBuffer(ShaderID_inputMeshVertexData, meshVertexDataCB);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void ScheduleSkinningDispatch(in DeformationRuntimeData drd)
|
|
{
|
|
var frameDeformedVerticesCount = drd.frameDeformedVerticesCount;
|
|
frameVertexSkinningWorkloadCB = ComputeBufferTools.CreateOrGrowGraphicsBuffer<uint>(frameVertexSkinningWorkloadCB, GraphicsBuffer.Target.Raw, GraphicsBuffer.UsageFlags.None, frameDeformedVerticesCount, false);
|
|
|
|
// Schedule workload generation dispatch if we have visible/existing skinned mesh renderers
|
|
if (drd.frameActiveDeformedMeshesCount > 0)
|
|
{
|
|
var cs0 = createPerVertexDeformationWorkloadKernel.computeShader;
|
|
cs0.SetBuffer(createPerVertexDeformationWorkloadKernel, ShaderID_outFramePerVertexWorkload, frameVertexSkinningWorkloadCB);
|
|
cs0.SetBuffer(createPerVertexDeformationWorkloadKernel, ShaderID_frameDeformedMeshes, frameMeshDeformationDescriptionBuffer);
|
|
cs0.SetInt(ShaderID_totalDeformedMeshesCount, drd.frameActiveDeformedMeshesCount);
|
|
createPerVertexDeformationWorkloadKernel.Dispatch(drd.frameActiveDeformedMeshesCount, 1, 1);
|
|
}
|
|
|
|
var deformedVerticesBufferSize = frameDeformedVerticesCount + drd.maximumVerticesAcrossAllRegisteredMeshes;
|
|
#if RUKHANKA_HALF_DEFORMED_DATA
|
|
finalDeformedVerticesCB = ComputeBufferTools.CreateOrGrowGraphicsBuffer<PackedDeformedVertex>(finalDeformedVerticesCB, GraphicsBuffer.Target.Structured, GraphicsBuffer.UsageFlags.None, deformedVerticesBufferSize, false);
|
|
#else
|
|
finalDeformedVerticesCB = ComputeBufferTools.CreateOrGrowGraphicsBuffer<DeformedVertex>(finalDeformedVerticesCB, GraphicsBuffer.Target.Structured, GraphicsBuffer.UsageFlags.None, deformedVerticesBufferSize, false);
|
|
#endif
|
|
|
|
var outDeformedVerticesCB = finalDeformedVerticesCB;
|
|
|
|
// Schedule skinning even for zero visible meshes, because we need to actualize void mesh zone to properly cull invisible meshes
|
|
if (deformedVerticesBufferSize > 0)
|
|
{
|
|
var cs1 = skinningKernel.computeShader;
|
|
cs1.SetBuffer(skinningKernel, ShaderID_frameDeformedMeshes, frameMeshDeformationDescriptionBuffer);
|
|
cs1.SetBuffer(skinningKernel, ShaderID_framePerVertexWorkload, frameVertexSkinningWorkloadCB);
|
|
cs1.SetBuffer(skinningKernel, ShaderID_inputMeshVertexData, meshVertexDataCB);
|
|
cs1.SetBuffer(skinningKernel, ShaderID_inputBoneInfluences, meshBoneWeightDataCB);
|
|
cs1.SetBuffer(skinningKernel, ShaderID_inputBlendShapes, meshBlendShapesDataCB);
|
|
cs1.SetBuffer(skinningKernel, ShaderID_frameSkinMatrices, frameSkinMatricesBuffer);
|
|
cs1.SetBuffer(skinningKernel, ShaderID_frameBlendShapeWeights, frameBlendShapeWeightsBuffer);
|
|
cs1.SetBuffer(skinningKernel, ShaderID_outDeformedVertices, outDeformedVerticesCB);
|
|
cs1.SetInt(ShaderID_totalSkinnedVerticesCount, frameDeformedVerticesCount);
|
|
cs1.SetInt(ShaderID_voidMeshVertexCount, drd.maximumVerticesAcrossAllRegisteredMeshes);
|
|
|
|
var maxWorkGroupSize = (int)skinningKernel.GetMaxWorkGroupSize().x;
|
|
var numDispatchCalls = 0;
|
|
for (var currentVertexOffset = 0; currentVertexOffset < deformedVerticesBufferSize; currentVertexOffset += maxWorkGroupSize)
|
|
{
|
|
var deformedVertexCount = math.min(maxWorkGroupSize, deformedVerticesBufferSize - currentVertexOffset);
|
|
cs1.SetInt(ShaderID_currentSkinnedVertexOffset, currentVertexOffset);
|
|
skinningKernel.Dispatch(deformedVertexCount, 1, 1);
|
|
numDispatchCalls += 1;
|
|
}
|
|
}
|
|
|
|
Shader.SetGlobalBuffer(ShaderID_DeformedMeshData, outDeformedVerticesCB);
|
|
|
|
#if RUKHANKA_ENABLE_DEFORMATION_MOTION_VECTORS
|
|
if (finalDeformedVerticesCB1 != null)
|
|
Shader.SetGlobalBuffer(ShaderID_PreviousFrameDeformedMeshData, finalDeformedVerticesCB1);
|
|
(finalDeformedVerticesCB, finalDeformedVerticesCB1) = (finalDeformedVerticesCB1, finalDeformedVerticesCB);
|
|
bufferSwapCounter += 1;
|
|
#endif
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
(JobHandle, ThreadedSparseUploader) CopySkinMatricesToGPUBuffer(in DeformationRuntimeData drd, JobHandle dependsOn)
|
|
{
|
|
var skinMatrixCount = drd.frameSkinMatrixCount;
|
|
frameSkinMatricesBuffer.Grow(skinMatrixCount);
|
|
frameSkinMatricesBuffer.BeginFrame();
|
|
sparseUploader.ReplaceBuffer(frameSkinMatricesBuffer);
|
|
|
|
var q = SystemAPI.QueryBuilder()
|
|
.WithAll<SkinnedMeshRendererComponent>()
|
|
.Build();
|
|
var numEntities = q.CalculateEntityCount();
|
|
|
|
var skinMatrixDataSize = skinMatrixCount * UnsafeUtility.SizeOf<SkinMatrix>();
|
|
var maxDataUploadSize = drd.maximumSkinMatrixCountAcrossAllRegisteredMeshes * UnsafeUtility.SizeOf<SkinMatrix>();
|
|
var gpuSkinMatrixThreadedUploader = sparseUploader.Begin(skinMatrixDataSize, maxDataUploadSize, numEntities);
|
|
|
|
var isEditor = (this.World.Flags & WorldFlags.Editor) == WorldFlags.Editor;
|
|
var copySkinMatricesToGPUJob = new CopySkinMatricesToGPUJob()
|
|
{
|
|
entityToSMRFrameDataMap = drd.entityToSMRFrameDataMap,
|
|
mappedGPUSkinMatrixBuffer = gpuSkinMatrixThreadedUploader,
|
|
gpuAnimationEngineTag = SystemAPI.GetComponentLookup<GPUAnimationEngineTag>(true),
|
|
isEditor = isEditor,
|
|
};
|
|
|
|
var jh = copySkinMatricesToGPUJob.ScheduleParallel(dependsOn);
|
|
return (jh, gpuSkinMatrixThreadedUploader);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
JobHandle CopyBlendShapeWeightsToGPUBuffer(in DeformationRuntimeData drd, JobHandle dependsOn)
|
|
{
|
|
var blendShapeWeightsCount = drd.frameBlendShapeWeightsCount;
|
|
frameBlendShapeWeightsBuffer.Grow(blendShapeWeightsCount);
|
|
frameBlendShapeWeightsBuffer.BeginFrame();
|
|
|
|
var gpuBufferOutArr = frameBlendShapeWeightsBuffer.LockBufferForWrite(0, blendShapeWeightsCount);
|
|
|
|
var copyBlendShapeWeightsToGPUJob = new CopyBlendShapeWeightToGPUJob()
|
|
{
|
|
entityToSMRFrameDataMap = drd.entityToSMRFrameDataMap,
|
|
mappedGPUBlendShapeWeightsBuffer = gpuBufferOutArr
|
|
};
|
|
|
|
var jh = copyBlendShapeWeightsToGPUJob.ScheduleParallel(dependsOn);
|
|
return jh;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
JobHandle PrepareMeshGPUSkinningData(ref DeformationRuntimeData drd, JobHandle dependsOn)
|
|
{
|
|
var deformedMeshCount = activeDeformedEntitiesQuery.CalculateEntityCount();
|
|
frameMeshDeformationDescriptionBuffer.Grow(deformedMeshCount);
|
|
frameMeshDeformationDescriptionBuffer.BeginFrame();
|
|
var gpuBufferMeshDeformationOutArr = frameMeshDeformationDescriptionBuffer.LockBufferForWrite(0, deformedMeshCount);
|
|
|
|
var setDeformedMeshIndicesJH = SetDeformedMeshIndicesForRenderEntities(ref drd, dependsOn);
|
|
var prepareSkinningDataJH = PrepareSkinningCommands(ref drd, gpuBufferMeshDeformationOutArr, dependsOn);
|
|
|
|
var rv = JobHandle.CombineDependencies(setDeformedMeshIndicesJH, prepareSkinningDataJH);
|
|
|
|
return rv;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
JobHandle PrepareMeshGPUSkinningDataForInplaceSkinning(ref DeformationRuntimeData drd, JobHandle dependsOn)
|
|
{
|
|
var deformedMeshCount = activeDeformedMeshesQuery.CalculateEntityCount();
|
|
frameMeshDeformationDescriptionBuffer.Grow(deformedMeshCount);
|
|
frameMeshDeformationDescriptionBuffer.BeginFrame();
|
|
var gpuBufferMeshDeformationOutArr = frameMeshDeformationDescriptionBuffer.LockBufferForWrite(0, deformedMeshCount);
|
|
|
|
var setDeformedMeshIndicesJH = SetDeformedMeshIndicesForRenderEntities(ref drd, dependsOn);
|
|
var prepareSkinningDataJH = PrepareSkinningCommands(ref drd, gpuBufferMeshDeformationOutArr, dependsOn);
|
|
|
|
var rv = JobHandle.CombineDependencies(setDeformedMeshIndicesJH, prepareSkinningDataJH);
|
|
|
|
return rv;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
unsafe JobHandle PrepareSkinningCommands(ref DeformationRuntimeData drd, NativeArray<MeshFrameDeformationDescription> gpuBufferMeshDeformationOutArr, JobHandle dependsOn)
|
|
{
|
|
var prepareSkinningDataJob = new PrepareSkinningCommandsJob()
|
|
{
|
|
meshFrameDeformationData = gpuBufferMeshDeformationOutArr,
|
|
entityToSMRFrameDataMap = drd.entityToSMRFrameDataMap,
|
|
registeredSkinnedMeshes = drd.registeredSkinnedMeshesMap,
|
|
frameActiveDeformedMeshesCounter = new UnsafeAtomicCounter32(UnsafeUtility.AddressOf(ref drd.frameActiveDeformedMeshesCount))
|
|
};
|
|
var jh = prepareSkinningDataJob.ScheduleParallel(dependsOn);
|
|
return jh;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
unsafe JobHandle SetDeformedMeshIndicesForRenderEntities(ref DeformationRuntimeData drd, JobHandle dependsOn)
|
|
{
|
|
var setDeformedMeshIndexJob = new SetDeformedMeshIndicesJob()
|
|
{
|
|
frameDeformedVerticesCounter = new UnsafeAtomicCounter32(UnsafeUtility.AddressOf(ref drd.frameDeformedVerticesCount)),
|
|
entityToSMRFrameDataMap = drd.entityToSMRFrameDataMap,
|
|
#if RUKHANKA_ENABLE_DEFORMATION_MOTION_VECTORS
|
|
currentFrameDeformedBufferIndex = (int)(bufferSwapCounter & 1)
|
|
#endif
|
|
};
|
|
|
|
var jh = setDeformedMeshIndexJob.ScheduleParallel(dependsOn);
|
|
return jh;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void InitComputeShaders()
|
|
{
|
|
if (meshDeformationSystemCS != null)
|
|
return;
|
|
|
|
meshDeformationSystemCS = Resources.Load<ComputeShader>("RukhankaMeshDeformation");
|
|
fillInitialMeshDataKernel = new ComputeKernel(meshDeformationSystemCS, "CopyInitialMeshData");
|
|
fillInitialMeshBlendShapesKernel = new ComputeKernel(meshDeformationSystemCS, "CopyInitialMeshBlendShapes");
|
|
createPerVertexDeformationWorkloadKernel = new ComputeKernel(meshDeformationSystemCS, "CreatePerVertexDeformationWorkload");
|
|
skinningKernel = new ComputeKernel(meshDeformationSystemCS, "Skinning");
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CopyNewMeshesToInitialMeshDataBuffer(in DeformationRuntimeData drd)
|
|
{
|
|
if (drd.newSkinnedMeshesToRegister.IsEmpty)
|
|
return;
|
|
|
|
meshVertexDataCB = ComputeBufferTools.CreateOrGrowGraphicsBuffer<SourceMeshVertex>(meshVertexDataCB, GraphicsBuffer.Target.Raw, GraphicsBuffer.UsageFlags.None, drd.totalSkinnedVerticesCount, true);
|
|
meshBoneWeightDataCB = ComputeBufferTools.CreateOrGrowGraphicsBuffer<BoneWeight1>(meshBoneWeightDataCB, GraphicsBuffer.Target.Raw, GraphicsBuffer.UsageFlags.None, drd.totalBoneWeightsCount, true);
|
|
meshBlendShapesDataCB = ComputeBufferTools.CreateOrGrowGraphicsBuffer<DeformedVertex>(meshBlendShapesDataCB, GraphicsBuffer.Target.Raw, GraphicsBuffer.UsageFlags.None, drd.totalBlendShapeVerticesCount, true);
|
|
|
|
InitComputeShaders();
|
|
var meshToBoneWeightsOffsetMap = CreateNewMeshesBoneIndicesComputeBuffer(drd);
|
|
|
|
sparseUploader.ReplaceBuffer(meshBoneWeightDataCB);
|
|
var boneWeightDataSize = drd.totalBoneWeightsCount * UnsafeUtility.SizeOf<BoneWeight1>();
|
|
var tsu = sparseUploader.Begin(boneWeightDataSize, boneWeightDataSize, 1);
|
|
|
|
foreach (var sm in drd.newSkinnedMeshesToRegister)
|
|
{
|
|
var batchMeshID = sm.Key;
|
|
var skinnedMeshHash = sm.Value.Value.hash;
|
|
var smd = drd.registeredSkinnedMeshesMap[skinnedMeshHash];
|
|
var mesh = entitiesGraphicsSystem.GetMesh(batchMeshID);
|
|
var boneWeightsOffsetForMesh = meshToBoneWeightsOffsetMap[batchMeshID];
|
|
|
|
CopyMeshVertexData(smd, boneWeightsOffsetForMesh, mesh);
|
|
CopyMeshBoneWeightsData(smd, mesh, tsu);
|
|
CopyMeshBlendShapes(smd, mesh);
|
|
}
|
|
sparseUploader.EndAndCommit(tsu);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
unsafe NativeHashMap<BatchMeshID, int> CreateNewMeshesBoneIndicesComputeBuffer(in DeformationRuntimeData drd)
|
|
{
|
|
var meshToBoneWeightsOffsetMap = new NativeHashMap<BatchMeshID, int>(0xff, Allocator.Temp);
|
|
var newMeshesBonesPerVertexData = new NativeList<uint>(0xff, Allocator.Temp);
|
|
|
|
foreach (var newMesh in drd.newSkinnedMeshesToRegister)
|
|
{
|
|
var baseVertexIndex = newMeshesBonesPerVertexData.Length;
|
|
var mesh = entitiesGraphicsSystem.GetMesh(newMesh.Key);
|
|
|
|
if (!HasSupportedVertexLayout(mesh))
|
|
continue;
|
|
|
|
meshToBoneWeightsOffsetMap[newMesh.Key] = baseVertexIndex;
|
|
newMeshesBonesPerVertexData.Resize(baseVertexIndex + mesh.vertexCount, NativeArrayOptions.UninitializedMemory);
|
|
|
|
ref var bwi = ref newMesh.Value.Value.boneWeightsIndices;
|
|
//BurstAssert.IsTrue(bwi.Length == mesh.vertexCount, "Bone weights offsets array does not match vertex count.");
|
|
UnsafeUtility.MemCpy(newMeshesBonesPerVertexData.GetUnsafePtr() + baseVertexIndex, bwi.GetUnsafePtr(), bwi.Length * 4);
|
|
}
|
|
|
|
newMeshBonesPerVertexCB = ComputeBufferTools.CreateOrGrowGraphicsBuffer<uint>(newMeshBonesPerVertexCB, GraphicsBuffer.Target.Raw, GraphicsBuffer.UsageFlags.None, newMeshesBonesPerVertexData.Length, false);
|
|
newMeshBonesPerVertexCB.SetData(newMeshesBonesPerVertexData.AsArray());
|
|
return meshToBoneWeightsOffsetMap;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CopyMeshBlendShapes(SkinnedMeshDescription smd, Mesh mesh)
|
|
{
|
|
if (mesh.blendShapeCount == 0)
|
|
return;
|
|
|
|
using var meshAllBlendShapes = mesh.GetBlendShapeBuffer(BlendShapeBufferLayout.PerShape);
|
|
var blendShapeVertexDeltaSize = UnsafeUtility.SizeOf<BlendShapeVertexDelta>();
|
|
Assert.IsTrue(blendShapeVertexDeltaSize == meshAllBlendShapes.stride);
|
|
|
|
var cs = fillInitialMeshBlendShapesKernel.computeShader;
|
|
cs.SetBuffer(fillInitialMeshBlendShapesKernel, ShaderID_meshBlendShapesBuffer, meshAllBlendShapes);
|
|
cs.SetBuffer(fillInitialMeshBlendShapesKernel, ShaderID_outInitialMeshBlendShapesData, meshBlendShapesDataCB);
|
|
|
|
var deformedVertexSize = UnsafeUtility.SizeOf<DeformedVertex>();
|
|
ComputeBufferTools.Clear(meshBlendShapesDataCB, (uint)(smd.baseBlendShapeIndex * deformedVertexSize), (uint)(smd.vertexCount * deformedVertexSize * mesh.blendShapeCount));
|
|
|
|
for (var i = 0; i < mesh.blendShapeCount; ++i)
|
|
{
|
|
var bsr = mesh.GetBlendShapeBufferRange(i);
|
|
cs.SetInt(ShaderID_inputBlendShapeVerticesCount, (int)(bsr.endIndex - bsr.startIndex) + 1);
|
|
cs.SetInt(ShaderID_inputBlendShapeVertexOffset, (int)bsr.startIndex);
|
|
cs.SetInt(ShaderID_outBlendShapeVertexOffset, smd.baseBlendShapeIndex + i * smd.vertexCount );
|
|
fillInitialMeshBlendShapesKernel.Dispatch(smd.vertexCount, 1, 1);
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CopyMeshBoneWeightsData(SkinnedMeshDescription smd, Mesh mesh, ThreadedSparseUploader boneWeightDataUploader)
|
|
{
|
|
var meshAllBoneWeights = mesh.GetAllBoneWeights();
|
|
boneWeightDataUploader.AddUpload(meshAllBoneWeights, smd.baseBoneWeightIndex * UnsafeUtility.SizeOf<BoneWeight1>(), 1);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CopyMeshVertexData(SkinnedMeshDescription smd, int meshBoneWeightsOffset, Mesh mesh)
|
|
{
|
|
// Copy initial vertex data
|
|
mesh.vertexBufferTarget |= GraphicsBuffer.Target.Raw;
|
|
using var meshVertexBuffer = mesh.GetVertexBuffer(0);
|
|
|
|
meshDeformationSystemCS.SetInt(ShaderID_totalMeshVertices, smd.vertexCount);
|
|
meshDeformationSystemCS.SetInt(ShaderID_outDataVertexOffset, smd.baseVertex);
|
|
var vertexBufferStride = mesh.GetVertexBufferStride(0);
|
|
meshDeformationSystemCS.SetInt(ShaderID_inputVertexSizeInBytes, vertexBufferStride);
|
|
meshDeformationSystemCS.SetInt(ShaderID_inputBonesWeightsDataOffset, meshBoneWeightsOffset);
|
|
meshDeformationSystemCS.SetInt(ShaderID_outBonesWeightsDataOffset, smd.baseBoneWeightIndex);
|
|
meshDeformationSystemCS.SetBuffer(fillInitialMeshDataKernel, ShaderID_meshVertexData, meshVertexBuffer);
|
|
meshDeformationSystemCS.SetBuffer(fillInitialMeshDataKernel, ShaderID_outInitialDeformedMeshData, meshVertexDataCB);
|
|
meshDeformationSystemCS.SetBuffer(fillInitialMeshDataKernel, ShaderID_meshBonesPerVertexData, newMeshBonesPerVertexCB);
|
|
|
|
fillInitialMeshDataKernel.Dispatch(smd.vertexCount, 1, 1);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
bool HasSupportedVertexLayout(Mesh mesh)
|
|
{
|
|
if (mesh.vertexAttributeCount < inputMeshVertexDesc.Length)
|
|
{
|
|
Debug.LogError($"Unsupported vertex layout for deformations in mesh ({mesh.name}). Expecting {inputMeshVertexDesc.Length} attributes but mash has only {mesh.vertexAttributeCount}.");
|
|
return false;
|
|
}
|
|
|
|
// Check each attribute
|
|
for (var i = 0; i < inputMeshVertexDesc.Length; ++i)
|
|
{
|
|
var attrib = mesh.GetVertexAttribute(i);
|
|
var vd = inputMeshVertexDesc[i];
|
|
|
|
if (attrib.attribute != vd.vertexAttribute)
|
|
{
|
|
Debug.LogError($"Attribute mismatch for deformations in mesh ({mesh.name}). Expecting '{vd.vertexAttribute}', got '{attrib.attribute}'.");
|
|
return false;
|
|
}
|
|
|
|
if (attrib.format != vd.vertexAttributeFormat)
|
|
{
|
|
Debug.LogError($"Format mismatch for deformations in mesh ({mesh.name}). Expecting '{vd.vertexAttributeFormat}', got '{attrib.format}'.");
|
|
return false;
|
|
}
|
|
|
|
if (attrib.dimension != vd.dimension)
|
|
{
|
|
Debug.LogError($"Attribute dimension mismatch for deformations in mesh ({mesh.name}). Expecting '{vd.dimension}', got '{attrib.dimension}'.");
|
|
return false;
|
|
}
|
|
|
|
if (attrib.stream != vd.streamIndex)
|
|
{
|
|
Debug.LogError($"Attribute stream index mismatch for deformations in mesh ({mesh.name}). Expecting '{vd.streamIndex}', got '{attrib.stream}'.");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
}
|