还原水插件

This commit is contained in:
2026-03-05 00:14:42 +08:00
parent 0de35591e7
commit e82f2ea6b7
270 changed files with 2773 additions and 12445 deletions

View File

@@ -38,17 +38,12 @@ namespace WaveHarmonic.Crest
internal static NoneProvider None { get; } = new();
internal static ICollisionProvider Create(WaterRenderer water)
{
return water.MultipleViewpoints ? new CollisionQueryPerCamera(water) : new CollisionQueryWithPasses(water);
}
/// <summary>
/// Gives a flat, still water.
/// </summary>
internal sealed class NoneProvider : ICollisionProvider
{
public int Query(int _0, float _1, Vector3[] _2, Vector3[] result0, Vector3[] result1, Vector3[] result2, CollisionLayer _3 = CollisionLayer.Everything, Vector3? _4 = null)
public int Query(int _0, float _1, Vector3[] _2, Vector3[] result0, Vector3[] result1, Vector3[] result2, CollisionLayer _3 = CollisionLayer.Everything)
{
if (result0 != null) System.Array.Fill(result0, Vector3.zero);
if (result1 != null) System.Array.Fill(result1, Vector3.up);
@@ -56,7 +51,7 @@ namespace WaveHarmonic.Crest
return 0;
}
public int Query(int _0, float _1, Vector3[] _2, float[] result0, Vector3[] result1, Vector3[] result2, CollisionLayer _3 = CollisionLayer.Everything, Vector3? _4 = null)
public int Query(int _0, float _1, Vector3[] _2, float[] result0, Vector3[] result1, Vector3[] result2, CollisionLayer _3 = CollisionLayer.Everything)
{
if (result0 != null) System.Array.Fill(result0, WaterRenderer.Instance.SeaLevel);
if (result1 != null) System.Array.Fill(result1, Vector3.up);
@@ -71,12 +66,12 @@ namespace WaveHarmonic.Crest
/// <param name="heights">Resulting heights (displacement Y + sea level) at the query positions. Pass null if this information is not required.</param>
/// <param name="normals">Resulting normals at the query positions. Pass null if this information is not required.</param>
/// <param name="velocities">Resulting velocities at the query positions. Pass null if this information is not required.</param>
/// <inheritdoc cref="IQueryProvider.Query(int, float, Vector3[], int, Vector3?)" />
int Query(int hash, float minimumLength, Vector3[] points, float[] heights, Vector3[] normals, Vector3[] velocities, CollisionLayer layer = CollisionLayer.Everything, Vector3? center = null);
/// <inheritdoc cref="IQueryProvider.Query(int, float, Vector3[], int)" />
int Query(int hash, float minimumLength, Vector3[] points, float[] heights, Vector3[] normals, Vector3[] velocities, CollisionLayer layer = CollisionLayer.Everything);
/// <param name="displacements">Resulting displacement vectors at the query positions. Add sea level to Y to get world space height.</param>
/// <inheritdoc cref="IQueryProvider.Query(int, float, Vector3[], int, Vector3?)" />
/// <inheritdoc cref="Query(int, float, Vector3[], float[], Vector3[], Vector3[], CollisionLayer, Vector3?)" />
int Query(int hash, float minimumLength, Vector3[] points, Vector3[] displacements, Vector3[] normals, Vector3[] velocities, CollisionLayer layer = CollisionLayer.Everything, Vector3? center = null);
/// <inheritdoc cref="IQueryProvider.Query(int, float, Vector3[], int)" />
/// <inheritdoc cref="Query(int, float, Vector3[], float[], Vector3[], Vector3[], CollisionLayer)" />
int Query(int hash, float minimumLength, Vector3[] points, Vector3[] displacements, Vector3[] normals, Vector3[] velocities, CollisionLayer layer = CollisionLayer.Everything);
}
}

View File

@@ -2,7 +2,6 @@
// Copyright © 2024 Wave Harmonic. All rights reserved.
using UnityEngine;
using WaveHarmonic.Crest.Internal;
namespace WaveHarmonic.Crest
{
@@ -11,10 +10,10 @@ namespace WaveHarmonic.Crest
/// </summary>
sealed class CollisionQuery : QueryBase, ICollisionProvider
{
public CollisionQuery(WaterRenderer water) : base(water.AnimatedWavesLod) { }
public CollisionQuery(WaterRenderer water) : base(water) { }
protected override int Kernel => 0;
public int Query(int ownerHash, float minSpatialLength, Vector3[] queryPoints, Vector3[] resultDisplacements, Vector3[] resultNormals, Vector3[] resultVelocities, CollisionLayer layer = CollisionLayer.Everything, Vector3? center = null)
public int Query(int ownerHash, float minSpatialLength, Vector3[] queryPoints, Vector3[] resultDisplacements, Vector3[] resultNormals, Vector3[] resultVelocities, CollisionLayer layer = CollisionLayer.Everything)
{
var result = (int)QueryStatus.OK;
@@ -36,7 +35,7 @@ namespace WaveHarmonic.Crest
return result;
}
public int Query(int ownerHash, float minimumSpatialLength, Vector3[] queryPoints, float[] resultHeights, Vector3[] resultNormals, Vector3[] resultVelocities, CollisionLayer layer = CollisionLayer.Everything, Vector3? center = null)
public int Query(int ownerHash, float minimumSpatialLength, Vector3[] queryPoints, float[] resultHeights, Vector3[] resultNormals, Vector3[] resultVelocities, CollisionLayer layer = CollisionLayer.Everything)
{
var result = (int)QueryStatus.OK;
@@ -59,102 +58,6 @@ namespace WaveHarmonic.Crest
}
}
sealed class CollisionQueryPerCamera : QueryPerCamera<CollisionQueryWithPasses>, ICollisionProvider
{
public CollisionQueryPerCamera() : base(WaterRenderer.Instance) { }
public CollisionQueryPerCamera(WaterRenderer water) : base(water) { }
public int Query(int hash, float minimumLength, Vector3[] points, float[] heights, Vector3[] normals, Vector3[] velocities, CollisionLayer layer = CollisionLayer.Everything, Vector3? center = null)
{
if (_Water._InCameraLoop)
{
return _Providers[_Water.CurrentCamera].Query(hash, minimumLength, points, heights, normals, velocities, layer, center);
}
var lastStatus = -1;
var lastDistance = Mathf.Infinity;
var newCenter = FindCenter(points, center);
foreach (var provider in _Providers)
{
var camera = provider.Key;
if (!_Water.ShouldExecuteQueries(camera))
{
continue;
}
var distance = (newCenter - camera.transform.position.XZ()).sqrMagnitude;
if (lastStatus == (int)QueryBase.QueryStatus.OK && lastDistance < distance)
{
continue;
}
var status = provider.Value.Query(hash, minimumLength, points, heights, normals, velocities, layer, center);
if (lastStatus < 0 || status == (int)QueryBase.QueryStatus.OK)
{
lastStatus = status;
lastDistance = distance;
}
}
return lastStatus;
}
public int Query(int hash, float minimumLength, Vector3[] points, Vector3[] displacements, Vector3[] normals, Vector3[] velocities, CollisionLayer layer = CollisionLayer.Everything, Vector3? center = null)
{
if (_Water._InCameraLoop)
{
return _Providers[_Water.CurrentCamera].Query(hash, minimumLength, points, displacements, normals, velocities, layer, center);
}
var lastStatus = -1;
var lastDistance = Mathf.Infinity;
var newCenter = FindCenter(points, center);
foreach (var provider in _Providers)
{
var camera = provider.Key;
if (!_Water.ShouldExecuteQueries(camera))
{
continue;
}
var distance = (newCenter - camera.transform.position.XZ()).sqrMagnitude;
if (lastStatus == (int)QueryBase.QueryStatus.OK && lastDistance < distance)
{
continue;
}
var status = provider.Value.Query(hash, minimumLength, points, displacements, normals, velocities, layer, center);
if (lastStatus < 0 || status == (int)QueryBase.QueryStatus.OK)
{
lastStatus = status;
lastDistance = distance;
}
}
return lastStatus;
}
public void SendReadBack(WaterRenderer water, CollisionLayers layers)
{
_Providers[water.CurrentCamera].SendReadBack(water, layers);
}
public void UpdateQueries(WaterRenderer water, CollisionLayer layer)
{
_Providers[water.CurrentCamera].UpdateQueries(water, layer);
}
}
sealed class CollisionQueryWithPasses : ICollisionProvider, IQueryable
{
readonly CollisionQuery _AnimatedWaves;
@@ -166,14 +69,6 @@ namespace WaveHarmonic.Crest
public int RequestCount => _AnimatedWaves.RequestCount + _DynamicWaves.RequestCount + _Displacement.RequestCount;
public int QueryCount => _AnimatedWaves.QueryCount + _DynamicWaves.QueryCount + _Displacement.QueryCount;
public CollisionQueryWithPasses()
{
_Water = WaterRenderer.Instance;
_AnimatedWaves = new(_Water);
_DynamicWaves = new(_Water);
_Displacement = new(_Water);
}
public CollisionQueryWithPasses(WaterRenderer water)
{
_Water = water;
@@ -212,12 +107,12 @@ namespace WaveHarmonic.Crest
return _AnimatedWaves;
}
public int Query(int hash, float minimumLength, Vector3[] points, float[] heights, Vector3[] normals, Vector3[] velocities, CollisionLayer layer = CollisionLayer.Everything, Vector3? center = null)
public int Query(int hash, float minimumLength, Vector3[] points, float[] heights, Vector3[] normals, Vector3[] velocities, CollisionLayer layer = CollisionLayer.Everything)
{
return GetProvider(layer).Query(hash, minimumLength, points, heights, normals, velocities);
}
public int Query(int hash, float minimumLength, Vector3[] points, Vector3[] displacements, Vector3[] normals, Vector3[] velocities, CollisionLayer layer = CollisionLayer.Everything, Vector3? center = null)
public int Query(int hash, float minimumLength, Vector3[] points, Vector3[] displacements, Vector3[] normals, Vector3[] velocities, CollisionLayer layer = CollisionLayer.Everything)
{
return GetProvider(layer).Query(hash, minimumLength, points, displacements, normals, velocities);
}
@@ -256,55 +151,13 @@ namespace WaveHarmonic.Crest
_DynamicWaves.CleanUp();
_Displacement.CleanUp();
}
public void Initialize(WaterRenderer water)
{
}
}
// These are required because of collision layer.
static partial class Extensions
{
public static void UpdateQueries(this ICollisionProvider self, WaterRenderer water, CollisionLayer layer)
{
if (self is CollisionQueryPerCamera a)
{
a.UpdateQueries(water, layer);
}
else if (self is CollisionQueryWithPasses b)
{
b.UpdateQueries(water, layer);
}
else if (self is ICollisionProvider.NoneProvider c)
{
}
else
{
Debug.LogError("Crest: no valid query provider. Report this to developers!");
}
}
public static void UpdateQueries(this ICollisionProvider self, WaterRenderer water, CollisionLayer layer) => (self as CollisionQueryWithPasses)?.UpdateQueries(water, layer);
public static void UpdateQueries(this ICollisionProvider self, WaterRenderer water) => (self as IQueryable)?.UpdateQueries(water);
public static void SendReadBack(this ICollisionProvider self, WaterRenderer water, CollisionLayers layer)
{
if (self is CollisionQueryPerCamera a)
{
a.SendReadBack(water, layer);
}
else if (self is CollisionQueryWithPasses b)
{
b.SendReadBack(water, layer);
}
else if (self is ICollisionProvider.NoneProvider c)
{
}
else
{
Debug.LogError("Crest: no valid query provider. Report this to developers!");
}
}
public static void SendReadBack(this ICollisionProvider self, WaterRenderer water, CollisionLayers layer) => (self as CollisionQueryWithPasses)?.SendReadBack(water, layer);
public static void CleanUp(this ICollisionProvider self) => (self as IQueryable)?.CleanUp();
}
}

View File

@@ -59,9 +59,7 @@ namespace WaveHarmonic.Crest
{
distance = -1f;
var id = GetHashCode();
Validate(allowMultipleCallsPerFrame: false, id);
Validate(allowMultipleCallsPerFrame: false);
var water = WaterRenderer.Instance;
var provider = water == null ? null : water.AnimatedWavesLod.Provider;
@@ -72,7 +70,7 @@ namespace WaveHarmonic.Crest
_QueryPosition[i] = origin + i * _RayStepSize * direction;
}
var status = provider.Query(id, _MinimumLength, _QueryPosition, _QueryResult, null, null, layer);
var status = provider.Query(GetHashCode(), _MinimumLength, _QueryPosition, _QueryResult, null, null, layer);
if (!provider.RetrieveSucceeded(status))
{

View File

@@ -15,14 +15,9 @@ namespace WaveHarmonic.Crest
{
internal static NoneProvider None { get; } = new();
internal static IDepthProvider Create(WaterRenderer water)
{
return water.MultipleViewpoints ? new DepthQueryPerCamera(water) : new DepthQuery(water);
}
internal sealed class NoneProvider : IDepthProvider
{
public int Query(int _0, float _1, Vector3[] _2, Vector3[] result, Vector3? _3 = null)
public int Query(int _0, float _1, Vector3[] _2, Vector3[] result)
{
if (result != null) System.Array.Clear(result, 0, result.Length);
return 0;
@@ -33,7 +28,7 @@ namespace WaveHarmonic.Crest
/// Query water depth data at a set of points.
/// </summary>
/// <param name="results">Water depth and distance to shoreline (XY respectively). Both are signed.</param>
/// <inheritdoc cref="IQueryProvider.Query(int, float, Vector3[], int, Vector3?)" />
int Query(int hash, float minimumLength, Vector3[] points, Vector3[] results, Vector3? center = null);
/// <inheritdoc cref="IQueryProvider.Query(int, float, Vector3[], int)" />
int Query(int hash, float minimumLength, Vector3[] points, Vector3[] results);
}
}

View File

@@ -5,15 +5,14 @@ using UnityEngine;
namespace WaveHarmonic.Crest
{
sealed class DepthQuery : QueryBaseSimple, IDepthProvider
sealed class DepthQuery : QueryBase, IDepthProvider
{
public DepthQuery() : base(WaterRenderer.Instance.DepthLod) { }
public DepthQuery(WaterRenderer water) : base(water.DepthLod) { }
public DepthQuery(WaterRenderer water) : base(water) { }
protected override int Kernel => 2;
public override int Query(int hash, float minimumSpatialLength, Vector3[] queries, Vector3[] results, Vector3? center = null)
public override int Query(int hash, float minimumSpatialLength, Vector3[] queries, Vector3[] results)
{
var id = base.Query(hash, minimumSpatialLength, queries, results, center);
var id = base.Query(hash, minimumSpatialLength, queries, results);
// Infinity will become NaN. Convert back to infinity.
// Negative infinity should not happen.
@@ -28,9 +27,4 @@ namespace WaveHarmonic.Crest
return id;
}
}
sealed class DepthQueryPerCamera : QueryPerCameraSimple<DepthQuery>, IDepthProvider
{
public DepthQueryPerCamera(WaterRenderer water) : base(water) { }
}
}

View File

@@ -15,17 +15,12 @@ namespace WaveHarmonic.Crest
{
internal static NoneProvider None { get; } = new();
internal static IFlowProvider Create(WaterRenderer water)
{
return water.MultipleViewpoints ? new FlowQueryPerCamera(water) : new FlowQuery(water);
}
/// <summary>
/// Gives a stationary water (no horizontal flow).
/// </summary>
internal sealed class NoneProvider : IFlowProvider
{
public int Query(int _0, float _1, Vector3[] _2, Vector3[] result, Vector3? _3 = null)
public int Query(int _0, float _1, Vector3[] _2, Vector3[] result)
{
if (result != null) System.Array.Clear(result, 0, result.Length);
return 0;
@@ -36,7 +31,7 @@ namespace WaveHarmonic.Crest
/// Query water flow data (horizontal motion) at a set of points.
/// </summary>
/// <param name="results">Water surface flow velocities at the query positions.</param>
/// <inheritdoc cref="IQueryProvider.Query(int, float, Vector3[], int, Vector3?)" />
int Query(int hash, float minimumLength, Vector3[] points, Vector3[] results, Vector3? center = null);
/// <inheritdoc cref="IQueryProvider.Query(int, float, Vector3[], int)" />
int Query(int hash, float minimumLength, Vector3[] points, Vector3[] results);
}
}

View File

@@ -6,15 +6,9 @@ namespace WaveHarmonic.Crest
/// <summary>
/// Samples horizontal motion of water volume
/// </summary>
sealed class FlowQuery : QueryBaseSimple, IFlowProvider
sealed class FlowQuery : QueryBase, IFlowProvider
{
public FlowQuery() : base(WaterRenderer.Instance.FlowLod) { }
public FlowQuery(WaterRenderer water) : base(water.FlowLod) { }
public FlowQuery(WaterRenderer water) : base(water) { }
protected override int Kernel => 1;
}
sealed class FlowQueryPerCamera : QueryPerCameraSimple<FlowQuery>, IFlowProvider
{
public FlowQueryPerCamera(WaterRenderer water) : base(water) { }
}
}

View File

@@ -9,7 +9,6 @@ using System.Collections.Generic;
using Unity.Collections;
using UnityEngine;
using UnityEngine.Rendering;
using WaveHarmonic.Crest.Internal;
namespace WaveHarmonic.Crest
{
@@ -23,9 +22,8 @@ namespace WaveHarmonic.Crest
/// <param name="minimumLength">The minimum spatial length of the object, such as the width of a boat. Useful for filtering out detail when not needed. Set to zero to get full available detail.</param>
/// <param name="points">The world space points that will be queried.</param>
/// <param name="layer">The layer this query targets.</param>
/// <param name="center">The center of all the query positions. Used to choose the closest query provider.</param>
/// <returns>The status of the query.</returns>
internal static int Query(int hash, float minimumLength, Vector3[] points, int layer, Vector3? center) => throw new System.NotImplementedException("Crest: this method is for documentation reuse only. Do not invoke.");
internal static int Query(int hash, float minimumLength, Vector3[] points, int layer) => 0;
/// <summary>
/// Check if the query results could be retrieved successfully using the return code
@@ -47,12 +45,6 @@ namespace WaveHarmonic.Crest
void UpdateQueries(WaterRenderer water);
void SendReadBack(WaterRenderer water);
void CleanUp();
void Initialize(WaterRenderer water);
}
interface IQueryableSimple : IQueryable
{
int Query(int hash, float minimumLength, Vector3[] queries, Vector3[] results, Vector3? center);
}
/// <summary>
@@ -72,7 +64,6 @@ namespace WaveHarmonic.Crest
const int k_NormalAdditionalQueryCount = 2;
readonly WaterRenderer _Water;
readonly IQueryableLod<IQueryProvider> _Lod;
readonly PropertyWrapperCompute _Wrapper;
@@ -94,8 +85,8 @@ namespace WaveHarmonic.Crest
internal const int k_DefaultMaximumQueryCount = 4096;
readonly int _MaximumQueryCount;
readonly Vector3[] _QueryPositionXZ_MinimumGridSize;
readonly int _MaximumQueryCount = k_DefaultMaximumQueryCount;
readonly Vector3[] _QueryPositionXZ_MinimumGridSize = new Vector3[k_DefaultMaximumQueryCount];
/// <summary>
/// Holds information about all query points. Maps from unique hash code to position in point array.
@@ -262,15 +253,17 @@ namespace WaveHarmonic.Crest
InvalidDtForVelocity = 16,
}
public QueryBase(IQueryableLod<IQueryProvider> lod)
public QueryBase(WaterRenderer water)
{
_Water = lod.Water;
_Lod = lod;
_Water = water;
_DataArrivedAction = new(DataArrived);
_MaximumQueryCount = lod.MaximumQueryCount;
_QueryPositionXZ_MinimumGridSize = new Vector3[_MaximumQueryCount];
if (_MaximumQueryCount != water._AnimatedWavesLod.MaximumQueryCount)
{
_MaximumQueryCount = water._AnimatedWavesLod.MaximumQueryCount;
_QueryPositionXZ_MinimumGridSize = new Vector3[_MaximumQueryCount];
}
_ComputeBufferQueries = new(_MaximumQueryCount, 12, ComputeBufferType.Default);
_ComputeBufferResults = new(_MaximumQueryCount, 12, ComputeBufferType.Default);
@@ -284,13 +277,7 @@ namespace WaveHarmonic.Crest
Debug.LogError($"Crest: Could not load Query compute shader");
return;
}
_Wrapper = new(_Water.SimulationBuffer, shader, Kernel);
}
void LogMaximumQueryCountExceededError()
{
Debug.LogError($"Crest: Maximum query count ({_MaximumQueryCount}) exceeded, increase the <i>{nameof(WaterRenderer)} > Simulations > {_Lod.Name} > {nameof(_Lod.MaximumQueryCount)}</i> to support a higher number of queries.", _Water);
_Wrapper = new(water.SimulationBuffer, shader, Kernel);
}
/// <summary>
@@ -301,7 +288,7 @@ namespace WaveHarmonic.Crest
{
if (queryPoints.Length + _SegmentRegistrarRingBuffer.Current._QueryCount > _MaximumQueryCount)
{
LogMaximumQueryCountExceededError();
Debug.LogError($"Crest: Max query count ({_MaximumQueryCount}) exceeded, increase the max query count in the Animated Waves Settings to support a higher number of queries.");
return false;
}
@@ -361,7 +348,7 @@ namespace WaveHarmonic.Crest
if (countPts + segment.x > _QueryPositionXZ_MinimumGridSize.Length)
{
LogMaximumQueryCountExceededError();
Debug.LogError("Crest: Too many wave height queries. Increase Max Query Count in the Animated Waves Settings.");
return false;
}
@@ -631,23 +618,7 @@ namespace WaveHarmonic.Crest
_SegmentRegistrarRingBuffer.ClearAll();
}
public virtual void Initialize(WaterRenderer water)
{
}
public int ResultGuidCount => _ResultSegments != null ? _ResultSegments.Count : 0;
public int RequestCount => _Requests != null ? _Requests.Count : 0;
public int QueryCount => _SegmentRegistrarRingBuffer != null ? _SegmentRegistrarRingBuffer.Current._QueryCount : 0;
}
abstract class QueryBaseSimple : QueryBase, IQueryableSimple
{
protected QueryBaseSimple(IQueryableLod<IQueryProvider> lod) : base(lod) { }
public virtual int Query(int ownerHash, float minSpatialLength, Vector3[] queryPoints, Vector3[] results, Vector3? center)
public virtual int Query(int ownerHash, float minSpatialLength, Vector3[] queryPoints, Vector3[] results)
{
var result = (int)QueryStatus.OK;
@@ -663,159 +634,12 @@ namespace WaveHarmonic.Crest
return result;
}
}
abstract class QueryPerCamera<T> : IQueryable where T : IQueryable, new()
{
internal readonly WaterRenderer _Water;
internal readonly Dictionary<Camera, T> _Providers = new();
public int ResultGuidCount => _ResultSegments != null ? _ResultSegments.Count : 0;
public QueryPerCamera(WaterRenderer water)
{
_Water = water;
Initialize(water);
}
public int RequestCount => _Requests != null ? _Requests.Count : 0;
public int ResultGuidCount
{
get
{
var total = 0;
foreach (var (camera, provider) in _Providers)
{
if (_Water.ShouldExecuteQueries(camera)) total += provider.ResultGuidCount;
}
return total;
}
}
public int RequestCount
{
get
{
var total = 0;
foreach (var (camera, provider) in _Providers)
{
if (_Water.ShouldExecuteQueries(camera)) total += provider.RequestCount;
}
return total;
}
}
public int QueryCount
{
get
{
var total = 0;
foreach (var (camera, provider) in _Providers)
{
if (_Water.ShouldExecuteQueries(camera)) total += provider.QueryCount;
}
return total;
}
}
public void CleanUp()
{
foreach (var provider in _Providers.Values)
{
provider?.CleanUp();
}
}
public void Initialize(WaterRenderer water)
{
var camera = water.CurrentCamera;
if (camera == null)
{
camera = water.Viewer;
}
if (camera == null)
{
return;
}
if (!_Providers.ContainsKey(camera))
{
// Cannot use parameters. We could use System.Activator.CreateInstance to get
// around that, but instead we just use WaterRenderer.Instance.
_Providers.Add(camera, new());
}
}
public void SendReadBack(WaterRenderer water)
{
_Providers[water.CurrentCamera].SendReadBack(water);
}
public void UpdateQueries(WaterRenderer water)
{
_Providers[water.CurrentCamera].UpdateQueries(water);
}
public Vector2 FindCenter(Vector3[] queries, Vector3? center)
{
if (center != null)
{
return ((Vector3)center).XZ();
}
// Calculate center if none provided.
var sum = Vector2.zero;
foreach (var point in queries)
{
sum += point.XZ();
}
return new(sum.x / queries.Length, sum.y / queries.Length);
}
}
abstract class QueryPerCameraSimple<T> : QueryPerCamera<T>, IQueryableSimple where T : IQueryableSimple, new()
{
protected QueryPerCameraSimple(WaterRenderer water) : base(water) { }
public int Query(int id, float length, Vector3[] queries, Vector3[] results, Vector3? center = null)
{
if (_Water._InCameraLoop)
{
return _Providers[_Water.CurrentCamera].Query(id, length, queries, results, center);
}
var lastStatus = -1;
var lastDistance = Mathf.Infinity;
var newCenter = FindCenter(queries, center);
foreach (var provider in _Providers)
{
var camera = provider.Key;
if (!_Water.ShouldExecuteQueries(camera))
{
continue;
}
var distance = (newCenter - camera.transform.position.XZ()).sqrMagnitude;
if (lastStatus == (int)QueryBase.QueryStatus.OK && lastDistance < distance)
{
continue;
}
var status = provider.Value.Query(id, length, queries, results, center);
if (lastStatus < 0 || status == (int)QueryBase.QueryStatus.OK)
{
lastStatus = status;
lastDistance = distance;
}
}
return lastStatus;
}
public int QueryCount => _SegmentRegistrarRingBuffer != null ? _SegmentRegistrarRingBuffer.Current._QueryCount : 0;
}
static partial class Extensions

View File

@@ -40,13 +40,6 @@ namespace WaveHarmonic.Crest
[@DecoratedField, SerializeField]
QuerySource _Source;
[Tooltip("The viewer as the source of the queries.\n\nOnly needs to be set if using multiple viewpoints on the Water Renderer.")]
[@Predicated(nameof(_Source), inverted: false, nameof(QuerySource.Viewer), hide: true)]
[@GenerateAPI]
[@DecoratedField]
[SerializeField]
Camera _Viewer;
[Tooltip(ICollisionProvider.k_LayerTooltip)]
[@GenerateAPI]
[@DecoratedField, SerializeField]
@@ -181,11 +174,6 @@ namespace WaveHarmonic.Crest
var distance = water.ViewerHeightAboveWater;
if (water.MultipleViewpoints && (_Viewer == null || !water.GetViewerHeightAboveWater(_Viewer, out distance)))
{
return;
}
if (_Source == QuerySource.Transform)
{
if (!_SampleHeightHelper.SampleHeight(transform.position, out var height, minimumLength: 2f * _MinimumWavelength, _Layer)) return;
@@ -245,11 +233,6 @@ namespace WaveHarmonic.Crest
var distance = water.ViewerDistanceToShoreline;
if (water.MultipleViewpoints && (_Viewer == null || !water.GetViewerDistanceToShoreline(_Viewer, out distance)))
{
return;
}
if (_Source == QuerySource.Transform)
{
if (!_SampleDepthHelper.SampleDistanceToWaterEdge(transform.position, out distance))

View File

@@ -1,7 +1,6 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
using System.Collections.Generic;
using UnityEngine;
// Linter does not support mixing inheritdoc plus defining own parameters.
@@ -21,7 +20,7 @@ namespace WaveHarmonic.Crest.Internal
private protected readonly Vector3[] _QueryPosition;
private protected readonly Vector3[] _QueryResult;
readonly Dictionary<int, int> _LastFrame = new();
int _LastFrame = -1;
private protected SampleHelper(int queryCount = 1)
{
@@ -30,29 +29,15 @@ namespace WaveHarmonic.Crest.Internal
}
[System.Diagnostics.Conditional("UNITY_EDITOR")]
private protected void Validate(bool allowMultipleCallsPerFrame, int id)
private protected void Validate(bool allowMultipleCallsPerFrame)
{
if (!_LastFrame.ContainsKey(id))
{
_LastFrame.Add(id, -1);
}
#if UNITY_EDITOR
// Prevent false positives spamming the console.
if (!UnityEditor.EditorApplication.isFocused || (Application.isPlaying && UnityEditor.EditorApplication.isPaused))
{
_LastFrame[id] = Time.frameCount;
return;
}
#endif
if (!Time.inFixedTimeStep && !allowMultipleCallsPerFrame && _LastFrame[id] == Time.frameCount)
if (Application.isPlaying && !Time.inFixedTimeStep && !allowMultipleCallsPerFrame && _LastFrame == Time.frameCount)
{
var type = GetType().Name;
Debug.LogWarning($"Crest: {type} sample called multiple times in one frame which is not expected. Each {type} object services a single sample per frame. To perform multiple queries, create multiple {type} objects or use the query provider API directly.");
}
_LastFrame[id] = Time.frameCount;
_LastFrame = Time.frameCount;
}
// The first method is there just to get inheritdoc to work as it does not like
@@ -118,7 +103,7 @@ namespace WaveHarmonic.Crest
var isVelocity = (options & QueryOptions.Velocity) == QueryOptions.Velocity;
var isNormal = (options & QueryOptions.Normal) == QueryOptions.Normal;
Validate(allowMultipleCallsPerFrame, id);
Validate(allowMultipleCallsPerFrame);
_QueryPosition[0] = position;
@@ -130,8 +115,7 @@ namespace WaveHarmonic.Crest
_QueryResult,
isNormal ? _QueryResultNormal : null,
isVelocity ? _QueryResultVelocity : null,
layer,
position
layer
);
if (!provider.RetrieveSucceeded(status))
@@ -250,13 +234,11 @@ namespace WaveHarmonic.Crest
return false;
}
var id = GetHashCode();
Validate(false, id);
Validate(false);
_QueryPosition[0] = position;
var status = flowProvider.Query(id, minimumLength, _QueryPosition, _QueryResult, position);
var status = flowProvider.Query(GetHashCode(), minimumLength, _QueryPosition, _QueryResult);
if (!flowProvider.RetrieveSucceeded(status))
{
@@ -277,25 +259,25 @@ namespace WaveHarmonic.Crest
/// </summary>
public sealed class SampleDepthHelper : Internal.SampleHelper
{
internal bool Sample(int id, Vector3 position, out Vector2 result, bool allowMultipleCallsPerFrame = false)
bool Sample(Vector3 position, out Vector2 result)
{
var water = WaterRenderer.Instance;
var depthProvider = water == null ? null : water.DepthLod.Provider;
if (depthProvider == null)
{
result = new(Mathf.Infinity, Mathf.Infinity);
result = Vector2.zero;
return false;
}
Validate(allowMultipleCallsPerFrame, id);
Validate(false);
_QueryPosition[0] = position;
var status = depthProvider.Query(id, minimumLength: 0, _QueryPosition, _QueryResult, position);
var status = depthProvider.Query(GetHashCode(), minimumLength: 0, _QueryPosition, _QueryResult);
if (!depthProvider.RetrieveSucceeded(status))
{
result = new(Mathf.Infinity, Mathf.Infinity);
result = Vector2.zero;
return false;
}
@@ -303,18 +285,13 @@ namespace WaveHarmonic.Crest
return true;
}
bool Sample(Vector3 position, out Vector2 result)
{
return Sample(GetHashCode(), position, out result);
}
/// <summary>
/// Sample both the water depth and water edge distance.
/// </summary>
/// <param name="depth">Filled by the water depth at the query position.</param>
/// <param name="distance">Filled by the distance to water edge at the query position.</param>
/// <inheritdoc cref="Internal.SampleHelper.Sample" />
internal bool Sample(Vector3 position, out float depth, out float distance)
bool Sample(Vector3 position, out float depth, out float distance)
{
var success = Sample(position, out var result);
depth = result.x;
@@ -335,12 +312,7 @@ namespace WaveHarmonic.Crest
/// <inheritdoc cref="Sample(Vector3, out float, out float)"/>
public bool SampleDistanceToWaterEdge(Vector3 position, out float distance)
{
return SampleDistanceToWaterEdge(GetHashCode(), position, out distance);
}
internal bool SampleDistanceToWaterEdge(int id, Vector3 position, out float distance)
{
var success = Sample(id, position, out var result);
var success = Sample(position, out var result);
distance = result.y;
return success;
}