public class MeshVelocityBaker2 : SingleBufferGpuAlgorithm
public SkinnedMeshRenderer[] skinnedMeshes;
public BoxCollider bounds;
public TransformMode localToWorldMode = TransformMode.RootBone;
[Presetable] [Range(0, 1)] public float multiplier;
[ReadOnly] public int3[] _offsets_strides_prefixsum_array;
private Matrix4x4[] matrices;
private Matrix4x4[] matrices_prev;
private GraphicsBuffer[] _MeshVertexBuffer;
private GraphicsBuffer[] _MeshVertexBuffer_Prev;
private GraphicsBuffer _MergedPositions;
private GraphicsBuffer _MergedPositions_Prev;
private ComputeBuffer _Offsets_Strides_PrefixSum;
private ComputeBuffer _Matrices;
private ComputeBuffer _Matrices_Prev;
private const string MULTIPLIER = "Multiplier";
private const string VOLUME_DIMENSIONS = "VolumeDimensions";
private const string BOUNDS_MIN = "BoundsMin";
private const string BOUNDS_SIZE = "BoundsSize";
private const string FRAMERATE = "FrameRate";
private static readonly int Multiplier = Shader.PropertyToID(MULTIPLIER);
private static readonly int VolumeDimensions = Shader.PropertyToID(VOLUME_DIMENSIONS);
private static readonly int FrameRate = Shader.PropertyToID(FRAMERATE);
private static readonly int BoundsMin = Shader.PropertyToID(BOUNDS_MIN);
private static readonly int BoundsSize = Shader.PropertyToID(BOUNDS_SIZE);
private static readonly int MeshIndex = Shader.PropertyToID("_MeshIndex");
private static readonly int MeshVertexBuffer = Shader.PropertyToID("_MeshVertexBuffer");
private static readonly int MeshVertexBufferPrev = Shader.PropertyToID("_MeshVertexBuffer_Prev");
private static readonly int MergedPositions = Shader.PropertyToID("_MergedPositions");
private static readonly int MergedPositionsPrev = Shader.PropertyToID("_MergedPositions_Prev");
private static readonly int OffsetsStridesPrefixSum = Shader.PropertyToID("_Offsets_Strides_PrefixSum");
private static readonly int Matrices = Shader.PropertyToID("_Matrices");
private static readonly int MatricesPrev = Shader.PropertyToID("_Matrices_Prev");
private static readonly int VelocityField = Shader.PropertyToID("_VelocityField");
// Update is called once per frame
protected override void Initialize()
algorithmParameters.computeShaderInfo = VoxelUtilsComputeResources.Instance.MeshVelocityBaker2;
public override void InitializeBuffers()
base.InitializeBuffers();
if (skinnedMeshes == null || skinnedMeshes.Length == 0)
Debug.LogWarning("MeshVelocityBaker2 requires at least one skinned mesh.");
VertexAttributeDescriptor descPosition = default;
_offsets_strides_prefixsum_array = new int3[skinnedMeshes.Length];
matrices = new Matrix4x4[skinnedMeshes.Length];
matrices_prev = new Matrix4x4[skinnedMeshes.Length];
for (var i = 0; i < skinnedMeshes.Length; i++)
var smr = skinnedMeshes[i];
smr.vertexBufferTarget |= GraphicsBuffer.Target.Raw | GraphicsBuffer.Target.Vertex;
var msh = smr.sharedMesh;
foreach (var descriptor in msh.GetVertexAttributes())
if (descriptor.attribute != VertexAttribute.Position) continue;
descPosition = descriptor;
_offsets_strides_prefixsum_array[i] = new int3(
msh.GetVertexAttributeOffset(VertexAttribute.Position),
msh.GetVertexBufferStride(descPosition.stream),
vertexCount += msh.vertexCount;
_MeshVertexBuffer = new GraphicsBuffer[skinnedMeshes.Length];
_MeshVertexBuffer_Prev = new GraphicsBuffer[skinnedMeshes.Length];
_MergedPositions = new GraphicsBuffer(GraphicsBuffer.Target.Structured, vertexCount, sizeof(float) * 3);
_MergedPositions_Prev = new GraphicsBuffer(GraphicsBuffer.Target.Structured, vertexCount, sizeof(float) * 3);
_Matrices = new ComputeBuffer(skinnedMeshes.Length, sizeof(float) * 16);
_Matrices_Prev = new ComputeBuffer(skinnedMeshes.Length, sizeof(float) * 16);
_Offsets_Strides_PrefixSum = new ComputeBuffer(skinnedMeshes.Length, sizeof(int) * 3);
_Offsets_Strides_PrefixSum.SetData(_offsets_strides_prefixsum_array);
protected override void Schedule()
if (skinnedMeshes == null || skinnedMeshes.Length == 0)
//-------------------------------------------
Compute.SetFloat(Multiplier, multiplier);
Compute.SetVector(VolumeDimensions, new Vector4(Resolution.x, Resolution.y, Resolution.z));
Compute.SetFloat(FrameRate, 1 / Time.deltaTime);
Compute.SetVector(BoundsMin, new float4(bounds.bounds.min, 1));
Compute.SetVector(BoundsSize, new float4(bounds.bounds.size, 1));
//-------------------------------------------
for (var i = 0; i < skinnedMeshes.Length; i++)
var smr = skinnedMeshes[i];
// Only need to get this reference once
_MeshVertexBuffer[i] ??= smr.GetVertexBuffer();
_MeshVertexBuffer_Prev[i] ??= smr.GetPreviousVertexBuffer();
if (_MeshVertexBuffer[i] == null || _MeshVertexBuffer_Prev[i] == null)
matrices_prev[i] = matrices[i];
matrices[i] = localToWorldMode switch
TransformMode.RootBone => smr.rootBone.localToWorldMatrix,
TransformMode.Parent => smr.transform.parent.localToWorldMatrix,
TransformMode.Local => smr.localToWorldMatrix,
TransformMode.None => Matrix4x4.identity,
_ => throw new ArgumentOutOfRangeException()
Compute.SetBuffer(KernelIndices[0], MergedPositions, _MergedPositions);
Compute.SetBuffer(KernelIndices[0], MergedPositionsPrev, _MergedPositions_Prev);
Compute.SetBuffer(KernelIndices[0], OffsetsStridesPrefixSum, _Offsets_Strides_PrefixSum);
_Matrices.SetData(matrices);
_Matrices_Prev.SetData(matrices_prev);
Compute.SetBuffer(KernelIndices[0], Matrices, _Matrices);
Compute.SetBuffer(KernelIndices[0], MatricesPrev, _Matrices_Prev);
//-------------------------------------------
if (bufferA.Buffer != null)
Compute.SetTexture(KernelIndices[1], VelocityField, bufferA.Buffer);
//-------------------------------------------
if (_MergedPositions != null)
Compute.SetBuffer(KernelIndices[2], MergedPositions, _MergedPositions);
if (_MergedPositions_Prev != null)
Compute.SetBuffer(KernelIndices[2], MergedPositionsPrev, _MergedPositions_Prev);
if (bufferA.Buffer != null)
Compute.SetTexture(KernelIndices[2], VelocityField, bufferA.Buffer);
protected override void Execute()
// Merge all mesh vertices, one after the other
var totalVertexCount = 0;
for (var i = 0; i < skinnedMeshes.Length; i++)
if (_MeshVertexBuffer[i] == null || _MeshVertexBuffer_Prev[i] == null)
var vertexCount = skinnedMeshes[i].sharedMesh.vertexCount;
var threadGroupSize = KernelThreadGroupSizes[0];
var dispatchGroups = new int3(Mathf.CeilToInt(vertexCount / (float)threadGroupSize.x), 1, 1);
Compute.SetInt(MeshIndex, i);
Compute.SetBuffer(KernelIndices[0], MeshVertexBuffer, _MeshVertexBuffer[i]);
Compute.SetBuffer(KernelIndices[0], MeshVertexBufferPrev, _MeshVertexBuffer_Prev[i]);
Dispatch(0, dispatchGroups);
totalVertexCount += vertexCount;
Dispatch(2, new int3(Mathf.CeilToInt(totalVertexCount/(float)KernelThreadGroupSizes[2].x),1,1));
public override void ClearBuffers()
DisposeGraphicsBufferArray(_MeshVertexBuffer);
DisposeGraphicsBufferArray(_MeshVertexBuffer_Prev);
_MergedPositions?.Dispose();
_MergedPositions_Prev?.Dispose();
_Matrices_Prev?.Dispose();
_Offsets_Strides_PrefixSum?.Dispose();
private static void DisposeGraphicsBufferArray(GraphicsBuffer[] buffers)
for (var i = 0; i < buffers.Length; i++)