580 lines
16 KiB
C#
580 lines
16 KiB
C#
using System.Collections.Generic;
|
|
using UltimateWater.Internal;
|
|
using UltimateWater.Utils;
|
|
using UnityEngine;
|
|
using UnityEngine.Rendering;
|
|
|
|
namespace UltimateWater
|
|
{
|
|
[RequireComponent(typeof(MeshFilter))]
|
|
[AddComponentMenu("Ultimate Water/Dynamic/Water Simulation Area")]
|
|
public class WaterSimulationArea : MonoBehaviour, ILocalDisplacementRenderer, IDynamicWaterEffects
|
|
{
|
|
[SerializeField]
|
|
private Water _Water;
|
|
|
|
[SerializeField]
|
|
private WaterRipplesProfile _Profile;
|
|
|
|
[SerializeField]
|
|
[Range(1f, 32f)]
|
|
[Tooltip("How many simulation pixels per one unit are used")]
|
|
[Header("Settings")]
|
|
private int _PixelsPerUnit = 16;
|
|
|
|
[SerializeField]
|
|
[Range(0.125f, 2f)]
|
|
[Tooltip("What resolution is used for dynamic depth rendering")]
|
|
private float _DepthScale = 0.5f;
|
|
|
|
[SerializeField]
|
|
[Tooltip("Does the water can be stopped by Blocking objects")]
|
|
private bool _EnableStaticCalculations;
|
|
|
|
[SerializeField]
|
|
[Range(0.125f, 2f)]
|
|
[Tooltip("What resolution is used for static depth information")]
|
|
private float _StaticDepthScale = 1f;
|
|
|
|
[SerializeField]
|
|
[Header("Edge fade")]
|
|
private bool _Fade;
|
|
|
|
[SerializeField]
|
|
private float _FadePower = 0.5f;
|
|
|
|
private Camera _DepthCamera;
|
|
|
|
private readonly RenderTexture[] _Buffers = new RenderTexture[3];
|
|
|
|
private RenderTexture _PreviousDepth;
|
|
|
|
private RenderTexture _Depth;
|
|
|
|
private RenderTexture _StaticDepth;
|
|
|
|
private Material _RipplesMaterialCache;
|
|
|
|
private MeshFilter _MeshFilter;
|
|
|
|
[HideInInspector]
|
|
[SerializeField]
|
|
private Material _DisplacementMaterial;
|
|
|
|
private Vector3 _Position;
|
|
|
|
private Vector3 _Scale;
|
|
|
|
private Material _TranslateMaterial;
|
|
|
|
private CommandBuffer _CommandBuffer;
|
|
|
|
private static readonly float[] _Array = new float[2048];
|
|
|
|
private const int _MaxArrayElements = 512;
|
|
|
|
public WaterRipplesProfile Profile
|
|
{
|
|
get
|
|
{
|
|
return _Profile;
|
|
}
|
|
set
|
|
{
|
|
_Profile = value;
|
|
UpdateShaderVariables();
|
|
}
|
|
}
|
|
|
|
public int PixelsPerUnit
|
|
{
|
|
get
|
|
{
|
|
return _PixelsPerUnit;
|
|
}
|
|
set
|
|
{
|
|
_PixelsPerUnit = value;
|
|
}
|
|
}
|
|
|
|
public Vector2 Resolution
|
|
{
|
|
get
|
|
{
|
|
Vector2 vector = Size * _PixelsPerUnit;
|
|
return new Vector2((int)vector.x, (int)vector.y);
|
|
}
|
|
}
|
|
|
|
public Vector2 Size
|
|
{
|
|
get
|
|
{
|
|
return new Vector2(base.transform.localScale.x * 10f, base.transform.localScale.z * 10f);
|
|
}
|
|
set
|
|
{
|
|
base.transform.localScale = new Vector3(value.x * 0.1f, 1f, value.y * 0.1f);
|
|
}
|
|
}
|
|
|
|
public Vector2 DepthResolution
|
|
{
|
|
get
|
|
{
|
|
return Resolution * _DepthScale;
|
|
}
|
|
}
|
|
|
|
private Material _RipplesMaterial
|
|
{
|
|
get
|
|
{
|
|
if (_RipplesMaterialCache != null)
|
|
{
|
|
return _RipplesMaterialCache;
|
|
}
|
|
Shader shader = ShaderUtility.Instance.Get(ShaderList.Simulation);
|
|
_RipplesMaterialCache = new Material(shader);
|
|
return _RipplesMaterialCache;
|
|
}
|
|
}
|
|
|
|
private int _Width
|
|
{
|
|
get
|
|
{
|
|
return (int)Resolution.x;
|
|
}
|
|
}
|
|
|
|
private int _Height
|
|
{
|
|
get
|
|
{
|
|
return (int)Resolution.y;
|
|
}
|
|
}
|
|
|
|
private int _DepthWidth
|
|
{
|
|
get
|
|
{
|
|
return (int)(Resolution.x * _DepthScale);
|
|
}
|
|
}
|
|
|
|
private int _DepthHeight
|
|
{
|
|
get
|
|
{
|
|
return (int)(Resolution.y * _DepthScale);
|
|
}
|
|
}
|
|
|
|
private int _StaticWidth
|
|
{
|
|
get
|
|
{
|
|
return (int)(Resolution.x * _StaticDepthScale);
|
|
}
|
|
}
|
|
|
|
private int _StaticHeight
|
|
{
|
|
get
|
|
{
|
|
return (int)(Resolution.y * _StaticDepthScale);
|
|
}
|
|
}
|
|
|
|
public Vector2 GetLocalPixelPosition(Vector3 globalPosition)
|
|
{
|
|
Vector2 vector = new Vector2(base.transform.position.x, base.transform.position.z);
|
|
Vector2 result = vector - new Vector2(globalPosition.x, globalPosition.z) + Size * 0.5f;
|
|
result.x *= _PixelsPerUnit;
|
|
result.y *= _PixelsPerUnit;
|
|
return result;
|
|
}
|
|
|
|
public void AddForce(List<WaterForce.Data> data, float radius = 1f)
|
|
{
|
|
Vector2 resolution = Resolution;
|
|
int num = 0;
|
|
for (int i = 0; i < data.Count; i++)
|
|
{
|
|
Vector2 localPixelPosition = GetLocalPixelPosition(data[i].Position);
|
|
if (ContainsLocalRaw(localPixelPosition, resolution) && !(data[i].Force <= 0f))
|
|
{
|
|
_Array[num * 4] = localPixelPosition.x;
|
|
_Array[num * 4 + 1] = localPixelPosition.y;
|
|
_Array[num * 4 + 2] = data[i].Force * 500f;
|
|
_Array[num * 4 + 3] = 0f;
|
|
num++;
|
|
if (num == 512)
|
|
{
|
|
DispatchAddForce(num);
|
|
num = 0;
|
|
}
|
|
}
|
|
}
|
|
DispatchAddForce(num);
|
|
}
|
|
|
|
private void Awake()
|
|
{
|
|
_MeshFilter = GetComponent<MeshFilter>();
|
|
_TranslateMaterial = new Material(ShaderUtility.Instance.Get(ShaderList.Translate));
|
|
if (_Water == null)
|
|
{
|
|
_Water = Utilities.GetWaterReference();
|
|
}
|
|
if (_DisplacementMaterial == null)
|
|
{
|
|
_DisplacementMaterial = new Material(Resources.Load<Material>("Materials/Overlay (Displacements)"));
|
|
}
|
|
}
|
|
|
|
private void OnEnable()
|
|
{
|
|
_Position = base.transform.position;
|
|
_Scale = base.transform.localScale;
|
|
_CommandBuffer = new CommandBuffer
|
|
{
|
|
name = "[Ultimate Water]: Water Simulation Area"
|
|
};
|
|
CreateDepthCamera();
|
|
CreateTextures();
|
|
UpdateShaderVariables();
|
|
RenderStaticDepthTexture();
|
|
DynamicWater.AddRenderer(this);
|
|
WaterRipples.Register(this);
|
|
}
|
|
|
|
private void OnDisable()
|
|
{
|
|
WaterRipples.Unregister(this);
|
|
DynamicWater.RemoveRenderer(this);
|
|
ReleaseDepthCamera();
|
|
ReleaseTextures();
|
|
if (_CommandBuffer != null)
|
|
{
|
|
_CommandBuffer.Release();
|
|
_CommandBuffer = null;
|
|
}
|
|
}
|
|
|
|
private void OnDestroy()
|
|
{
|
|
OnDisable();
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
base.transform.position = new Vector3(base.transform.position.x, _Water.transform.position.y, base.transform.position.z);
|
|
base.transform.rotation = Quaternion.identity;
|
|
base.transform.localScale = new Vector3(_Scale.x, 1f, _Scale.z);
|
|
RenderDepth();
|
|
}
|
|
|
|
private void OnValidate()
|
|
{
|
|
ShaderUtility.Instance.Use(ShaderList.Simulation);
|
|
ShaderUtility.Instance.Use(ShaderList.Translate);
|
|
ShaderUtility.Instance.Use(ShaderList.Velocity);
|
|
ShaderUtility.Instance.Use(ShaderList.Depth);
|
|
}
|
|
|
|
private void Reset()
|
|
{
|
|
GameObject gameObject = GameObject.CreatePrimitive(PrimitiveType.Plane);
|
|
MeshFilter component = GetComponent<MeshFilter>();
|
|
component.sharedMesh = gameObject.GetComponent<MeshFilter>().sharedMesh;
|
|
component.hideFlags = HideFlags.HideInInspector;
|
|
Object.DestroyImmediate(gameObject);
|
|
}
|
|
|
|
public void Refresh()
|
|
{
|
|
if (Application.isPlaying && Application.isEditor)
|
|
{
|
|
OnDisable();
|
|
OnEnable();
|
|
}
|
|
}
|
|
|
|
internal void Simulate()
|
|
{
|
|
Shader.SetGlobalFloat("_WaterHeight", _Water.transform.position.y);
|
|
if (base.transform.position.x != _Position.x || base.transform.position.z != _Position.z)
|
|
{
|
|
RenderStaticDepthTexture();
|
|
MoveSimulation();
|
|
_Position = base.transform.position;
|
|
}
|
|
if (_Buffers[0].Verify(false) || _Buffers[1].Verify(false))
|
|
{
|
|
_Buffers[0].Clear(Color.clear);
|
|
_Buffers[1].Clear(Color.clear);
|
|
}
|
|
RipplesShader.SetPrimary(_Buffers[0], _RipplesMaterial);
|
|
RipplesShader.SetSecondary(_Buffers[1], _RipplesMaterial);
|
|
switch (WaterQualitySettings.Instance.Ripples.ShaderMode)
|
|
{
|
|
case WaterRipplesData.ShaderModes.ComputeShader:
|
|
SimulateComputeShader();
|
|
break;
|
|
case WaterRipplesData.ShaderModes.PixelShader:
|
|
SimulatePixelShader();
|
|
break;
|
|
}
|
|
}
|
|
|
|
internal void Smooth()
|
|
{
|
|
if (!(Profile.Sigma <= 0.1f))
|
|
{
|
|
TemporaryRenderTexture temporary = RenderTexturesCache.GetTemporary(_Width, _Height, 0, WaterQualitySettings.Instance.Ripples.SimulationFormat, true, true);
|
|
TextureUtility.Verify(temporary);
|
|
GaussianShader.VerticalInput = _Buffers[1];
|
|
GaussianShader.VerticalOutput = temporary;
|
|
GaussianShader.Dispatch(GaussianShader.KernelType.Vertical, _Width, _Height);
|
|
GaussianShader.HorizontalInput = temporary;
|
|
GaussianShader.HorizontalOutput = _Buffers[1];
|
|
GaussianShader.Dispatch(GaussianShader.KernelType.Horizontal, _Width, _Height);
|
|
temporary.Dispose();
|
|
}
|
|
}
|
|
|
|
internal void Swap()
|
|
{
|
|
TextureUtility.Swap(ref _Buffers[0], ref _Buffers[1]);
|
|
}
|
|
|
|
internal void UpdateShaderVariables()
|
|
{
|
|
if (!(Profile == null) && !_DisplacementMaterial.IsNullReference(this))
|
|
{
|
|
float num = (float)_PixelsPerUnit / 32f;
|
|
RipplesShader.SetPropagation(Profile.Propagation * num, _RipplesMaterial);
|
|
RipplesShader.SetStaticDepth(DefaultTextures.Get(Color.clear), _RipplesMaterial);
|
|
RipplesShader.SetDamping(Profile.Damping / 32f, _RipplesMaterial);
|
|
RipplesShader.SetGain(Profile.Gain, _RipplesMaterial);
|
|
RipplesShader.SetHeightGain(Profile.HeightGain, _RipplesMaterial);
|
|
RipplesShader.SetHeightOffset(Profile.HeightOffset, _RipplesMaterial);
|
|
float[] array = Math.GaussianTerms(Profile.Sigma);
|
|
GaussianShader.Term0 = array[0];
|
|
GaussianShader.Term1 = array[1];
|
|
GaussianShader.Term2 = array[2];
|
|
_DisplacementMaterial.SetFloat("_Amplitude", Profile.Amplitude * 0.05f);
|
|
_DisplacementMaterial.SetFloat("_Spread", Profile.Spread);
|
|
_DisplacementMaterial.SetFloat("_Multiplier", Profile.Multiplier);
|
|
_DisplacementMaterial.SetFloat("_Fadeout", (!_Fade) ? 0f : 1f);
|
|
_DisplacementMaterial.SetFloat("_FadePower", _FadePower);
|
|
}
|
|
}
|
|
|
|
private void DispatchAddForce(int count)
|
|
{
|
|
if (count > 0)
|
|
{
|
|
ComputeShader shader = SetupShader.Shader;
|
|
shader.SetTexture(SetupShader.Multi, "Previous", _Buffers[0]);
|
|
shader.SetTexture(SetupShader.Multi, "Current", _Buffers[1]);
|
|
shader.SetFloats("data", _Array);
|
|
shader.Dispatch(SetupShader.Multi, count, 1, 1);
|
|
}
|
|
}
|
|
|
|
private void SimulateComputeShader()
|
|
{
|
|
RipplesShader.Size = Resolution;
|
|
RipplesShader.Dispatch(_Width, _Height);
|
|
}
|
|
|
|
private void SimulatePixelShader()
|
|
{
|
|
RenderTexture renderTexture = _Buffers[1].CreateTemporary();
|
|
renderTexture.Clear(Color.clear);
|
|
Graphics.Blit(null, renderTexture, _RipplesMaterial);
|
|
Graphics.Blit(renderTexture, _Buffers[1]);
|
|
renderTexture.ReleaseTemporary();
|
|
}
|
|
|
|
private void CreateDepthCamera()
|
|
{
|
|
if (Resolution.x <= 0f || Resolution.y <= 0f)
|
|
{
|
|
Debug.LogError("WaterSimulationArea: invalid resolution");
|
|
}
|
|
GameObject gameObject = new GameObject("Depth");
|
|
gameObject.transform.SetParent(base.transform);
|
|
_DepthCamera = gameObject.AddComponent<Camera>();
|
|
_DepthCamera.enabled = false;
|
|
_DepthCamera.backgroundColor = Color.clear;
|
|
_DepthCamera.clearFlags = CameraClearFlags.Color;
|
|
_DepthCamera.orthographic = true;
|
|
_DepthCamera.orthographicSize = Size.y * 0.5f;
|
|
_DepthCamera.aspect = Resolution.x / Resolution.y;
|
|
_DepthCamera.transform.localRotation = Quaternion.Euler(new Vector3(90f, 180f, 0f));
|
|
_DepthCamera.transform.localScale = Vector3.one;
|
|
_DepthCamera.depthTextureMode = DepthTextureMode.Depth;
|
|
SetDepthCameraParameters(ref _DepthCamera);
|
|
}
|
|
|
|
private void ReleaseDepthCamera()
|
|
{
|
|
if (_DepthCamera != null)
|
|
{
|
|
Object.Destroy(_DepthCamera.gameObject);
|
|
}
|
|
_DepthCamera = null;
|
|
}
|
|
|
|
private void RenderDepth()
|
|
{
|
|
if (!_DepthCamera.IsNullReference(this))
|
|
{
|
|
TextureUtility.Swap(ref _Depth, ref _PreviousDepth);
|
|
GL.PushMatrix();
|
|
GL.modelview = _DepthCamera.worldToCameraMatrix;
|
|
GL.LoadProjectionMatrix(_DepthCamera.projectionMatrix);
|
|
_CommandBuffer.Clear();
|
|
_CommandBuffer.SetRenderTarget(_Depth);
|
|
_CommandBuffer.ClearRenderTarget(true, true, Color.clear);
|
|
List<IWavesInteractive> interactions = DynamicWater.Interactions;
|
|
for (int i = 0; i < interactions.Count; i++)
|
|
{
|
|
interactions[i].Render(_DepthCamera, _CommandBuffer);
|
|
}
|
|
Graphics.ExecuteCommandBuffer(_CommandBuffer);
|
|
GL.PopMatrix();
|
|
RipplesShader.SetDepth(_Depth, _RipplesMaterial);
|
|
RipplesShader.SetPreviousDepth(_PreviousDepth, _RipplesMaterial);
|
|
}
|
|
}
|
|
|
|
private void MoveSimulation()
|
|
{
|
|
Vector3 vector = _Position - base.transform.position;
|
|
float x = vector.x * (float)_PixelsPerUnit;
|
|
float y = vector.z * (float)_PixelsPerUnit;
|
|
_TranslateMaterial.SetVector("_Offset", new Vector4(x, y, 0f, 0f));
|
|
for (int i = 0; i < 2; i++)
|
|
{
|
|
Graphics.Blit(_Buffers[i], _Buffers[2], _TranslateMaterial);
|
|
TextureUtility.Swap(ref _Buffers[i], ref _Buffers[2]);
|
|
}
|
|
SendSimulationMatrix(_Buffers[1]);
|
|
}
|
|
|
|
private static bool ContainsLocalRaw(Vector2 localPosition, Vector2 resolution)
|
|
{
|
|
return localPosition.x >= 0f && localPosition.x < resolution.x && localPosition.y >= 0f && localPosition.y < resolution.y;
|
|
}
|
|
|
|
private void RenderStaticDepthTexture()
|
|
{
|
|
if (!_EnableStaticCalculations)
|
|
{
|
|
if (_StaticDepth != null)
|
|
{
|
|
_StaticDepth.Release();
|
|
_StaticDepth = null;
|
|
}
|
|
}
|
|
else if (!_DepthCamera.IsNullReference(this))
|
|
{
|
|
if (_StaticDepth == null)
|
|
{
|
|
TextureUtility.RenderTextureDesc renderTextureDesc = new TextureUtility.RenderTextureDesc("[UWS] - Static Depth");
|
|
renderTextureDesc.Width = _StaticWidth;
|
|
renderTextureDesc.Height = _StaticHeight;
|
|
renderTextureDesc.Depth = 24;
|
|
renderTextureDesc.Format = RenderTextureFormat.RFloat;
|
|
renderTextureDesc.Filter = FilterMode.Point;
|
|
TextureUtility.RenderTextureDesc desc = renderTextureDesc;
|
|
_StaticDepth = desc.CreateRenderTexture();
|
|
_StaticDepth.Clear(Color.clear);
|
|
}
|
|
float y = _DepthCamera.transform.localPosition.y;
|
|
float nearClipPlane = _DepthCamera.nearClipPlane;
|
|
float farClipPlane = _DepthCamera.farClipPlane;
|
|
SetDepthCameraParameters(ref _DepthCamera, 40f, 0f, 80f);
|
|
_DepthCamera.cullingMask = WaterQualitySettings.Instance.Ripples.StaticDepthMask;
|
|
_DepthCamera.targetTexture = _StaticDepth;
|
|
_DepthCamera.SetReplacementShader(ShaderUtility.Instance.Get(ShaderList.Depth), string.Empty);
|
|
_DepthCamera.Render();
|
|
SetDepthCameraParameters(ref _DepthCamera, y, nearClipPlane, farClipPlane);
|
|
RipplesShader.SetStaticDepth(_StaticDepth, _RipplesMaterial);
|
|
}
|
|
}
|
|
|
|
private void CreateTextures()
|
|
{
|
|
ReleaseTextures();
|
|
TextureUtility.RenderTextureDesc renderTextureDesc = new TextureUtility.RenderTextureDesc("[UWS] WaterSimulationArea - Simulation");
|
|
renderTextureDesc.Width = _Width;
|
|
renderTextureDesc.Height = _Height;
|
|
renderTextureDesc.Format = WaterQualitySettings.Instance.Ripples.SimulationFormat;
|
|
renderTextureDesc.Filter = FilterMode.Bilinear;
|
|
renderTextureDesc.EnableRandomWrite = true;
|
|
TextureUtility.RenderTextureDesc desc = renderTextureDesc;
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
_Buffers[i] = desc.CreateRenderTexture();
|
|
_Buffers[i].name = "[UWS] WaterSimulationArea - Buffer[" + i + "]";
|
|
_Buffers[i].Clear(Color.clear);
|
|
}
|
|
SendSimulationMatrix(_Buffers[1]);
|
|
renderTextureDesc = new TextureUtility.RenderTextureDesc("[UWS] WaterSimulationArea - Depth");
|
|
renderTextureDesc.Width = _DepthWidth;
|
|
renderTextureDesc.Height = _DepthHeight;
|
|
renderTextureDesc.Depth = 24;
|
|
renderTextureDesc.Format = RenderTextureFormat.ARGBHalf;
|
|
renderTextureDesc.Filter = FilterMode.Point;
|
|
TextureUtility.RenderTextureDesc desc2 = renderTextureDesc;
|
|
_Depth = desc2.CreateRenderTexture();
|
|
_PreviousDepth = desc2.CreateRenderTexture();
|
|
_Depth.Clear(Color.clear);
|
|
_PreviousDepth.Clear(Color.clear);
|
|
}
|
|
|
|
private void ReleaseTextures()
|
|
{
|
|
TextureUtility.Release(ref _Depth);
|
|
TextureUtility.Release(ref _PreviousDepth);
|
|
TextureUtility.Release(ref _StaticDepth);
|
|
TextureUtility.Release(ref _Buffers[0]);
|
|
TextureUtility.Release(ref _Buffers[1]);
|
|
TextureUtility.Release(ref _Buffers[2]);
|
|
}
|
|
|
|
private void SendSimulationMatrix(Texture texture)
|
|
{
|
|
_DisplacementMaterial.SetTexture("_SimulationMatrix", texture);
|
|
}
|
|
|
|
private static void SetDepthCameraParameters(ref Camera camera, float height = 10f, float near = 0f, float far = 20f)
|
|
{
|
|
camera.nearClipPlane = near;
|
|
camera.farClipPlane = far;
|
|
camera.transform.localPosition = new Vector3(0f, height, 0f);
|
|
}
|
|
|
|
public void RenderLocalDisplacement(CommandBuffer commandBuffer, DynamicWaterCameraData overlays)
|
|
{
|
|
commandBuffer.DrawMesh(_MeshFilter.sharedMesh, base.transform.localToWorldMatrix, _DisplacementMaterial);
|
|
}
|
|
|
|
public void Enable()
|
|
{
|
|
}
|
|
|
|
public void Disable()
|
|
{
|
|
}
|
|
}
|
|
}
|