using System.Collections.Generic; using System.Linq; using Moonlit.Utils; using UnityEngine; using UnityEngine.Rendering; namespace Moonlit.FootstepPro { [AddComponentMenu("FootprintSystem/System")] [ExecuteInEditMode] public class FootprintSystem : Singleton { public enum FadeType { Time = 0, Count = 1, TimeOrCount = 2 } [SerializeField] private Mesh _CubeMesh; [SerializeField] private Material _MaskMaterial; public FadeType Fade; [Header("Time fade settings")] public float FadeLength; [Header("Count fade settings")] public int FadeCount = 100; [Header("Settings")] public bool HideInHierarchy = true; public bool FrustumCulling = true; [Header("Debug")] public bool DrawEditorGizmos = true; private List _Footprints = new List(); private Dictionary _Cameras = new Dictionary(); private List _CameraSettings = new List(); private List _Masks = new List(); private void Update() { for (int i = 0; i < _Footprints.Count; i++) { Footprint footprint = _Footprints[i]; footprint.Advance(); } } private void OnWillRenderObject() { Camera current = Camera.current; CameraSettings component = current.GetComponent(); if (component != null && component.Disable) { ReleaseCamera(current); return; } Plane[] frustumPlanes = GeometryUtility.CalculateFrustumPlanes(current); CreateCameraCommandBuffer(current); Render(_Cameras[current], frustumPlanes); } private void OnDisable() { foreach (KeyValuePair camera in _Cameras) { ReleaseCamera(camera, false); } _Cameras.Clear(); } private void OnValidate() { foreach (Footprint footprint in _Footprints) { HierarchyHide(footprint); } } public void Register(Footprint footprint) { if (!RemoveByCount() && !_Footprints.Contains(footprint)) { HierarchyHide(footprint); AddToRenderQueue(footprint); } } public void Unregister(Footprint footprint) { RemoveFromRenderQueue(footprint); } public void RegisterMask(DynamicMask mask) { if (!_Masks.Contains(mask)) { _Masks.Add(mask); } } public void UnregisterMask(DynamicMask mask) { _Masks.Remove(mask); } private bool RemoveByCount() { if ((Fade == FadeType.Count || Fade == FadeType.TimeOrCount) && _Footprints.Count > FadeCount && _Footprints.Count > 0) { Object.Destroy(_Footprints[0]); return true; } return false; } private void Render(CommandBuffer commandBuffer, Plane[] frustumPlanes) { int num = Shader.PropertyToID("_NormalsCopy"); int num2 = Shader.PropertyToID("_MaskTexture"); commandBuffer.GetTemporaryRT(num, -1, -1); commandBuffer.GetTemporaryRT(num2, 800, 600); RenderMaskTexture(commandBuffer, num2); commandBuffer.Blit(BuiltinRenderTextureType.GBuffer2, num); RenderTargetIdentifier[] colors = new RenderTargetIdentifier[2] { BuiltinRenderTextureType.GBuffer0, BuiltinRenderTextureType.GBuffer2 }; commandBuffer.SetRenderTarget(colors, BuiltinRenderTextureType.CameraTarget); for (int i = 0; i < _Footprints.Count; i++) { Footprint footprint = _Footprints[i]; if (!FrustumCulling || IsDecalVisible(frustumPlanes, _CubeMesh, footprint.transform.position)) { commandBuffer.DrawMesh(_CubeMesh, footprint.transform.localToWorldMatrix, footprint.Material); } } commandBuffer.ReleaseTemporaryRT(num); } private void CreateCameraCommandBuffer(Camera camera) { if (_Cameras.ContainsKey(camera)) { _Cameras[camera].Clear(); return; } _Cameras[camera] = new CommandBuffer(); _Cameras[camera].name = "Screen Space Decals"; camera.AddCommandBuffer(CameraEvent.BeforeLighting, _Cameras[camera]); } private void AddToRenderQueue(Footprint footprint) { if (!_Footprints.Contains(footprint)) { _Footprints.Add(footprint); } } private void RemoveFromRenderQueue(Footprint footprint) { if (_Footprints.Contains(footprint)) { _Footprints.Remove(footprint); } } private void RenderMaskTexture(CommandBuffer commandBuffer, int maskID) { commandBuffer.SetRenderTarget(maskID); commandBuffer.ClearRenderTarget(false, true, Color.black); for (int i = 0; i < _Masks.Count; i++) { Renderer component = _Masks[i].GetComponent(); if (component != null) { commandBuffer.DrawRenderer(component, _MaskMaterial); } } } private void ReleaseCamera(KeyValuePair entry, bool removeFromList = true) { if (entry.Key != null && entry.Value != null) { entry.Key.RemoveCommandBuffer(CameraEvent.BeforeLighting, entry.Value); entry.Value.Release(); if (removeFromList) { _Cameras.Remove(entry.Key); } } } private void ReleaseCamera(Camera camera) { if (_Cameras.ContainsKey(camera)) { ReleaseCamera(_Cameras.First((KeyValuePair x) => x.Key == camera)); } } private void HierarchyHide(Footprint footprint) { footprint.gameObject.hideFlags = (HideInHierarchy ? HideFlags.HideInHierarchy : HideFlags.None); } private bool IsDecalVisible(Plane[] frustumPlanes, Mesh mesh, Vector3 position) { Bounds bounds = new Bounds(position, mesh.bounds.size); return GeometryUtility.TestPlanesAABB(frustumPlanes, bounds); } } }