Netcode Bootstrap

This commit is contained in:
Luis Gonzalez
2026-05-31 14:27:52 -07:00
parent 99d8d2d2a9
commit 7fa77ce821
1813 changed files with 2921554 additions and 84 deletions
@@ -0,0 +1,38 @@
using Unity.Entities;
using Unity.Mathematics;
using Unity.Rendering;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
namespace Rukhanka
{
// Just a copy of EG SkinMatrix
public struct SkinMatrix: IBufferElementData
{
public float3x4 Value;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Just a copy of EG BlendShapeWeight
public struct BlendShapeWeight : IBufferElementData
{
public float Value;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#if RUKHANKA_ENABLE_DEFORMATION_MOTION_VECTORS
[MaterialProperty("_DeformationParamsForMotionVectors")]
public struct DeformedMeshIndex: IComponentData
{
public uint4 Value;
}
#else
[MaterialProperty("_DeformedMeshIndex")]
public struct DeformedMeshIndex: IComponentData
{
public uint Value;
}
#endif
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 10bc942d521b8c74698b2b8bb41ecbb9
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/Deformation/DeformationComponents.cs
uploadId: 897522
@@ -0,0 +1,153 @@
using System;
using Rukhanka.Toolbox;
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
using Unity.Rendering;
using UnityEngine.Rendering;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
namespace Rukhanka
{
internal struct SkinnedMeshRendererFrameDeformationData
{
public int skinMatrixIndex;
public int deformedVertexIndex;
public int blendShapeWeightIndex;
public static SkinnedMeshRendererFrameDeformationData MakeDefault()
{
var rv = new SkinnedMeshRendererFrameDeformationData()
{
deformedVertexIndex = -1,
skinMatrixIndex = -1,
blendShapeWeightIndex = -1
};
return rv;
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
internal struct SourceMeshVertex
{
#if !RUKHANKA_INPLACE_SKINNING
public float3 position;
public float3 normal;
public float3 tangent;
#endif
public uint boneWeightsOffsetAndCount;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
internal struct DeformedVertex
{
public float3 position;
public float3 normal;
public float3 tangent;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
internal struct PackedDeformedVertex
{
public uint4 pack0;
public uint pack1;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
internal struct BlendShapeVertexDelta
{
public int originalMeshVertexIndex;
public float3 positionDelta;
public float3 normalDelta;
public float3 tangentDelta;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
internal struct SkinnedMeshDescription
{
public int baseVertex;
public int vertexCount;
public int baseBoneWeightIndex;
public int baseBlendShapeIndex;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
internal struct MeshFrameDeformationDescription
{
public int baseSkinMatrixIndex;
public int baseBlendShapeWeightIndex;
public int baseOutVertexIndex;
public int baseInputMeshVertexIndex;
public int baseInputMeshBlendShapeIndex;
public int meshVerticesCount;
public int meshBlendShapesCount;
}
//-----------------------------------------------------------------------------------------------------------------//
internal struct DeformationRuntimeData: IComponentData, IDisposable
{
public NativeParallelHashMap<int, BRGRenderMeshArray> renderMeshArrays;
public NativeParallelHashMap<Hash128, SkinnedMeshDescription> registeredSkinnedMeshesMap;
public NativeParallelHashMap<BatchMeshID, BlobAssetReference<SkinnedMeshInfoBlob>> newSkinnedMeshesToRegister;
public NativeParallelHashMap<Entity, SkinnedMeshRendererFrameDeformationData> entityToSMRFrameDataMap;
public int totalSkinnedVerticesCount;
public int totalBoneWeightsCount;
public int totalBlendShapeVerticesCount;
public int frameSkinMatrixCount;
public int frameBlendShapeWeightsCount;
public int frameDeformedVerticesCount;
public int frameActiveDeformedMeshesCount;
public int maximumVerticesAcrossAllRegisteredMeshes;
public int maximumSkinMatrixCountAcrossAllRegisteredMeshes;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public void Dispose()
{
registeredSkinnedMeshesMap.Dispose();
newSkinnedMeshesToRegister.Dispose();
entityToSMRFrameDataMap.Dispose();
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public static DeformationRuntimeData Construct(ref SystemState ss)
{
var registerMeshAndMaterialSystem = ss.World.GetExistingSystemManaged<RegisterMaterialsAndMeshesSystem>();
BurstAssert.IsTrue(registerMeshAndMaterialSystem != null, $"{nameof(RegisterMaterialsAndMeshesSystem)} was not found!");
if (registerMeshAndMaterialSystem == null)
return default;
var rv = new DeformationRuntimeData();
rv.renderMeshArrays = registerMeshAndMaterialSystem.BRGRenderMeshArrays;
BurstAssert.IsTrue(rv.renderMeshArrays.IsCreated, "Render mesh arrays is not valid. Probably wrong system creation order.");
rv.registeredSkinnedMeshesMap = new (0xff, Allocator.Persistent);
rv.newSkinnedMeshesToRegister = new (0xffff, Allocator.Persistent);
rv.entityToSMRFrameDataMap = new (0xff, Allocator.Persistent);
rv.totalSkinnedVerticesCount = 0;
rv.totalBoneWeightsCount = 0;
rv.totalBlendShapeVerticesCount = 0;
rv.frameSkinMatrixCount = 0;
rv.frameBlendShapeWeightsCount = 0;
rv.frameDeformedVerticesCount = 0;
rv.frameActiveDeformedMeshesCount = 0;
rv.maximumVerticesAcrossAllRegisteredMeshes = 0;
rv.maximumSkinMatrixCountAcrossAllRegisteredMeshes = 0;
return rv;
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 3b0a18f102cadf141aeb8e3d6d34d7e6
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/Deformation/DeformationStructures.cs
uploadId: 897522
@@ -0,0 +1,27 @@
using Unity.Entities;
using Unity.Rendering;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
namespace Rukhanka
{
[WorldSystemFilter(WorldSystemFilterFlags.Default | WorldSystemFilterFlags.Editor)]
[UpdateInGroup(typeof(PresentationSystemGroup))]
[UpdateAfter(typeof(RegisterMaterialsAndMeshesSystem))]
[UpdateAfter(typeof(UpdatePresentationSystemGroup))]
[UpdateBefore(typeof(EntitiesGraphicsSystem))]
public partial class RukhankaDeformationSystemGroup: ComponentSystemGroup
{
protected override void OnCreate()
{
#if !HYBRID_RENDERER_DISABLED
if (!EntitiesGraphicsUtils.IsEntitiesGraphicsSupportedOnSystem())
#endif
{
Enabled = false;
UnityEngine.Debug.Log("No SRP present, no compute shader support, or running with -nographics. Rukhanka Mesh Deformation Systems disabled.");
}
base.OnCreate();
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 1c93484e0aed8374c8d20b05b1ab679a
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/Deformation/DeformationSystemGroup.cs
uploadId: 897522
@@ -0,0 +1,554 @@
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;
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 4bc9121c8c63c494089c778003a9a9b2
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/Deformation/MeshDeformationSystem.cs
uploadId: 897522
@@ -0,0 +1,191 @@
using Rukhanka.Toolbox;
using Unity.Burst;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Entities;
using Unity.Jobs;
using Unity.Rendering;
using Hash128 = Unity.Entities.Hash128;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
namespace Rukhanka
{
public partial class MeshDeformationSystem
{
//-----------------------------------------------------------------------------------------------------------------//
[BurstCompile]
struct ResetFrameCountersJob: IJob
{
[NativeDisableUnsafePtrRestriction]
public UnsafeAtomicCounter32 frameActiveDeformedMeshesCounter;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public void Execute()
{
frameActiveDeformedMeshesCounter.Reset(0);
}
}
//-----------------------------------------------------------------------------------------------------------------//
[BurstCompile]
partial struct PrepareSkinningCommandsJob: IJobEntity
{
[ReadOnly]
public NativeParallelHashMap<Entity, SkinnedMeshRendererFrameDeformationData> entityToSMRFrameDataMap;
[ReadOnly]
public NativeParallelHashMap<Hash128, SkinnedMeshDescription> registeredSkinnedMeshes;
[NativeDisableUnsafePtrRestriction]
public UnsafeAtomicCounter32 frameActiveDeformedMeshesCounter;
[NativeDisableParallelForRestriction]
public NativeArray<MeshFrameDeformationDescription> meshFrameDeformationData;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void Execute(Entity e, in SkinnedMeshRendererComponent arc)
{
if (!entityToSMRFrameDataMap.TryGetValue(e, out var smrdd))
return;
if (!registeredSkinnedMeshes.TryGetValue(arc.smrInfoBlob.Value.hash, out var smd))
{
#if RUKHANKA_DEBUG_INFO
BurstAssert.IsTrue(false, $"Skinned mesh '{arc.smrInfoBlob.Value.skeletonName.ToFixedString()}' is not properly registered.");
#else
BurstAssert.IsTrue(false, $"Skinned mesh with hash '{arc.smrInfoBlob.Value.hash.Value}' is not properly registered. Enable 'RUKHANKA_DEBUG_INFO' to see the mesh name.");
#endif
return;
}
// Mesh skinning data offsets
var meshFrameData = new MeshFrameDeformationDescription();
meshFrameData.baseOutVertexIndex = smrdd.deformedVertexIndex;
meshFrameData.baseSkinMatrixIndex = smrdd.skinMatrixIndex;
meshFrameData.baseBlendShapeWeightIndex = smrdd.blendShapeWeightIndex;
meshFrameData.baseInputMeshVertexIndex = smd.baseVertex;
meshFrameData.baseInputMeshBlendShapeIndex = smd.baseBlendShapeIndex;
meshFrameData.meshVerticesCount = arc.smrInfoBlob.Value.meshVerticesCount;
meshFrameData.meshBlendShapesCount = arc.smrInfoBlob.Value.meshBlendShapesCount;
var currentMeshFrameDeformationDataIndex = frameActiveDeformedMeshesCounter.Add(1);
#if RUKHANKA_INPLACE_SKINNING
// Yes, I am overwriting the value here. Frame active meshes counter need to be incremented in any case
currentMeshFrameDeformationDataIndex = smrdd.deformedVertexIndex;
#endif
meshFrameDeformationData[currentMeshFrameDeformationDataIndex] = meshFrameData;
}
}
//-----------------------------------------------------------------------------------------------------------------//
[BurstCompile]
[WithAll(typeof(SkinnedMeshRendererComponent))]
partial struct SetDeformedMeshIndicesJob: IJobEntity
{
[ReadOnly]
public NativeParallelHashMap<Entity, SkinnedMeshRendererFrameDeformationData> entityToSMRFrameDataMap;
[NativeDisableUnsafePtrRestriction]
public UnsafeAtomicCounter32 frameDeformedVerticesCounter;
#if RUKHANKA_ENABLE_DEFORMATION_MOTION_VECTORS
// Either 0 or 1
public int currentFrameDeformedBufferIndex;
#endif
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
unsafe void Execute(Entity e, ref DeformedMeshIndex dri)
{
#if RUKHANKA_ENABLE_DEFORMATION_MOTION_VECTORS
dri.Value[2] = (uint)currentFrameDeformedBufferIndex;
#endif
// Handle invisible mesh renderers by assigning theirs deformed mesh index to some value that can be handled in shaders:
// * In case of in-place skinning I need to simply check for this index for some given predefined distinct value
// * In case of preskinning path set deformed mesh index beyond valid data. Previously I have added some zeroes at the end
// of skinned vertices data. Indexing it will return zero values for skinning mesh
if (!entityToSMRFrameDataMap.TryGetValue(e, out var smrdd))
{
#if RUKHANKA_ENABLE_DEFORMATION_MOTION_VECTORS
#if RUKHANKA_INPLACE_SKINNING
dri.Value[currentFrameDeformedBufferIndex] = 0xffffffff;
#else
dri.Value[currentFrameDeformedBufferIndex] = (uint)*frameDeformedVerticesCounter.Counter;
#endif
#else
#if RUKHANKA_INPLACE_SKINNING
dri.Value = 0xffffffff;
#else
dri.Value = (uint)*frameDeformedVerticesCounter.Counter;
#endif
#endif
return;
}
#if RUKHANKA_ENABLE_DEFORMATION_MOTION_VECTORS
dri.Value[currentFrameDeformedBufferIndex] = (uint)smrdd.deformedVertexIndex;
#else
dri.Value = (uint)smrdd.deformedVertexIndex;
#endif
}
}
//-----------------------------------------------------------------------------------------------------------------//
[BurstCompile]
partial struct CopySkinMatricesToGPUJob: IJobEntity
{
[ReadOnly]
public NativeParallelHashMap<Entity, SkinnedMeshRendererFrameDeformationData> entityToSMRFrameDataMap;
[ReadOnly]
public ComponentLookup<GPUAnimationEngineTag> gpuAnimationEngineTag;
[NativeDisableParallelForRestriction]
public ThreadedSparseUploader mappedGPUSkinMatrixBuffer;
public bool isEditor;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
unsafe void Execute(Entity e, SkinnedMeshRendererComponent arc, in DynamicBuffer<SkinMatrix> skinMatrices)
{
bool isGPUAnimator = !isEditor && arc.IsGPUAnimator(gpuAnimationEngineTag);
if (isGPUAnimator || !entityToSMRFrameDataMap.TryGetValue(e, out var smrdd))
return;
var srcPtr = skinMatrices.GetUnsafeReadOnlyPtr();
var srcSize = skinMatrices.Length * UnsafeUtility.SizeOf<SkinMatrix>();
var dstOffset = smrdd.skinMatrixIndex * UnsafeUtility.SizeOf<SkinMatrix>();
mappedGPUSkinMatrixBuffer.AddUpload(srcPtr, srcSize, dstOffset);
}
}
//-----------------------------------------------------------------------------------------------------------------//
[BurstCompile]
partial struct CopyBlendShapeWeightToGPUJob: IJobEntity
{
[ReadOnly]
public NativeParallelHashMap<Entity, SkinnedMeshRendererFrameDeformationData> entityToSMRFrameDataMap;
[NativeDisableParallelForRestriction]
public NativeArray<float> mappedGPUBlendShapeWeightsBuffer;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
unsafe void Execute(Entity e, in DynamicBuffer<BlendShapeWeight> blendShapeWeights)
{
if (!entityToSMRFrameDataMap.TryGetValue(e, out var smrdd))
return;
var dstPtr = (float*)mappedGPUBlendShapeWeightsBuffer.GetUnsafePtr() + smrdd.blendShapeWeightIndex;
UnsafeUtility.MemCpy(dstPtr, blendShapeWeights.GetUnsafeReadOnlyPtr(), UnsafeUtility.SizeOf<float>() * blendShapeWeights.Length);
}
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: a488787543b55e04ca6cbf69c833fc52
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/Deformation/MeshDeformationSystem_Jobs.cs
uploadId: 897522
@@ -0,0 +1,38 @@
using UnityEngine;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
namespace Rukhanka
{
public partial class MeshDeformationSystem
{
readonly int ShaderID_inputVertexSizeInBytes = Shader.PropertyToID("inputVertexSizeInBytes");
readonly int ShaderID_outDataVertexOffset = Shader.PropertyToID("outDataVertexOffset");
readonly int ShaderID_totalMeshVertices = Shader.PropertyToID("totalMeshVertices");
readonly int ShaderID_meshVertexData = Shader.PropertyToID("meshVertexData");
readonly int ShaderID_outInitialDeformedMeshData = Shader.PropertyToID("outInitialDeformedMeshData");
readonly int ShaderID_meshBonesPerVertexData = Shader.PropertyToID("meshBonesPerVertexData");
readonly int ShaderID_inputBonesWeightsDataOffset = Shader.PropertyToID("inputBonesWeightsDataOffset");
readonly int ShaderID_outBonesWeightsDataOffset = Shader.PropertyToID("outBonesWeightsDataOffset");
readonly int ShaderID_frameDeformedMeshes = Shader.PropertyToID("frameDeformedMeshes");
readonly int ShaderID_outFramePerVertexWorkload = Shader.PropertyToID("outFramePerVertexWorkload");
readonly int ShaderID_framePerVertexWorkload = Shader.PropertyToID("framePerVertexWorkload");
readonly int ShaderID_inputMeshVertexData = Shader.PropertyToID("inputMeshVertexData");
readonly int ShaderID_inputBoneInfluences = Shader.PropertyToID("inputBoneInfluences");
readonly int ShaderID_inputBlendShapes = Shader.PropertyToID("inputBlendShapes");
readonly int ShaderID_frameSkinMatrices = Shader.PropertyToID("frameSkinMatrices");
readonly int ShaderID_frameBlendShapeWeights = Shader.PropertyToID("frameBlendShapeWeights");
readonly int ShaderID_outDeformedVertices = Shader.PropertyToID("outDeformedVertices");
readonly int ShaderID_totalDeformedMeshesCount = Shader.PropertyToID("totalDeformedMeshesCount");
readonly int ShaderID_totalSkinnedVerticesCount = Shader.PropertyToID("totalSkinnedVerticesCount");
readonly int ShaderID_voidMeshVertexCount = Shader.PropertyToID("voidMeshVertexCount");
readonly int ShaderID_currentSkinnedVertexOffset = Shader.PropertyToID("currentSkinnedVertexOffset");
readonly int ShaderID_DeformedMeshData = Shader.PropertyToID("_DeformedMeshData");
readonly int ShaderID_PreviousFrameDeformedMeshData = Shader.PropertyToID("_PreviousFrameDeformedMeshData");
readonly int ShaderID_meshBlendShapesBuffer = Shader.PropertyToID("meshBlendShapesBuffer");
readonly int ShaderID_outInitialMeshBlendShapesData = Shader.PropertyToID("outInitialMeshBlendShapesData");
readonly int ShaderID_inputBlendShapeVerticesCount = Shader.PropertyToID("inputBlendShapeVerticesCount");
readonly int ShaderID_inputBlendShapeVertexOffset = Shader.PropertyToID("inputBlendShapeVertexOffset");
readonly int ShaderID_outBlendShapeVertexOffset = Shader.PropertyToID("outBlendShapeVertexOffset");
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 18b67e86e77ad8d43be1128243afd5ae
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/Deformation/MeshDeformationSystem_ShaderVars.cs
uploadId: 897522
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4e239222e57e79545b256b045a930c76
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,90 @@
#pragma once
/////////////////////////////////////////////////////////////////////////////////
#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/Deformation/Resources/DeformationCommon.hlsl"
#ifdef RUKHANKA_INPLACE_SKINNING
#include "Packages/com.rukhanka.animation/Rukhanka.Runtime/Deformation/Resources/Skinning.hlsl"
#endif
#ifdef RUKHANKA_HALF_DEFORMED_DATA
StructuredBuffer<PackedDeformedVertex> _DeformedMeshData;
#else
StructuredBuffer<DeformedVertex> _DeformedMeshData;
#endif
#undef _DeformedMeshIndex
#undef _DeformationParamsForMotionVectors
/////////////////////////////////////////////////////////////////////////////////
DeformedVertex GetDeformedVertexForMesh(uint vertexID, DeformedVertex originalVertex, uint vertexOffsetOrMeshIndex)
{
#ifndef RUKHANKA_INPLACE_SKINNING
uint vertexOffsetForMesh = vertexOffsetOrMeshIndex;
#ifdef RUKHANKA_HALF_DEFORMED_DATA
PackedDeformedVertex vertexData = _DeformedMeshData[vertexOffsetForMesh + vertexID];
DeformedVertex rv = vertexData.Unpack();
#else
DeformedVertex rv = _DeformedMeshData[vertexOffsetForMesh + vertexID];
#endif // RUKHANKA_HALF_DEFORMED_DATA
//-------- Inplace skinning code path -------------//
#else
DeformedVertex rv = originalVertex;
uint meshIndex = vertexOffsetOrMeshIndex;
if (meshIndex != 0xffffffff)
{
MeshFrameDeformationDescription mfd = frameDeformedMeshes[meshIndex];
uint absoluteInputMeshVertexIndex = vertexID + mfd.baseInputMeshVertexIndex;
SourceSkinnedMeshVertex smv = SourceSkinnedMeshVertex::ReadFromRawBuffer(inputMeshVertexData, absoluteInputMeshVertexIndex);
rv = ApplyBlendShapes(rv, vertexID, mfd);
rv = ApplySkinMatrices(rv, smv.boneWeightsOffset, smv.boneWeightsCount, mfd);
}
else
{
rv = (DeformedVertex)0;
}
#endif
return rv;
}
/////////////////////////////////////////////////////////////////////////////////
void ComputeDeformedVertex_float(in uint vertexID, in float3 vertex, in float3 normal, in float3 tangent, out float3 deformedVertex, out float3 deformedNormal, out float3 deformedTangent)
{
deformedVertex = vertex;
deformedNormal = normal;
deformedTangent = tangent;
#ifdef DOTS_INSTANCING_ON
#ifdef RUKHANKA_ENABLE_DEFORMATION_MOTION_VECTORS
const uint4 materialProperty = asuint(UNITY_ACCESS_DOTS_INSTANCED_PROP(float4, _DeformationParamsForMotionVectors));
const uint currentFrameIndex = materialProperty[2];
const uint index = materialProperty[currentFrameIndex];
#else
const uint index = asuint(UNITY_ACCESS_DOTS_INSTANCED_PROP(float, _DeformedMeshIndex));
#endif // RUKHANKA_ENABLE_DEFORMATION_MOTION_VECTORS
DeformedVertex v;
v.position = deformedVertex;
v.normal = deformedNormal;
v.tangent = deformedTangent;
v = GetDeformedVertexForMesh(vertexID, v, index);
deformedVertex = v.position;
deformedNormal = v.normal;
deformedTangent = v.tangent;
#endif // DOTS_INSTANCING_ON
}
@@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: a387024f61981304ca17ea1e3ffd997b
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/Deformation/Resources/ComputeDeformedVertex.hlsl
uploadId: 897522
@@ -0,0 +1,52 @@
#ifndef COPY_INITIAL_MESH_BLEND_SHAPES_HLSL_
#define COPY_INITIAL_MESH_BLEND_SHAPES_HLSL_
/////////////////////////////////////////////////////////////////////////////////
ByteAddressBuffer meshBlendShapesBuffer;
RWByteAddressBuffer outInitialMeshBlendShapesData;
uint inputBlendShapeVerticesCount;
uint inputBlendShapeVertexOffset;
uint outBlendShapeVertexOffset;
/////////////////////////////////////////////////////////////////////////////////
InputBlendShapeVertex ReadBlendShapeVertexDelta(uint vertexID)
{
uint vertexByteOffset = vertexID * InputBlendShapeVertex::size;
uint4 v0 = meshBlendShapesBuffer.Load4(vertexByteOffset + 0);
uint4 v1 = meshBlendShapesBuffer.Load4(vertexByteOffset + 16);
uint2 v2 = meshBlendShapesBuffer.Load2(vertexByteOffset + 32);
InputBlendShapeVertex rv;
rv.meshVertexIndex = v0.x;
rv.positionDelta = asfloat(v0.yzw);
rv.normalDelta = asfloat(v1.xyz);
rv.tangentDelta = asfloat(uint3(v1.w, v2.xy));
return rv;
}
/////////////////////////////////////////////////////////////////////////////////
[numthreads(128, 1, 1)]
void CopyInitialMeshBlendShapes(uint tid: SV_DispatchThreadID)
{
if (tid >= inputBlendShapeVerticesCount)
return;
InputBlendShapeVertex v = ReadBlendShapeVertexDelta(tid + inputBlendShapeVertexOffset);
uint4 o0 = asuint(float4(v.positionDelta, v.normalDelta.x));
uint4 o1 = asuint(float4(v.normalDelta.yz, v.tangentDelta.xy));
uint o2 = asuint(v.tangentDelta.z);
uint outVertexOffset = v.meshVertexIndex + outBlendShapeVertexOffset;
uint outVertexByteOffset = outVertexOffset * DeformedVertex::size;
outInitialMeshBlendShapesData.Store4(outVertexByteOffset + 0, o0);
outInitialMeshBlendShapesData.Store4(outVertexByteOffset + 16, o1);
outInitialMeshBlendShapesData.Store(outVertexByteOffset + 32, o2);
}
#endif // COPY_INITIAL_MESH_BLEND_SHAPES_HLSL_
@@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: b3da0895509ba5a44b90be032bfb9819
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/Deformation/Resources/CopyInitialMeshBlendShapes.hlsl
uploadId: 897522
@@ -0,0 +1,57 @@
#ifndef COPY_INITIAL_MESH_DATA_HLSL_
#define COPY_INITIAL_MESH_DATA_HLSL_
/////////////////////////////////////////////////////////////////////////////////
ByteAddressBuffer meshVertexData;
ByteAddressBuffer meshBonesPerVertexData;
RWByteAddressBuffer outInitialDeformedMeshData;
int inputVertexSizeInBytes;
int outDataVertexOffset;
uint totalMeshVertices;
int outBonesWeightsDataOffset;
int inputBonesWeightsDataOffset;
/////////////////////////////////////////////////////////////////////////////////
SourceSkinnedMeshVertex ReadSourceVertex(int vertexIndex)
{
int vertexByteOffset = vertexIndex * inputVertexSizeInBytes;
SourceSkinnedMeshVertex rv = (SourceSkinnedMeshVertex)0;
#ifndef RUKHANKA_INPLACE_SKINNING
CHECK_RAW_BUFFER_OUT_OF_BOUNDS(RUKHANKADEBUGMARKERS_DEFORMATION_COPY_MESH_DATA, vertexByteOffset, 40, meshVertexData);
float4 v0 = asfloat(meshVertexData.Load4(vertexByteOffset + 0));
float4 v1 = asfloat(meshVertexData.Load4(vertexByteOffset + 16));
float1 v2 = asfloat(meshVertexData.Load(vertexByteOffset + 32));
rv.position = v0.xyz;
rv.normal = float3(v0.w, v1.xy);
rv.tangent = float3(v1.zw, v2.x);
#endif
int baseBoneWeightIndex = inputBonesWeightsDataOffset + vertexIndex;
CHECK_RAW_BUFFER_OUT_OF_BOUNDS(RUKHANKADEBUGMARKERS_DEFORMATION_COPY_MESH_DATA, baseBoneWeightIndex * 4, 4, meshBonesPerVertexData);
uint boneWeightsOffsetAndCountPacked = meshBonesPerVertexData.Load(baseBoneWeightIndex * 4);
rv.boneWeightsOffset = SourceSkinnedMeshVertex::GetBoneWeightsOffsetFromPackedUINT(boneWeightsOffsetAndCountPacked) + outBonesWeightsDataOffset;
rv.boneWeightsCount = SourceSkinnedMeshVertex::GetBoneWeightsCountFromPackedUINT(boneWeightsOffsetAndCountPacked);
return rv;
}
/////////////////////////////////////////////////////////////////////////////////
[numthreads(128, 1, 1)]
void CopyInitialMeshData(uint tid: SV_DispatchThreadID)
{
if (tid >= totalMeshVertices)
return;
SourceSkinnedMeshVertex v = ReadSourceVertex(tid);
int outVertexOffset = tid + outDataVertexOffset;
v.WriteIntoRawBuffer(outInitialDeformedMeshData, outVertexOffset);
}
#endif // COPY_INITIAL_MESH_DATA_HLSL_
@@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: e286f97e31bb11943af1a6b97b753b28
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/Deformation/Resources/CopyInitialMeshData.hlsl
uploadId: 897522
@@ -0,0 +1,26 @@
#ifndef CREATE_PER_VERTEX_DEFORMATION_WORKLOAD_HLSL_
#define CREATE_PER_VERTEX_DEFORMATION_WORKLOAD_HLSL_
/////////////////////////////////////////////////////////////////////////////////
RWByteAddressBuffer outFramePerVertexWorkload;
uint totalDeformedMeshesCount;
/////////////////////////////////////////////////////////////////////////////////
[numthreads(128, 1, 1)]
void CreatePerVertexDeformationWorkload(uint tid: SV_DispatchThreadID)
{
if (tid >= totalDeformedMeshesCount)
return;
MeshFrameDeformationDescription md = frameDeformedMeshes[tid];
for (int i = 0; i < md.meshVerticesCount; ++i)
{
int outVertexIndex = i + md.baseOutVertexIndex;
outFramePerVertexWorkload.Store(outVertexIndex * 4, tid);
}
}
#endif // CREATE_PER_VERTEX_DEFORMATION_WORKLOAD_HLSL_
@@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: fd557c506788751409cfce5117c0adbc
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/Deformation/Resources/CreatePerVertexDeformationWorkload.hlsl
uploadId: 897522
@@ -0,0 +1,205 @@
#ifndef DEFORMATION_COMMON_HLSL_
#define DEFORMATION_COMMON_HLSL_
/////////////////////////////////////////////////////////////////////////////////
struct SourceSkinnedMeshVertex
{
#ifndef RUKHANKA_INPLACE_SKINNING
float3 position;
float3 normal;
float3 tangent;
static const int size = 40;
#else
static const int size = 4;
#endif
uint boneWeightsOffset;
uint boneWeightsCount;
static uint GetBoneWeightsOffsetFromPackedUINT(uint boneWeightsOffsetAndCount)
{
return boneWeightsOffsetAndCount >> 8;
}
static uint GetBoneWeightsCountFromPackedUINT(uint boneWeightsOffsetAndCount)
{
return boneWeightsOffsetAndCount & 0xff;
}
static uint PackBoneOffsetAndCount(uint count, uint offset)
{
return count | (offset << 8);
}
void WriteIntoRawBuffer(RWByteAddressBuffer outBuffer, uint index)
{
uint boneWeightsOffsetAndCount = PackBoneOffsetAndCount(boneWeightsCount, boneWeightsOffset);
uint byteOffset = index * size;
CHECK_RAW_BUFFER_OUT_OF_BOUNDS(RUKHANKADEBUGMARKERS_DEFORMATION_SKINNED_MESH_VERTEX_WRITE, byteOffset, size, outBuffer);
#ifndef RUKHANKA_INPLACE_SKINNING
uint4 u0 = asuint(float4(position, normal.x));
uint4 u1 = asuint(float4(normal.yz, tangent.xy));
uint2 u2 = uint2(asuint(tangent.z), boneWeightsOffsetAndCount);
outBuffer.Store4(byteOffset + 0, u0);
outBuffer.Store4(byteOffset + 16, u1);
outBuffer.Store2(byteOffset + 32, u2);
#else
outBuffer.Store(byteOffset + 0, boneWeightsOffsetAndCount);
#endif
}
static SourceSkinnedMeshVertex ReadFromRawBuffer(ByteAddressBuffer inBuffer, uint index)
{
SourceSkinnedMeshVertex rv;
uint byteOffset = index * size;
CHECK_RAW_BUFFER_OUT_OF_BOUNDS(RUKHANKADEBUGMARKERS_DEFORMATION_SKINNED_MESH_VERTEX_READ, byteOffset, size, inBuffer);
#ifndef RUKHANKA_INPLACE_SKINNING
uint4 u0 = inBuffer.Load4(byteOffset + 0);
uint4 u1 = inBuffer.Load4(byteOffset + 16);
uint2 u2 = inBuffer.Load2(byteOffset + 32);
rv.position = asfloat(u0.xyz);
rv.normal = asfloat(uint3(u0.w, u1.xy));
rv.tangent = asfloat(uint3(u1.zw, u2.x));
uint boneWeightsOffsetAndCount = u2.y;
#else
uint boneWeightsOffsetAndCount = inBuffer.Load(byteOffset + 0);
#endif
rv.boneWeightsOffset = GetBoneWeightsOffsetFromPackedUINT(boneWeightsOffsetAndCount);
rv.boneWeightsCount = GetBoneWeightsCountFromPackedUINT(boneWeightsOffsetAndCount);
return rv;
}
};
/////////////////////////////////////////////////////////////////////////////////
struct DeformedVertex
{
float3 position;
float3 normal;
float3 tangent;
static const int size = 3 * 3 * 4;
static DeformedVertex ReadFromRawBuffer(ByteAddressBuffer inBuffer, uint index)
{
uint byteOffset = index * size;
uint4 v0 = inBuffer.Load4(byteOffset + 0);
uint4 v1 = inBuffer.Load4(byteOffset + 16);
uint v2 = inBuffer.Load(byteOffset + 32);
CHECK_RAW_BUFFER_OUT_OF_BOUNDS(RUKHANKADEBUGMARKERS_DEFORMATION_DEFORMED_VERTEX_READ, byteOffset, size * 4, inBuffer);
DeformedVertex rv;
rv.position = asfloat(v0.xyz);
rv.normal = asfloat(uint3(v0.w, v1.xy));
rv.tangent = asfloat(uint3(v1.zw, v2.x));
return rv;
}
void Scale(float v)
{
position *= v;
normal *= v;
tangent *= v;
}
};
/////////////////////////////////////////////////////////////////////////////////
struct PackedDeformedVertex
{
uint4 pack0;
uint pack1;
DeformedVertex Unpack()
{
DeformedVertex rv;
rv.position.x = f16tof32(pack0.x >> 16);
rv.position.y = f16tof32(pack0.x);
rv.position.z = f16tof32(pack0.y >> 16);
rv.normal.x = f16tof32(pack0.y);
rv.normal.y = f16tof32(pack0.z >> 16);
rv.normal.z = f16tof32(pack0.z);
rv.tangent.x = f16tof32(pack0.w >> 16);
rv.tangent.y = f16tof32(pack0.w);
rv.tangent.z = f16tof32(pack1 >> 16);
return rv;
}
static PackedDeformedVertex Pack(DeformedVertex v)
{
PackedDeformedVertex rv;
uint3 p = f32tof16(v.position);
uint3 n = f32tof16(v.normal);
uint3 t = f32tof16(v.tangent);
rv.pack0.x = p.x << 16 | p.y;
rv.pack0.y = p.z << 16 | n.x;
rv.pack0.z = n.y << 16 | n.z;
rv.pack0.w = t.x << 16 | t.y;
rv.pack1 = t.z << 16;
return rv;
}
};
/////////////////////////////////////////////////////////////////////////////////
struct BoneInfluence
{
float weight;
int boneIndex;
static BoneInfluence ReadFromRawBuffer(ByteAddressBuffer inBuffer, uint index)
{
uint byteOffset = index * 8;
uint2 u0 = inBuffer.Load2(byteOffset + 0);
CHECK_RAW_BUFFER_OUT_OF_BOUNDS(RUKHANKADEBUGMARKERS_DEFORMATION_BONE_INFLUENCE_READ, byteOffset, 8, inBuffer);
BoneInfluence rv;
rv.weight = asfloat(u0.x);
rv.boneIndex = u0.y;
return rv;
}
};
/////////////////////////////////////////////////////////////////////////////////
struct MeshFrameDeformationDescription
{
int baseSkinMatrixIndex;
int baseBlendShapeWeightIndex;
int baseOutVertexIndex;
int baseInputMeshVertexIndex;
int baseInputMeshBlendShapeIndex;
int meshVerticesCount;
int meshBlendShapesCount;
};
/////////////////////////////////////////////////////////////////////////////////
struct InputBlendShapeVertex
{
uint meshVertexIndex;
float3 positionDelta;
float3 normalDelta;
float3 tangentDelta;
static const int size = (1 + 3 * 3) * 4;
};
/////////////////////////////////////////////////////////////////////////////////
StructuredBuffer<MeshFrameDeformationDescription> frameDeformedMeshes;
#endif // DEFORMATION_COMMON_HLSL_
@@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: f4b5c1545ea2e514fb6d61c2b2215f6b
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/Deformation/Resources/DeformationCommon.hlsl
uploadId: 897522
@@ -0,0 +1,17 @@
#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/Deformation/Resources/DeformationCommon.hlsl"
#include "Packages/com.rukhanka.animation/Rukhanka.Runtime/Deformation/Resources/CopyInitialMeshData.hlsl"
#include "Packages/com.rukhanka.animation/Rukhanka.Runtime/Deformation/Resources/CopyInitialMeshBlendShapes.hlsl"
#include "Packages/com.rukhanka.animation/Rukhanka.Runtime/Deformation/Resources/CreatePerVertexDeformationWorkload.hlsl"
#include "Packages/com.rukhanka.animation/Rukhanka.Runtime/Deformation/Resources/Skinning.hlsl"
//#pragma enable_d3d11_debug_symbols
//#pragma use_dxc
/////////////////////////////////////////////////////////////////////////////////
#pragma kernel CopyInitialMeshData
#pragma kernel CopyInitialMeshBlendShapes
#pragma kernel CreatePerVertexDeformationWorkload
#pragma kernel Skinning
@@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: c82cd9a4a3fa4854a9b4dd3290fd978f
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/Deformation/Resources/RukhankaMeshDeformation.compute
uploadId: 897522
@@ -0,0 +1,178 @@
#ifndef SKINNING_HLSL_
#define SKINNING_HLSL_
/////////////////////////////////////////////////////////////////////////////////
#pragma warning (disable: 4000)
#include "Packages/com.rukhanka.animation/Rukhanka.Runtime/Common/Shaders/GPUStructures/BoneTransform.hlsl"
#include "Packages/com.rukhanka.animation/Rukhanka.Runtime/Common/Shaders/GPUStructures/DualQuaternion.hlsl"
#include "Packages/com.rukhanka.animation/Rukhanka.Runtime/Common/Shaders/GPUStructures/SkinMatrix.hlsl"
/////////////////////////////////////////////////////////////////////////////////
ByteAddressBuffer framePerVertexWorkload;
// SourceSkinnedMeshVertex
ByteAddressBuffer inputMeshVertexData;
// BoneInfluence
ByteAddressBuffer inputBoneInfluences;
// DeformedVertex
ByteAddressBuffer inputBlendShapes;
// SkinMatrix
ByteAddressBuffer frameSkinMatrices;
StructuredBuffer<float> frameBlendShapeWeights;
#ifdef RUKHANKA_HALF_DEFORMED_DATA
RWStructuredBuffer<PackedDeformedVertex> outDeformedVertices;
#else
RWStructuredBuffer<DeformedVertex> outDeformedVertices;
#endif
uint totalSkinnedVerticesCount;
uint voidMeshVertexCount;
int currentSkinnedVertexOffset;
/////////////////////////////////////////////////////////////////////////////////
DeformedVertex ApplyBlendShapes(DeformedVertex v, uint meshVertexIndex, MeshFrameDeformationDescription mfd)
{
if (mfd.baseBlendShapeWeightIndex < 0)
return v;
for (int i = 0; i < mfd.meshBlendShapesCount; ++i)
{
float blendShapeWeight = frameBlendShapeWeights[mfd.baseBlendShapeWeightIndex + i];
if (blendShapeWeight == 0)
continue;
DeformedVertex blendShapeDelta = DeformedVertex::ReadFromRawBuffer
(
inputBlendShapes,
mfd.baseInputMeshBlendShapeIndex + meshVertexIndex + i * mfd.meshVerticesCount
);
blendShapeDelta.Scale(blendShapeWeight * 0.01f);
v.position += blendShapeDelta.position;
v.normal += blendShapeDelta.normal;
v.tangent += blendShapeDelta.tangent;
}
return v;
}
/////////////////////////////////////////////////////////////////////////////////
DeformedVertex ApplySkinMatrices
(
DeformedVertex v,
uint vertexBoneWeightsOffset,
uint vertexBoneWeightsCount,
MeshFrameDeformationDescription mfd
)
{
if (mfd.baseSkinMatrixIndex < 0 || vertexBoneWeightsCount == 0)
return v;
// Skinning loop
#ifdef RUKHANKA_DUAL_QUATERNION_SKINNING
DualQuaternion adq = (DualQuaternion)0;
#endif
DeformedVertex rv = (DeformedVertex)0;
float4 refRot = 0;
float3 skinnedScale = 0;
for (uint i = 0; i < vertexBoneWeightsCount; ++i)
{
uint boneInfluenceIndex = i + vertexBoneWeightsOffset;
BoneInfluence bi = BoneInfluence::ReadFromRawBuffer(inputBoneInfluences, boneInfluenceIndex);
int skinMatrixIndex = bi.boneIndex + mfd.baseSkinMatrixIndex;
float3x4 skinMatrix = SkinMatrix::ReadFromRawBuffer(frameSkinMatrices, skinMatrixIndex);
#ifdef RUKHANKA_DUAL_QUATERNION_SKINNING
BoneTransform skinPose = BoneTransform::FromMatrix(skinMatrix);
skinnedScale += skinPose.scale * bi.weight;
if (i == 0)
refRot = skinPose.rot.value;
else if (dot(skinPose.rot.value, refRot) < 0)
bi.weight = -bi.weight;
DualQuaternion dq = DualQuaternion::Construct(skinPose.pos, skinPose.rot);
DualQuaternion sdq = DualQuaternion::Scale(dq, bi.weight);
adq = DualQuaternion::Add(adq, sdq);
#else
rv.position += mul(skinMatrix, float4(v.position, 1)) * bi.weight;
rv.normal += mul(skinMatrix, float4(v.normal, 0)) * bi.weight;
rv.tangent += mul(skinMatrix, float4(v.tangent, 0)) * bi.weight;
#endif
}
#ifdef RUKHANKA_DUAL_QUATERNION_SKINNING
adq = DualQuaternion::Normalize(adq);
BoneTransform bt = adq.GetBoneTransform();
bt.scale = skinnedScale;
rv.position = BoneTransform::TransformPoint(bt, v.position);
rv.normal = BoneTransform::TransformDirection(bt, v.normal);
rv.tangent = BoneTransform::TransformDirection(bt, v.tangent);
#endif
return rv;
}
/////////////////////////////////////////////////////////////////////////////////
[numthreads(128, 1, 1)]
void Skinning(uint tid: SV_DispatchThreadID)
{
uint skinnedVertexIndex = tid + currentSkinnedVertexOffset;
// Skip zero vertex because it is uninitialized marker
if (skinnedVertexIndex >= totalSkinnedVerticesCount || skinnedVertexIndex == 0)
{
if (skinnedVertexIndex < totalSkinnedVerticesCount + voidMeshVertexCount)
{
#ifdef RUKHANKA_HALF_DEFORMED_DATA
outDeformedVertices[skinnedVertexIndex] = (PackedDeformedVertex)0;
#else
outDeformedVertices[skinnedVertexIndex] = (DeformedVertex)0;
#endif
CHECK_STRUCTURED_BUFFER_OUT_OF_BOUNDS(RUKHANKADEBUGMARKERS_DEFORMATION_COPY_MESH_DATA, skinnedVertexIndex, outDeformedVertices);
}
return;
}
CHECK_RAW_BUFFER_OUT_OF_BOUNDS(RUKHANKADEBUGMARKERS_DEFORMATION_PER_VERTEX_WORKLOAD_READ, skinnedVertexIndex * 4, 4, framePerVertexWorkload);
uint frameDeformedMeshIndex = framePerVertexWorkload.Load(skinnedVertexIndex * 4);
CHECK_STRUCTURED_BUFFER_OUT_OF_BOUNDS(RUKHANKADEBUGMARKERS_DEFORMATION_FRAME_DEFORMED_VERTEX_READ, frameDeformedMeshIndex, frameDeformedMeshes);
MeshFrameDeformationDescription mfd = frameDeformedMeshes[frameDeformedMeshIndex];
uint meshVertexIndex = skinnedVertexIndex - mfd.baseOutVertexIndex;
uint absoluteInputMeshVertexIndex = meshVertexIndex + mfd.baseInputMeshVertexIndex;
SourceSkinnedMeshVertex smv = SourceSkinnedMeshVertex::ReadFromRawBuffer(inputMeshVertexData, absoluteInputMeshVertexIndex);
DeformedVertex rv = (DeformedVertex)0;
#ifndef RUKHANKA_INPLACE_SKINNING
rv.position = smv.position;
rv.tangent = smv.tangent;
rv.normal = smv.normal;
#endif
rv = ApplyBlendShapes(rv, meshVertexIndex, mfd);
rv = ApplySkinMatrices(rv, smv.boneWeightsOffset, smv.boneWeightsCount, mfd);
#ifdef RUKHANKA_HALF_DEFORMED_DATA
outDeformedVertices[skinnedVertexIndex] = PackedDeformedVertex::Pack(rv);
#else
outDeformedVertices[skinnedVertexIndex] = rv;
#endif
CHECK_STRUCTURED_BUFFER_OUT_OF_BOUNDS(RUKHANKADEBUGMARKERS_DEFORMATION_DEFORMED_VERTEX_WRITE, skinnedVertexIndex, outDeformedVertices);
}
/////////////////////////////////////////////////////////////////////////////////
#endif // SKINNING_HLSL_
@@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: c99bf944d5948e649b631c5ae38ac8a2
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/Deformation/Resources/Skinning.hlsl
uploadId: 897522
@@ -0,0 +1,162 @@
using Unity.Burst;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Entities;
using Unity.Jobs;
using Unity.Rendering;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
namespace Rukhanka
{
[WorldSystemFilter(WorldSystemFilterFlags.Default | WorldSystemFilterFlags.Editor)]
[UpdateInGroup(typeof(RukhankaDeformationSystemGroup))]
[CreateAfter(typeof(RegisterMaterialsAndMeshesSystem))]
public partial struct SkinnedMeshPreparationSystem: ISystem
{
SharedComponentTypeHandle<RenderMeshArray> renderMeshArrayTypeHandle;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public void OnCreate(ref SystemState ss)
{
#if !HYBRID_RENDERER_DISABLED
if (!EntitiesGraphicsUtils.IsEntitiesGraphicsSupportedOnSystem())
#endif
{
ss.Enabled = false;
return;
}
var deformationRuntimeData = DeformationRuntimeData.Construct(ref ss);
ss.EntityManager.CreateSingleton(deformationRuntimeData, "Rukhanka Deformation Runtime Data");
renderMeshArrayTypeHandle = ss.GetSharedComponentTypeHandle<RenderMeshArray>();
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public void OnDestroy(ref SystemState ss)
{
if (SystemAPI.TryGetSingletonRW<DeformationRuntimeData>(out var deformationRuntimeData))
deformationRuntimeData.ValueRW.Dispose();
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
[BurstCompile]
public void OnUpdate(ref SystemState ss)
{
renderMeshArrayTypeHandle.Update(ref ss);
ref var deformationRuntimeData = ref SystemAPI.GetSingletonRW<DeformationRuntimeData>().ValueRW;
// Reset data used in current frame
var resetFrameDataJH = ResetNewFrameData(ref ss, ref deformationRuntimeData, ss.Dependency);
// Gather meshes that new in this frame and absent in previous frames
var getNewSkinnedMeshesJH = GatherNewMeshes(ref ss, ref deformationRuntimeData, resetFrameDataJH);
// Register new meshes in internal skinned mesh database
var registerNewSkinnedMeshesJH = RegisterNewMeshes(ref ss, ref deformationRuntimeData, getNewSkinnedMeshesJH);
// Compute all frame deformed meshes skin matrices offsets in global GPU buffer
var computeFrameSkinMatrixDataJH = ComputeFrameSkinnedMeshData(ref ss, ref deformationRuntimeData, resetFrameDataJH);
var combinedJobHandle = JobHandle.CombineDependencies(computeFrameSkinMatrixDataJH, registerNewSkinnedMeshesJH);
ss.Dependency = combinedJobHandle;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
unsafe JobHandle ResetNewFrameData(ref SystemState ss, ref DeformationRuntimeData drd, JobHandle dependsOn)
{
var frameSkinnedMeshesQuery = SystemAPI.QueryBuilder()
.WithAny<Rukhanka.SkinMatrix, Rukhanka.BlendShapeWeight>()
.Build();
var resetFrameDataJob = new ResetFrameDataJob()
{
frameSkinnedMeshesCount = frameSkinnedMeshesQuery.CalculateEntityCount(),
frameSkinMatrixCounter = new UnsafeAtomicCounter32(UnsafeUtility.AddressOf(ref drd.frameSkinMatrixCount)),
frameBlendShapeWeightCounter = new UnsafeAtomicCounter32(UnsafeUtility.AddressOf(ref drd.frameBlendShapeWeightsCount)),
newSkinnedMeshesToRegister = drd.newSkinnedMeshesToRegister,
entityToSMRFrameDataMap = drd.entityToSMRFrameDataMap,
frameDeformedVertexCount = new UnsafeAtomicCounter32(UnsafeUtility.AddressOf(ref drd.frameDeformedVerticesCount)),
};
var resetFrameDataJH = resetFrameDataJob.Schedule(dependsOn);
return resetFrameDataJH;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
JobHandle GatherNewMeshes(ref SystemState ss, ref DeformationRuntimeData drd, JobHandle dependsOn)
{
var deformedSubMeshQuery = SystemAPI.QueryBuilder()
.WithAll<MaterialMeshInfo, DeformedMeshIndex, SkinnedMeshRendererComponent>()
.Build();
var getNewSkinnedMeshesJob = new GetFrameNewSkinnedMeshesJob()
{
existingSkinnedMeshes = drd.registeredSkinnedMeshesMap,
newSkinnedMeshes = drd.newSkinnedMeshesToRegister.AsParallelWriter(),
renderMeshArrays = drd.renderMeshArrays,
materialMeshInfoTypeHandle = SystemAPI.GetComponentTypeHandle<MaterialMeshInfo>(true),
renderMeshArrayTypeHandle = renderMeshArrayTypeHandle,
animatedSkinnedMeshTypeHandle = SystemAPI.GetComponentTypeHandle<SkinnedMeshRendererComponent>(true),
};
var getNewSkinnedMeshesJH = getNewSkinnedMeshesJob.ScheduleParallel(deformedSubMeshQuery, dependsOn);
return getNewSkinnedMeshesJH;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
unsafe JobHandle RegisterNewMeshes(ref SystemState ss, ref DeformationRuntimeData drd, JobHandle dependsOn)
{
var registerNewSkinnedMeshesJob = new RegisterNewSkinnedMeshesJob()
{
existingSkinnedMeshes = drd.registeredSkinnedMeshesMap,
newSkinnedMeshes = drd.newSkinnedMeshesToRegister,
totalSkinnedVerticesCount = new UnsafeAtomicCounter32(UnsafeUtility.AddressOf(ref drd.totalSkinnedVerticesCount)),
totalBoneWeightsCount = new UnsafeAtomicCounter32(UnsafeUtility.AddressOf(ref drd.totalBoneWeightsCount)),
totalBlendShapeVerticesCount = new UnsafeAtomicCounter32(UnsafeUtility.AddressOf(ref drd.totalBlendShapeVerticesCount)),
maximumVerticesAcrossAllRegisteredMeshes = new UnsafeAtomicCounter32(UnsafeUtility.AddressOf(ref drd.maximumVerticesAcrossAllRegisteredMeshes)),
maximumSkinMatrixCountAcrossAllRegisteredMeshes = new UnsafeAtomicCounter32(UnsafeUtility.AddressOf(ref drd.maximumSkinMatrixCountAcrossAllRegisteredMeshes)),
};
var registerNewSkinnedMeshesJH = registerNewSkinnedMeshesJob.Schedule(dependsOn);
return registerNewSkinnedMeshesJH;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
unsafe JobHandle ComputeFrameSkinnedMeshData(ref SystemState ss, ref DeformationRuntimeData drd, JobHandle dependsOn)
{
// If culling context is present then skin only meshes in visible LOD level
SystemAPI.TryGetSingleton<AnimationCullingContext>(out var animationCullingContext);
var computeFrameSkinnedMeshesJob = new ComputeFrameSkinnedMeshesJob()
{
skinMatrixOffsetCounter = new UnsafeAtomicCounter32(UnsafeUtility.AddressOf(ref drd.frameSkinMatrixCount)),
blendShapeWeightOffsetCounter = new UnsafeAtomicCounter32(UnsafeUtility.AddressOf(ref drd.frameBlendShapeWeightsCount)),
frameDeformedVerticesCounter = new UnsafeAtomicCounter32(UnsafeUtility.AddressOf(ref drd.frameDeformedVerticesCount)),
cullAnimationsTagLookup = SystemAPI.GetComponentLookup<CullAnimationsTag>(true),
entityToSMRFrameData = drd.entityToSMRFrameDataMap.AsParallelWriter(),
skinMatrixBufferLookup = SystemAPI.GetBufferLookup<Rukhanka.SkinMatrix>(true),
blendShapeWeightBufferLookup = SystemAPI.GetBufferLookup<Rukhanka.BlendShapeWeight>(true),
lodRangeLookup = SystemAPI.GetComponentLookup<LODRange>(true),
lodWorldRefPointLookup = SystemAPI.GetComponentLookup<LODWorldReferencePoint>(true),
lodAffectors = animationCullingContext.lodAffectors,
// Disable culling in editor world
#if UNITY_EDITOR
isEditorWorld = ss.WorldUnmanaged.Flags == WorldFlags.Editor
#endif
};
dependsOn = computeFrameSkinnedMeshesJob.ScheduleParallel(dependsOn);
return dependsOn;
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 719f458d822bef44b8c2e55cd4c6b01e
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/Deformation/SkinnedMeshPreparationSystem.cs
uploadId: 897522
@@ -0,0 +1,259 @@
using System;
using Unity.Burst;
using Unity.Burst.Intrinsics;
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 Hash128 = Unity.Entities.Hash128;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
namespace Rukhanka
{
public partial struct SkinnedMeshPreparationSystem
{
//-----------------------------------------------------------------------------------------------------------------//
[BurstCompile]
struct GetFrameNewSkinnedMeshesJob: IJobChunk
{
[ReadOnly]
public NativeParallelHashMap<int, BRGRenderMeshArray> renderMeshArrays;
[ReadOnly]
public SharedComponentTypeHandle<RenderMeshArray> renderMeshArrayTypeHandle;
[ReadOnly]
public ComponentTypeHandle<MaterialMeshInfo> materialMeshInfoTypeHandle;
[ReadOnly]
public ComponentTypeHandle<SkinnedMeshRendererComponent> animatedSkinnedMeshTypeHandle;
[ReadOnly]
public NativeParallelHashMap<Hash128, SkinnedMeshDescription> existingSkinnedMeshes;
public NativeParallelHashMap<BatchMeshID, BlobAssetReference<SkinnedMeshInfoBlob>>.ParallelWriter newSkinnedMeshes;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask)
{
if (!renderMeshArrays.IsCreated)
return;
int renderMeshArrayIndex = chunk.GetSharedComponentIndex(renderMeshArrayTypeHandle);
BRGRenderMeshArray chunkRenderMeshArray = default;
if (renderMeshArrayIndex >= 0)
renderMeshArrays.TryGetValue(renderMeshArrayIndex, out chunkRenderMeshArray);
var materialMeshInfos = chunk.GetNativeArray(ref materialMeshInfoTypeHandle);
var animatedSkinnedMeshInfos = chunk.GetNativeArray(ref animatedSkinnedMeshTypeHandle);
var cee = new ChunkEntityEnumerator(useEnabledMask, chunkEnabledMask, chunk.Count);
while (cee.NextEntityIndex(out var i))
{
var mmi = materialMeshInfos[i];
var asm = animatedSkinnedMeshInfos[i];
var meshID = chunkRenderMeshArray.GetMeshID(mmi);
if (!existingSkinnedMeshes.ContainsKey(asm.smrInfoBlob.Value.hash))
{
newSkinnedMeshes.TryAdd(meshID, asm.smrInfoBlob);
}
}
}
}
//-----------------------------------------------------------------------------------------------------------------//
[BurstCompile]
struct RegisterNewSkinnedMeshesJob: IJob
{
public NativeParallelHashMap<BatchMeshID, BlobAssetReference<SkinnedMeshInfoBlob>> newSkinnedMeshes;
public NativeParallelHashMap<Hash128, SkinnedMeshDescription> existingSkinnedMeshes;
[NativeDisableUnsafePtrRestriction]
public UnsafeAtomicCounter32 totalSkinnedVerticesCount;
[NativeDisableUnsafePtrRestriction]
public UnsafeAtomicCounter32 totalBoneWeightsCount;
[NativeDisableUnsafePtrRestriction]
public UnsafeAtomicCounter32 totalBlendShapeVerticesCount;
[NativeDisableUnsafePtrRestriction]
public UnsafeAtomicCounter32 maximumVerticesAcrossAllRegisteredMeshes;
[NativeDisableUnsafePtrRestriction]
public UnsafeAtomicCounter32 maximumSkinMatrixCountAcrossAllRegisteredMeshes;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public unsafe void Execute()
{
var baseVertex = *totalSkinnedVerticesCount.Counter;
var baseBoneWeightIndex = *totalBoneWeightsCount.Counter;
var baseBlendShapeIndex = *totalBlendShapeVerticesCount.Counter;
foreach (var sm in newSkinnedMeshes)
{
var smd = new SkinnedMeshDescription();
ref var skinnedMeshBlob = ref sm.Value.Value;
var numMeshVertices = skinnedMeshBlob.meshVerticesCount;
smd.baseVertex = baseVertex;
baseVertex += numMeshVertices;
smd.vertexCount = numMeshVertices;
var numBoneWeightIndicesCount = skinnedMeshBlob.meshBoneWeightsCount;
smd.baseBoneWeightIndex = baseBoneWeightIndex;
baseBoneWeightIndex += numBoneWeightIndicesCount;
var blendShapesDataSize = skinnedMeshBlob.meshBlendShapesCount * numMeshVertices;
smd.baseBlendShapeIndex = baseBlendShapeIndex;
baseBlendShapeIndex += blendShapesDataSize;
maximumVerticesAcrossAllRegisteredMeshes.Reset(math.max(skinnedMeshBlob.boneWeightsIndices.Length, *maximumVerticesAcrossAllRegisteredMeshes.Counter));
maximumSkinMatrixCountAcrossAllRegisteredMeshes.Reset(math.max(skinnedMeshBlob.bones.Length, *maximumSkinMatrixCountAcrossAllRegisteredMeshes.Counter));
existingSkinnedMeshes.Add(skinnedMeshBlob.hash, smd);
}
totalSkinnedVerticesCount.Reset(baseVertex);
totalBoneWeightsCount.Reset(baseBoneWeightIndex);
totalBlendShapeVerticesCount.Reset(baseBlendShapeIndex);
}
}
//-----------------------------------------------------------------------------------------------------------------//
[BurstCompile]
struct ResetFrameDataJob: IJob
{
public NativeParallelHashMap<BatchMeshID, BlobAssetReference<SkinnedMeshInfoBlob>> newSkinnedMeshesToRegister;
public NativeParallelHashMap<Entity, SkinnedMeshRendererFrameDeformationData> entityToSMRFrameDataMap;
[NativeDisableUnsafePtrRestriction]
public UnsafeAtomicCounter32 frameSkinMatrixCounter;
[NativeDisableUnsafePtrRestriction]
public UnsafeAtomicCounter32 frameBlendShapeWeightCounter;
[NativeDisableUnsafePtrRestriction]
public UnsafeAtomicCounter32 frameDeformedVertexCount;
public int frameSkinnedMeshesCount;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public void Execute()
{
newSkinnedMeshesToRegister.Clear();
entityToSMRFrameDataMap.Clear();
entityToSMRFrameDataMap.Capacity = math.max(frameSkinnedMeshesCount, entityToSMRFrameDataMap.Capacity);
frameSkinMatrixCounter.Reset(0);
frameBlendShapeWeightCounter.Reset(0);
#if RUKHANKA_INPLACE_SKINNING
// In case of in-place skinning frameDeformedVertexCount indexes meshes and not vertices, so reset it to zero here
var deformedVertexCountResetValue = 0;
#else
// Zero index is considered 'uninitialized' in 'ApplyPreviousFrameDeformedVertexPosition' function in deformed shader code
// Default value starts from one
var deformedVertexCountResetValue = 1;
#endif
frameDeformedVertexCount.Reset(deformedVertexCountResetValue);
}
}
//-----------------------------------------------------------------------------------------------------------------//
[BurstCompile]
partial struct ComputeFrameSkinnedMeshesJob: IJobEntity
{
[NativeDisableUnsafePtrRestriction]
public UnsafeAtomicCounter32 skinMatrixOffsetCounter;
[NativeDisableUnsafePtrRestriction]
public UnsafeAtomicCounter32 blendShapeWeightOffsetCounter;
[NativeDisableUnsafePtrRestriction]
public UnsafeAtomicCounter32 frameDeformedVerticesCounter;
[ReadOnly]
public ComponentLookup<CullAnimationsTag> cullAnimationsTagLookup;
[ReadOnly]
public BufferLookup<Rukhanka.SkinMatrix> skinMatrixBufferLookup;
[ReadOnly]
public BufferLookup<Rukhanka.BlendShapeWeight> blendShapeWeightBufferLookup;
[ReadOnly]
[NativeDisableContainerSafetyRestriction]
public NativeList<LODGroupExtensions.LODParams> lodAffectors;
[ReadOnly]
public ComponentLookup<LODRange> lodRangeLookup;
[ReadOnly]
public ComponentLookup<LODWorldReferencePoint> lodWorldRefPointLookup;
public NativeParallelHashMap<Entity, SkinnedMeshRendererFrameDeformationData>.ParallelWriter entityToSMRFrameData;
#if UNITY_EDITOR
public bool isEditorWorld;
#endif
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void Execute(Entity e, in SkinnedMeshRendererComponent asm)
{
#if UNITY_EDITOR
if (!isEditorWorld)
#endif
if (cullAnimationsTagLookup.HasComponent(asm.animatedRigEntity) && cullAnimationsTagLookup.IsComponentEnabled(asm.animatedRigEntity))
return;
if (!IsLODActive(e))
return;
var smrdd = SkinnedMeshRendererFrameDeformationData.MakeDefault();
#if RUKHANKA_INPLACE_SKINNING
var currentDeformedMeshIndex = frameDeformedVerticesCounter.Add(1);
smrdd.deformedVertexIndex = currentDeformedMeshIndex;
#else
var currentDeformedVertexIndex = frameDeformedVerticesCounter.Add(asm.smrInfoBlob.Value.meshVerticesCount);
smrdd.deformedVertexIndex = currentDeformedVertexIndex;
#endif
if (skinMatrixBufferLookup.TryGetBuffer(e, out var smb))
{
var currentSkinMatrixBufferOffset = skinMatrixOffsetCounter.Add(smb.Length);
smrdd.skinMatrixIndex = currentSkinMatrixBufferOffset;
}
if (blendShapeWeightBufferLookup.TryGetBuffer(e, out var blendShapeWeights))
{
var currentBlendShapeWeightsBufferOffset = blendShapeWeightOffsetCounter.Add(blendShapeWeights.Length);
smrdd.blendShapeWeightIndex = currentBlendShapeWeightsBufferOffset;
}
entityToSMRFrameData.TryAdd(e, smrdd);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
float CalculateLODDistance(in LODRange lodRange, in LODWorldReferencePoint lodRefPoint, in LODGroupExtensions.LODParams lodParams)
{
float rv = lodParams.distanceScale;
if (!lodParams.isOrtho)
rv *= math.length(lodParams.cameraPos - lodRefPoint.Value);
return rv;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool IsLODActive(Entity e)
{
if (!lodAffectors.IsCreated || !lodRangeLookup.TryGetComponent(e, out var lodRange) || !lodWorldRefPointLookup.TryGetComponent(e, out var lodRefPoint))
return true;
for (var i = 0; i < lodAffectors.Length; ++i)
{
var la = lodAffectors[i];
var d = CalculateLODDistance(lodRange, lodRefPoint, la);
var isLodActive = d < lodRange.MaxDist && d >= lodRange.MinDist;
if (isLodActive)
return true;
}
return false;
}
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: d9c7705a36a0cb4429caa4d2e27e59b7
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/Deformation/SkinnedMeshPreparationSystem_Jobs.cs
uploadId: 897522