Files
2026-05-31 14:27:52 -07:00

259 lines
9.7 KiB
C#

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;
}
}
}
}