升级6.4.升级水,升级天气

This commit is contained in:
2026-04-05 00:26:54 +08:00
parent 63bc9b5536
commit 5f7cbfb713
635 changed files with 34718 additions and 22567 deletions

View File

@@ -1,13 +1,15 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering;
using WaveHarmonic.Crest.Internal;
namespace WaveHarmonic.Crest
{
abstract class BakedWaveData : ScriptableObject
abstract class BakedWaveData : CustomScriptableObject
{
public abstract ICollisionProvider CreateCollisionProvider();
public abstract float WindSpeed { get; }
@@ -20,6 +22,7 @@ namespace WaveHarmonic.Crest
/// <summary>
/// The source of collisions (ie water shape).
/// </summary>
[System.Obsolete("Please use QuerySource and LodQuerySource.")]
[@GenerateDoc]
public enum CollisionSource
{
@@ -67,13 +70,9 @@ namespace WaveHarmonic.Crest
{
// NOTE: numbers must be in order for defaults to work (everything first).
/// <inheritdoc cref="Generated.CollisionLayers.Everything"/>
[Tooltip("All layers.")]
Everything = -1,
/// <inheritdoc cref="Generated.CollisionLayers.Nothing"/>
[Tooltip("No extra layers (ie single layer).")]
Nothing,
Nothing = 0,
/// <inheritdoc cref="Generated.CollisionLayers.DynamicWaves"/>
[Tooltip("Separate layer for dynamic waves.\n\nDynamic waves are normally combined together for efficiency. By enabling this layer, dynamic waves are combined and added in a separate pass.")]
@@ -82,6 +81,29 @@ namespace WaveHarmonic.Crest
/// <inheritdoc cref="Generated.CollisionLayers.Displacement"/>
[Tooltip("Extra displacement layer for visual displacement.")]
Displacement = 1 << 2,
/// <inheritdoc cref="Generated.CollisionLayers.Everything"/>
[Tooltip("All layers.")]
Everything = ~0,
}
/// <summary>
/// The wave sampling method to determine quality and performance.
/// </summary>
[@GenerateDoc]
public enum WaveSampling
{
/// <inheritdoc cref="Generated.WaveSampling.Automatic"/>
[Tooltip("Automatically chooses the other options as needed (512+ resolution needs precision).")]
Automatic,
/// <inheritdoc cref="Generated.WaveSampling.Performance"/>
[Tooltip("Reduces samples by copying waves from higher LODs to lower LODs.\n\nBest for resolutions lower than 512.")]
Performance,
/// <inheritdoc cref="Generated.WaveSampling.Precision"/>
[Tooltip("Samples directly from the wave buffers to preserve wave quality.\n\nNeeded for higher resolutions (512+). Higher LOD counts can also benefit with this enabled.")]
Precision,
}
/// <summary>
@@ -108,14 +130,33 @@ namespace WaveHarmonic.Crest
[@FilterEnum(nameof(_TextureFormatMode), Filtered.Mode.Exclude, (int)LodTextureFormatMode.Automatic)]
public sealed partial class AnimatedWavesLod : Lod<ICollisionProvider>
{
[Tooltip("Collision layers to enable.\n\nSome layers will have overhead with CPU, GPU and memory.")]
[@Enable(nameof(_QuerySource), nameof(LodQuerySource.GPU))]
[@GenerateAPI]
[@DecoratedField, SerializeField]
internal CollisionLayers _CollisionLayers = CollisionLayers.Everything;
[@Enable(nameof(_QuerySource), nameof(LodQuerySource.CPU))]
[@DecoratedField, SerializeField]
internal BakedWaveData _BakedWaveData;
[@Space(10)]
[Tooltip("Shifts wavelengths to maintain quality for higher resolutions.\n\nSet this to 2 to improve wave quality. In some cases like flowing rivers, this can make a substantial difference to visual stability. We recommend doubling the Resolution on the WaterRenderer component to preserve detail after making this change.")]
[@Range(1f, 4f)]
[Tooltip("The wave sampling method to determine quality and performance.")]
[@GenerateAPI]
[@DecoratedField]
[SerializeField]
internal WaveSampling _WaveSampling;
[Tooltip("Shifts wavelengths to maintain quality for higher resolutions.\n\nSet this to 2 to improve wave quality. In some cases like flowing rivers, this can make a substantial difference to visual stability. We recommend doubling the Resolution on the WaterRenderer component to preserve detail after making this change.")]
[@Enable(nameof(_WaveSampling), nameof(WaveSampling.Performance))]
[@Range(1f, 4f)]
[@GenerateAPI(Getter.Custom)]
[SerializeField]
float _WaveResolutionMultiplier = 1f;
[@Space(10)]
[Tooltip("How much waves are dampened in shallow water.")]
[@Range(0f, 1f)]
[@GenerateAPI]
@@ -129,30 +170,6 @@ namespace WaveHarmonic.Crest
float _ShallowsMaximumDepth = 1000f;
[@Heading("Collisions")]
[Tooltip("Where to obtain water shape on CPU for physics / gameplay.")]
[@GenerateAPI(Setter.Internal)]
[@DecoratedField, SerializeField]
internal CollisionSource _CollisionSource = CollisionSource.GPU;
[Tooltip("Collision layers to enable.\n\nSome layers will have overhead with CPU, GPU and memory.")]
[@Predicated(nameof(_CollisionSource), inverted: true, nameof(CollisionSource.GPU))]
[@GenerateAPI]
[@DecoratedField, SerializeField]
internal CollisionLayers _CollisionLayers = CollisionLayers.Everything;
[Tooltip("Maximum number of wave queries that can be performed when using GPU queries.")]
[@Predicated(nameof(_CollisionSource), true, nameof(CollisionSource.GPU))]
[@GenerateAPI(Setter.None)]
[@DecoratedField, SerializeField]
int _MaximumQueryCount = QueryBase.k_DefaultMaximumQueryCount;
[@Predicated(nameof(_CollisionSource), true, nameof(CollisionSource.CPU))]
[@DecoratedField, SerializeField]
internal BakedWaveData _BakedWaveData;
const string k_DrawCombine = "Combine";
@@ -160,7 +177,6 @@ namespace WaveHarmonic.Crest
{
public static readonly int s_WaveBuffer = Shader.PropertyToID("_Crest_WaveBuffer");
public static readonly int s_DynamicWavesTarget = Shader.PropertyToID("_Crest_DynamicWavesTarget");
public static readonly int s_AnimatedWavesTarget = Shader.PropertyToID("_Crest_AnimatedWavesTarget");
public static readonly int s_AttenuationInShallows = Shader.PropertyToID("_Crest_AttenuationInShallows");
}
@@ -171,6 +187,9 @@ namespace WaveHarmonic.Crest
/// </summary>
internal static bool s_Combine = true;
WaterResources.ShapeCombineCompute _CombineShader;
RenderTexture _PersistentDataTexture;
internal override string ID => "AnimatedWaves";
internal override string Name => "Animated Waves";
internal override Color GizmoColor => s_GizmoColor;
@@ -187,17 +206,13 @@ namespace WaveHarmonic.Crest
_ => throw new System.NotImplementedException(),
};
ComputeShader _CombineShader;
int _KernalShapeCombine = -1;
int _KernalShapeCombine_DISABLE_COMBINE = -1;
int _KernalShapeCombine_FLOW_ON = -1;
int _KernalShapeCombine_FLOW_ON_DISABLE_COMBINE = -1;
int _KernalShapeCombine_DYNAMIC_WAVE_SIM_ON = -1;
int _KernalShapeCombine_DYNAMIC_WAVE_SIM_ON_DISABLE_COMBINE = -1;
int _KernalShapeCombine_FLOW_ON_DYNAMIC_WAVE_SIM_ON = -1;
int _KernalShapeCombine_FLOW_ON_DYNAMIC_WAVE_SIM_ON_DISABLE_COMBINE = -1;
internal bool PreserveWaveQuality => WaveSampling switch
{
WaveSampling.Automatic => Resolution >= 512,
WaveSampling.Performance => false,
WaveSampling.Precision => true,
_ => throw new System.NotImplementedException(),
};
internal AnimatedWavesLod()
{
@@ -208,35 +223,67 @@ namespace WaveHarmonic.Crest
internal override void Initialize()
{
_CombineShader = WaterResources.Instance.Compute._ShapeCombine;
if (_CombineShader == null)
_CombineShader = WaterResources.Instance._ComputeLibrary._ShapeCombineCompute;
if (_CombineShader._Shader == null)
{
_Valid = false;
return;
}
base.Initialize();
if (Persistent && !_Water.IsMultipleViewpointMode)
{
_PersistentDataTexture = CreateLodDataTextures();
}
}
private protected override void Allocate()
internal override void Destroy()
{
base.Allocate();
base.Destroy();
_KernalShapeCombine = _CombineShader.FindKernel("ShapeCombine");
_KernalShapeCombine_DISABLE_COMBINE = _CombineShader.FindKernel("ShapeCombine_DISABLE_COMBINE");
_KernalShapeCombine_FLOW_ON = _CombineShader.FindKernel("ShapeCombine_FLOW_ON");
_KernalShapeCombine_FLOW_ON_DISABLE_COMBINE = _CombineShader.FindKernel("ShapeCombine_FLOW_ON_DISABLE_COMBINE");
_KernalShapeCombine_DYNAMIC_WAVE_SIM_ON = _CombineShader.FindKernel("ShapeCombine_DYNAMIC_WAVE_SIM_ON");
_KernalShapeCombine_DYNAMIC_WAVE_SIM_ON_DISABLE_COMBINE = _CombineShader.FindKernel("ShapeCombine_DYNAMIC_WAVE_SIM_ON_DISABLE_COMBINE");
_KernalShapeCombine_FLOW_ON_DYNAMIC_WAVE_SIM_ON = _CombineShader.FindKernel("ShapeCombine_FLOW_ON_DYNAMIC_WAVE_SIM_ON");
_KernalShapeCombine_FLOW_ON_DYNAMIC_WAVE_SIM_ON_DISABLE_COMBINE = _CombineShader.FindKernel("ShapeCombine_FLOW_ON_DYNAMIC_WAVE_SIM_ON_DISABLE_COMBINE");
if (_PersistentDataTexture != null) _PersistentDataTexture.Release();
Helpers.Destroy(_PersistentDataTexture);
foreach (var data in _AdditionalCameraData.Values)
{
if (data != null) data.Release();
Helpers.Destroy(data);
}
_AdditionalCameraData.Clear();
}
internal override void SetGlobals(bool enable)
{
base.SetGlobals(enable);
if (_Water.IsRunningWithoutGraphics)
{
return;
}
if (Persistent)
{
Shader.SetGlobalTexture(_TextureSourceShaderID, enable && Enabled ? _PersistentDataTexture : NullTexture);
}
}
internal override void BuildCommandBuffer(WaterRenderer water, CommandBuffer buffer)
{
buffer.BeginSample(ID);
FlipBuffers();
FlipBuffers(buffer);
// Flip textures
if (Persistent)
{
(_PersistentDataTexture, DataTexture) = (DataTexture, _PersistentDataTexture);
// Update current and previous. Latter for MVs and/or VFX.
buffer.SetGlobalTexture(_TextureSourceShaderID, _PersistentDataTexture);
buffer.SetGlobalTexture(_TextureShaderID, DataTexture);
}
Shader.SetGlobalFloat(ShaderIDs.s_AttenuationInShallows, _AttenuationInShallows);
@@ -244,6 +291,18 @@ namespace WaveHarmonic.Crest
buffer.GetTemporaryRT(ShaderIDs.s_WaveBuffer, DataTexture.descriptor);
CoreUtils.SetRenderTarget(buffer, ShaderIDs.s_WaveBuffer, ClearFlag.Color, ClearColor);
// Custom clear because clear not working.
if (Helpers.RequiresCustomClear && WaterResources.Instance.Compute._Clear != null)
{
var compute = WaterResources.Instance._ComputeLibrary._ClearCompute;
var wrapper = new PropertyWrapperCompute(buffer, compute._Shader, compute._KernelClearTarget);
compute.SetVariantForFormat(wrapper, DataTexture.graphicsFormat);
wrapper.SetTexture(Crest.ShaderIDs.s_Target, ShaderIDs.s_WaveBuffer);
wrapper.SetVector(Crest.ShaderIDs.s_ClearMask, Color.white);
wrapper.SetVector(Crest.ShaderIDs.s_ClearColor, ClearColor);
wrapper.Dispatch(Resolution / k_ThreadGroupSizeX, Resolution / k_ThreadGroupSizeY, Slices);
}
// LOD dependent data.
// Write to per-octave _WaveBuffers. Not the same as _AnimatedWaves.
// Draw any data with lod preference.
@@ -254,54 +313,53 @@ namespace WaveHarmonic.Crest
// Combine the LODs - copy results from biggest LOD down to LOD 0
{
var combineShaderKernel = _KernalShapeCombine;
var combineShaderKernel_lastLOD = _KernalShapeCombine_DISABLE_COMBINE;
{
var isFlowOn = _Water._FlowLod.Enabled;
var isDynamicWavesOn = _Water._DynamicWavesLod.Enabled && !_CollisionLayers.HasFlag(CollisionLayers.DynamicWaves);
var wrapper = new PropertyWrapperCompute
(
buffer,
_CombineShader._Shader,
PreserveWaveQuality
? _CombineShader._CopyAnimatedWavesKernel
: _CombineShader._CombineAnimatedWavesKernel
);
// Set the shader kernels that we will use.
if (isFlowOn && isDynamicWavesOn)
{
combineShaderKernel = _KernalShapeCombine_FLOW_ON_DYNAMIC_WAVE_SIM_ON;
combineShaderKernel_lastLOD = _KernalShapeCombine_FLOW_ON_DYNAMIC_WAVE_SIM_ON_DISABLE_COMBINE;
}
else if (isFlowOn)
{
combineShaderKernel = _KernalShapeCombine_FLOW_ON;
combineShaderKernel_lastLOD = _KernalShapeCombine_FLOW_ON_DISABLE_COMBINE;
}
else if (isDynamicWavesOn)
{
combineShaderKernel = _KernalShapeCombine_DYNAMIC_WAVE_SIM_ON;
combineShaderKernel_lastLOD = _KernalShapeCombine_DYNAMIC_WAVE_SIM_ON_DISABLE_COMBINE;
}
if (_Water._DynamicWavesLod.Enabled)
{
_Water._DynamicWavesLod.Bind(wrapper);
}
buffer.BeginSample(k_DrawCombine);
// Start with last LOD which does not combine.
wrapper.SetKeyword(_CombineShader._CombineKeyword, false);
wrapper.SetKeyword(_CombineShader._DynamicWavesKeyword, _Water._DynamicWavesLod.Enabled && !PreserveWaveQuality && !_CollisionLayers.HasFlag(CollisionLayers.DynamicWaves));
// Combine waves.
for (var slice = lastSlice; slice >= 0; slice--)
// The per-octave wave buffers we read from.
wrapper.SetTexture(ShaderIDs.s_WaveBuffer, ShaderIDs.s_WaveBuffer);
// Set the animated waves texture where we read/write to combine the results.
wrapper.SetTexture(Crest.ShaderIDs.s_Target, DataTexture);
if (PreserveWaveQuality)
{
var kernel = slice < lastSlice && s_Combine
? combineShaderKernel : combineShaderKernel_lastLOD;
var wrapper = new PropertyWrapperCompute(buffer, _CombineShader, kernel);
// The per-octave wave buffers we read from.
wrapper.SetTexture(ShaderIDs.s_WaveBuffer, ShaderIDs.s_WaveBuffer);
if (_Water._DynamicWavesLod.Enabled) _Water._DynamicWavesLod.Bind(wrapper);
// Set the animated waves texture where we read/write to combine the results. Use
// compute suffix to avoid collision as a file already uses the normal name.
wrapper.SetTexture(Crest.ShaderIDs.s_Target, DataTexture);
wrapper.SetInteger(Lod.ShaderIDs.s_LodIndex, slice);
wrapper.Dispatch(threadSize, threadSize, 1);
wrapper.Dispatch(threadSize, threadSize, Slices);
}
else
{
buffer.BeginSample(k_DrawCombine);
buffer.EndSample(k_DrawCombine);
// Combine waves.
for (var slice = lastSlice; slice >= 0; slice--)
{
wrapper.SetInteger(Lod.ShaderIDs.s_LodIndex, slice);
wrapper.Dispatch(threadSize, threadSize, 1);
if (slice == lastSlice)
{
// From here on, use combine.
wrapper.SetKeyword(_CombineShader._CombineKeyword, s_Combine);
}
}
buffer.EndSample(k_DrawCombine);
}
}
buffer.ReleaseTemporaryRT(ShaderIDs.s_WaveBuffer);
@@ -312,20 +370,15 @@ namespace WaveHarmonic.Crest
// Alpha channel is cleared in combine step, but if any inputs draw in post-combine
// step then alpha may have data.
var clear = WaterResources.Instance.Compute._Clear;
if (drawn && clear != null)
if (drawn && WaterResources.Instance.Compute._Clear != null)
{
buffer.SetComputeTextureParam(clear, 0, Crest.ShaderIDs.s_Target, DataTexture);
buffer.SetComputeVectorParam(clear, Crest.ShaderIDs.s_ClearMask, Color.black);
buffer.SetComputeVectorParam(clear, Crest.ShaderIDs.s_ClearColor, Color.clear);
buffer.DispatchCompute
(
clear,
0,
Resolution / k_ThreadGroupSizeX,
Resolution / k_ThreadGroupSizeY,
Slices
);
var compute = WaterResources.Instance._ComputeLibrary._ClearCompute;
var wrapper = new PropertyWrapperCompute(buffer, compute._Shader, compute._KernelClearTarget);
compute.SetVariantForFormat(wrapper, DataTexture.graphicsFormat);
wrapper.SetTexture(Crest.ShaderIDs.s_Target, DataTexture);
wrapper.SetVector(Crest.ShaderIDs.s_ClearMask, Color.black);
wrapper.SetVector(Crest.ShaderIDs.s_ClearColor, Color.clear);
wrapper.Dispatch(Resolution / k_ThreadGroupSizeX, Resolution / k_ThreadGroupSizeY, Slices);
}
// Pack height data into alpha channel.
@@ -355,15 +408,21 @@ namespace WaveHarmonic.Crest
}
// Transfer Dynamic Waves to Animated Waves.
if (_CollisionLayers.HasFlag(CollisionLayers.DynamicWaves) && _Water._DynamicWavesLod.Enabled)
if ((_CollisionLayers.HasFlag(CollisionLayers.DynamicWaves) || PreserveWaveQuality) && _Water._DynamicWavesLod.Enabled)
{
buffer.BeginSample(k_DrawCombine);
// Clearing not required as we overwrite enter texture.
buffer.GetTemporaryRT(ShaderIDs.s_DynamicWavesTarget, DataTexture.descriptor);
var wrapper = new PropertyWrapperCompute(buffer, _CombineShader, 9);
var wrapper = new PropertyWrapperCompute(buffer, _CombineShader._Shader, _CombineShader._CombineDynamicWavesKernel);
wrapper.SetTexture(ShaderIDs.s_DynamicWavesTarget, ShaderIDs.s_DynamicWavesTarget);
// Flow keyword is already set, and Dynamic Waves already bound. If binding Dynamic
// Waves becomes kernel specific (eg binding textures), then we need to rebind.
// Start with last LOD which does not combine.
wrapper.SetKeyword(_CombineShader._CombineKeyword, false);
wrapper.SetTexture(Crest.ShaderIDs.s_Target, ShaderIDs.s_DynamicWavesTarget);
_Water._DynamicWavesLod.Bind(wrapper);
@@ -373,18 +432,21 @@ namespace WaveHarmonic.Crest
wrapper.SetInteger(Lod.ShaderIDs.s_LodIndex, slice);
wrapper.Dispatch(threadSize, threadSize, 1);
// Change to kernel with combine enabled.
if (slice == lastSlice)
{
wrapper = new(buffer, _CombineShader, 8);
// From here on, use combine.
wrapper.SetKeyword(_CombineShader._CombineKeyword, s_Combine);
}
}
// Copy Dynamic Waves displacement into Animated Waves.
if (WaterResources.Instance.Compute._Blit != null)
{
wrapper = new(buffer, _CombineShader, 10);
wrapper.SetTexture(ShaderIDs.s_AnimatedWavesTarget, DataTexture);
wrapper.SetTexture(ShaderIDs.s_DynamicWavesTarget, ShaderIDs.s_DynamicWavesTarget);
var compute = WaterResources.Instance._ComputeLibrary._BlitCompute;
wrapper = new(buffer, compute._Shader, 0);
compute.SetVariantForFormat(wrapper, DataTexture.graphicsFormat);
wrapper.SetTexture(Crest.ShaderIDs.s_Source, ShaderIDs.s_DynamicWavesTarget);
wrapper.SetTexture(Crest.ShaderIDs.s_Target, DataTexture);
wrapper.Dispatch(threadSize, threadSize, Slices);
}
@@ -393,7 +455,10 @@ namespace WaveHarmonic.Crest
// Query collisions including Dynamic Waves.
// Does not require copying the water level as they are added with zero alpha.
Provider.UpdateQueries(_Water, CollisionLayer.AfterDynamicWaves);
if (_CollisionLayers.HasFlag(CollisionLayers.DynamicWaves))
{
Provider.UpdateQueries(_Water, CollisionLayer.AfterDynamicWaves);
}
}
if (_CollisionLayers.HasFlag(CollisionLayers.Displacement))
@@ -408,13 +473,6 @@ namespace WaveHarmonic.Crest
Queryable?.UpdateQueries(_Water);
}
if (BufferCount > 1)
{
// Update current and previous. Latter for MVs and/or VFX.
Shader.SetGlobalTexture(_TextureSourceShaderID, _Targets.Previous(1));
Shader.SetGlobalTexture(_TextureShaderID, DataTexture);
}
buffer.EndSample(ID);
}
@@ -426,50 +484,59 @@ namespace WaveHarmonic.Crest
/// <summary>
/// Provides water shape to CPU.
/// </summary>
private protected override ICollisionProvider CreateProvider(bool enable)
private protected override ICollisionProvider CreateProvider(bool onEnable)
{
ICollisionProvider result = null;
ICollisionProvider provider = ICollisionProvider.None;
Queryable?.CleanUp();
if (!enable)
// Respect user choice and early exit. If not in OnEnable, then none provider.
// Do not check Enabled, as it includes _Valid which checks for GPU.
if (!_Enabled || !onEnable)
{
return ICollisionProvider.None;
return provider;
}
switch (_CollisionSource)
var source = QuerySource;
if (_Water.Surface.IsQuadMesh)
{
case CollisionSource.None:
result = ICollisionProvider.None;
break;
case CollisionSource.GPU:
if (_Valid && !_Water.IsRunningWithoutGraphics)
source = LodQuerySource.None;
}
switch (source)
{
case LodQuerySource.GPU:
{
if (_Valid)
{
result = new CollisionQueryWithPasses(_Water);
provider = ICollisionProvider.Create(_Water);
}
if (_Water.IsRunningWithoutGraphics)
{
Debug.LogError($"Crest: GPU queries not supported in headless/batch mode. To resolve, assign an Animated Wave Settings asset to the {nameof(WaterRenderer)} component and set the Collision Source to be a CPU option.");
Debug.LogError("Crest: GPU queries requires a GPU. Please consider CPU queries if running from a server without a GPU.");
}
break;
case CollisionSource.CPU:
}
case LodQuerySource.CPU:
{
if (_BakedWaveData != null)
{
result = _BakedWaveData.CreateCollisionProvider();
provider = _BakedWaveData.CreateCollisionProvider();
}
break;
}
}
if (result == null)
{
// This should not be hit, but can be if compute shaders aren't loaded correctly.
// They will print out appropriate errors. Don't just return null and have null reference
// exceptions spamming the logs.
return ICollisionProvider.None;
}
// This should not be hit, but can be if compute shaders aren't loaded correctly.
// They will print out appropriate errors. Don't just return null and have null
// reference exceptions spamming the logs.
provider ??= ICollisionProvider.None;
return result;
return provider;
}
//
@@ -484,15 +551,17 @@ namespace WaveHarmonic.Crest
public readonly float _ViewerAltitudeLevelAlpha;
public readonly int _Slice;
public readonly int _Slices;
public readonly bool _HighQualityCombine;
public WavelengthFilter(WaterRenderer water, int slice)
public WavelengthFilter(WaterRenderer water, int slice, int resolution)
{
_Slice = slice;
_Slices = water.LodLevels;
_Maximum = water.MaximumWavelength(slice);
_Maximum = water.MaximumWavelength(slice, resolution);
_Minimum = _Maximum * 0.5f;
_TransitionThreshold = water.MaximumWavelength(_Slices - 1) * 0.5f;
_TransitionThreshold = water.MaximumWavelength(_Slices - 1, resolution) * 0.5f;
_ViewerAltitudeLevelAlpha = water.ViewerAltitudeLevelAlpha;
_HighQualityCombine = water.AnimatedWavesLod.PreserveWaveQuality;
}
}
@@ -513,7 +582,7 @@ namespace WaveHarmonic.Crest
// If approaching end of lod chain, start smoothly transitioning any large wavelengths across last two lods
if (wavelength >= filter._TransitionThreshold)
{
if (filter._Slice == filter._Slices - 2)
if (filter._Slice == filter._Slices - 2 && !filter._HighQualityCombine)
{
return 1f - filter._ViewerAltitudeLevelAlpha;
}
@@ -529,12 +598,12 @@ namespace WaveHarmonic.Crest
return 1f;
}
return 0f;
return filter._HighQualityCombine ? 1f : 0f;
}
internal static float FilterByWavelength(WaterRenderer water, int slice, float wavelength)
internal static float FilterByWavelength(WaterRenderer water, int slice, float wavelength, int resolution)
{
return FilterByWavelength(new(water, slice), wavelength);
return FilterByWavelength(new(water, slice, resolution), wavelength);
}
@@ -550,8 +619,83 @@ namespace WaveHarmonic.Crest
{
s_Inputs.Clear();
}
}
// Multiple Viewpoints
partial class AnimatedWavesLod
{
readonly Dictionary<Camera, RenderTexture> _AdditionalCameraData = new();
internal override void LoadCameraData(Camera camera)
{
base.LoadCameraData(camera);
if (!Persistent)
{
return;
}
if (!_AdditionalCameraData.ContainsKey(camera))
{
_PersistentDataTexture = CreateLodDataTextures();
Clear(_PersistentDataTexture);
_AdditionalCameraData.Add(camera, _PersistentDataTexture);
}
else
{
_PersistentDataTexture = _AdditionalCameraData[camera];
}
}
internal override void StoreCameraData(Camera camera)
{
base.StoreCameraData(camera);
if (!Persistent)
{
return;
}
_AdditionalCameraData[camera] = _PersistentDataTexture;
}
internal override void RemoveCameraData(Camera camera)
{
base.RemoveCameraData(camera);
if (_AdditionalCameraData.ContainsKey(camera))
{
var rt = _AdditionalCameraData[camera];
if (rt != null) rt.Release();
Helpers.Destroy(rt);
_AdditionalCameraData.Remove(camera);
}
}
}
// API
partial class AnimatedWavesLod
{
float GetWaveResolutionMultiplier()
{
return PreserveWaveQuality ? 1f : _WaveResolutionMultiplier;
}
}
partial class AnimatedWavesLod
{
[@HideInInspector]
[@System.Obsolete("Please use QuerySource instead.")]
[Tooltip("Where to obtain water shape on CPU for physics / gameplay.")]
[@GenerateAPI(Setter.Internal)]
[@DecoratedField, SerializeField]
internal CollisionSource _CollisionSource = CollisionSource.GPU;
}
#if UNITY_EDITOR
// Editor
partial class AnimatedWavesLod
{
[@OnChange]
private protected override void OnChange(string propertyPath, object previousValue)
{
@@ -560,13 +704,10 @@ namespace WaveHarmonic.Crest
switch (propertyPath)
{
case nameof(_CollisionLayers):
case nameof(_CollisionSource):
if (_Water == null || !_Water.isActiveAndEnabled || !Enabled) return;
Queryable?.CleanUp();
InitializeProvider(true);
ResetQueryChange();
break;
}
}
#endif
}
#endif
}