276 lines
9.7 KiB
C#
276 lines
9.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>
|
|
/// The mode for <see cref="WatertightHull"/>.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Each mode has its strengths and weaknesses.
|
|
/// </remarks>
|
|
[@GenerateDoc]
|
|
public enum WatertightHullMode
|
|
{
|
|
/// <inheritdoc cref="Generated.WatertightHullMode.Displacement"/>
|
|
[Tooltip("Use displacement to remove water.\n\nUsing displacement will also affect the underwater and can nest bouyant objects. Requires the displacement layer to be enabled.")]
|
|
Displacement,
|
|
|
|
/// <inheritdoc cref="Generated.WatertightHullMode.Clip"/>
|
|
[Tooltip("Clips the surface to remove water.\n\nThis option is more precise and can be submerged.")]
|
|
Clip,
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes water from a provided hull using the clip simulation.
|
|
/// </summary>
|
|
[@ExecuteDuringEditMode]
|
|
[AddComponentMenu(Constants.k_MenuPrefixInputs + "Watertight Hull")]
|
|
[@HelpURL("Manual/Clipping.html#watertight-hull")]
|
|
public sealed partial class WatertightHull : ManagedBehaviour<WaterRenderer>
|
|
{
|
|
[@Label("Convex Hull")]
|
|
[Tooltip("The convex hull to keep water out.")]
|
|
[@GenerateAPI]
|
|
[@DecoratedField, SerializeField]
|
|
internal Mesh _Mesh;
|
|
|
|
[Tooltip("Order this input will render.\n\nQueue is 'Queue + SiblingIndex'")]
|
|
[@GenerateAPI(Setter.Custom)]
|
|
[@DecoratedField, SerializeField]
|
|
int _Queue;
|
|
|
|
[@Space(10)]
|
|
|
|
[Tooltip("Which mode to use.")]
|
|
[@GenerateAPI(Setter.Custom)]
|
|
[@DecoratedField, SerializeField]
|
|
WatertightHullMode _Mode = WatertightHullMode.Displacement;
|
|
|
|
[Tooltip("Inverts the effect to remove clipping (ie add water).")]
|
|
[@Predicated(nameof(_Mode), inverted: true, nameof(WatertightHullMode.Clip), hide: true)]
|
|
[@GenerateAPI]
|
|
[@DecoratedField, SerializeField]
|
|
bool _Inverted;
|
|
|
|
[@Label("Use Clip")]
|
|
[Tooltip("Whether to also to clip the surface when using displacement mode.\n\nDisplacement mode can have a leaky hull by allowing chop top push waves across the hull boundaries slightly. Clipping the surface will remove these interior leaks.")]
|
|
[@Predicated(nameof(_Mode), inverted: true, nameof(WatertightHullMode.Displacement), hide: true)]
|
|
[@GenerateAPI(Setter.Custom)]
|
|
[@DecoratedField, SerializeField]
|
|
bool _UseClipWithDisplacement = true;
|
|
|
|
[@Space(10)]
|
|
|
|
[@DecoratedField, SerializeField]
|
|
internal DebugFields _Debug = new();
|
|
|
|
[System.Serializable]
|
|
internal sealed class DebugFields
|
|
{
|
|
[@DecoratedField, SerializeField]
|
|
public bool _DrawBounds;
|
|
}
|
|
|
|
Material _ClipMaterial;
|
|
Material _AnimatedWavesMaterial;
|
|
|
|
internal bool Enabled => enabled && _Mesh != null;
|
|
|
|
bool _RecalculateBounds = true;
|
|
Rect _Rect;
|
|
|
|
internal Rect Rect
|
|
{
|
|
get
|
|
{
|
|
if (_RecalculateBounds)
|
|
{
|
|
_Rect = transform.TransformBounds(_Mesh.bounds).RectXZ();
|
|
_RecalculateBounds = false;
|
|
}
|
|
|
|
return _Rect;
|
|
}
|
|
}
|
|
|
|
readonly SampleCollisionHelper _SampleCollisionHelper = new();
|
|
Vector3 _Displacement;
|
|
|
|
internal bool UsesClip => _Mode == WatertightHullMode.Clip || _UseClipWithDisplacement;
|
|
internal bool UsesDisplacement => _Mode == WatertightHullMode.Displacement;
|
|
|
|
static class ShaderIDs
|
|
{
|
|
public static int s_Inverted = Shader.PropertyToID("_Crest_Inverted");
|
|
}
|
|
|
|
private protected override void Initialize()
|
|
{
|
|
base.Initialize();
|
|
|
|
if (UsesClip)
|
|
{
|
|
_ClipInput ??= new(this);
|
|
_ClipMaterial = new(WaterResources.Instance.Shaders._ClipConvexHull);
|
|
ILodInput.Attach(_ClipInput, ClipLod.s_Inputs);
|
|
}
|
|
|
|
if (UsesDisplacement)
|
|
{
|
|
_AnimatedWavesInput ??= new(this);
|
|
_AnimatedWavesMaterial = new(Shader.Find("Crest/Inputs/Animated Waves/Push Water Under Convex Hull"));
|
|
_AnimatedWavesMaterial.SetFloat(LodInput.ShaderIDs.s_Weight, 1f);
|
|
ILodInput.Attach(_AnimatedWavesInput, AnimatedWavesLod.s_Inputs);
|
|
}
|
|
}
|
|
|
|
private protected override void OnDisable()
|
|
{
|
|
base.OnDisable();
|
|
Helpers.Destroy(_ClipMaterial);
|
|
ILodInput.Detach(_ClipInput, ClipLod.s_Inputs);
|
|
Helpers.Destroy(_AnimatedWavesMaterial);
|
|
ILodInput.Detach(_AnimatedWavesInput, AnimatedWavesLod.s_Inputs);
|
|
}
|
|
|
|
private protected override System.Action<WaterRenderer> OnUpdateMethod => OnUpdate;
|
|
void OnUpdate(WaterRenderer water)
|
|
{
|
|
if (_Mode == WatertightHullMode.Displacement)
|
|
{
|
|
_SampleCollisionHelper.SampleDisplacement(transform.position, out _Displacement);
|
|
}
|
|
|
|
if (transform.hasChanged)
|
|
{
|
|
_RecalculateBounds = true;
|
|
}
|
|
}
|
|
|
|
private protected override System.Action<WaterRenderer> OnLateUpdateMethod => OnLateUpdate;
|
|
void OnLateUpdate(WaterRenderer water)
|
|
{
|
|
transform.hasChanged = false;
|
|
}
|
|
|
|
void DrawClip(Lod simulation, CommandBuffer buffer, RenderTargetIdentifier target, int pass = -1, float weight = 1, int slice = -1)
|
|
{
|
|
_ClipMaterial.SetBoolean(ShaderIDs.s_Inverted, _Inverted);
|
|
buffer.DrawMesh(_Mesh, transform.localToWorldMatrix, _ClipMaterial);
|
|
}
|
|
|
|
void DrawDisplacement(Lod simulation, CommandBuffer buffer, RenderTargetIdentifier target, int pass = -1, float weight = 1, int slice = -1)
|
|
{
|
|
_AnimatedWavesMaterial.SetVector(LodInput.ShaderIDs.s_DisplacementAtInputPosition, _Displacement);
|
|
buffer.DrawMesh(_Mesh, transform.localToWorldMatrix, _AnimatedWavesMaterial);
|
|
}
|
|
|
|
void SetQueue(int previous, int current)
|
|
{
|
|
if (previous == current) return;
|
|
if (_ClipInput == null || !isActiveAndEnabled) return;
|
|
if (UsesClip) ILodInput.Attach(_ClipInput, ClipLod.s_Inputs);
|
|
if (UsesDisplacement) ILodInput.Attach(_AnimatedWavesInput, AnimatedWavesLod.s_Inputs);
|
|
}
|
|
|
|
void SetMode(WatertightHullMode previous, WatertightHullMode current)
|
|
{
|
|
if (previous == current) return;
|
|
OnDisable(); OnEnable();
|
|
}
|
|
|
|
void SetUseClipWithDisplacement(bool previous, bool current)
|
|
{
|
|
if (previous == current) return;
|
|
OnDisable(); OnEnable();
|
|
}
|
|
|
|
#if UNITY_EDITOR
|
|
[@OnChange]
|
|
void OnChange(string propertyPath, object previousValue)
|
|
{
|
|
switch (propertyPath)
|
|
{
|
|
case nameof(_Queue):
|
|
SetQueue((int)previousValue, _Queue);
|
|
break;
|
|
case nameof(_Mode):
|
|
SetMode((WatertightHullMode)previousValue, _Mode);
|
|
break;
|
|
case nameof(_UseClipWithDisplacement):
|
|
SetUseClipWithDisplacement((bool)previousValue, _UseClipWithDisplacement);
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
partial class WatertightHull
|
|
{
|
|
ClipInput _ClipInput;
|
|
|
|
sealed class ClipInput : ILodInput
|
|
{
|
|
readonly WatertightHull _Input;
|
|
public ClipInput(WatertightHull input) => _Input = input;
|
|
public bool Enabled => _Input.Enabled;
|
|
public bool IsCompute => false;
|
|
public int Queue => _Input.Queue;
|
|
public int Pass => -1;
|
|
public Rect Rect => _Input.Rect;
|
|
public MonoBehaviour Component => _Input;
|
|
public float Filter(WaterRenderer water, int slice) => 1f;
|
|
public void Draw(Lod lod, CommandBuffer buffer, RenderTargetIdentifier target, int pass = -1, float weight = 1, int slice = -1) => _Input.DrawClip(lod, buffer, target, pass, weight, slice);
|
|
}
|
|
}
|
|
|
|
partial class WatertightHull
|
|
{
|
|
DisplacementInput _AnimatedWavesInput;
|
|
|
|
sealed class DisplacementInput : ILodInput
|
|
{
|
|
readonly WatertightHull _Input;
|
|
public DisplacementInput(WatertightHull input) => _Input = input;
|
|
public bool Enabled => _Input.Enabled;
|
|
public bool IsCompute => false;
|
|
public int Queue => _Input.Queue;
|
|
public int Pass => (int)DisplacementPass.LodIndependentLast;
|
|
public Rect Rect => _Input.Rect;
|
|
public MonoBehaviour Component => _Input;
|
|
public float Filter(WaterRenderer water, int slice) => 1f;
|
|
public void Draw(Lod lod, CommandBuffer buffer, RenderTargetIdentifier target, int pass = -1, float weight = 1, int slice = -1) => _Input.DrawDisplacement(lod, buffer, target, pass, weight, slice);
|
|
}
|
|
}
|
|
|
|
partial class WatertightHull : ISerializationCallbackReceiver
|
|
{
|
|
[SerializeField, HideInInspector]
|
|
#pragma warning disable 414
|
|
int _Version = 1;
|
|
#pragma warning restore 414
|
|
|
|
/// <inheritdoc/>
|
|
void ISerializationCallbackReceiver.OnAfterDeserialize()
|
|
{
|
|
if (_Version < 1)
|
|
{
|
|
// Keep clip for existing.
|
|
_Mode = WatertightHullMode.Clip;
|
|
_Version = 1;
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
void ISerializationCallbackReceiver.OnBeforeSerialize()
|
|
{
|
|
|
|
}
|
|
}
|
|
}
|