using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
namespace UnityEngine.Rendering.Universal
{
internal struct DecalSubDrawCall
{
public int start;
public int end;
public int count { get => end - start; }
}
///
/// Contains information about draw calls.
///
internal class DecalDrawCallChunk : DecalChunk
{
public NativeArray decalToWorlds;
public NativeArray normalToDecals;
public NativeArray renderingLayerMasks;
public NativeArray subCalls;
public NativeArray subCallCounts;
public int subCallCount { set { subCallCounts[0] = value; } get => subCallCounts[0]; }
public override void RemoveAtSwapBack(int entityIndex)
{
RemoveAtSwapBack(ref decalToWorlds, entityIndex, count);
RemoveAtSwapBack(ref normalToDecals, entityIndex, count);
RemoveAtSwapBack(ref renderingLayerMasks, entityIndex, count);
RemoveAtSwapBack(ref subCalls, entityIndex, count);
count--;
}
public override void SetCapacity(int newCapacity)
{
decalToWorlds.ResizeArray(newCapacity);
normalToDecals.ResizeArray(newCapacity);
renderingLayerMasks.ResizeArray(newCapacity);
subCalls.ResizeArray(newCapacity);
capacity = newCapacity;
}
public override void Dispose()
{
subCallCounts.Dispose();
if (capacity == 0)
return;
decalToWorlds.Dispose();
normalToDecals.Dispose();
renderingLayerMasks.Dispose();
subCalls.Dispose();
count = 0;
capacity = 0;
}
}
///
/// Outputs draw calls into .
///
internal class DecalCreateDrawCallSystem
{
private DecalEntityManager m_EntityManager;
private ProfilingSampler m_Sampler;
private float m_MaxDrawDistance;
///
/// Provides acces to the maximum draw distance.
///
public float maxDrawDistance
{
get { return m_MaxDrawDistance; }
set { m_MaxDrawDistance = value; }
}
public DecalCreateDrawCallSystem(DecalEntityManager entityManager, float maxDrawDistance)
{
m_EntityManager = entityManager;
m_Sampler = new ProfilingSampler("DecalCreateDrawCallSystem.Execute");
m_MaxDrawDistance = maxDrawDistance;
}
public void Execute()
{
using (new ProfilingScope(m_Sampler))
{
for (int i = 0; i < m_EntityManager.chunkCount; ++i)
Execute(m_EntityManager.cachedChunks[i], m_EntityManager.culledChunks[i], m_EntityManager.drawCallChunks[i], m_EntityManager.cachedChunks[i].count);
}
}
private void Execute(DecalCachedChunk cachedChunk, DecalCulledChunk culledChunk, DecalDrawCallChunk drawCallChunk, int count)
{
if (count == 0)
return;
DrawCallJob drawCallJob = new DrawCallJob()
{
decalToWorlds = cachedChunk.decalToWorlds,
normalToWorlds = cachedChunk.normalToWorlds,
sizeOffsets = cachedChunk.sizeOffsets,
drawDistances = cachedChunk.drawDistances,
angleFades = cachedChunk.angleFades,
uvScaleBiases = cachedChunk.uvScaleBias,
layerMasks = cachedChunk.layerMasks,
sceneLayerMasks = cachedChunk.sceneLayerMasks,
fadeFactors = cachedChunk.fadeFactors,
boundingSpheres = cachedChunk.boundingSpheres,
renderingLayerMasks = cachedChunk.renderingLayerMasks,
cameraPosition = culledChunk.cameraPosition,
sceneCullingMask = culledChunk.sceneCullingMask,
cullingMask = culledChunk.cullingMask,
visibleDecalIndices = culledChunk.visibleDecalIndices,
visibleDecalCount = culledChunk.visibleDecalCount,
maxDrawDistance = m_MaxDrawDistance,
decalToWorldsDraw = drawCallChunk.decalToWorlds,
normalToDecalsDraw = drawCallChunk.normalToDecals,
renderingLayerMasksDraw = drawCallChunk.renderingLayerMasks,
subCalls = drawCallChunk.subCalls,
subCallCount = drawCallChunk.subCallCounts,
};
var handle = drawCallJob.Schedule(cachedChunk.currentJobHandle);
drawCallChunk.currentJobHandle = handle;
cachedChunk.currentJobHandle = handle;
}
#if ENABLE_BURST_1_0_0_OR_NEWER
[Unity.Burst.BurstCompile]
#endif
struct DrawCallJob : IJob
{
[ReadOnly] public NativeArray decalToWorlds;
[ReadOnly] public NativeArray normalToWorlds;
[ReadOnly] public NativeArray sizeOffsets;
[ReadOnly] public NativeArray drawDistances;
[ReadOnly] public NativeArray angleFades;
[ReadOnly] public NativeArray uvScaleBiases;
[ReadOnly] public NativeArray layerMasks;
[ReadOnly] public NativeArray sceneLayerMasks;
[ReadOnly] public NativeArray fadeFactors;
[ReadOnly] public NativeArray boundingSpheres;
[ReadOnly] public NativeArray renderingLayerMasks;
public Vector3 cameraPosition;
public ulong sceneCullingMask;
public int cullingMask;
[ReadOnly] public NativeArray visibleDecalIndices;
public int visibleDecalCount;
public float maxDrawDistance;
[WriteOnly] public NativeArray decalToWorldsDraw;
[WriteOnly] public NativeArray normalToDecalsDraw;
[WriteOnly] public NativeArray renderingLayerMasksDraw;
[WriteOnly] public NativeArray subCalls;
[WriteOnly] public NativeArray subCallCount;
public void Execute()
{
int subCallIndex = 0;
int instanceIndex = 0;
int instanceStart = 0;
for (int i = 0; i < visibleDecalCount; ++i)
{
int decalIndex = visibleDecalIndices[i];
#if UNITY_EDITOR
ulong decalSceneCullingMask = sceneLayerMasks[decalIndex];
if ((sceneCullingMask & decalSceneCullingMask) == 0)
continue;
#endif
int decalMask = 1 << layerMasks[decalIndex];
if ((cullingMask & decalMask) == 0)
continue;
BoundingSphere boundingSphere = boundingSpheres[decalIndex];
float2 drawDistance = drawDistances[decalIndex];
float distanceToDecal = (cameraPosition - boundingSphere.position).magnitude;
float cullDistance = math.min(drawDistance.x, maxDrawDistance) + boundingSphere.radius;
if (distanceToDecal > cullDistance)
continue;
decalToWorldsDraw[instanceIndex] = decalToWorlds[decalIndex];
float fadeFactorScaler = fadeFactors[decalIndex];
float2 angleFade = angleFades[decalIndex];
float4 uvScaleBias = uvScaleBiases[decalIndex];
float4x4 normalToDecals = normalToWorlds[decalIndex];
// NormalToWorldBatchis a Matrix4x4x but is a Rotation matrix so bottom row and last column can be used for other data to save space
float fadeFactor = fadeFactorScaler * math.clamp((cullDistance - distanceToDecal) / (cullDistance * (1.0f - drawDistance.y)), 0.0f, 1.0f);
normalToDecals.c0.w = uvScaleBias.x;
normalToDecals.c1.w = uvScaleBias.y;
normalToDecals.c2.w = uvScaleBias.z;
normalToDecals.c3 = new float4(fadeFactor * 1.0f, angleFade.x, angleFade.y, uvScaleBias.w);
normalToDecalsDraw[instanceIndex] = normalToDecals;
renderingLayerMasksDraw[instanceIndex] = (float)renderingLayerMasks[decalIndex];
instanceIndex++;
int instanceCount = instanceIndex - instanceStart;
bool isReachedMaximumBatchSize = instanceCount >= DecalDrawSystem.MaxBatchSize;
if (isReachedMaximumBatchSize)
{
subCalls[subCallIndex++] = new DecalSubDrawCall()
{
start = instanceStart,
end = instanceIndex,
};
instanceStart = instanceIndex;
}
}
int remainingInstanceCount = instanceIndex - instanceStart;
if (remainingInstanceCount != 0)
{
subCalls[subCallIndex++] = new DecalSubDrawCall()
{
start = instanceStart,
end = instanceIndex,
};
}
subCallCount[0] = subCallIndex;
}
}
}
}