Files
2026-03-04 10:03:45 +08:00

263 lines
8.1 KiB
C#

using System;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Serialization;
namespace UltimateWater.Internal
{
public sealed class DynamicWaterInteraction : MonoBehaviour, ILocalFoamRenderer, IDynamicWaterEffects
{
private struct VertexData
{
public Vector3 Position;
public Vector3 Normal;
}
[HideInInspector]
[SerializeField]
[FormerlySerializedAs("colliderInteractionShader")]
private ComputeShader _ColliderInteractionShader;
[HideInInspector]
[SerializeField]
[FormerlySerializedAs("maskDisplayShader")]
private Shader _MaskDisplayShader;
[HideInInspector]
[SerializeField]
[FormerlySerializedAs("baseShader")]
private Shader _BaseShader;
[Header("Contact Foam")]
[SerializeField]
[FormerlySerializedAs("foam")]
private bool _Foam = true;
[SerializeField]
[FormerlySerializedAs("foamPatternTiling")]
private float _FoamPatternTiling = 1f;
[SerializeField]
[FormerlySerializedAs("foamRange")]
private float _FoamRange = 1.6f;
[SerializeField]
[FormerlySerializedAs("uniformFoamAmount")]
private float _UniformFoamAmount = 30.5f;
[SerializeField]
[FormerlySerializedAs("noisyFoamAmount")]
private float _NoisyFoamAmount = 30.5f;
[Range(0f, 1f)]
[SerializeField]
[FormerlySerializedAs("foamIntensity")]
private float _FoamIntensity = 0.45f;
[SerializeField]
[FormerlySerializedAs("detectContactArea")]
private bool _DetectContactArea;
[SerializeField]
[FormerlySerializedAs("foamOcclusionMapResolution")]
private int _FoamOcclusionMapResolution = 128;
private int _OcclusionMap2Hash;
private int _OcclusionMapProjectionMatrixHash;
[SerializeField]
[FormerlySerializedAs("meshFilters")]
private MeshFilter[] _MeshFilters;
[Header("Waves")]
[SerializeField]
[FormerlySerializedAs("waves")]
private bool _Waves = true;
[SerializeField]
[FormerlySerializedAs("water")]
private WaveParticlesSystemGPU _Water;
[Range(0f, 4f)]
[SerializeField]
[FormerlySerializedAs("waveEmissionFrequency")]
private float _WaveEmissionFrequency = 1f;
private Material _Material;
private int _OcclusionMapHash;
private ComputeBuffer _ColliderVerticesBuffer;
private ComputeBuffer _ObjectToWorld;
private static readonly Matrix4x4[] _MatrixTemp = new Matrix4x4[1];
public void Enable()
{
throw new NotImplementedException();
}
public void Disable()
{
throw new NotImplementedException();
}
public void RenderLocalFoam(CommandBuffer commandBuffer, DynamicWaterCameraData overlays)
{
if (_DetectContactArea)
{
Bounds bounds = _MeshFilters[0].GetComponent<MeshRenderer>().bounds;
for (int num = _MeshFilters.Length - 1; num > 0; num--)
{
bounds.Encapsulate(_MeshFilters[num].GetComponent<MeshRenderer>().bounds);
}
Vector3 center = bounds.center;
Vector3 extents = bounds.extents;
extents.x += _FoamRange;
extents.y += _FoamRange;
extents.z += _FoamRange;
center.y -= extents.y + 1f;
commandBuffer.GetTemporaryRT(_OcclusionMap2Hash, _FoamOcclusionMapResolution, _FoamOcclusionMapResolution, 0, FilterMode.Point, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);
commandBuffer.SetRenderTarget(_OcclusionMap2Hash);
commandBuffer.ClearRenderTarget(clearDepth: false, clearColor: true, new Color(0f, 0f, 0f, 0f));
Camera effectsCamera = overlays.Camera.EffectsCamera;
effectsCamera.transform.position = center;
effectsCamera.transform.rotation = Quaternion.LookRotation(Vector3.up, Vector3.forward);
effectsCamera.orthographic = true;
effectsCamera.orthographicSize = Mathf.Max(extents.x, extents.z);
effectsCamera.nearClipPlane = 1f;
effectsCamera.farClipPlane = extents.y * 2f + 10f;
Matrix4x4 matrix4x = effectsCamera.projectionMatrix * effectsCamera.worldToCameraMatrix;
commandBuffer.SetGlobalMatrix(_OcclusionMapProjectionMatrixHash, matrix4x);
commandBuffer.SetViewProjectionMatrices(Matrix4x4.identity, matrix4x);
for (int num2 = _MeshFilters.Length - 1; num2 >= 0; num2--)
{
commandBuffer.DrawMesh(_MeshFilters[num2].sharedMesh, _MeshFilters[num2].transform.localToWorldMatrix, _Material, 0, 1);
}
commandBuffer.GetTemporaryRT(_OcclusionMapHash, _FoamOcclusionMapResolution, _FoamOcclusionMapResolution, 0, FilterMode.Bilinear, RenderTextureFormat.R8, RenderTextureReadWrite.Linear);
commandBuffer.Blit(_OcclusionMap2Hash, _OcclusionMapHash, _Material, 2);
commandBuffer.ReleaseTemporaryRT(_OcclusionMap2Hash);
commandBuffer.SetRenderTarget(overlays.FoamMap);
Camera planeProjectorCamera = overlays.Camera.PlaneProjectorCamera;
commandBuffer.SetViewProjectionMatrices(planeProjectorCamera.worldToCameraMatrix, planeProjectorCamera.projectionMatrix);
}
for (int num3 = _MeshFilters.Length - 1; num3 >= 0; num3--)
{
commandBuffer.DrawMesh(_MeshFilters[num3].sharedMesh, _MeshFilters[num3].transform.localToWorldMatrix, _Material, 0, (!_DetectContactArea) ? 3 : 0, overlays.DynamicWater.Water.Renderer.PropertyBlock);
}
if (_DetectContactArea)
{
commandBuffer.ReleaseTemporaryRT(_OcclusionMapHash);
}
if (_Waves && _WaveEmissionFrequency != 0f)
{
_MatrixTemp[0] = base.transform.localToWorldMatrix;
_ObjectToWorld.SetData(_MatrixTemp);
_ColliderInteractionShader.SetVector("_LocalMapsCoords", overlays.Camera.LocalMapsShaderCoords);
_ColliderInteractionShader.SetTexture(0, "TotalDisplacementMap", overlays.GetTotalDisplacementMap());
_ColliderInteractionShader.SetBuffer(0, "Vertices", _ColliderVerticesBuffer);
_ColliderInteractionShader.SetBuffer(0, "Particles", _Water.ParticlesBuffer);
_ColliderInteractionShader.SetBuffer(0, "ObjectToWorld", _ObjectToWorld);
_ColliderInteractionShader.Dispatch(0, Mathf.CeilToInt((float)(_ColliderVerticesBuffer.count >> 1) / 256f), 1, 1);
}
}
private void Start()
{
OnValidate();
if (_MeshFilters == null || _MeshFilters.Length == 0)
{
_MeshFilters = GetComponentsInChildren<MeshFilter>(includeInactive: true);
}
_Material = new Material(_BaseShader)
{
hideFlags = HideFlags.DontSave
};
_OcclusionMapHash = ShaderVariables.OcclusionMap;
_OcclusionMap2Hash = ShaderVariables.OcclusionMap2;
_OcclusionMapProjectionMatrixHash = ShaderVariables.OcclusionMapProjection;
OnValidate();
if (_Waves && _WaveEmissionFrequency != 0f)
{
CreateComputeBuffers();
}
}
private void OnEnable()
{
if (_Foam)
{
DynamicWater.AddRenderer(this);
}
}
private void OnDisable()
{
DynamicWater.RemoveRenderer(this);
}
private void OnDestroy()
{
if (_ColliderVerticesBuffer != null)
{
_ColliderVerticesBuffer.Release();
_ColliderVerticesBuffer = null;
}
if (_ObjectToWorld != null)
{
_ObjectToWorld.Release();
_ObjectToWorld = null;
}
}
private void OnValidate()
{
if (_MaskDisplayShader == null)
{
_MaskDisplayShader = Shader.Find("UltimateWater/Utility/ShorelineMaskRender");
}
if (_BaseShader == null)
{
_BaseShader = Shader.Find("UltimateWater/Utility/DynamicWaterInteraction");
}
if (_Material != null)
{
_Material.SetVector("_FoamIntensity", new Vector4(_UniformFoamAmount, _NoisyFoamAmount, _FoamIntensity, 0f));
_Material.SetFloat("_FoamRange", _FoamRange);
_Material.SetFloat("_FoamIntensityMaskTiling", _FoamPatternTiling);
}
}
private void CreateComputeBuffers()
{
Mesh sharedMesh = GetComponent<MeshCollider>().sharedMesh;
Vector3[] vertices = sharedMesh.vertices;
Vector3[] normals = sharedMesh.normals;
int[] indices = sharedMesh.GetIndices(0);
_ColliderVerticesBuffer = new ComputeBuffer(indices.Length * 2, 24, ComputeBufferType.Default);
_ObjectToWorld = new ComputeBuffer(1, 64, ComputeBufferType.Default);
VertexData[] array = new VertexData[indices.Length * 2];
int num = 0;
for (int i = 0; i < indices.Length; i++)
{
int num2 = indices[i];
int num3 = indices[(i % 3 == 0) ? (i + 2) : (i - 1)];
array[num++] = new VertexData
{
Position = vertices[num3],
Normal = normals[num3]
};
array[num++] = new VertexData
{
Position = vertices[num2],
Normal = normals[num2]
};
}
_ColliderVerticesBuffer.SetData(array);
}
}
}