(Update) Clean Projet

This commit is contained in:
2026-06-24 14:38:51 +02:00
parent 48588ccfed
commit ef01552268
2629 changed files with 1523 additions and 430219 deletions
@@ -0,0 +1,267 @@
// Stylized Water 3 by Staggart Creations (http://staggart.xyz)
// COPYRIGHT PROTECTED UNDER THE UNITY ASSET STORE EULA (https://unity.com/legal/as-terms)
// • Copying or referencing source code for the production of new asset store, or public, content is strictly prohibited!
// • Uploading this file to a public repository will subject it to an automated DMCA takedown request.
using System;
using System.Collections.Generic;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using UnityEngine;
using UnityEngine.Profiling;
using UnityEngine.Rendering;
using Unity.Mathematics;
namespace StylizedWater3
{
public static partial class HeightQuerySystem
{
public class Query
{
/// <summary>
/// Maximum allowed number of sample positions allowed within a single height query
/// A value too high may result in decreased performance, as the data payload needed to be retrieved from the GPU becomes too large.
/// </summary>
public const int MAX_SIZE = 128;
//Input
private NativeArray<float3> samplePositions;
public readonly GraphicsBuffer inputPositionBuffer;
//Output
public NativeArray<float> outputOffsets;
public readonly GraphicsBuffer outputOffsetsBuffer;
private int currentBufferIndex = 0;
//Need to keep a buffer alive so it can be used the next frame
private readonly NativeArray<float>[] readbackBuffers = new NativeArray<float>[2];
//Every request is issued with an ID
public readonly Dictionary<int, AsyncRequest> requests = new Dictionary<int, AsyncRequest>();
//Index of the last request.
public int sampleCount;
private bool hasPendingRequest;
public List<int> availableIndices;
public Query()
{
RecreateIndexPool();
//CPU input
samplePositions = new NativeArray<float3>(MAX_SIZE, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
//GPU input
inputPositionBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, MAX_SIZE, 3 * sizeof(float));
inputPositionBuffer.name = "Water Height Query: Sample Positions";
//GPU output
outputOffsetsBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, MAX_SIZE, sizeof(float));
inputPositionBuffer.name = "Water Height Query: Sampled Heights";
}
public int GetNextAvailableIndex()
{
var index = availableIndices[0];
//Remove as it is now no longer available
availableIndices.Remove(index);
return index;
}
public void ReleaseIndex(int index)
{
availableIndices.Add(index);
//Sorting optimizes the array occupancy
availableIndices.Sort();
}
private void RecreateIndexPool()
{
//Populate pool of available indices
availableIndices = new List<int>();
for (int i = 0; i < MAX_SIZE; i++)
{
availableIndices.Add(i);
}
}
//Combine all the positions from the requests into one list
private void PopulateSampleList()
{
//Copy all the sample positions in the queue to the summed array
foreach (KeyValuePair<int, AsyncRequest> request in requests)
{
for (int i = 0; i < request.Value.indices.Count; i++)
{
int index = request.Value.indices[i];
//NOTE: Setting items of a NativeArray is slow
samplePositions[index] = request.Value.sampler.positions[i];
}
}
//Note: unused indices are not initialized.
//Compute shader is configured not to sample them. Doing so otherwise causes VRAM corruption on Metal.
}
//Dispatch the compute shader. The "offsets" array will be populated based on the current GPU buffer contents.
public void Dispatch(ComputeCommandBuffer cmd, ComputeShader cs, int kernel)
{
Profiler.BeginSample($"{PROFILER_PREFIX} Setup and dispatch");
PopulateSampleList();
cmd.SetBufferData(inputPositionBuffer, samplePositions);
cmd.SetComputeIntParam(cs, "sampleCount", sampleCount);
cmd.SetComputeBufferParam(cs, kernel, "positions", inputPositionBuffer);
//Output
cmd.SetComputeBufferParam(cs, kernel, "offsets", outputOffsetsBuffer);
cmd.DispatchCompute(cs, kernel, RenderPass.THREAD_GROUPS, 1, 1);
Profiler.EndSample();
}
void ValidateNativeBuffer(ref NativeArray<float> buffer)
{
if (!buffer.IsCreated || buffer.Length != MAX_SIZE)
{
if (buffer.IsCreated) buffer.Dispose();
buffer = new NativeArray<float>(MAX_SIZE, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
}
}
public void Readback(UnsafeCommandBuffer cmd)
{
//Array was disposed of after readback request. Forced to recreate it
//https://forum.unity.com/threads/asyncgpureadback-requestintonativearray-causes-invalidoperationexception-on-nativearray.1011955
//AllocateReadbackBuffer();
//Query may have been disposed, but an async readback was still pending...
if (hasPendingRequest)
{
return;
}
hasPendingRequest = true;
//After the readback request is complete Unity will dispose of this array automatically. Possibly when 'GetData' is called.
//Hence a swap-buffer method is employed
ValidateNativeBuffer(ref readbackBuffers[0]);
ValidateNativeBuffer(ref readbackBuffers[1]);
NativeArray<float> nextBuffer = readbackBuffers[NextBufferIndex()];
#if UNITY_EDITOR
//Unity dev: Remove when bug is fixed
AtomicSafetyHandle ash = NativeArrayUnsafeUtility.GetAtomicSafetyHandle(nextBuffer);
AtomicSafetyHandle.CheckReadAndThrow(ash);
AtomicSafetyHandle.CheckDeallocateAndThrow(ash);
#endif
cmd.RequestAsyncReadbackIntoNativeArray(ref nextBuffer, outputOffsetsBuffer, MAX_SIZE * outputOffsetsBuffer.stride, 0, OnCompleteReadback);
}
private void SwapCurrentBuffer()
{
currentBufferIndex = (currentBufferIndex + 1) % 2;
}
int NextBufferIndex()
{
//return 0;
return (currentBufferIndex + 1) % 2;
}
private void OnCompleteReadback(AsyncGPUReadbackRequest asyncGPUReadbackRequest)
{
if (asyncGPUReadbackRequest.hasError)
{
throw new Exception("Error reading GPU water height with AsyncGPUReadbackRequest.");
}
Profiler.BeginSample($"{PROFILER_PREFIX} Readback data");
outputOffsets = asyncGPUReadbackRequest.GetData<float>();
foreach (KeyValuePair<int, AsyncRequest> m_request in requests)
{
AsyncRequest request = m_request.Value;
int queryLength = request.indices.Count;
for (int i = 0; i < queryLength; i++)
{
//List of indices this request occupies in the query
int index = request.indices[i];
var waterHeight = outputOffsets[index];
//Height value equals a void do not assign it
if (request.invalidateMisses && EqualsVoid(waterHeight))
{
//Debug.Log($"Height request for {request.label} at index {index} was invalidated (value={waterHeight}).");
continue;
}
request.sampler.heightValues[i] = waterHeight;
}
//Issue a callback event for the external scripts that issued the request
request.InvokeCallback();
}
outputOffsets.Dispose();
SwapCurrentBuffer();
hasPendingRequest = false;
Profiler.EndSample();
}
public void Clear()
{
foreach (KeyValuePair<int, AsyncRequest> request in requests)
{
request.Value.sampler.Dispose();
}
requests.Clear();
RecreateIndexPool();
}
public void Dispose()
{
Clear();
//Remove itself from the list of queries
queries.Remove(this);
QueryCount--;
//Wait before we freeing the resources
if (hasPendingRequest) AsyncGPUReadback.WaitAllRequests();
samplePositions.Dispose();
inputPositionBuffer.Dispose();
//Dispose any allocated arrays
outputOffsetsBuffer.Dispose();
if (readbackBuffers[0].IsCreated)
readbackBuffers[0].Dispose();
if (readbackBuffers[1].IsCreated)
readbackBuffers[1].Dispose();
//If the query is being disposed, whilst no readback request was pending then this array would not be allocated
if(outputOffsets.IsCreated) outputOffsets.Dispose();
}
}
}
}
@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: c49a6fa4f6da4e81b30d4703ef16ca15
timeCreated: 1718013507
AssetOrigin:
serializedVersion: 1
productId: 287769
packageName: Stylized Water 3
packageVersion: 3.2.6
assetPath: Assets/Stylized Water 3/Runtime/HeightQueries/HeightQuerySystem.Query.cs
uploadId: 895866
@@ -0,0 +1,142 @@
// Stylized Water 3 by Staggart Creations (http://staggart.xyz)
// COPYRIGHT PROTECTED UNDER THE UNITY ASSET STORE EULA (https://unity.com/legal/as-terms)
// • Copying or referencing source code for the production of new asset store, or public, content is strictly prohibited!
// • Uploading this file to a public repository will subject it to an automated DMCA takedown request.
using System;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.RenderGraphModule;
using UnityEngine.Rendering.Universal;
namespace StylizedWater3
{
public static partial class HeightQuerySystem
{
public class RenderPass : ScriptableRenderPass
{
public const int THREAD_GROUPS = 64; //Value must be mirrored in compute shader
private const string PROFILER_PREFIX = "[GPU] Water Height Query:";
private static readonly ProfilingSampler computeProfilerSampler = new ProfilingSampler( $"{PROFILER_PREFIX} Dispatch");
private static readonly ProfilingSampler readbackAsyncProfilerSampler = new ProfilingSampler( $"{PROFILER_PREFIX} Readback Async");
private ComputeShader cs;
private int kernel = -1;
public void Setup(StylizedWaterRenderFeature renderFeature, ComputeShader heightReadbackCs)
{
if (!heightReadbackCs)
{
Debug.LogError("[Stylized Water 3] Height query render pass initialized with an empty compute shader reference. Was it deleted from the project? Or not referenced on the render feature?" +
" This may happen when deleting the Library folder, creating a race condition where the Compute shader isn't yet imported. You should not add render features in play mode!.", renderFeature);
return;
}
this.cs = heightReadbackCs;
this.kernel = cs.FindKernel("SampleOffsets");
}
private class PassData
{
// Compute shader.
public ComputeShader cs;
public int kernel;
// Buffer handles for the compute buffers.
public GraphicsBuffer[] input;
public GraphicsBuffer[] output;
}
public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameContext)
{
if (kernel < 0) return;
HeightPrePass.FrameData heightPrePassData = frameContext.Get<HeightPrePass.FrameData>();
bool cull = HeightQuerySystem.QueryCount == 0;
using(var builder = renderGraph.AddComputePass($"Water Height Query Sampling", out PassData passData))
{
passData.cs = this.cs;
passData.kernel = this.kernel;
passData.input = new GraphicsBuffer[HeightQuerySystem.QueryCount];
passData.output = new GraphicsBuffer[HeightQuerySystem.QueryCount];
for (int i = 0; i < HeightQuerySystem.QueryCount; i++)
{
passData.input[i] = HeightQuerySystem.queries[i].inputPositionBuffer;
BufferHandle inputBufferHandle = renderGraph.ImportBuffer(passData.input[i]);
builder.UseBuffer(inputBufferHandle);
passData.output[i] = HeightQuerySystem.queries[i].outputOffsetsBuffer;
BufferHandle outputBufferHandle = renderGraph.ImportBuffer(passData.output[i]);
builder.UseBuffer(outputBufferHandle);
}
//Input
builder.UseTexture(heightPrePassData._WaterHeightBuffer);
builder.AllowPassCulling(cull);
builder.SetRenderFunc((PassData data, ComputeGraphContext cgContext) => ExecuteSampling(data, cgContext));
}
using(var builder = renderGraph.AddUnsafePass("Water Height Query: Async Readback", out PassData passData))
{
//WebGPU and Nintendo Switch would not support this
builder.EnableAsyncCompute(SystemInfo.supportsAsyncCompute);
builder.AllowPassCulling(cull);
builder.SetRenderFunc((PassData data, UnsafeGraphContext cgContext) => ExecuteReadback(data, cgContext));
}
}
private void ExecuteSampling(PassData data, ComputeGraphContext context)
{
#if UNITY_EDITOR
if (HeightQuerySystem.DISABLE_IN_EDIT_MODE && Application.isPlaying == false) return;
#endif
var cmd = context.cmd;
using (new ProfilingScope(cmd, computeProfilerSampler))
{
foreach (var q in HeightQuerySystem.queries)
{
q.Dispatch(cmd, data.cs, data.kernel);
}
}
}
//Pass using "cmd.RequestAsyncReadbackIntoNativeArray"
private void ExecuteReadback(PassData data, UnsafeGraphContext context)
{
#if UNITY_EDITOR
if (HeightQuerySystem.DISABLE_IN_EDIT_MODE && Application.isPlaying == false) return;
#endif
var cmd = context.cmd;
using (new ProfilingScope(cmd, readbackAsyncProfilerSampler))
{
foreach (var q in HeightQuerySystem.queries)
{
q.Readback(cmd);
}
}
}
#if !UNITY_6000_4_OR_NEWER
#pragma warning disable CS0672
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) { }
#pragma warning restore CS0672
#endif
public void Dispose()
{
}
}
}
}
@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: e53401f9bd904222bee47b59d0342925
timeCreated: 1718013618
AssetOrigin:
serializedVersion: 1
productId: 287769
packageName: Stylized Water 3
packageVersion: 3.2.6
assetPath: Assets/Stylized Water 3/Runtime/HeightQueries/HeightQuerySystem.Rendering.cs
uploadId: 895866
@@ -0,0 +1,80 @@
// Stylized Water 3 by Staggart Creations (http://staggart.xyz)
// COPYRIGHT PROTECTED UNDER THE UNITY ASSET STORE EULA (https://unity.com/legal/as-terms)
// • Copying or referencing source code for the production of new asset store, or public, content is strictly prohibited!
// • Uploading this file to a public repository will subject it to an automated DMCA takedown request.
using System;
using System.Collections.Generic;
namespace StylizedWater3
{
public partial class HeightQuerySystem
{
/// <summary>
/// Issues the data from a <see cref="HeightQuerySystem.Sampler"/> to an asynchronous GPU readback request.
/// </summary>
public class AsyncRequest
{
public delegate void OnRequestCompleted();
/// <summary>
/// Callback fired whenever a height query was successfully returned from the GPU.
/// </summary>
public event OnRequestCompleted onCompleted;
//GUID
public readonly int hashCode;
//Identifier, mainly for debugging
public string label;
public Sampler sampler;
/// <summary>
/// If the sampling position falls outside the camera frustum, or is not above a water surface, it will hit a void. A default value of -1000 is then used.
/// Use this option to specify if the (invalid) value should be kept or not. If not, the value will represent that of the last successful hit.
/// </summary>
public bool invalidateMisses = true;
//The indices this request occupies in the array
//TODO: Allow a request to span over multiple queries. This will work around the limit of 128 sample points.
//A request would then reference multiple sets of indices, one per query
public readonly List<int> indices = new List<int>();
public int SampleCount => indices.Count;
public AsyncRequest(int hashCode, Sampler sampler, string label = "")
{
this.hashCode = hashCode;
this.sampler = sampler;
this.label = label;
}
public void Issue()
{
if (IsSupported() == false)
{
throw new System.ComponentModel.WarningException(HeightQuerySystem.UNSUPPORTED_MESSAGE);
}
AddRequest(this);
}
public void Withdraw()
{
WithdrawRequest(this);
}
public void Dispose()
{
Withdraw();
sampler.Dispose();
}
internal void InvokeCallback()
{
onCompleted?.Invoke();
}
}
}
}
@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 0fef246c1fe84870b35f921448c136e3
timeCreated: 1718111030
AssetOrigin:
serializedVersion: 1
productId: 287769
packageName: Stylized Water 3
packageVersion: 3.2.6
assetPath: Assets/Stylized Water 3/Runtime/HeightQueries/HeightQuerySystem.Request.cs
uploadId: 895866
@@ -0,0 +1,104 @@
// Stylized Water 3 by Staggart Creations (http://staggart.xyz)
// COPYRIGHT PROTECTED UNDER THE UNITY ASSET STORE EULA (https://unity.com/legal/as-terms)
// • Copying or referencing source code for the production of new asset store, or public, content is strictly prohibited!
// • Uploading this file to a public repository will subject it to an automated DMCA takedown request.
using System;
using Unity.Collections;
using Unity.Mathematics;
using UnityEngine;
using Object = UnityEngine.Object;
namespace StylizedWater3
{
public static partial class HeightQuerySystem
{
/// <summary>
/// Holds a list of sampling positions, and the returned water height values relative to them
/// </summary>
public class Sampler
{
/// <summary>
/// Input sample positions in world-space
/// </summary>
public NativeArray<float3> positions;
/// <summary>
/// Output height values at each sampling <see cref="positions"/>
/// </summary>
public NativeArray<float> heightValues;
private int currentSampleCount = 0;
/// <summary>
/// Checks if the sampler has been initialized
/// </summary>
/// <returns></returns>
public bool IsCreated()
{
return currentSampleCount > 0;
}
public int SampleCount => currentSampleCount;
/// <summary>
/// Initialize the sampler with a number of sampling points. This allocates the memory required.
/// </summary>
/// <param name="sampleCount">Number of positions to sample at. For best performance, this number should be conservative!</param>
/// <param name="cpu">Specify if this sampler is used with the CPU-height query method. If so, no limit is imposed on the sample count</param>
/// <exception cref="Exception"></exception>
public void SetSampleCount(int sampleCount, bool cpu = false)
{
if (cpu == false && sampleCount > Query.MAX_SIZE)
{
Dispose();
throw new Exception($"The number of sample positions ({sampleCount}) exceeds the maximum capacity ({Query.MAX_SIZE}) of a single sampler." +
$" Decrease the number of input positions, or issue multiple smaller requests");
}
//Changed
if (currentSampleCount != sampleCount)
{
#if SWS_DEV
if(currentSampleCount > 0) Debug.Log($"Sampler count changed from {currentSampleCount} to {sampleCount}");
#endif
if (positions.IsCreated) positions.Dispose();
//Input data
positions = new NativeArray<float3>(sampleCount, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
if (heightValues.IsCreated) heightValues.Dispose();
//Output data
heightValues = new NativeArray<float>(sampleCount, Allocator.Persistent);
}
currentSampleCount = sampleCount;
}
[Obsolete("Use SetSampleCount() instead. Method was renamed for clarity")]
public void Initialize(int sampleCount, bool cpu = false)
{
SetSampleCount(sampleCount, cpu);
}
public void SetSamplePosition(int index, float3 value)
{
if (index > currentSampleCount)
{
throw new Exception($"Index out of range. This sampler was initialized with {currentSampleCount} number of samples. Dispose() and SetSampleCount() the sampler to increase the number of samples!");
}
positions[index] = value;
}
public void Dispose()
{
if (positions.IsCreated) positions.Dispose();
if (heightValues.IsCreated) heightValues.Dispose();
currentSampleCount = 0;
}
}
}
}
@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: d5b78379f8de42328f0f5b50b094f050
timeCreated: 1728760960
AssetOrigin:
serializedVersion: 1
productId: 287769
packageName: Stylized Water 3
packageVersion: 3.2.6
assetPath: Assets/Stylized Water 3/Runtime/HeightQueries/HeightQuerySystem.Sampler.cs
uploadId: 895866
@@ -0,0 +1,275 @@
// Stylized Water 3 by Staggart Creations (http://staggart.xyz)
// COPYRIGHT PROTECTED UNDER THE UNITY ASSET STORE EULA (https://unity.com/legal/as-terms)
// • Copying or referencing source code for the production of new asset store, or public, content is strictly prohibited!
// • Uploading this file to a public repository will subject it to an automated DMCA takedown request.
using System;
using System.Collections.Generic;
using Unity.Collections;
using UnityEngine;
using UnityEngine.Profiling;
using Unity.Mathematics;
namespace StylizedWater3
{
public static partial class HeightQuerySystem
{
private const string PROFILER_PREFIX = "[GPU] Water Height Query:";
public static bool DISABLE_IN_EDIT_MODE
{
#if UNITY_EDITOR
get { return UnityEditor.EditorPrefs.GetBool("SW3_HeightQuerySystem_EditMode", false); }
set { UnityEditor.EditorPrefs.SetBool("SW3_HeightQuerySystem_EditMode", value); }
#else
get { return false; }
#endif
}
/// <summary>
/// Reports if the current device/platform supports Compute Shaders
/// </summary>
/// <returns></returns>
public static bool IsSupported()
{
#if UNITY_WEBGL && !UNITY_6000_1_OR_NEWER
return false;
#else
return SystemInfo.supportsComputeShaders;
#endif
}
internal const string UNSUPPORTED_MESSAGE = "[Stylized Water 3] Compute shaders are reportedly not supported on this platform. The GPU Height readback technique relies on this, so is not supported either. " +
"If you are using any \"Align To Water\" components, or custom buoyancy physics using the API, switch all of them to the \"CPU\" method.";
/// <summary>
/// Verifies if the returned height value is valid. If not, the sampling position fell outside the camera frustum or was not above any water surface
/// If false, do not incorporate this value in any processing!
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static bool EqualsVoid(float value)
{
return value <= HeightPrePass.VOID_THRESHOLD;
}
/// <summary>
/// Given 4 height values (each representing the points of a +sign) a normal vector can be derived
/// </summary>
/// <param name="left"></param>
/// <param name="right"></param>
/// <param name="down"></param>
/// <param name="up"></param>
/// <param name="strength"></param>
/// <returns></returns>
public static Vector3 DeriveNormal(float left, float right, float down, float up, float strength = 1f)
{
float xDelta = (left - right) * strength;
float zDelta = (down - up) * strength;
return Vector3.Normalize(new Vector3(xDelta, 1.0f, zDelta));
}
/// <summary>
/// A generic front-end to determine the method used to sampling the water height
/// </summary>
[Serializable]
public class Interface
{
public enum Method
{
[InspectorName("GPU (Async height readback)")]
GPU,
[InspectorName("CPU (Wave pattern replication)")]
CPU
}
[Tooltip("Two completely different methods can be used to reproduce the wave height." +
"\n\n" +
"[GPU] Requires the \"Height Pre-pass\" feature to be enabled on the render feature. This queues up height samples and processes them in a compute shader, the result will be read back from the GPU asynchronously. " +
"\n\n" +
"Height values will represent the water surface height as it literally appears in the world, including any and all displacement effects. Slowest method, but ultimately more flexible." +
"\n\n" +
"[CPU] Given a water level, and water material, the same wave pattern can be 1:1 replicated through script. Does not include displacement effects and supports flat water geometry only. Fastest method")]
public Method method = Method.GPU;
[Tooltip("This reference is required to grab the wave distance and height values")]
public WaterObject waterObject;
[Tooltip("Try to find the Water Object below or above the Transform's position. This is slower than assigning a specific Water Object directly!")]
public bool autoFind = true;
public WaveProfile waveProfile;
public enum WaterLevelSource
{
FixedValue,
[InspectorName("Water Object Y-position")]
WaterObject,
Transform,
Ocean
}
[Tooltip("Configure what should be used to set the base water level. Relative wave height is added to this value")]
public WaterLevelSource waterLevelSource = WaterLevelSource.WaterObject;
[Tooltip("This transform's Y-position is used as the base water level, this value is important and required for correct rendering. As such, underwater rendering does not work with rivers or other non-flat water")]
public Transform waterLevelTransform;
public float waterLevel;
/// <summary>
/// Based on the current configuration, retrieve the water level height value
/// </summary>
/// <returns></returns>
public float GetWaterLevel()
{
if (waterLevelSource == WaterLevelSource.WaterObject && waterObject) return waterObject.transform.position.y;
if (waterLevelSource == WaterLevelSource.Transform && waterLevelTransform) return waterLevelTransform.position.y;
if (waterLevelSource == WaterLevelSource.Ocean && OceanFollowBehaviour.Instance)
{
//Store it, so that it is always valid even when the singleton hasn't loaded yet
waterLevel = OceanFollowBehaviour.Instance.transform.position.y;
return waterLevel;
}
return waterLevel;
}
public bool IsRiverMaterial()
{
return waterObject.material.IsKeywordEnabled(ShaderParams.Keywords.River);
}
public WaterObject GetWaterObject(Vector3 worldPosition)
{
if (autoFind) waterObject = WaterObject.Find(worldPosition, false);
return waterObject;
}
public bool HasMissingReferences()
{
return (waterObject && waterObject.material && waveProfile) == false;
}
}
/// <summary>
/// List of queries being submitted the next frame
/// </summary>
public static readonly List<Query> queries = new List<Query>();
public static int QueryCount { get; private set; }
/// <summary>
/// If there are any height queries present, the displacement pre-pass must execute to ensure data is being returned
/// </summary>
public static bool RequiresHeightPrepass => QueryCount > 0;
private static void AddRequest(AsyncRequest request)
{
//Find the next available query with enough space for the positions
//-If not, create a new query
Profiler.BeginSample($"{PROFILER_PREFIX} Add Request");
foreach (Query q in queries)
{
if (q.requests.ContainsKey(request.hashCode))
{
throw new Exception($"A request with the ID {request.hashCode} has already been issued. Use the \"onReadbackCompleted\" callback to receive the data." +
$"Use the DisposeRequest function only when you are sure you no longer need the data.");
}
}
int queryIndex = queries.Count;
int sampleCount = request.sampler.SampleCount;
if (sampleCount == 0)
{
return;
}
void CreateNewQuery()
{
queries.Add(new Query());
QueryCount++;
queryIndex++;
}
//Initial query needs to be created
if (queryIndex == 0)
{
CreateNewQuery();
}
int occupiedIndices = HeightQuerySystem.Query.MAX_SIZE - queries[queryIndex - 1].availableIndices.Count;
//Not enough space in the latest query for this many samples
if (occupiedIndices + sampleCount >= HeightQuerySystem.Query.MAX_SIZE)
{
CreateNewQuery();
//Debug.Log($"Query #{queryIndex-1} contains {occupiedIndices}, requires {occupiedIndices + sampleCount}. Created a new query (#{queryIndex}).");
}
var query = queries[queryIndex-1];
//Assign the indices available in the query to the request
for (int i = 0; i < sampleCount; i++)
{
request.indices.Add(query.GetNextAvailableIndex());
}
query.sampleCount += sampleCount;
query.requests.Add(request.hashCode, request);
Profiler.EndSample();
}
private static void WithdrawRequest(AsyncRequest request)
{
Profiler.BeginSample($"{PROFILER_PREFIX} Withdraw Request");
for (int i = 0; i < queries.Count; i++)
{
var query = queries[i];
if (query.requests.TryGetValue(request.hashCode, out _))
{
//Remove request from query
query.requests.Remove(request.hashCode);
int indexCount = request.indices.Count;
//Return the occupied indices to the pool
for (int j = 0; j < indexCount; j++)
{
query.ReleaseIndex(request.indices[j]);
}
//Update the current sample count
query.sampleCount -= indexCount;
//Clear the list of occupied indices, these will be repopulate should the request be issued again
request.indices.Clear();
//If the query is now completely empty, yeet it
if (query.requests.Count == 0)
{
//Debug.Log($"Query #{i} is now empty and was disposed");
query.Dispose();
}
}
}
Profiler.EndSample();
}
/// <summary>
/// Clears all queries from the system
/// </summary>
//Need to force a clean start when entering/exiting play mode. Otherwise certain arrays will get de-allocated.
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterAssembliesLoaded)]
public static void Clear()
{
for (int i = 0; i < queries.Count; i++)
{
queries[i].Dispose();
}
}
}
}
@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 4a62da4d17a748dc840316692da46106
timeCreated: 1717752156
AssetOrigin:
serializedVersion: 1
productId: 287769
packageName: Stylized Water 3
packageVersion: 3.2.6
assetPath: Assets/Stylized Water 3/Runtime/HeightQueries/HeightQuerySystem.cs
uploadId: 895866