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