using Unity.Collections;
using UnityEngine.Experimental.Rendering;
namespace UnityEngine.Rendering.Universal
{
///
/// Abstract class that render decals using .
/// Supports rendering with and graphics draw calls.
///
internal abstract class DecalDrawSystem
{
readonly static internal uint MaxBatchSize = 250;
protected DecalEntityManager m_EntityManager;
private Matrix4x4[] m_WorldToDecals;
private Matrix4x4[] m_NormalToDecals;
private float[] m_DecalLayerMasks;
private ProfilingSampler m_Sampler;
public Material overrideMaterial { get; set; }
public DecalDrawSystem(string sampler, DecalEntityManager entityManager)
{
m_EntityManager = entityManager;
m_WorldToDecals = new Matrix4x4[MaxBatchSize];
m_NormalToDecals = new Matrix4x4[MaxBatchSize];
m_DecalLayerMasks = new float[MaxBatchSize];
m_Sampler = new ProfilingSampler(sampler);
}
public void Execute(CommandBuffer cmd)
{
Execute(CommandBufferHelpers.GetRasterCommandBuffer(cmd));
}
internal void Execute(RasterCommandBuffer cmd)
{
using (new ProfilingScope(cmd, m_Sampler))
{
for (int i = 0; i < m_EntityManager.chunkCount; ++i)
{
Execute(
cmd,
m_EntityManager.entityChunks[i],
m_EntityManager.cachedChunks[i],
m_EntityManager.drawCallChunks[i],
m_EntityManager.entityChunks[i].count);
}
}
}
protected virtual Material GetMaterial(DecalEntityChunk decalEntityChunk) => decalEntityChunk.material;
protected abstract int GetPassIndex(DecalCachedChunk decalCachedChunk);
private void Execute(RasterCommandBuffer cmd, DecalEntityChunk decalEntityChunk, DecalCachedChunk decalCachedChunk, DecalDrawCallChunk decalDrawCallChunk, int count)
{
decalCachedChunk.currentJobHandle.Complete();
decalDrawCallChunk.currentJobHandle.Complete();
Material material = GetMaterial(decalEntityChunk);
int passIndex = GetPassIndex(decalCachedChunk);
if (count == 0 || passIndex == -1 || material == null)
return;
if (SystemInfo.supportsInstancing && material.enableInstancing)
{
DrawInstanced(cmd, decalEntityChunk, decalCachedChunk, decalDrawCallChunk, passIndex);
}
else
{
Draw(cmd, decalEntityChunk, decalCachedChunk, decalDrawCallChunk, passIndex);
}
}
private void Draw(RasterCommandBuffer cmd, DecalEntityChunk decalEntityChunk, DecalCachedChunk decalCachedChunk, DecalDrawCallChunk decalDrawCallChunk, int passIndex)
{
var mesh = m_EntityManager.decalProjectorMesh;
var material = GetMaterial(decalEntityChunk);
decalCachedChunk.propertyBlock.SetVector("unity_LightData", new Vector4(1, 1, 1, 0)); // GetMainLight requires z component to be set
int subCallCount = decalDrawCallChunk.subCallCount;
for (int i = 0; i < subCallCount; ++i)
{
var subCall = decalDrawCallChunk.subCalls[i];
for (int j = subCall.start; j < subCall.end; ++j)
{
decalCachedChunk.propertyBlock.SetMatrix("_NormalToWorld", decalDrawCallChunk.normalToDecals[j]);
decalCachedChunk.propertyBlock.SetFloat("_DecalLayerMaskFromDecal", decalDrawCallChunk.renderingLayerMasks[j]);
cmd.DrawMesh(mesh, decalDrawCallChunk.decalToWorlds[j], material, 0, passIndex, decalCachedChunk.propertyBlock);
}
}
}
private void DrawInstanced(RasterCommandBuffer cmd, DecalEntityChunk decalEntityChunk, DecalCachedChunk decalCachedChunk, DecalDrawCallChunk decalDrawCallChunk, int passIndex)
{
var mesh = m_EntityManager.decalProjectorMesh;
var material = GetMaterial(decalEntityChunk);
decalCachedChunk.propertyBlock.SetVector("unity_LightData", new Vector4(1, 1, 1, 0)); // GetMainLight requires z component to be set
int subCallCount = decalDrawCallChunk.subCallCount;
for (int i = 0; i < subCallCount; ++i)
{
var subCall = decalDrawCallChunk.subCalls[i];
var decalToWorldSlice = decalDrawCallChunk.decalToWorlds.Reinterpret();
NativeArray.Copy(decalToWorldSlice, subCall.start, m_WorldToDecals, 0, subCall.count);
var normalToWorldSlice = decalDrawCallChunk.normalToDecals.Reinterpret();
NativeArray.Copy(normalToWorldSlice, subCall.start, m_NormalToDecals, 0, subCall.count);
var decalLayerMaskSlice = decalDrawCallChunk.renderingLayerMasks.Reinterpret();
NativeArray.Copy(decalLayerMaskSlice, subCall.start, m_DecalLayerMasks, 0, subCall.count);
decalCachedChunk.propertyBlock.SetMatrixArray("_NormalToWorld", m_NormalToDecals);
decalCachedChunk.propertyBlock.SetFloatArray("_DecalLayerMaskFromDecal", m_DecalLayerMasks);
cmd.DrawMeshInstanced(mesh, 0, material, passIndex, m_WorldToDecals, subCall.end - subCall.start, decalCachedChunk.propertyBlock);
}
}
public void Execute(in CameraData cameraData)
{
using (new ProfilingScope(m_Sampler))
{
for (int i = 0; i < m_EntityManager.chunkCount; ++i)
{
Execute(
cameraData,
m_EntityManager.entityChunks[i],
m_EntityManager.cachedChunks[i],
m_EntityManager.drawCallChunks[i],
m_EntityManager.entityChunks[i].count);
}
}
}
private void Execute(in CameraData cameraData, DecalEntityChunk decalEntityChunk, DecalCachedChunk decalCachedChunk, DecalDrawCallChunk decalDrawCallChunk, int count)
{
decalCachedChunk.currentJobHandle.Complete();
decalDrawCallChunk.currentJobHandle.Complete();
Material material = GetMaterial(decalEntityChunk);
int passIndex = GetPassIndex(decalCachedChunk);
if (count == 0 || passIndex == -1 || material == null)
return;
if (SystemInfo.supportsInstancing && material.enableInstancing)
{
DrawInstanced(cameraData, decalEntityChunk, decalCachedChunk, decalDrawCallChunk);
}
else
{
Draw(cameraData, decalEntityChunk, decalCachedChunk, decalDrawCallChunk);
}
}
private void Draw(in CameraData cameraData, DecalEntityChunk decalEntityChunk, DecalCachedChunk decalCachedChunk, DecalDrawCallChunk decalDrawCallChunk)
{
var mesh = m_EntityManager.decalProjectorMesh;
var material = GetMaterial(decalEntityChunk);
int subCallCount = decalDrawCallChunk.subCallCount;
for (int i = 0; i < subCallCount; ++i)
{
var subCall = decalDrawCallChunk.subCalls[i];
for (int j = subCall.start; j < subCall.end; ++j)
{
decalCachedChunk.propertyBlock.SetMatrix("_NormalToWorld", decalDrawCallChunk.normalToDecals[j]);
decalCachedChunk.propertyBlock.SetFloat("_DecalLayerMaskFromDecal", decalDrawCallChunk.renderingLayerMasks[j]);
// RENDERGRAPH TODO: schedule drawmesh through commandBuffer?
Graphics.DrawMesh(mesh, decalDrawCallChunk.decalToWorlds[j], material, decalCachedChunk.layerMasks[j], cameraData.camera, 0, decalCachedChunk.propertyBlock);
}
}
}
private void DrawInstanced(in CameraData cameraData, DecalEntityChunk decalEntityChunk, DecalCachedChunk decalCachedChunk, DecalDrawCallChunk decalDrawCallChunk)
{
var mesh = m_EntityManager.decalProjectorMesh;
var material = GetMaterial(decalEntityChunk);
decalCachedChunk.propertyBlock.SetVector("unity_LightData", new Vector4(1, 1, 1, 0)); // GetMainLight requires z component to be set
int subCallCount = decalDrawCallChunk.subCallCount;
for (int i = 0; i < subCallCount; ++i)
{
var subCall = decalDrawCallChunk.subCalls[i];
var decalToWorldSlice = decalDrawCallChunk.decalToWorlds.Reinterpret();
NativeArray.Copy(decalToWorldSlice, subCall.start, m_WorldToDecals, 0, subCall.count);
var normalToWorldSlice = decalDrawCallChunk.normalToDecals.Reinterpret();
NativeArray.Copy(normalToWorldSlice, subCall.start, m_NormalToDecals, 0, subCall.count);
var decalLayerMaskSlice = decalDrawCallChunk.renderingLayerMasks.Reinterpret();
NativeArray.Copy(decalLayerMaskSlice, subCall.start, m_DecalLayerMasks, 0, subCall.count);
decalCachedChunk.propertyBlock.SetMatrixArray("_NormalToWorld", m_NormalToDecals);
decalCachedChunk.propertyBlock.SetFloatArray("_DecalLayerMaskFromDecal", m_DecalLayerMasks);
// RENDERGRAPH TODO: schedule drawmesh through commandBuffer?
Graphics.DrawMeshInstanced(mesh, 0, material,
m_WorldToDecals, subCall.count, decalCachedChunk.propertyBlock, ShadowCastingMode.On, true, 0, cameraData.camera);
}
}
}
}