// Crest Water System // Copyright © 2024 Wave Harmonic. All rights reserved. using UnityEngine; using UnityEngine.Experimental.Rendering; using UnityEngine.Rendering; using WaveHarmonic.Crest.Utility; namespace WaveHarmonic.Crest { /// /// Simulates horizontal motion of water. /// [FilterEnum(nameof(_QuerySource), Filtered.Mode.Exclude, (int)LodQuerySource.CPU)] [FilterEnum(nameof(_TextureFormatMode), Filtered.Mode.Exclude, (int)LodTextureFormatMode.Automatic)] public sealed partial class FlowLod : Lod { const string k_FlowKeyword = "CREST_FLOW_ON_INTERNAL"; static new class ShaderIDs { public static readonly int s_Flow = Shader.PropertyToID("g_Crest_Flow"); } internal static readonly Color s_GizmoColor = new(0f, 0f, 1f, 0.5f); internal override string ID => "Flow"; internal override Color GizmoColor => s_GizmoColor; private protected override Color ClearColor => Color.black; private protected override bool NeedToReadWriteTextureData => true; private protected override GraphicsFormat RequestedTextureFormat => _TextureFormatMode switch { LodTextureFormatMode.Performance => GraphicsFormat.R16G16_SFloat, LodTextureFormatMode.Precision => GraphicsFormat.R32G32_SFloat, LodTextureFormatMode.Manual => _TextureFormat, _ => throw new System.NotImplementedException(), }; internal FlowLod() { _Resolution = 128; _TextureFormat = GraphicsFormat.R16G16_SFloat; _MaximumQueryCount = 1024; } internal override void Enable() { base.Enable(); Shader.EnableKeyword(k_FlowKeyword); } internal override void Disable() { base.Disable(); Shader.DisableKeyword(k_FlowKeyword); } internal override void BuildCommandBuffer(WaterRenderer water, CommandBuffer buffer) { var time = water.CurrentTime; var period = 1f; var half = period * 0.5f; var offset0 = Helpers.Fmod(time, period); var weight0 = offset0 / half; if (weight0 > 1f) weight0 = 2f - weight0; var offset1 = Helpers.Fmod(time + half, period); var weight1 = 1f - weight0; Shader.SetGlobalVector(ShaderIDs.s_Flow, new(offset0, weight0, offset1, weight1)); base.BuildCommandBuffer(water, buffer); } private protected override IFlowProvider CreateProvider(bool onEnable) { Queryable?.CleanUp(); // Flow is GPU only, and can only be queried using the compute path. return onEnable && Enabled && QuerySource == LodQuerySource.GPU ? IFlowProvider.Create(_Water) : IFlowProvider.None; } internal override void SetGlobals(bool onEnable) { base.SetGlobals(onEnable); // Zero offset. Use the first sample. Shader.SetGlobalVector(ShaderIDs.s_Flow, new(0, 1, 0, 0)); } internal static readonly SortedList s_Inputs = new(Helpers.DuplicateComparison); private protected override SortedList Inputs => s_Inputs; [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] static void OnLoad() { s_Inputs.Clear(); } } }