642 lines
15 KiB
C#
642 lines
15 KiB
C#
using System;
|
|
using UltimateWater.Internal;
|
|
using UnityEngine;
|
|
using UnityEngine.Events;
|
|
|
|
namespace UltimateWater
|
|
{
|
|
public sealed class WindWaves : WaterModule
|
|
{
|
|
[Serializable]
|
|
public class WindWavesEvent : UnityEvent<WindWaves>
|
|
{
|
|
}
|
|
|
|
[Serializable]
|
|
public sealed class Data
|
|
{
|
|
public Transform WindDirectionPointer;
|
|
|
|
[SerializeField]
|
|
[Tooltip("Higher values increase quality, but also decrease performance. Directly controls quality of waves, foam and spray.")]
|
|
public int Resolution = 256;
|
|
|
|
[SerializeField]
|
|
[Tooltip("Determines if 32-bit precision buffers should be used for computations (Default: off). Not supported on most mobile devices. This setting has impact on performance, even on PCs.\n\nTips:\n 1024 and higher - The difference is clearly visible, use this option on higher quality settings.\n 512 or lower - Keep it disabled.")]
|
|
public bool HighPrecision = true;
|
|
|
|
[Tooltip("What error in world units is acceptable for elevation sampling used by physics and custom scripts? Lower values mean better precision, but higher CPU usage.")]
|
|
public float CpuDesiredStandardError = 0.12f;
|
|
|
|
[Tooltip("Copying wave spectrum from other fluid will make this instance a lot faster.")]
|
|
public Water CopyFrom;
|
|
|
|
[Tooltip("Setting this property to any value greater than zero will loop the water spectra in that time. A good value is 10 seconds. Set to 0 to resolve sea state at each frame without any looping (best quality).")]
|
|
public float LoopDuration;
|
|
|
|
public WindWavesEvent WindDirectionChanged;
|
|
|
|
public WindWavesEvent ResolutionChanged;
|
|
|
|
public float MipBias;
|
|
|
|
public WavesRendererFFT.Data WavesRendererFFTData;
|
|
|
|
public WavesRendererGerstner.Data WavesRendererGerstnerData;
|
|
}
|
|
|
|
private readonly Water _Water;
|
|
|
|
private readonly Data _Data;
|
|
|
|
private Vector4 _TileSizeScales = new Vector4(0.79241f, 0.163151f, 3.175131f, 13.731513f);
|
|
|
|
private int _FinalResolution;
|
|
|
|
private bool _FinalHighPrecision;
|
|
|
|
private float _WindSpeedMagnitude;
|
|
|
|
private float _SpectrumDirectionality;
|
|
|
|
private float _TileSize;
|
|
|
|
private float _LastTileSize = float.NaN;
|
|
|
|
private float _LastUniformWaterScale = float.NaN;
|
|
|
|
private Vector4 _TileSizes;
|
|
|
|
private Vector4 _TileSizesInv;
|
|
|
|
private Vector4 _UnscaledTileSizes;
|
|
|
|
private Vector2 _WindDirection;
|
|
|
|
private Vector2 _WindSpeed;
|
|
|
|
private WaveSpectrumRenderMode _FinalRenderMode;
|
|
|
|
private SpectrumResolver _SpectrumResolver;
|
|
|
|
private Water _RuntimeCopyFrom;
|
|
|
|
private bool _IsClone;
|
|
|
|
private bool _WindSpeedChanged;
|
|
|
|
private bool _HasWindDirectionPointer;
|
|
|
|
private WavesRendererFFT _WaterWavesFFT;
|
|
|
|
private WavesRendererGerstner _WaterWavesGerstner;
|
|
|
|
private DynamicSmoothness _DynamicSmoothness;
|
|
|
|
public Water CopyFrom
|
|
{
|
|
get
|
|
{
|
|
return _RuntimeCopyFrom;
|
|
}
|
|
set
|
|
{
|
|
if (_Data.CopyFrom != value || _RuntimeCopyFrom != value)
|
|
{
|
|
_Data.CopyFrom = value;
|
|
_RuntimeCopyFrom = value;
|
|
_IsClone = value != null;
|
|
_DynamicSmoothness.OnCopyModeChanged();
|
|
_WaterWavesFFT.OnCopyModeChanged();
|
|
}
|
|
}
|
|
}
|
|
|
|
public SpectrumResolver SpectrumResolver
|
|
{
|
|
get
|
|
{
|
|
return (!(_Data.CopyFrom == null)) ? _Data.CopyFrom.WindWaves._SpectrumResolver : _SpectrumResolver;
|
|
}
|
|
}
|
|
|
|
public WavesRendererFFT WaterWavesFFT
|
|
{
|
|
get
|
|
{
|
|
return _WaterWavesFFT;
|
|
}
|
|
}
|
|
|
|
public WavesRendererGerstner WaterWavesGerstner
|
|
{
|
|
get
|
|
{
|
|
return _WaterWavesGerstner;
|
|
}
|
|
}
|
|
|
|
public DynamicSmoothness DynamicSmoothness
|
|
{
|
|
get
|
|
{
|
|
return _DynamicSmoothness;
|
|
}
|
|
}
|
|
|
|
public WaveSpectrumRenderMode FinalRenderMode
|
|
{
|
|
get
|
|
{
|
|
return _FinalRenderMode;
|
|
}
|
|
}
|
|
|
|
public Vector4 TileSizes
|
|
{
|
|
get
|
|
{
|
|
return _TileSizes;
|
|
}
|
|
}
|
|
|
|
public Vector4 TileSizesInv
|
|
{
|
|
get
|
|
{
|
|
return _TileSizesInv;
|
|
}
|
|
}
|
|
|
|
public Vector4 UnscaledTileSizes
|
|
{
|
|
get
|
|
{
|
|
return _UnscaledTileSizes;
|
|
}
|
|
}
|
|
|
|
public Vector2 WindSpeed
|
|
{
|
|
get
|
|
{
|
|
return _WindSpeed;
|
|
}
|
|
}
|
|
|
|
public bool WindSpeedChanged
|
|
{
|
|
get
|
|
{
|
|
return _WindSpeedChanged;
|
|
}
|
|
}
|
|
|
|
public Vector2 WindDirection
|
|
{
|
|
get
|
|
{
|
|
return _WindDirection;
|
|
}
|
|
}
|
|
|
|
public Transform WindDirectionPointer
|
|
{
|
|
get
|
|
{
|
|
return _Data.WindDirectionPointer;
|
|
}
|
|
set
|
|
{
|
|
_Data.WindDirectionPointer = value;
|
|
_HasWindDirectionPointer = _Data.WindDirectionPointer != null;
|
|
}
|
|
}
|
|
|
|
public WindWavesEvent WindDirectionChanged
|
|
{
|
|
get
|
|
{
|
|
return _Data.WindDirectionChanged;
|
|
}
|
|
}
|
|
|
|
public WindWavesEvent ResolutionChanged
|
|
{
|
|
get
|
|
{
|
|
return (_Data.ResolutionChanged == null) ? (_Data.ResolutionChanged = new WindWavesEvent()) : _Data.ResolutionChanged;
|
|
}
|
|
}
|
|
|
|
public int Resolution
|
|
{
|
|
get
|
|
{
|
|
return _Data.Resolution;
|
|
}
|
|
set
|
|
{
|
|
if (_Data.Resolution != value)
|
|
{
|
|
_Data.Resolution = value;
|
|
ResolveFinalSettings(WaterQualitySettings.Instance.CurrentQualityLevel);
|
|
}
|
|
}
|
|
}
|
|
|
|
public int FinalResolution
|
|
{
|
|
get
|
|
{
|
|
return _FinalResolution;
|
|
}
|
|
}
|
|
|
|
public bool FinalHighPrecision
|
|
{
|
|
get
|
|
{
|
|
return _FinalHighPrecision;
|
|
}
|
|
}
|
|
|
|
public bool HighPrecision
|
|
{
|
|
get
|
|
{
|
|
return _Data.HighPrecision;
|
|
}
|
|
}
|
|
|
|
public float CpuDesiredStandardError
|
|
{
|
|
get
|
|
{
|
|
return _Data.CpuDesiredStandardError;
|
|
}
|
|
}
|
|
|
|
public float LoopDuration
|
|
{
|
|
get
|
|
{
|
|
return _Data.LoopDuration;
|
|
}
|
|
}
|
|
|
|
public Vector4 TileSizeScales
|
|
{
|
|
get
|
|
{
|
|
return _TileSizeScales;
|
|
}
|
|
}
|
|
|
|
public float MaxVerticalDisplacement
|
|
{
|
|
get
|
|
{
|
|
return _SpectrumResolver.MaxVerticalDisplacement;
|
|
}
|
|
}
|
|
|
|
public float MaxHorizontalDisplacement
|
|
{
|
|
get
|
|
{
|
|
return _SpectrumResolver.MaxHorizontalDisplacement;
|
|
}
|
|
}
|
|
|
|
public float SpectrumDirectionality
|
|
{
|
|
get
|
|
{
|
|
return _SpectrumDirectionality;
|
|
}
|
|
}
|
|
|
|
public WindWaves(Water water, Data data)
|
|
{
|
|
_Water = water;
|
|
_Data = data;
|
|
_RuntimeCopyFrom = data.CopyFrom;
|
|
_IsClone = _RuntimeCopyFrom != null;
|
|
CheckSupport();
|
|
Validate();
|
|
Shader spectrumShader = Shader.Find("UltimateWater/Spectrum/Water Spectrum");
|
|
if (_SpectrumResolver == null)
|
|
{
|
|
_SpectrumResolver = new SpectrumResolver(water, this, spectrumShader);
|
|
}
|
|
if (data.WindDirectionChanged == null)
|
|
{
|
|
data.WindDirectionChanged = new WindWavesEvent();
|
|
}
|
|
CreateObjects();
|
|
ResolveFinalSettings(WaterQualitySettings.Instance.CurrentQualityLevel);
|
|
if (Application.isPlaying)
|
|
{
|
|
water.ProfilesManager.Changed.AddListener(OnProfilesChanged);
|
|
OnProfilesChanged(water);
|
|
}
|
|
}
|
|
|
|
public Vector2 GetHorizontalDisplacementAt(float x, float z, float time)
|
|
{
|
|
return _SpectrumResolver.GetHorizontalDisplacementAt(x, z, time);
|
|
}
|
|
|
|
public float GetHeightAt(float x, float z, float time)
|
|
{
|
|
return _SpectrumResolver.GetHeightAt(x, z, time);
|
|
}
|
|
|
|
public Vector4 GetForceAndHeightAt(float x, float z, float time)
|
|
{
|
|
return _SpectrumResolver.GetForceAndHeightAt(x, z, time);
|
|
}
|
|
|
|
public Vector3 GetDisplacementAt(float x, float z, float time)
|
|
{
|
|
return _SpectrumResolver.GetDisplacementAt(x, z, time);
|
|
}
|
|
|
|
internal override void Validate()
|
|
{
|
|
if (Application.isPlaying)
|
|
{
|
|
CopyFrom = _Data.CopyFrom;
|
|
}
|
|
if (_Data.CpuDesiredStandardError < 1E-05f)
|
|
{
|
|
_Data.CpuDesiredStandardError = 1E-05f;
|
|
}
|
|
_HasWindDirectionPointer = _Data.WindDirectionPointer != null;
|
|
if (_SpectrumResolver != null)
|
|
{
|
|
ResolveFinalSettings(WaterQualitySettings.Instance.CurrentQualityLevel);
|
|
_WaterWavesFFT.Validate();
|
|
_WaterWavesGerstner.OnValidate(this);
|
|
}
|
|
if (_Water != null && _TileSize != 0f)
|
|
{
|
|
UpdateShaderParams();
|
|
}
|
|
if (_WaterWavesFFT != null && _WaterWavesFFT.NormalMaps != null && _WaterWavesFFT.NormalMaps.Length != 0)
|
|
{
|
|
_WaterWavesFFT.GetNormalMap(0).mipMapBias = _Data.MipBias;
|
|
_WaterWavesFFT.GetNormalMap(1).mipMapBias = _Data.MipBias;
|
|
}
|
|
}
|
|
|
|
internal override void Update()
|
|
{
|
|
UpdateWind();
|
|
if (_IsClone)
|
|
{
|
|
_TileSize = _RuntimeCopyFrom.WindWaves._TileSize;
|
|
UpdateShaderParams();
|
|
}
|
|
else if (Application.isPlaying)
|
|
{
|
|
_SpectrumResolver.Update();
|
|
_DynamicSmoothness.Update();
|
|
UpdateShaderParams();
|
|
}
|
|
}
|
|
|
|
internal void ResolveFinalSettings(WaterQualityLevel quality)
|
|
{
|
|
CreateObjects();
|
|
WaterWavesMode wavesMode = quality.WavesMode;
|
|
if (wavesMode == WaterWavesMode.DisallowAll)
|
|
{
|
|
_WaterWavesFFT.Disable();
|
|
_WaterWavesGerstner.Disable();
|
|
return;
|
|
}
|
|
bool flag = SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.ARGBFloat) || SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.ARGBHalf);
|
|
int num = Mathf.Min(_Data.Resolution, quality.MaxSpectrumResolution, SystemInfo.maxTextureSize);
|
|
bool flag2 = _Data.HighPrecision && quality.AllowHighPrecisionTextures && SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.ARGBFloat);
|
|
WindWavesRenderMode windWavesMode = _Water.ShaderSet.WindWavesMode;
|
|
if (windWavesMode == WindWavesRenderMode.FullFFT && wavesMode == WaterWavesMode.AllowAll && flag)
|
|
{
|
|
_FinalRenderMode = WaveSpectrumRenderMode.FullFFT;
|
|
}
|
|
else if (windWavesMode <= WindWavesRenderMode.GerstnerAndFFTNormals && wavesMode <= WaterWavesMode.AllowNormalFFT && flag)
|
|
{
|
|
_FinalRenderMode = WaveSpectrumRenderMode.GerstnerAndFFTNormals;
|
|
}
|
|
else
|
|
{
|
|
_FinalRenderMode = WaveSpectrumRenderMode.Gerstner;
|
|
}
|
|
if (_FinalResolution != num)
|
|
{
|
|
lock (this)
|
|
{
|
|
_FinalResolution = num;
|
|
_FinalHighPrecision = flag2;
|
|
if (_SpectrumResolver != null)
|
|
{
|
|
_SpectrumResolver.OnMapsFormatChanged(true);
|
|
}
|
|
if (ResolutionChanged != null)
|
|
{
|
|
ResolutionChanged.Invoke(this);
|
|
}
|
|
}
|
|
}
|
|
else if (_FinalHighPrecision != flag2)
|
|
{
|
|
lock (this)
|
|
{
|
|
_FinalHighPrecision = flag2;
|
|
if (_SpectrumResolver != null)
|
|
{
|
|
_SpectrumResolver.OnMapsFormatChanged(false);
|
|
}
|
|
}
|
|
}
|
|
switch (_FinalRenderMode)
|
|
{
|
|
case WaveSpectrumRenderMode.FullFFT:
|
|
_WaterWavesFFT.RenderedMaps = WavesRendererFFT.MapType.Displacement | WavesRendererFFT.MapType.Normal;
|
|
_WaterWavesFFT.Enable();
|
|
_WaterWavesGerstner.Disable();
|
|
break;
|
|
case WaveSpectrumRenderMode.GerstnerAndFFTNormals:
|
|
_WaterWavesFFT.RenderedMaps = WavesRendererFFT.MapType.Normal;
|
|
_WaterWavesFFT.Enable();
|
|
_WaterWavesGerstner.Enable();
|
|
break;
|
|
case WaveSpectrumRenderMode.Gerstner:
|
|
_WaterWavesFFT.Disable();
|
|
_WaterWavesGerstner.Enable();
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void UpdateShaderParams()
|
|
{
|
|
float uniformWaterScale = _Water.UniformWaterScale;
|
|
if (_LastTileSize != _TileSize || _LastUniformWaterScale != uniformWaterScale)
|
|
{
|
|
MaterialPropertyBlock propertyBlock = _Water.Renderer.PropertyBlock;
|
|
float num = _TileSize * uniformWaterScale;
|
|
_TileSizes.x = num * _TileSizeScales.x;
|
|
_TileSizes.y = num * _TileSizeScales.y;
|
|
_TileSizes.z = num * _TileSizeScales.z;
|
|
_TileSizes.w = num * _TileSizeScales.w;
|
|
propertyBlock.SetVector(ShaderVariables.WaterTileSize, _TileSizes);
|
|
_TileSizesInv.x = 1f / _TileSizes.x;
|
|
_TileSizesInv.y = 1f / _TileSizes.y;
|
|
_TileSizesInv.z = 1f / _TileSizes.z;
|
|
_TileSizesInv.w = 1f / _TileSizes.w;
|
|
propertyBlock.SetVector(ShaderVariables.WaterTileSizeInv, _TileSizesInv);
|
|
_LastUniformWaterScale = uniformWaterScale;
|
|
_LastTileSize = _TileSize;
|
|
}
|
|
}
|
|
|
|
private void OnProfilesChanged(Water water)
|
|
{
|
|
_TileSize = 0f;
|
|
_WindSpeedMagnitude = 0f;
|
|
_SpectrumDirectionality = 0f;
|
|
Water.WeightedProfile[] profiles = water.ProfilesManager.Profiles;
|
|
for (int i = 0; i < profiles.Length; i++)
|
|
{
|
|
WaterProfileData profile = profiles[i].Profile;
|
|
float weight = profiles[i].Weight;
|
|
_TileSize += profile.TileSize * profile.TileScale * weight;
|
|
_WindSpeedMagnitude += profile.WindSpeed * weight;
|
|
_SpectrumDirectionality += profile.Directionality * weight;
|
|
}
|
|
WaterQualitySettings instance = WaterQualitySettings.Instance;
|
|
_TileSize *= instance.TileSizeScale;
|
|
_UnscaledTileSizes = _TileSize * _TileSizeScales;
|
|
UpdateShaderParams();
|
|
MaterialPropertyBlock propertyBlock = water.Renderer.PropertyBlock;
|
|
propertyBlock.SetVector(ShaderVariables.WaterTileSizeScales, new Vector4(_TileSizeScales.x / _TileSizeScales.y, _TileSizeScales.x / _TileSizeScales.z, _TileSizeScales.x / _TileSizeScales.w, 0f));
|
|
_SpectrumResolver.OnProfilesChanged();
|
|
propertyBlock.SetFloat(ShaderVariables.MaxDisplacement, _SpectrumResolver.MaxHorizontalDisplacement);
|
|
}
|
|
|
|
internal override void Destroy()
|
|
{
|
|
if (_SpectrumResolver != null)
|
|
{
|
|
_SpectrumResolver.OnDestroy();
|
|
_SpectrumResolver = null;
|
|
}
|
|
if (_WaterWavesFFT != null)
|
|
{
|
|
_WaterWavesFFT.OnDestroy();
|
|
}
|
|
}
|
|
|
|
private void UpdateWind()
|
|
{
|
|
Vector2 windDirection;
|
|
if (_HasWindDirectionPointer)
|
|
{
|
|
Vector3 forward = _Data.WindDirectionPointer.forward;
|
|
float num = Mathf.Sqrt(forward.x * forward.x + forward.z * forward.z);
|
|
windDirection = new Vector2(forward.x / num, forward.z / num);
|
|
}
|
|
else
|
|
{
|
|
windDirection = new Vector2(1f, 0f);
|
|
}
|
|
Vector2 windSpeed = new Vector2(windDirection.x * _WindSpeedMagnitude, windDirection.y * _WindSpeedMagnitude);
|
|
if (_WindSpeed.x != windSpeed.x || _WindSpeed.y != windSpeed.y)
|
|
{
|
|
_WindDirection = windDirection;
|
|
_WindSpeed = windSpeed;
|
|
_WindSpeedChanged = true;
|
|
_SpectrumResolver.SetWindDirection(_WindDirection);
|
|
}
|
|
else
|
|
{
|
|
_WindSpeedChanged = false;
|
|
}
|
|
}
|
|
|
|
private void CreateObjects()
|
|
{
|
|
if (_WaterWavesFFT == null)
|
|
{
|
|
_WaterWavesFFT = new WavesRendererFFT(_Water, this, _Data.WavesRendererFFTData);
|
|
}
|
|
if (_WaterWavesGerstner == null)
|
|
{
|
|
_WaterWavesGerstner = new WavesRendererGerstner(_Water, this, _Data.WavesRendererGerstnerData);
|
|
}
|
|
if (_DynamicSmoothness == null)
|
|
{
|
|
_DynamicSmoothness = new DynamicSmoothness(_Water, this);
|
|
}
|
|
}
|
|
|
|
private void CheckSupport()
|
|
{
|
|
if (_Data.HighPrecision && (!SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.RGFloat) || !SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.ARGBFloat)))
|
|
{
|
|
_FinalHighPrecision = false;
|
|
}
|
|
if (!_Data.HighPrecision && (!SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.RGHalf) || !SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.ARGBHalf)))
|
|
{
|
|
if (SystemInfo.SupportsRenderTextureFormat(RenderTextureFormat.RGFloat))
|
|
{
|
|
_FinalHighPrecision = true;
|
|
}
|
|
else if (_Water.ShaderSet.WindWavesMode == WindWavesRenderMode.FullFFT)
|
|
{
|
|
_FinalRenderMode = WaveSpectrumRenderMode.Gerstner;
|
|
}
|
|
}
|
|
}
|
|
|
|
internal override void OnWaterRender(WaterCamera waterCamera)
|
|
{
|
|
if (Application.isPlaying)
|
|
{
|
|
Camera cameraComponent = waterCamera.CameraComponent;
|
|
if (_WaterWavesFFT.Enabled)
|
|
{
|
|
_WaterWavesFFT.OnWaterRender(cameraComponent);
|
|
}
|
|
if (_WaterWavesGerstner.Enabled)
|
|
{
|
|
_WaterWavesGerstner.OnWaterRender(cameraComponent);
|
|
}
|
|
}
|
|
}
|
|
|
|
internal override void Enable()
|
|
{
|
|
UpdateWind();
|
|
ResolveFinalSettings(WaterQualitySettings.Instance.CurrentQualityLevel);
|
|
}
|
|
|
|
internal override void Disable()
|
|
{
|
|
if (_WaterWavesFFT != null)
|
|
{
|
|
_WaterWavesFFT.Disable();
|
|
}
|
|
if (_WaterWavesGerstner != null)
|
|
{
|
|
_WaterWavesGerstner.Disable();
|
|
}
|
|
if (_DynamicSmoothness != null)
|
|
{
|
|
_DynamicSmoothness.FreeResources();
|
|
}
|
|
}
|
|
}
|
|
}
|