Files
Fishing2/Packages/com.waveharmonic.crest/Runtime/Scripts/Data/Input/ClipLodInput.cs
2026-01-01 22:00:33 +08:00

157 lines
5.7 KiB
C#

// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
using UnityEngine;
using UnityEngine.Rendering;
using WaveHarmonic.Crest.Internal;
namespace WaveHarmonic.Crest
{
/// <summary>
/// Registers a custom input to the <see cref="ClipLod"/>.
/// </summary>
/// <remarks>
/// Attach this to objects that you want to use to clip the surface of the water.
/// </remarks>
[@HelpURL("Manual/Clipping.html#clip-inputs")]
public sealed partial class ClipLodInput : LodInput
{
[SerializeField, HideInInspector]
#pragma warning disable 414
int _Version = 0;
#pragma warning restore 414
[@Heading("Primitive")]
[Tooltip("The primitive to render (signed distance) into the simulation.")]
[@Predicated(nameof(_Mode), inverted: true, nameof(LodInputMode.Primitive), hide: true)]
[@GenerateAPI]
[@DecoratedField, SerializeField]
internal LodInputPrimitive _Primitive = LodInputPrimitive.Cube;
// Only Mode.Primitive SDF supports inverted.
[Tooltip("Removes clip surface data instead of adding it.")]
[@Predicated(nameof(_Mode), inverted: true, nameof(LodInputMode.Primitive), hide: true)]
[@GenerateAPI]
[@DecoratedField, SerializeField]
bool _Inverted;
[@Heading("Culling")]
[Tooltip("Prevents inputs from cancelling each other out when aligned vertically.\n\nIt is imperfect so custom logic might be needed for your use case.")]
[@Predicated(nameof(_Mode), inverted: false, nameof(LodInputMode.Paint), hide: true)]
[@Predicated(nameof(_Mode), inverted: true, nameof(LodInputMode.Renderer))]
[@GenerateAPI]
[@DecoratedField, SerializeField]
bool _WaterHeightDistanceCulling = false;
internal override LodInputMode DefaultMode => LodInputMode.Primitive;
// The clip surface samples at the displaced position in the water shader, so the displacement correction is not needed.
private protected override bool FollowHorizontalMotion => true;
readonly SampleCollisionHelper _SampleHeightHelper = new();
ComputeShader PrimitiveShader => WaterResources.Instance.Compute._ClipPrimitive;
static LocalKeyword KeywordInverted => WaterResources.Instance.Keywords.ClipPrimitiveInverted;
static LocalKeyword KeywordSphere => WaterResources.Instance.Keywords.ClipPrimitiveSphere;
static LocalKeyword KeywordCube => WaterResources.Instance.Keywords.ClipPrimitiveCube;
static LocalKeyword KeywordRectangle => WaterResources.Instance.Keywords.ClipPrimitiveRectangle;
bool _Enabled = true;
Rect _Rect;
internal override void InferBlend()
{
base.InferBlend();
_Blend = LodInputBlend.Maximum;
}
internal override bool Enabled => _Enabled && Mode switch
{
LodInputMode.Primitive => enabled && PrimitiveShader != null,
_ => base.Enabled,
};
internal override Rect Rect
{
get
{
if (Mode == LodInputMode.Primitive)
{
if (_RecalculateBounds)
{
// This mode has full transform support so need to get rect from bounds.
_Rect = transform.Bounds().RectXZ();
_RecalculateBounds = false;
}
return _Rect;
}
return base.Rect;
}
}
internal override void Draw(Lod simulation, CommandBuffer buffer, RenderTargetIdentifier target, int pass = -1, float weight = 1f, int slices = -1)
{
if (Mode == LodInputMode.Primitive)
{
var wrapper = new PropertyWrapperCompute(buffer, PrimitiveShader, 0);
wrapper.SetMatrix(Crest.ShaderIDs.s_Matrix, transform.worldToLocalMatrix);
// For culling.
wrapper.SetVector(Crest.ShaderIDs.s_Position, transform.position);
wrapper.SetFloat(Crest.ShaderIDs.s_Diameter, transform.lossyScale.Maximum());
wrapper.SetKeyword(KeywordInverted, _Inverted);
wrapper.SetKeyword(KeywordSphere, _Primitive == LodInputPrimitive.Sphere);
wrapper.SetKeyword(KeywordCube, _Primitive == LodInputPrimitive.Cube);
wrapper.SetKeyword(KeywordRectangle, _Primitive == LodInputPrimitive.Quad);
wrapper.SetTexture(Crest.ShaderIDs.s_Target, target);
var threads = simulation.Resolution / Lod.k_ThreadGroupSize;
wrapper.Dispatch(threads, threads, slices);
}
else
{
base.Draw(simulation, buffer, target, pass, weight, slices);
}
}
private protected override void OnUpdate(WaterRenderer water)
{
base.OnUpdate(water);
if (Mode != LodInputMode.Renderer)
{
_Enabled = true;
return;
}
if (!base.Enabled)
{
return;
}
if (Data is not RendererLodInputData data || data._Renderer == null)
{
return;
}
// Prevents possible conflicts since overlapping doesn't work for every case for convex hull.
if (_WaterHeightDistanceCulling)
{
var position = transform.position;
if (_SampleHeightHelper.SampleHeight(position, out var waterHeight))
{
position.y = waterHeight;
_Enabled = Mathf.Abs(data._Renderer.bounds.ClosestPoint(position).y - waterHeight) < 1;
}
}
}
}
}