454 lines
13 KiB
C#
454 lines
13 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using UltimateWater.Internal;
|
|
using UnityEngine;
|
|
using UnityEngine.Serialization;
|
|
|
|
namespace UltimateWater
|
|
{
|
|
public sealed class WaveParticle : IPoint2D
|
|
{
|
|
[FormerlySerializedAs("direction")]
|
|
public Vector2 Direction;
|
|
|
|
[FormerlySerializedAs("speed")]
|
|
public float Speed;
|
|
|
|
[FormerlySerializedAs("targetSpeed")]
|
|
public float TargetSpeed = 1f;
|
|
|
|
[FormerlySerializedAs("baseFrequency")]
|
|
public float BaseFrequency;
|
|
|
|
[FormerlySerializedAs("frequency")]
|
|
public float Frequency;
|
|
|
|
[FormerlySerializedAs("baseAmplitude")]
|
|
public float BaseAmplitude;
|
|
|
|
[FormerlySerializedAs("amplitude")]
|
|
public float Amplitude;
|
|
|
|
[FormerlySerializedAs("fadeFactor")]
|
|
public float FadeFactor;
|
|
|
|
[FormerlySerializedAs("energyBalance")]
|
|
public float EnergyBalance;
|
|
|
|
[FormerlySerializedAs("targetEnergyBalance")]
|
|
public float TargetEnergyBalance;
|
|
|
|
[FormerlySerializedAs("shoaling")]
|
|
public float Shoaling;
|
|
|
|
[FormerlySerializedAs("invkh")]
|
|
public float Invkh = 1f;
|
|
|
|
[FormerlySerializedAs("targetInvKh")]
|
|
public float TargetInvKh = 1f;
|
|
|
|
[FormerlySerializedAs("baseSpeed")]
|
|
public float BaseSpeed;
|
|
|
|
[FormerlySerializedAs("lifetime")]
|
|
public float Lifetime;
|
|
|
|
[FormerlySerializedAs("amplitudeModifiers")]
|
|
public float AmplitudeModifiers;
|
|
|
|
[FormerlySerializedAs("amplitudeModifiers2")]
|
|
public float AmplitudeModifiers2 = 1f;
|
|
|
|
[FormerlySerializedAs("expansionEnergyLoss")]
|
|
public float ExpansionEnergyLoss;
|
|
|
|
[FormerlySerializedAs("isShoreWave")]
|
|
public bool IsShoreWave;
|
|
|
|
[FormerlySerializedAs("isAlive")]
|
|
public bool IsAlive = true;
|
|
|
|
[FormerlySerializedAs("disallowSubdivision")]
|
|
public bool DisallowSubdivision;
|
|
|
|
[FormerlySerializedAs("leftNeighbour")]
|
|
public WaveParticle LeftNeighbour;
|
|
|
|
[FormerlySerializedAs("rightNeighbour")]
|
|
public WaveParticle RightNeighbour;
|
|
|
|
[FormerlySerializedAs("group")]
|
|
public WaveParticlesGroup Group;
|
|
|
|
private Vector2 _Position;
|
|
|
|
private static readonly Stack<WaveParticle> _WaveParticlesCache;
|
|
|
|
private static readonly float[] _AmplitudeFuncPrecomp;
|
|
|
|
private static readonly float[] _FrequencyFuncPrecomp;
|
|
|
|
public Vector2 Position
|
|
{
|
|
get
|
|
{
|
|
return _Position;
|
|
}
|
|
}
|
|
|
|
public Vector4 PackedParticleData
|
|
{
|
|
get
|
|
{
|
|
return new Vector4(Direction.x * 2f * (float)Math.PI / Frequency, Direction.y * 2f * (float)Math.PI / Frequency, Shoaling, Speed);
|
|
}
|
|
}
|
|
|
|
public Vector3 VertexData
|
|
{
|
|
get
|
|
{
|
|
return new Vector3(_Position.x, _Position.y, Amplitude);
|
|
}
|
|
}
|
|
|
|
public Vector3 DebugData
|
|
{
|
|
get
|
|
{
|
|
return new Vector3(Group.Id, 0f, 0f);
|
|
}
|
|
}
|
|
|
|
static WaveParticle()
|
|
{
|
|
_WaveParticlesCache = new Stack<WaveParticle>();
|
|
_AmplitudeFuncPrecomp = new float[2048];
|
|
_FrequencyFuncPrecomp = new float[2048];
|
|
for (int i = 0; i < 2048; i++)
|
|
{
|
|
double num = ((float)i + 0.49f) / 2047f;
|
|
double kh = 4.0 * (1.0 - Math.Pow(1.0 - num, 0.33333333));
|
|
_AmplitudeFuncPrecomp[i] = ComputeAmplitudeAtShore(kh);
|
|
_FrequencyFuncPrecomp[i] = Mathf.Sqrt(1f / ComputeWavelengthAtShore(kh));
|
|
}
|
|
}
|
|
|
|
private WaveParticle(Vector2 position, Vector2 direction, float baseFrequency, float baseAmplitude, float lifetime, bool isShoreWave)
|
|
{
|
|
_Position = position;
|
|
Direction = direction;
|
|
BaseFrequency = baseFrequency;
|
|
BaseAmplitude = baseAmplitude;
|
|
FadeFactor = 0f;
|
|
Frequency = baseFrequency;
|
|
Amplitude = baseAmplitude;
|
|
IsShoreWave = isShoreWave;
|
|
BaseSpeed = 2.5f * Mathf.Sqrt(9.81f / baseFrequency);
|
|
Lifetime = lifetime;
|
|
CostlyUpdate(null, 0.1f);
|
|
}
|
|
|
|
public static WaveParticle Create(Vector3 position, Vector2 direction, float baseFrequency, float baseAmplitude, float lifetime, bool isShoreWave)
|
|
{
|
|
return Create(new Vector2(position.x, position.z), direction, baseFrequency, baseAmplitude, lifetime, isShoreWave);
|
|
}
|
|
|
|
public static WaveParticle Create(Vector2 position, Vector2 direction, float baseFrequency, float baseAmplitude, float lifetime, bool isShoreWave)
|
|
{
|
|
WaveParticle waveParticle;
|
|
if (_WaveParticlesCache.Count != 0)
|
|
{
|
|
waveParticle = _WaveParticlesCache.Pop();
|
|
waveParticle._Position = position;
|
|
waveParticle.Direction = direction;
|
|
waveParticle.BaseFrequency = baseFrequency;
|
|
waveParticle.BaseAmplitude = baseAmplitude;
|
|
waveParticle.FadeFactor = 0f;
|
|
waveParticle.IsShoreWave = isShoreWave;
|
|
waveParticle.BaseSpeed = 2.2f * Mathf.Sqrt(9.81f / baseFrequency);
|
|
waveParticle.Amplitude = baseAmplitude;
|
|
waveParticle.Frequency = baseFrequency;
|
|
waveParticle.TargetSpeed = 1f;
|
|
waveParticle.Invkh = 1f;
|
|
waveParticle.TargetInvKh = 1f;
|
|
waveParticle.EnergyBalance = 0f;
|
|
waveParticle.Shoaling = 0f;
|
|
waveParticle.Speed = 0f;
|
|
waveParticle.TargetEnergyBalance = 0f;
|
|
waveParticle.Lifetime = lifetime;
|
|
waveParticle.AmplitudeModifiers = 0f;
|
|
waveParticle.AmplitudeModifiers2 = 1f;
|
|
waveParticle.ExpansionEnergyLoss = 0f;
|
|
waveParticle.IsAlive = true;
|
|
waveParticle.DisallowSubdivision = false;
|
|
if (waveParticle.LeftNeighbour != null || waveParticle.RightNeighbour != null)
|
|
{
|
|
waveParticle.LeftNeighbour = null;
|
|
waveParticle.RightNeighbour = null;
|
|
}
|
|
waveParticle.CostlyUpdate(null, 0.1f);
|
|
}
|
|
else
|
|
{
|
|
waveParticle = new WaveParticle(position, direction, baseFrequency, baseAmplitude, lifetime, isShoreWave);
|
|
}
|
|
return (waveParticle.BaseAmplitude == 0f) ? null : waveParticle;
|
|
}
|
|
|
|
public void Destroy()
|
|
{
|
|
BaseAmplitude = (Amplitude = 0f);
|
|
IsAlive = false;
|
|
if (LeftNeighbour != null)
|
|
{
|
|
LeftNeighbour.RightNeighbour = RightNeighbour;
|
|
LeftNeighbour.DisallowSubdivision = true;
|
|
}
|
|
if (RightNeighbour != null)
|
|
{
|
|
RightNeighbour.LeftNeighbour = LeftNeighbour;
|
|
RightNeighbour.DisallowSubdivision = true;
|
|
}
|
|
if (Group != null && Group.LeftParticle == this)
|
|
{
|
|
Group.LeftParticle = RightNeighbour;
|
|
}
|
|
LeftNeighbour = null;
|
|
RightNeighbour = null;
|
|
}
|
|
|
|
public void DelayedDestroy()
|
|
{
|
|
BaseAmplitude = (Amplitude = 0f);
|
|
IsAlive = false;
|
|
}
|
|
|
|
public void AddToCache()
|
|
{
|
|
_WaveParticlesCache.Push(this);
|
|
}
|
|
|
|
public WaveParticle Clone(Vector2 position)
|
|
{
|
|
WaveParticle waveParticle = Create(position, Direction, BaseFrequency, BaseAmplitude, Lifetime, IsShoreWave);
|
|
if (waveParticle == null)
|
|
{
|
|
return null;
|
|
}
|
|
waveParticle.Amplitude = Amplitude;
|
|
waveParticle.Frequency = Frequency;
|
|
waveParticle.Speed = Speed;
|
|
waveParticle.TargetSpeed = TargetSpeed;
|
|
waveParticle.EnergyBalance = EnergyBalance;
|
|
waveParticle.Shoaling = Shoaling;
|
|
waveParticle.Group = Group;
|
|
return waveParticle;
|
|
}
|
|
|
|
public void Update(float deltaTime, float step, float invStep)
|
|
{
|
|
if (Lifetime > 0f)
|
|
{
|
|
if (FadeFactor != 1f)
|
|
{
|
|
FadeFactor += deltaTime;
|
|
if (FadeFactor > 1f)
|
|
{
|
|
FadeFactor = 1f;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
FadeFactor -= deltaTime;
|
|
if (FadeFactor <= 0f)
|
|
{
|
|
Destroy();
|
|
return;
|
|
}
|
|
}
|
|
if (TargetEnergyBalance < EnergyBalance)
|
|
{
|
|
float num = step * 0.005f;
|
|
EnergyBalance = EnergyBalance * (1f - num) + TargetEnergyBalance * num;
|
|
}
|
|
else
|
|
{
|
|
float num2 = step * 0.0008f;
|
|
EnergyBalance = EnergyBalance * (1f - num2) + TargetEnergyBalance * num2;
|
|
}
|
|
BaseAmplitude += deltaTime * EnergyBalance;
|
|
BaseAmplitude *= step * ExpansionEnergyLoss + 1f;
|
|
if (BaseAmplitude <= 0.01f)
|
|
{
|
|
Destroy();
|
|
return;
|
|
}
|
|
Speed = invStep * Speed + step * TargetSpeed;
|
|
float num3 = Speed + EnergyBalance * -20f;
|
|
Invkh = invStep * Invkh + step * TargetInvKh;
|
|
int num4 = (int)(2047f * (1f - Invkh * Invkh * Invkh) - 0.49f);
|
|
float num5 = ((num4 < 2048) ? _FrequencyFuncPrecomp[num4] : 1f);
|
|
Frequency = BaseFrequency * num5;
|
|
Amplitude = FadeFactor * BaseAmplitude * ((num4 < 2048) ? _AmplitudeFuncPrecomp[num4] : 1f);
|
|
Shoaling = AmplitudeModifiers * 0.004f * (0f - EnergyBalance) / Amplitude;
|
|
Amplitude *= AmplitudeModifiers;
|
|
float num6 = num3 * deltaTime;
|
|
_Position.x += Direction.x * num6;
|
|
_Position.y += Direction.y * num6;
|
|
}
|
|
|
|
public int CostlyUpdate(WaveParticlesQuadtree quadtree, float deltaTime)
|
|
{
|
|
float num;
|
|
if (Frequency < 0.025f)
|
|
{
|
|
float x = _Position.x + Direction.x / Frequency;
|
|
float z = _Position.y + Direction.y / Frequency;
|
|
num = Mathf.Max(StaticWaterInteraction.GetTotalDepthAt(_Position.x, _Position.y), StaticWaterInteraction.GetTotalDepthAt(x, z));
|
|
}
|
|
else
|
|
{
|
|
num = StaticWaterInteraction.GetTotalDepthAt(_Position.x, _Position.y);
|
|
}
|
|
if (num <= 0.001f)
|
|
{
|
|
Destroy();
|
|
return 0;
|
|
}
|
|
UpdateWaveParameters(deltaTime, num);
|
|
int numSubdivisions = 0;
|
|
if (quadtree != null && !DisallowSubdivision)
|
|
{
|
|
if (LeftNeighbour != null)
|
|
{
|
|
Subdivide(quadtree, LeftNeighbour, this, ref numSubdivisions);
|
|
}
|
|
if (RightNeighbour != null)
|
|
{
|
|
Subdivide(quadtree, this, RightNeighbour, ref numSubdivisions);
|
|
}
|
|
}
|
|
return numSubdivisions;
|
|
}
|
|
|
|
private void UpdateWaveParameters(float deltaTime, float depth)
|
|
{
|
|
Lifetime -= deltaTime;
|
|
TargetInvKh = 1f - 0.25f * BaseFrequency * depth;
|
|
if (TargetInvKh < 0f)
|
|
{
|
|
TargetInvKh = 0f;
|
|
}
|
|
int num = (int)(BaseFrequency * depth * 512f);
|
|
TargetSpeed = BaseSpeed * ((num < 2048) ? FastMath.PositiveTanhSqrtNoZero[num] : 1f);
|
|
if (TargetSpeed < 0.5f)
|
|
{
|
|
TargetSpeed = 0.5f;
|
|
}
|
|
float num2 = 0.135f / Frequency;
|
|
if (num2 < Amplitude)
|
|
{
|
|
TargetEnergyBalance = (0f - Amplitude) * 5f;
|
|
}
|
|
if (LeftNeighbour != null && RightNeighbour != null && !DisallowSubdivision)
|
|
{
|
|
Vector2 vector = new Vector2(RightNeighbour._Position.y - LeftNeighbour._Position.y, LeftNeighbour._Position.x - RightNeighbour._Position.x);
|
|
float num3 = Mathf.Sqrt(vector.x * vector.x + vector.y * vector.y);
|
|
if (num3 > 0.001f)
|
|
{
|
|
if (vector.x * Direction.x + vector.y * Direction.y < 0f)
|
|
{
|
|
num3 = 0f - num3;
|
|
}
|
|
vector.x /= num3;
|
|
vector.y /= num3;
|
|
float num4 = 0.6f * deltaTime;
|
|
if (num4 > 0.6f)
|
|
{
|
|
num4 = 0.6f;
|
|
}
|
|
Direction.x = Direction.x * (1f - num4) + vector.x * num4;
|
|
Direction.y = Direction.y * (1f - num4) + vector.y * num4;
|
|
float num5 = Mathf.Sqrt(Direction.x * Direction.x + Direction.y * Direction.y);
|
|
Direction.x /= num5;
|
|
Direction.y /= num5;
|
|
}
|
|
ExpansionEnergyLoss = -1f + 0.5f * (Direction.x * (LeftNeighbour.Direction.x + RightNeighbour.Direction.x) + Direction.y * (LeftNeighbour.Direction.y + RightNeighbour.Direction.y));
|
|
if (ExpansionEnergyLoss < -1f)
|
|
{
|
|
ExpansionEnergyLoss = -1f;
|
|
}
|
|
if (LeftNeighbour.DisallowSubdivision)
|
|
{
|
|
LeftNeighbour.ExpansionEnergyLoss = ExpansionEnergyLoss;
|
|
}
|
|
if (RightNeighbour.DisallowSubdivision)
|
|
{
|
|
RightNeighbour.ExpansionEnergyLoss = ExpansionEnergyLoss;
|
|
}
|
|
}
|
|
AmplitudeModifiers = 1f;
|
|
if (IsShoreWave)
|
|
{
|
|
int num6 = (int)(depth * 5.12f);
|
|
if (num6 < 2048)
|
|
{
|
|
AmplitudeModifiers *= 1f - FastMath.PositiveTanhSqrtNoZero[num6];
|
|
}
|
|
}
|
|
AmplitudeModifiers *= AmplitudeModifiers2;
|
|
}
|
|
|
|
private void Subdivide(WaveParticlesQuadtree quadtree, WaveParticle left, WaveParticle right, ref int numSubdivisions)
|
|
{
|
|
Vector2 vector = left._Position - right._Position;
|
|
float magnitude = vector.magnitude;
|
|
if (!(magnitude * Frequency > 1f) || !(magnitude > 1f) || quadtree.FreeSpace == 0)
|
|
{
|
|
return;
|
|
}
|
|
WaveParticle waveParticle = Create(right._Position + vector * 0.5f, (left.Direction + right.Direction) * 0.5f, (left.BaseFrequency + right.BaseFrequency) * 0.5f, (left.BaseAmplitude + right.BaseAmplitude) * 0.5f, (left.Lifetime + right.Lifetime) * 0.5f, left.IsShoreWave);
|
|
if (waveParticle != null)
|
|
{
|
|
waveParticle.Group = left.Group;
|
|
waveParticle.Amplitude = (left.Amplitude + right.Amplitude) * 0.5f;
|
|
waveParticle.Frequency = (left.Frequency + right.Frequency) * 0.5f;
|
|
waveParticle.Speed = (left.Speed + right.Speed) * 0.5f;
|
|
waveParticle.TargetSpeed = (left.TargetSpeed + right.TargetSpeed) * 0.5f;
|
|
waveParticle.EnergyBalance = (left.EnergyBalance + right.EnergyBalance) * 0.5f;
|
|
waveParticle.Shoaling = (left.Shoaling + right.Shoaling) * 0.5f;
|
|
waveParticle.TargetInvKh = (left.TargetInvKh + right.TargetInvKh) * 0.5f;
|
|
waveParticle.Lifetime = (left.Lifetime + right.Lifetime) * 0.5f;
|
|
waveParticle.TargetEnergyBalance = (left.TargetEnergyBalance + right.TargetEnergyBalance) * 0.5f;
|
|
waveParticle.AmplitudeModifiers = (left.AmplitudeModifiers + right.AmplitudeModifiers) * 0.5f;
|
|
waveParticle.AmplitudeModifiers2 = (left.AmplitudeModifiers2 + right.AmplitudeModifiers2) * 0.5f;
|
|
waveParticle.Invkh = (left.Invkh + right.Invkh) * 0.5f;
|
|
waveParticle.BaseSpeed = (left.BaseSpeed + right.BaseSpeed) * 0.5f;
|
|
waveParticle.ExpansionEnergyLoss = (left.ExpansionEnergyLoss + right.ExpansionEnergyLoss) * 0.5f;
|
|
waveParticle.Direction = left.Direction;
|
|
if (quadtree.AddElement(waveParticle))
|
|
{
|
|
waveParticle.LeftNeighbour = left;
|
|
waveParticle.RightNeighbour = right;
|
|
left.RightNeighbour = waveParticle;
|
|
right.LeftNeighbour = waveParticle;
|
|
}
|
|
numSubdivisions++;
|
|
}
|
|
}
|
|
|
|
private static float ComputeAmplitudeAtShore(double kh)
|
|
{
|
|
double num = Math.Cosh(kh);
|
|
return (float)Math.Sqrt(2.0 * num * num / (Math.Sinh(2.0 * kh) + 2.0 * kh));
|
|
}
|
|
|
|
private static float ComputeWavelengthAtShore(double kh)
|
|
{
|
|
return (float)Math.Pow(Math.Tanh(Math.Pow(kh * Math.Tanh(kh), 0.75)), 0.666666);
|
|
}
|
|
}
|
|
}
|