276 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
		
		
			
		
	
	
			276 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
|  | using Unity.Collections; | ||
|  | using Unity.Mathematics; | ||
|  | using UnityEngine.Jobs; | ||
|  | 
 | ||
|  | namespace UnityEngine.Rendering.Universal | ||
|  | { | ||
|  |     /// <summary> | ||
|  |     /// Contains <see cref="DecalProjector"/> cached properties needed for rendering. | ||
|  |     /// </summary> | ||
|  |     internal class DecalCachedChunk : DecalChunk | ||
|  |     { | ||
|  |         public MaterialPropertyBlock propertyBlock; | ||
|  |         public int passIndexDBuffer; | ||
|  |         public int passIndexEmissive; | ||
|  |         public int passIndexScreenSpace; | ||
|  |         public int passIndexGBuffer; | ||
|  |         public int drawOrder; | ||
|  |         public bool isCreated; | ||
|  | 
 | ||
|  |         public NativeArray<float4x4> decalToWorlds; | ||
|  |         public NativeArray<float4x4> normalToWorlds; | ||
|  |         public NativeArray<float4x4> sizeOffsets; | ||
|  |         public NativeArray<float2> drawDistances; | ||
|  |         public NativeArray<float2> angleFades; | ||
|  |         public NativeArray<float4> uvScaleBias; | ||
|  |         public NativeArray<int> layerMasks; | ||
|  |         public NativeArray<ulong> sceneLayerMasks; | ||
|  |         public NativeArray<float> fadeFactors; | ||
|  |         public NativeArray<BoundingSphere> boundingSpheres; | ||
|  |         public NativeArray<DecalScaleMode> scaleModes; | ||
|  |         public NativeArray<uint> renderingLayerMasks; | ||
|  |         public NativeArray<float3> positions; | ||
|  |         public NativeArray<quaternion> rotation; | ||
|  |         public NativeArray<float3> scales; | ||
|  |         public NativeArray<bool> dirty; | ||
|  | 
 | ||
|  |         public BoundingSphere[] boundingSphereArray; | ||
|  | 
 | ||
|  |         public override void RemoveAtSwapBack(int entityIndex) | ||
|  |         { | ||
|  |             RemoveAtSwapBack(ref decalToWorlds, entityIndex, count); | ||
|  |             RemoveAtSwapBack(ref normalToWorlds, entityIndex, count); | ||
|  |             RemoveAtSwapBack(ref sizeOffsets, entityIndex, count); | ||
|  |             RemoveAtSwapBack(ref drawDistances, entityIndex, count); | ||
|  |             RemoveAtSwapBack(ref angleFades, entityIndex, count); | ||
|  |             RemoveAtSwapBack(ref uvScaleBias, entityIndex, count); | ||
|  |             RemoveAtSwapBack(ref layerMasks, entityIndex, count); | ||
|  |             RemoveAtSwapBack(ref sceneLayerMasks, entityIndex, count); | ||
|  |             RemoveAtSwapBack(ref fadeFactors, entityIndex, count); | ||
|  |             RemoveAtSwapBack(ref boundingSphereArray, entityIndex, count); | ||
|  |             RemoveAtSwapBack(ref boundingSpheres, entityIndex, count); | ||
|  |             RemoveAtSwapBack(ref scaleModes, entityIndex, count); | ||
|  |             RemoveAtSwapBack(ref renderingLayerMasks, entityIndex, count); | ||
|  |             RemoveAtSwapBack(ref positions, entityIndex, count); | ||
|  |             RemoveAtSwapBack(ref rotation, entityIndex, count); | ||
|  |             RemoveAtSwapBack(ref scales, entityIndex, count); | ||
|  |             RemoveAtSwapBack(ref dirty, entityIndex, count); | ||
|  |             count--; | ||
|  |         } | ||
|  | 
 | ||
|  |         public override void SetCapacity(int newCapacity) | ||
|  |         { | ||
|  |             decalToWorlds.ResizeArray(newCapacity); | ||
|  |             normalToWorlds.ResizeArray(newCapacity); | ||
|  |             sizeOffsets.ResizeArray(newCapacity); | ||
|  |             drawDistances.ResizeArray(newCapacity); | ||
|  |             angleFades.ResizeArray(newCapacity); | ||
|  |             uvScaleBias.ResizeArray(newCapacity); | ||
|  |             layerMasks.ResizeArray(newCapacity); | ||
|  |             sceneLayerMasks.ResizeArray(newCapacity); | ||
|  |             fadeFactors.ResizeArray(newCapacity); | ||
|  |             boundingSpheres.ResizeArray(newCapacity); | ||
|  |             scaleModes.ResizeArray(newCapacity); | ||
|  |             renderingLayerMasks.ResizeArray(newCapacity); | ||
|  |             positions.ResizeArray(newCapacity); | ||
|  |             rotation.ResizeArray(newCapacity); | ||
|  |             scales.ResizeArray(newCapacity); | ||
|  |             dirty.ResizeArray(newCapacity); | ||
|  | 
 | ||
|  |             ArrayExtensions.ResizeArray(ref boundingSphereArray, newCapacity); | ||
|  |             capacity = newCapacity; | ||
|  |         } | ||
|  | 
 | ||
|  |         public override void Dispose() | ||
|  |         { | ||
|  |             if (capacity == 0) | ||
|  |                 return; | ||
|  | 
 | ||
|  |             decalToWorlds.Dispose(); | ||
|  |             normalToWorlds.Dispose(); | ||
|  |             sizeOffsets.Dispose(); | ||
|  |             drawDistances.Dispose(); | ||
|  |             angleFades.Dispose(); | ||
|  |             uvScaleBias.Dispose(); | ||
|  |             layerMasks.Dispose(); | ||
|  |             sceneLayerMasks.Dispose(); | ||
|  |             fadeFactors.Dispose(); | ||
|  |             boundingSpheres.Dispose(); | ||
|  |             scaleModes.Dispose(); | ||
|  |             renderingLayerMasks.Dispose(); | ||
|  |             positions.Dispose(); | ||
|  |             rotation.Dispose(); | ||
|  |             scales.Dispose(); | ||
|  |             dirty.Dispose(); | ||
|  |             count = 0; | ||
|  |             capacity = 0; | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     /// <summary> | ||
|  |     /// Caches <see cref="DecalProjector"/> properties into <see cref="DecalCachedChunk"/>. | ||
|  |     /// Uses jobs with <see cref="IJobParallelForTransform"/>. | ||
|  |     /// </summary> | ||
|  |     internal class DecalUpdateCachedSystem | ||
|  |     { | ||
|  |         private DecalEntityManager m_EntityManager; | ||
|  |         private ProfilingSampler m_Sampler; | ||
|  |         private ProfilingSampler m_SamplerJob; | ||
|  | 
 | ||
|  |         public DecalUpdateCachedSystem(DecalEntityManager entityManager) | ||
|  |         { | ||
|  |             m_EntityManager = entityManager; | ||
|  |             m_Sampler = new ProfilingSampler("DecalUpdateCachedSystem.Execute"); | ||
|  |             m_SamplerJob = new ProfilingSampler("DecalUpdateCachedSystem.ExecuteJob"); | ||
|  |         } | ||
|  | 
 | ||
|  |         public void Execute() | ||
|  |         { | ||
|  |             using (new ProfilingScope(m_Sampler)) | ||
|  |             { | ||
|  |                 for (int i = 0; i < m_EntityManager.chunkCount; ++i) | ||
|  |                     Execute(m_EntityManager.entityChunks[i], m_EntityManager.cachedChunks[i], m_EntityManager.entityChunks[i].count); | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         private void Execute(DecalEntityChunk entityChunk, DecalCachedChunk cachedChunk, int count) | ||
|  |         { | ||
|  |             if (count == 0) | ||
|  |                 return; | ||
|  | 
 | ||
|  |             cachedChunk.currentJobHandle.Complete(); | ||
|  | 
 | ||
|  |             // Make sure draw order is up to date | ||
|  |             var material = entityChunk.material; | ||
|  |             if (material.HasProperty("_DrawOrder")) | ||
|  |                 cachedChunk.drawOrder = material.GetInt("_DrawOrder"); | ||
|  | 
 | ||
|  |             // Shader can change any time in editor, so we have to update passes each time | ||
|  | #if !UNITY_EDITOR | ||
|  |             if (!cachedChunk.isCreated) | ||
|  | #endif | ||
|  |             { | ||
|  |                 int passIndexDBuffer = material.FindPass(DecalShaderPassNames.DBufferProjector); | ||
|  |                 cachedChunk.passIndexDBuffer = passIndexDBuffer; | ||
|  | 
 | ||
|  |                 int passIndexEmissive = material.FindPass(DecalShaderPassNames.DecalProjectorForwardEmissive); | ||
|  |                 cachedChunk.passIndexEmissive = passIndexEmissive; | ||
|  | 
 | ||
|  |                 int passIndexScreenSpace = material.FindPass(DecalShaderPassNames.DecalScreenSpaceProjector); | ||
|  |                 cachedChunk.passIndexScreenSpace = passIndexScreenSpace; | ||
|  | 
 | ||
|  |                 int passIndexGBuffer = material.FindPass(DecalShaderPassNames.DecalGBufferProjector); | ||
|  |                 cachedChunk.passIndexGBuffer = passIndexGBuffer; | ||
|  | 
 | ||
|  |                 cachedChunk.isCreated = true; | ||
|  |             } | ||
|  | 
 | ||
|  |             using (new ProfilingScope(m_SamplerJob)) | ||
|  |             { | ||
|  |                 UpdateTransformsJob updateTransformJob = new UpdateTransformsJob() | ||
|  |                 { | ||
|  |                     positions = cachedChunk.positions, | ||
|  |                     rotations = cachedChunk.rotation, | ||
|  |                     scales = cachedChunk.scales, | ||
|  |                     dirty = cachedChunk.dirty, | ||
|  |                     scaleModes = cachedChunk.scaleModes, | ||
|  |                     sizeOffsets = cachedChunk.sizeOffsets, | ||
|  |                     decalToWorlds = cachedChunk.decalToWorlds, | ||
|  |                     normalToWorlds = cachedChunk.normalToWorlds, | ||
|  |                     boundingSpheres = cachedChunk.boundingSpheres, | ||
|  |                     minDistance = System.Single.Epsilon, | ||
|  |                 }; | ||
|  | 
 | ||
|  |                 var handle = updateTransformJob.Schedule(entityChunk.transformAccessArray); | ||
|  |                 cachedChunk.currentJobHandle = handle; | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  | #if ENABLE_BURST_1_0_0_OR_NEWER | ||
|  |         [Unity.Burst.BurstCompile] | ||
|  | #endif | ||
|  |         public unsafe struct UpdateTransformsJob : IJobParallelForTransform | ||
|  |         { | ||
|  |             private static readonly quaternion k_MinusYtoZRotation = quaternion.EulerXYZ(-math.PI / 2.0f, 0, 0); | ||
|  | 
 | ||
|  |             public NativeArray<float3> positions; | ||
|  |             public NativeArray<quaternion> rotations; | ||
|  |             public NativeArray<float3> scales; | ||
|  |             public NativeArray<bool> dirty; | ||
|  | 
 | ||
|  |             [ReadOnly] public NativeArray<DecalScaleMode> scaleModes; | ||
|  |             [ReadOnly] public NativeArray<float4x4> sizeOffsets; | ||
|  |             [WriteOnly] public NativeArray<float4x4> decalToWorlds; | ||
|  |             [WriteOnly] public NativeArray<float4x4> normalToWorlds; | ||
|  |             [WriteOnly] public NativeArray<BoundingSphere> boundingSpheres; | ||
|  | 
 | ||
|  |             public float minDistance; | ||
|  | 
 | ||
|  |             private float DistanceBetweenQuaternions(quaternion a, quaternion b) | ||
|  |             { | ||
|  |                 return math.distancesq(a.value, b.value); | ||
|  |             } | ||
|  | 
 | ||
|  |             public void Execute(int index, TransformAccess transform) | ||
|  |             { | ||
|  |                 // Check if transform changed | ||
|  |                 bool positionChanged = math.distancesq(transform.position, positions[index]) > minDistance; | ||
|  |                 if (positionChanged) | ||
|  |                     positions[index] = transform.position; | ||
|  |                 bool rotationChanged = DistanceBetweenQuaternions(transform.rotation, rotations[index]) > minDistance; | ||
|  |                 if (rotationChanged) | ||
|  |                     rotations[index] = transform.rotation; | ||
|  |                 bool scaleChanged = math.distancesq(transform.localScale, scales[index]) > minDistance; | ||
|  |                 if (scaleChanged) | ||
|  |                     scales[index] = transform.localScale; | ||
|  | 
 | ||
|  |                 // Early out if transform did not changed | ||
|  |                 if (!positionChanged && !rotationChanged && !scaleChanged && !dirty[index]) | ||
|  |                     return; | ||
|  | 
 | ||
|  |                 float4x4 localToWorld; | ||
|  |                 if (scaleModes[index] == DecalScaleMode.InheritFromHierarchy) | ||
|  |                 { | ||
|  |                     localToWorld = transform.localToWorldMatrix; | ||
|  |                     localToWorld = math.mul(localToWorld, new float4x4(k_MinusYtoZRotation, float3.zero)); | ||
|  |                 } | ||
|  |                 else | ||
|  |                 { | ||
|  |                     quaternion rotation = math.mul(transform.rotation, k_MinusYtoZRotation); | ||
|  |                     localToWorld = float4x4.TRS(positions[index], rotation, new float3(1, 1, 1)); | ||
|  |                 } | ||
|  | 
 | ||
|  |                 float4x4 decalRotation = localToWorld; | ||
|  |                 // z/y axis swap for normal to decal space, Unity is column major | ||
|  |                 float4 temp = decalRotation.c1; | ||
|  |                 decalRotation.c1 = decalRotation.c2; | ||
|  |                 decalRotation.c2 = temp; | ||
|  |                 normalToWorlds[index] = decalRotation; | ||
|  | 
 | ||
|  |                 float4x4 sizeOffset = sizeOffsets[index]; | ||
|  |                 float4x4 decalToWorld = math.mul(localToWorld, sizeOffset); | ||
|  |                 decalToWorlds[index] = decalToWorld; | ||
|  |                 boundingSpheres[index] = GetDecalProjectBoundingSphere(decalToWorld); | ||
|  | 
 | ||
|  |                 dirty[index] = false; | ||
|  |             } | ||
|  | 
 | ||
|  |             private BoundingSphere GetDecalProjectBoundingSphere(Matrix4x4 decalToWorld) | ||
|  |             { | ||
|  |                 float4 min = new float4(-0.5f, -0.5f, -0.5f, 1.0f); | ||
|  |                 float4 max = new float4(0.5f, 0.5f, 0.5f, 1.0f); | ||
|  |                 min = math.mul(decalToWorld, min); | ||
|  |                 max = math.mul(decalToWorld, max); | ||
|  | 
 | ||
|  |                 float3 position = ((max + min) / 2f).xyz; | ||
|  |                 float radius = math.length(max - min) / 2f; | ||
|  | 
 | ||
|  |                 BoundingSphere res = new BoundingSphere(); | ||
|  |                 res.position = position; | ||
|  |                 res.radius = radius; | ||
|  |                 return res; | ||
|  |             } | ||
|  |         } | ||
|  |     } | ||
|  | } |