升级水插件
This commit is contained in:
@@ -38,12 +38,17 @@ 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)
|
||||
public int Query(int _0, float _1, Vector3[] _2, Vector3[] result0, Vector3[] result1, Vector3[] result2, CollisionLayer _3 = CollisionLayer.Everything, Vector3? _4 = null)
|
||||
{
|
||||
if (result0 != null) System.Array.Fill(result0, Vector3.zero);
|
||||
if (result1 != null) System.Array.Fill(result1, Vector3.up);
|
||||
@@ -51,7 +56,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)
|
||||
public int Query(int _0, float _1, Vector3[] _2, float[] result0, Vector3[] result1, Vector3[] result2, CollisionLayer _3 = CollisionLayer.Everything, Vector3? _4 = null)
|
||||
{
|
||||
if (result0 != null) System.Array.Fill(result0, WaterRenderer.Instance.SeaLevel);
|
||||
if (result1 != null) System.Array.Fill(result1, Vector3.up);
|
||||
@@ -66,12 +71,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)" />
|
||||
int Query(int hash, float minimumLength, Vector3[] points, float[] heights, Vector3[] normals, Vector3[] velocities, CollisionLayer layer = CollisionLayer.Everything);
|
||||
/// <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);
|
||||
|
||||
/// <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)" />
|
||||
/// <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);
|
||||
/// <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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Copyright © 2024 Wave Harmonic. All rights reserved.
|
||||
|
||||
using UnityEngine;
|
||||
using WaveHarmonic.Crest.Internal;
|
||||
|
||||
namespace WaveHarmonic.Crest
|
||||
{
|
||||
@@ -10,10 +11,10 @@ namespace WaveHarmonic.Crest
|
||||
/// </summary>
|
||||
sealed class CollisionQuery : QueryBase, ICollisionProvider
|
||||
{
|
||||
public CollisionQuery(WaterRenderer water) : base(water) { }
|
||||
public CollisionQuery(WaterRenderer water) : base(water.AnimatedWavesLod) { }
|
||||
protected override int Kernel => 0;
|
||||
|
||||
public int Query(int ownerHash, float minSpatialLength, Vector3[] queryPoints, Vector3[] resultDisplacements, Vector3[] resultNormals, Vector3[] resultVelocities, CollisionLayer layer = CollisionLayer.Everything)
|
||||
public int Query(int ownerHash, float minSpatialLength, Vector3[] queryPoints, Vector3[] resultDisplacements, Vector3[] resultNormals, Vector3[] resultVelocities, CollisionLayer layer = CollisionLayer.Everything, Vector3? center = null)
|
||||
{
|
||||
var result = (int)QueryStatus.OK;
|
||||
|
||||
@@ -35,7 +36,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)
|
||||
public int Query(int ownerHash, float minimumSpatialLength, Vector3[] queryPoints, float[] resultHeights, Vector3[] resultNormals, Vector3[] resultVelocities, CollisionLayer layer = CollisionLayer.Everything, Vector3? center = null)
|
||||
{
|
||||
var result = (int)QueryStatus.OK;
|
||||
|
||||
@@ -58,6 +59,102 @@ 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;
|
||||
@@ -69,6 +166,14 @@ 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;
|
||||
@@ -107,12 +212,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)
|
||||
public int Query(int hash, float minimumLength, Vector3[] points, float[] heights, Vector3[] normals, Vector3[] velocities, CollisionLayer layer = CollisionLayer.Everything, Vector3? center = null)
|
||||
{
|
||||
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)
|
||||
public int Query(int hash, float minimumLength, Vector3[] points, Vector3[] displacements, Vector3[] normals, Vector3[] velocities, CollisionLayer layer = CollisionLayer.Everything, Vector3? center = null)
|
||||
{
|
||||
return GetProvider(layer).Query(hash, minimumLength, points, displacements, normals, velocities);
|
||||
}
|
||||
@@ -151,13 +256,55 @@ 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) => (self as CollisionQueryWithPasses)?.UpdateQueries(water, layer);
|
||||
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) => (self as IQueryable)?.UpdateQueries(water);
|
||||
public static void SendReadBack(this ICollisionProvider self, WaterRenderer water, CollisionLayers layer) => (self as CollisionQueryWithPasses)?.SendReadBack(water, layer);
|
||||
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 CleanUp(this ICollisionProvider self) => (self as IQueryable)?.CleanUp();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,9 @@ namespace WaveHarmonic.Crest
|
||||
{
|
||||
distance = -1f;
|
||||
|
||||
Validate(allowMultipleCallsPerFrame: false);
|
||||
var id = GetHashCode();
|
||||
|
||||
Validate(allowMultipleCallsPerFrame: false, id);
|
||||
|
||||
var water = WaterRenderer.Instance;
|
||||
var provider = water == null ? null : water.AnimatedWavesLod.Provider;
|
||||
@@ -70,7 +72,7 @@ namespace WaveHarmonic.Crest
|
||||
_QueryPosition[i] = origin + i * _RayStepSize * direction;
|
||||
}
|
||||
|
||||
var status = provider.Query(GetHashCode(), _MinimumLength, _QueryPosition, _QueryResult, null, null, layer);
|
||||
var status = provider.Query(id, _MinimumLength, _QueryPosition, _QueryResult, null, null, layer);
|
||||
|
||||
if (!provider.RetrieveSucceeded(status))
|
||||
{
|
||||
|
||||
@@ -15,9 +15,14 @@ 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)
|
||||
public int Query(int _0, float _1, Vector3[] _2, Vector3[] result, Vector3? _3 = null)
|
||||
{
|
||||
if (result != null) System.Array.Clear(result, 0, result.Length);
|
||||
return 0;
|
||||
@@ -28,7 +33,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)" />
|
||||
int Query(int hash, float minimumLength, Vector3[] points, Vector3[] results);
|
||||
/// <inheritdoc cref="IQueryProvider.Query(int, float, Vector3[], int, Vector3?)" />
|
||||
int Query(int hash, float minimumLength, Vector3[] points, Vector3[] results, Vector3? center = null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,14 +5,15 @@ using UnityEngine;
|
||||
|
||||
namespace WaveHarmonic.Crest
|
||||
{
|
||||
sealed class DepthQuery : QueryBase, IDepthProvider
|
||||
sealed class DepthQuery : QueryBaseSimple, IDepthProvider
|
||||
{
|
||||
public DepthQuery(WaterRenderer water) : base(water) { }
|
||||
public DepthQuery() : base(WaterRenderer.Instance.DepthLod) { }
|
||||
public DepthQuery(WaterRenderer water) : base(water.DepthLod) { }
|
||||
protected override int Kernel => 2;
|
||||
|
||||
public override int Query(int hash, float minimumSpatialLength, Vector3[] queries, Vector3[] results)
|
||||
public override int Query(int hash, float minimumSpatialLength, Vector3[] queries, Vector3[] results, Vector3? center = null)
|
||||
{
|
||||
var id = base.Query(hash, minimumSpatialLength, queries, results);
|
||||
var id = base.Query(hash, minimumSpatialLength, queries, results, center);
|
||||
|
||||
// Infinity will become NaN. Convert back to infinity.
|
||||
// Negative infinity should not happen.
|
||||
@@ -27,4 +28,9 @@ namespace WaveHarmonic.Crest
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
sealed class DepthQueryPerCamera : QueryPerCameraSimple<DepthQuery>, IDepthProvider
|
||||
{
|
||||
public DepthQueryPerCamera(WaterRenderer water) : base(water) { }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,12 +15,17 @@ 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)
|
||||
public int Query(int _0, float _1, Vector3[] _2, Vector3[] result, Vector3? _3 = null)
|
||||
{
|
||||
if (result != null) System.Array.Clear(result, 0, result.Length);
|
||||
return 0;
|
||||
@@ -31,7 +36,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)" />
|
||||
int Query(int hash, float minimumLength, Vector3[] points, Vector3[] results);
|
||||
/// <inheritdoc cref="IQueryProvider.Query(int, float, Vector3[], int, Vector3?)" />
|
||||
int Query(int hash, float minimumLength, Vector3[] points, Vector3[] results, Vector3? center = null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,9 +6,15 @@ namespace WaveHarmonic.Crest
|
||||
/// <summary>
|
||||
/// Samples horizontal motion of water volume
|
||||
/// </summary>
|
||||
sealed class FlowQuery : QueryBase, IFlowProvider
|
||||
sealed class FlowQuery : QueryBaseSimple, IFlowProvider
|
||||
{
|
||||
public FlowQuery(WaterRenderer water) : base(water) { }
|
||||
public FlowQuery() : base(WaterRenderer.Instance.FlowLod) { }
|
||||
public FlowQuery(WaterRenderer water) : base(water.FlowLod) { }
|
||||
protected override int Kernel => 1;
|
||||
}
|
||||
|
||||
sealed class FlowQueryPerCamera : QueryPerCameraSimple<FlowQuery>, IFlowProvider
|
||||
{
|
||||
public FlowQueryPerCamera(WaterRenderer water) : base(water) { }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ using System.Collections.Generic;
|
||||
using Unity.Collections;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using WaveHarmonic.Crest.Internal;
|
||||
|
||||
namespace WaveHarmonic.Crest
|
||||
{
|
||||
@@ -22,8 +23,9 @@ 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) => 0;
|
||||
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.");
|
||||
|
||||
/// <summary>
|
||||
/// Check if the query results could be retrieved successfully using the return code
|
||||
@@ -45,6 +47,12 @@ 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>
|
||||
@@ -64,6 +72,7 @@ namespace WaveHarmonic.Crest
|
||||
const int k_NormalAdditionalQueryCount = 2;
|
||||
|
||||
readonly WaterRenderer _Water;
|
||||
readonly IQueryableLod<IQueryProvider> _Lod;
|
||||
|
||||
readonly PropertyWrapperCompute _Wrapper;
|
||||
|
||||
@@ -85,8 +94,8 @@ namespace WaveHarmonic.Crest
|
||||
|
||||
internal const int k_DefaultMaximumQueryCount = 4096;
|
||||
|
||||
readonly int _MaximumQueryCount = k_DefaultMaximumQueryCount;
|
||||
readonly Vector3[] _QueryPositionXZ_MinimumGridSize = new Vector3[k_DefaultMaximumQueryCount];
|
||||
readonly int _MaximumQueryCount;
|
||||
readonly Vector3[] _QueryPositionXZ_MinimumGridSize;
|
||||
|
||||
/// <summary>
|
||||
/// Holds information about all query points. Maps from unique hash code to position in point array.
|
||||
@@ -253,17 +262,15 @@ namespace WaveHarmonic.Crest
|
||||
InvalidDtForVelocity = 16,
|
||||
}
|
||||
|
||||
public QueryBase(WaterRenderer water)
|
||||
public QueryBase(IQueryableLod<IQueryProvider> lod)
|
||||
{
|
||||
_Water = water;
|
||||
_Water = lod.Water;
|
||||
_Lod = lod;
|
||||
|
||||
_DataArrivedAction = new(DataArrived);
|
||||
|
||||
if (_MaximumQueryCount != water._AnimatedWavesLod.MaximumQueryCount)
|
||||
{
|
||||
_MaximumQueryCount = water._AnimatedWavesLod.MaximumQueryCount;
|
||||
_QueryPositionXZ_MinimumGridSize = new Vector3[_MaximumQueryCount];
|
||||
}
|
||||
_MaximumQueryCount = lod.MaximumQueryCount;
|
||||
_QueryPositionXZ_MinimumGridSize = new Vector3[_MaximumQueryCount];
|
||||
|
||||
_ComputeBufferQueries = new(_MaximumQueryCount, 12, ComputeBufferType.Default);
|
||||
_ComputeBufferResults = new(_MaximumQueryCount, 12, ComputeBufferType.Default);
|
||||
@@ -277,7 +284,13 @@ namespace WaveHarmonic.Crest
|
||||
Debug.LogError($"Crest: Could not load Query compute shader");
|
||||
return;
|
||||
}
|
||||
_Wrapper = new(water.SimulationBuffer, shader, Kernel);
|
||||
|
||||
_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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -288,7 +301,7 @@ namespace WaveHarmonic.Crest
|
||||
{
|
||||
if (queryPoints.Length + _SegmentRegistrarRingBuffer.Current._QueryCount > _MaximumQueryCount)
|
||||
{
|
||||
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.");
|
||||
LogMaximumQueryCountExceededError();
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -348,7 +361,7 @@ namespace WaveHarmonic.Crest
|
||||
|
||||
if (countPts + segment.x > _QueryPositionXZ_MinimumGridSize.Length)
|
||||
{
|
||||
Debug.LogError("Crest: Too many wave height queries. Increase Max Query Count in the Animated Waves Settings.");
|
||||
LogMaximumQueryCountExceededError();
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -618,7 +631,23 @@ namespace WaveHarmonic.Crest
|
||||
_SegmentRegistrarRingBuffer.ClearAll();
|
||||
}
|
||||
|
||||
public virtual int Query(int ownerHash, float minSpatialLength, Vector3[] queryPoints, Vector3[] results)
|
||||
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)
|
||||
{
|
||||
var result = (int)QueryStatus.OK;
|
||||
|
||||
@@ -634,12 +663,159 @@ namespace WaveHarmonic.Crest
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public int ResultGuidCount => _ResultSegments != null ? _ResultSegments.Count : 0;
|
||||
abstract class QueryPerCamera<T> : IQueryable where T : IQueryable, new()
|
||||
{
|
||||
internal readonly WaterRenderer _Water;
|
||||
internal readonly Dictionary<Camera, T> _Providers = new();
|
||||
|
||||
public int RequestCount => _Requests != null ? _Requests.Count : 0;
|
||||
public QueryPerCamera(WaterRenderer water)
|
||||
{
|
||||
_Water = water;
|
||||
Initialize(water);
|
||||
}
|
||||
|
||||
public int QueryCount => _SegmentRegistrarRingBuffer != null ? _SegmentRegistrarRingBuffer.Current._QueryCount : 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;
|
||||
}
|
||||
}
|
||||
|
||||
static partial class Extensions
|
||||
|
||||
@@ -40,6 +40,13 @@ 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]
|
||||
@@ -174,6 +181,11 @@ 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;
|
||||
@@ -233,6 +245,11 @@ 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))
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// 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.
|
||||
@@ -20,7 +21,7 @@ namespace WaveHarmonic.Crest.Internal
|
||||
private protected readonly Vector3[] _QueryPosition;
|
||||
private protected readonly Vector3[] _QueryResult;
|
||||
|
||||
int _LastFrame = -1;
|
||||
readonly Dictionary<int, int> _LastFrame = new();
|
||||
|
||||
private protected SampleHelper(int queryCount = 1)
|
||||
{
|
||||
@@ -29,15 +30,29 @@ namespace WaveHarmonic.Crest.Internal
|
||||
}
|
||||
|
||||
[System.Diagnostics.Conditional("UNITY_EDITOR")]
|
||||
private protected void Validate(bool allowMultipleCallsPerFrame)
|
||||
private protected void Validate(bool allowMultipleCallsPerFrame, int id)
|
||||
{
|
||||
if (Application.isPlaying && !Time.inFixedTimeStep && !allowMultipleCallsPerFrame && _LastFrame == Time.frameCount)
|
||||
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)
|
||||
{
|
||||
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 = Time.frameCount;
|
||||
_LastFrame[id] = Time.frameCount;
|
||||
}
|
||||
|
||||
// The first method is there just to get inheritdoc to work as it does not like
|
||||
@@ -103,7 +118,7 @@ namespace WaveHarmonic.Crest
|
||||
var isVelocity = (options & QueryOptions.Velocity) == QueryOptions.Velocity;
|
||||
var isNormal = (options & QueryOptions.Normal) == QueryOptions.Normal;
|
||||
|
||||
Validate(allowMultipleCallsPerFrame);
|
||||
Validate(allowMultipleCallsPerFrame, id);
|
||||
|
||||
_QueryPosition[0] = position;
|
||||
|
||||
@@ -115,7 +130,8 @@ namespace WaveHarmonic.Crest
|
||||
_QueryResult,
|
||||
isNormal ? _QueryResultNormal : null,
|
||||
isVelocity ? _QueryResultVelocity : null,
|
||||
layer
|
||||
layer,
|
||||
position
|
||||
);
|
||||
|
||||
if (!provider.RetrieveSucceeded(status))
|
||||
@@ -234,11 +250,13 @@ namespace WaveHarmonic.Crest
|
||||
return false;
|
||||
}
|
||||
|
||||
Validate(false);
|
||||
var id = GetHashCode();
|
||||
|
||||
Validate(false, id);
|
||||
|
||||
_QueryPosition[0] = position;
|
||||
|
||||
var status = flowProvider.Query(GetHashCode(), minimumLength, _QueryPosition, _QueryResult);
|
||||
var status = flowProvider.Query(id, minimumLength, _QueryPosition, _QueryResult, position);
|
||||
|
||||
if (!flowProvider.RetrieveSucceeded(status))
|
||||
{
|
||||
@@ -259,25 +277,25 @@ namespace WaveHarmonic.Crest
|
||||
/// </summary>
|
||||
public sealed class SampleDepthHelper : Internal.SampleHelper
|
||||
{
|
||||
bool Sample(Vector3 position, out Vector2 result)
|
||||
internal bool Sample(int id, Vector3 position, out Vector2 result, bool allowMultipleCallsPerFrame = false)
|
||||
{
|
||||
var water = WaterRenderer.Instance;
|
||||
var depthProvider = water == null ? null : water.DepthLod.Provider;
|
||||
|
||||
if (depthProvider == null)
|
||||
{
|
||||
result = Vector2.zero;
|
||||
result = new(Mathf.Infinity, Mathf.Infinity);
|
||||
return false;
|
||||
}
|
||||
|
||||
Validate(false);
|
||||
Validate(allowMultipleCallsPerFrame, id);
|
||||
|
||||
_QueryPosition[0] = position;
|
||||
|
||||
var status = depthProvider.Query(GetHashCode(), minimumLength: 0, _QueryPosition, _QueryResult);
|
||||
var status = depthProvider.Query(id, minimumLength: 0, _QueryPosition, _QueryResult, position);
|
||||
if (!depthProvider.RetrieveSucceeded(status))
|
||||
{
|
||||
result = Vector2.zero;
|
||||
result = new(Mathf.Infinity, Mathf.Infinity);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -285,13 +303,18 @@ 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" />
|
||||
bool Sample(Vector3 position, out float depth, out float distance)
|
||||
internal bool Sample(Vector3 position, out float depth, out float distance)
|
||||
{
|
||||
var success = Sample(position, out var result);
|
||||
depth = result.x;
|
||||
@@ -312,7 +335,12 @@ namespace WaveHarmonic.Crest
|
||||
/// <inheritdoc cref="Sample(Vector3, out float, out float)"/>
|
||||
public bool SampleDistanceToWaterEdge(Vector3 position, out float distance)
|
||||
{
|
||||
var success = Sample(position, out var result);
|
||||
return SampleDistanceToWaterEdge(GetHashCode(), position, out distance);
|
||||
}
|
||||
|
||||
internal bool SampleDistanceToWaterEdge(int id, Vector3 position, out float distance)
|
||||
{
|
||||
var success = Sample(id, position, out var result);
|
||||
distance = result.y;
|
||||
return success;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user