/******************************************************************************/ /* Project - MudBun Publisher - Long Bunny Labs http://LongBunnyLabs.com Author - Ming-Lun "Allen" Chou http://AllenChou.net */ /******************************************************************************/ using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.Rendering; namespace MudBun { [ExecuteInEditMode] public class MudLockedMeshRenderer : MonoBehaviour, ISerializationCallbackReceiver { private class ComputeBufferCache { public int RefCount; public int [] IndirectDrawArgsData; public GenPoint [] GenPointsData; public ComputeBuffer IndirectDrawArgsBuffer; public ComputeBuffer GenPointsBuffer; public void Release() { IndirectDrawArgsData = null; GenPointsData = null; IndirectDrawArgsBuffer.Release(); GenPointsBuffer.Release(); IndirectDrawArgsBuffer = null; GenPointsBuffer = null; } } private static Dictionary s_bufferCachePool = new Dictionary(); [SerializeField] [HideInInspector] private int m_numVerts = 0; [SerializeField] [HideInInspector] private int [] m_indirectDrawArgsData; [SerializeField] [HideInInspector] private GenPoint [] m_aGenPointData; [SerializeField] [HideInInspector] private Aabb m_renderBoundsRs; public Material RenderMaterial; private Material m_materialCloned; private Material m_materialUsed; public Color MasterColor; public Color MasterEmission; public float MasterMetallic; public float MasterSmoothness; [SerializeField] [HideInInspector] private MudRendererBase.RenderModeEnum m_renderMode = MudRendererBase.RenderModeEnum.SmoothMesh; [SerializeField] [HideInInspector] private float m_voxelDensity = 1.0f; [ConditionalField("m_renderMode", MudRendererBase.RenderModeEnum.CircleSplats, MudRendererBase.RenderModeEnum.QuadSplats, Min = 0.0f, Max = 5.0f)] public float SplatSize = 1.0f; [ConditionalField("m_renderMode", MudRendererBase.RenderModeEnum.CircleSplats, MudRendererBase.RenderModeEnum.QuadSplats, Min = 0.0f, Max = 360.0f)] public float SplatRotation = 0.0f; [ConditionalField("m_renderMode", MudRendererBase.RenderModeEnum.CircleSplats, MudRendererBase.RenderModeEnum.QuadSplats, Min = 0.1f, Max = 10.0f)] public float SplatRotationNoisiness = 1.0f; [ConditionalField("m_renderMode", MudRendererBase.RenderModeEnum.CircleSplats, MudRendererBase.RenderModeEnum.QuadSplats, Min = 0.0f, Max = 1.0f)] public float SplatCameraFacing = 1.0f; public ShadowCastingMode CastShadows = ShadowCastingMode.On; public bool ReceiveShadows = true; private ComputeBuffer m_indirectDrawArgsBuffer; private ComputeBuffer m_genPointsBuffer; private MaterialPropertyBlock m_renderMaterialProps; [SerializeField] [HideInInspector] private int m_hash; public void Config ( ComputeBuffer drawArgsBuffer, ComputeBuffer getPointsBuffer, MudRendererBase renderer ) { m_indirectDrawArgsData = new int[5]; drawArgsBuffer.GetData(m_indirectDrawArgsData); m_numVerts = m_indirectDrawArgsData[0]; m_aGenPointData = new GenPoint[m_numVerts]; getPointsBuffer.GetData(m_aGenPointData); RenderMaterial = renderer.RenderMaterial; MasterColor = renderer.MasterColor; MasterEmission = renderer.MasterEmission; MasterMetallic = renderer.MasterMetallic; MasterSmoothness = renderer.MasterSmoothness; m_renderBoundsRs = renderer.RenderBoundsRs; m_renderMode = renderer.RenderMode; m_voxelDensity = renderer.VoxelDensity; SplatSize = renderer.SplatSize; SplatRotation = renderer.SplatRotationJitter; SplatCameraFacing = renderer.SplatCameraFacing; CastShadows = renderer.CastShadows; ReceiveShadows = renderer.ReceiveShadows; m_hash = Codec.Hash(GetHashCode()); m_hash = Codec.HashConcat(m_hash, DateTime.Now.Ticks); Validate(); } public void OnBeforeSerialize() { ComputeBufferCache bufferCache; if (s_bufferCachePool.TryGetValue(m_hash, out bufferCache)) { m_indirectDrawArgsData = bufferCache.IndirectDrawArgsData; m_aGenPointData = bufferCache.GenPointsData; } } public void OnAfterDeserialize() { } private void OnEnable() { } private void OnDisable() { m_indirectDrawArgsBuffer = null; m_genPointsBuffer = null; ComputeBufferCache bufferCache = null; if (s_bufferCachePool.TryGetValue(m_hash, out bufferCache)) { --bufferCache.RefCount; if (bufferCache.RefCount == 0) { bufferCache.Release(); s_bufferCachePool.Remove(m_hash); } } } private void Validate() { if (m_indirectDrawArgsBuffer == null || m_genPointsBuffer == null) { ComputeBufferCache bufferCache = null; if (s_bufferCachePool.TryGetValue(m_hash, out bufferCache)) { m_indirectDrawArgsBuffer = bufferCache.IndirectDrawArgsBuffer; m_genPointsBuffer = bufferCache.GenPointsBuffer; m_indirectDrawArgsData = null; m_aGenPointData = null; ++bufferCache.RefCount; } else if (m_indirectDrawArgsData != null && m_aGenPointData != null) { m_indirectDrawArgsBuffer = new ComputeBuffer(5, sizeof(int), ComputeBufferType.IndirectArguments); m_indirectDrawArgsBuffer.SetData(m_indirectDrawArgsData); m_genPointsBuffer = new ComputeBuffer(m_numVerts, GenPoint.Stride); m_genPointsBuffer.SetData(m_aGenPointData); s_bufferCachePool.Add ( m_hash, new ComputeBufferCache() { RefCount = 1, IndirectDrawArgsData = m_indirectDrawArgsData, GenPointsData = m_aGenPointData, IndirectDrawArgsBuffer = m_indirectDrawArgsBuffer, GenPointsBuffer = m_genPointsBuffer } ); m_indirectDrawArgsData = null; m_aGenPointData = null; } else { Assert.Warn("Null constant buffers and no data to initialize from."); } } if (m_renderMaterialProps == null) { m_renderMaterialProps = new MaterialPropertyBlock(); } } private void LateUpdate() { Validate(); Render(); } private void BindRenderResources() { MudRendererBase.ValidateShaderConstantId(); m_renderMaterialProps.SetBuffer(MudRendererBase.Const.GenPoints, m_genPointsBuffer); m_renderMaterialProps.SetColor(MudRendererBase.Const.MasterColor, MasterColor); m_renderMaterialProps.SetColor(MudRendererBase.Const.MasterEmission, MasterEmission); m_renderMaterialProps.SetFloat(MudRendererBase.Const.MasterMetallic, MasterMetallic); m_renderMaterialProps.SetFloat(MudRendererBase.Const.MasterSmoothness, MasterSmoothness); m_renderMaterialProps.SetFloat(MudRendererBase.Const.SplatSize, SplatSize * (1.5f / m_voxelDensity)); m_renderMaterialProps.SetFloat(MudRendererBase.Const.SplatRotationJitter, SplatRotation * MathUtil.Deg2Rad); m_renderMaterialProps.SetFloat(MudRendererBase.Const.SplatJitterNoisiness, SplatRotationNoisiness); m_renderMaterialProps.SetFloat(MudRendererBase.Const.SplatCameraFacing, SplatCameraFacing); m_renderMaterialProps.SetMatrix(MudRendererBase.Const.LocalToWorld, transform.localToWorldMatrix); m_renderMaterialProps.SetMatrix(MudRendererBase.Const.LocalToWorldIt, transform.localToWorldMatrix.inverse.transpose); m_renderMaterialProps.SetVector(MudRendererBase.Const.LocalToWorldScale, transform.localScale); } private void Render() { if (m_indirectDrawArgsBuffer == null || m_genPointsBuffer == null || RenderMaterial == null || m_renderBoundsRs.IsEmpty || RenderMaterial == null) return; BindRenderResources(); var renderBounds = m_renderBoundsRs; renderBounds.Transform(transform); if (m_materialCloned != RenderMaterial) { m_materialUsed = new Material(RenderMaterial); m_materialCloned = RenderMaterial; } m_materialUsed.CopyPropertiesFromMaterial(m_materialCloned); if (ReceiveShadows) RenderMaterial.EnableKeyword("MUDBUN_RECEIVE_SHADOWS"); else RenderMaterial.DisableKeyword("MUDBUN_RECEIVE_SHADOWS"); Graphics.DrawProceduralIndirect ( RenderMaterial, new Bounds(renderBounds.Center, renderBounds.Size), MeshTopology.Triangles, m_indirectDrawArgsBuffer, 0, null, m_renderMaterialProps, CastShadows, ReceiveShadows, gameObject.layer ); } } }