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 renderMeshArrays; [ReadOnly] public SharedComponentTypeHandle renderMeshArrayTypeHandle; [ReadOnly] public ComponentTypeHandle materialMeshInfoTypeHandle; [ReadOnly] public ComponentTypeHandle animatedSkinnedMeshTypeHandle; [ReadOnly] public NativeParallelHashMap existingSkinnedMeshes; public NativeParallelHashMap>.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> newSkinnedMeshes; public NativeParallelHashMap 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> newSkinnedMeshesToRegister; public NativeParallelHashMap 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 cullAnimationsTagLookup; [ReadOnly] public BufferLookup skinMatrixBufferLookup; [ReadOnly] public BufferLookup blendShapeWeightBufferLookup; [ReadOnly] [NativeDisableContainerSafetyRestriction] public NativeList lodAffectors; [ReadOnly] public ComponentLookup lodRangeLookup; [ReadOnly] public ComponentLookup lodWorldRefPointLookup; public NativeParallelHashMap.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; } } } }