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 frameSkinMatricesBuffer; FrameFencedGPUBufferPool frameBlendShapeWeightsBuffer; FrameFencedGPUBufferPool frameMeshDeformationDescriptionBuffer; ComputeShader meshDeformationSystemCS; ComputeKernel fillInitialMeshDataKernel; ComputeKernel fillInitialMeshBlendShapesKernel; ComputeKernel createPerVertexDeformationWorkloadKernel; ComputeKernel skinningKernel; EntitiesGraphicsSystem entitiesGraphicsSystem; GPUAnimationSystem gpuAnimationSystem; SharedComponentTypeHandle 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(); entitiesGraphicsSystem = World.GetExistingSystemManaged(); gpuAnimationSystem = World.GetExistingSystemManaged(); 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() .Build(); activeDeformedMeshesQuery = SystemAPI.QueryBuilder() .WithAll() .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().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(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(finalDeformedVerticesCB, GraphicsBuffer.Target.Structured, GraphicsBuffer.UsageFlags.None, deformedVerticesBufferSize, false); #else finalDeformedVerticesCB = ComputeBufferTools.CreateOrGrowGraphicsBuffer(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() .Build(); var numEntities = q.CalculateEntityCount(); var skinMatrixDataSize = skinMatrixCount * UnsafeUtility.SizeOf(); var maxDataUploadSize = drd.maximumSkinMatrixCountAcrossAllRegisteredMeshes * UnsafeUtility.SizeOf(); 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(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 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("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(meshVertexDataCB, GraphicsBuffer.Target.Raw, GraphicsBuffer.UsageFlags.None, drd.totalSkinnedVerticesCount, true); meshBoneWeightDataCB = ComputeBufferTools.CreateOrGrowGraphicsBuffer(meshBoneWeightDataCB, GraphicsBuffer.Target.Raw, GraphicsBuffer.UsageFlags.None, drd.totalBoneWeightsCount, true); meshBlendShapesDataCB = ComputeBufferTools.CreateOrGrowGraphicsBuffer(meshBlendShapesDataCB, GraphicsBuffer.Target.Raw, GraphicsBuffer.UsageFlags.None, drd.totalBlendShapeVerticesCount, true); InitComputeShaders(); var meshToBoneWeightsOffsetMap = CreateNewMeshesBoneIndicesComputeBuffer(drd); sparseUploader.ReplaceBuffer(meshBoneWeightDataCB); var boneWeightDataSize = drd.totalBoneWeightsCount * UnsafeUtility.SizeOf(); 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 CreateNewMeshesBoneIndicesComputeBuffer(in DeformationRuntimeData drd) { var meshToBoneWeightsOffsetMap = new NativeHashMap(0xff, Allocator.Temp); var newMeshesBonesPerVertexData = new NativeList(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(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(); 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(); 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(), 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; } } }