升级水插件

This commit is contained in:
2026-01-08 22:30:55 +08:00
parent febff82d24
commit ca68084264
415 changed files with 18138 additions and 7134 deletions

View File

@@ -1,4 +1,4 @@
// Crest Water System
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
using UnityEngine;

View File

@@ -1,4 +1,4 @@
// Crest Water System
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
using UnityEngine;

View File

@@ -1,4 +1,4 @@
// Crest Water System
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
using UnityEngine;

View File

@@ -1,4 +1,4 @@
// Crest Water System
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
using UnityEngine;
@@ -32,7 +32,7 @@ namespace WaveHarmonic.Crest
[Tooltip("Whether the data is relative to the input height.\n\nUseful for procedural placement.")]
[@GenerateAPI]
[@DecoratedField, SerializeField]
internal bool _Relative;
internal bool _Relative = true;
[@Label("Copy Signed Distance Field")]
[Tooltip("Whether to copy the signed distance field.")]

View File

@@ -1,11 +1,8 @@
// Crest Water System
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.HighDefinition;
using UnityEngine.Rendering.Universal;
using WaveHarmonic.Crest.Internal;
using WaveHarmonic.Crest.Utility;
@@ -14,55 +11,51 @@ namespace WaveHarmonic.Crest
/// <summary>
/// <see cref="DepthProbe"/>'s update mode.
/// </summary>
[@GenerateDoc]
public enum DepthProbeMode
{
/// <summary>
/// <see cref="DepthProbe"/> is updating in real-time, in accordance to <see cref="DepthProbeRefreshMode"/>.
/// </summary>
/// <inheritdoc cref="Generated.DepthProbeMode.RealTime"/>
[Tooltip("Update in real-time in accordance to refresh mode.")]
RealTime,
/// <summary>
/// <see cref="DepthProbe"/> is baked in the Editor.
/// </summary>
/// <inheritdoc cref="Generated.DepthProbeMode.Baked"/>
[Tooltip("Baked in the editor.")]
Baked,
}
/// <summary>
/// How the <see cref="DepthProbe"/> refreshes when using <see cref="DepthProbeMode.RealTime"/>.
/// </summary>
[@GenerateDoc]
public enum DepthProbeRefreshMode
{
/// <summary>
/// Populates the <see cref="DepthProbe"/> in Start.
/// </summary>
/// <inheritdoc cref="Generated.DepthProbeRefreshMode.OnStart"/>
[Tooltip("Populates the DepthProbe in Start.")]
OnStart = 0,
// EveryFrame = 1,
/// <summary>
/// Requires manual updating via <see cref="DepthProbe.Populate"/>.
/// </summary>
/// <inheritdoc cref="Generated.DepthProbeRefreshMode.ViaScripting"/>
[Tooltip("Requires manual updating via DepthProbe.Populate.")]
ViaScripting = 2,
}
/// <summary>
/// How a component is placed in the world.
/// </summary>
[@GenerateDoc]
public enum Placement
{
/// <summary>
/// The component is in a fixed position.
/// </summary>
/// <inheritdoc cref="Generated.Placement.Fixed"/>
[Tooltip("The component is in a fixed position.")]
Fixed,
/// <summary>
/// The component follows the transform.
/// </summary>
/// <inheritdoc cref="Generated.Placement.Transform"/>
[Tooltip("The component follows the transform.")]
Transform,
/// <summary>
/// The component follows the viewpoint.
/// </summary>
/// <inheritdoc cref="Generated.Placement.Viewpoint"/>
[Tooltip("The component follows the viewpoint.")]
Viewpoint,
}
@@ -317,17 +310,6 @@ namespace WaveHarmonic.Crest
public static readonly int s_VoronoiPingPong1 = Shader.PropertyToID("_Crest_VoronoiPingPong1");
}
#if d_UnityHDRP
static readonly List<FrameSettingsField> s_FrameSettingsFields = new()
{
FrameSettingsField.OpaqueObjects,
FrameSettingsField.TransparentObjects,
FrameSettingsField.TransparentPrepass,
FrameSettingsField.TransparentPostpass,
FrameSettingsField.AsyncCompute,
};
#endif
internal void Bind<T>(T wrapper) where T : IPropertyWrapper
{
wrapper.SetTexture(ShaderIDs.s_DepthProbe, Texture);
@@ -446,39 +428,13 @@ namespace WaveHarmonic.Crest
if (RenderPipelineHelper.IsUniversal)
{
#if d_UnityURP
var additionalCameraData = _Camera.GetUniversalAdditionalCameraData();
additionalCameraData.renderShadows = false;
additionalCameraData.requiresColorTexture = false;
additionalCameraData.requiresDepthTexture = false;
additionalCameraData.renderPostProcessing = false;
additionalCameraData.allowXRRendering = false;
SetUpCameraURP();
#endif
}
else if (RenderPipelineHelper.IsHighDefinition)
{
#if d_UnityHDRP
var additionalCameraData = _Camera.gameObject.AddComponent<HDAdditionalCameraData>();
additionalCameraData.clearColorMode = HDAdditionalCameraData.ClearColorMode.Color;
additionalCameraData.volumeLayerMask = 0;
additionalCameraData.probeLayerMask = 0;
additionalCameraData.xrRendering = false;
// Override camera frame settings to disable most of the expensive rendering for this camera.
// Most importantly, disable custom passes and post-processing as third-party stuff might throw
// errors because of this camera. Even with excluding a lot of HDRP features, it still does a
// lit pass which is not cheap.
additionalCameraData.customRenderingSettings = true;
foreach (FrameSettingsField frameSetting in System.Enum.GetValues(typeof(FrameSettingsField)))
{
if (!s_FrameSettingsFields.Contains(frameSetting))
{
// Enable override and then disable the feature.
additionalCameraData.renderingPathCustomFrameSettingsOverrideMask.mask[(uint)frameSetting] = true;
additionalCameraData.renderingPathCustomFrameSettings.SetEnabled(frameSetting, false);
}
}
SetUpCameraHD();
#endif
}
}
@@ -557,6 +513,10 @@ namespace WaveHarmonic.Crest
OnBeforeRender?.Invoke(this);
_CommandBuffer ??= new();
_CommandBuffer.Clear();
_CommandBuffer.name = "Crest.DepthProbe";
#if UNITY_EDITOR
try
#endif
@@ -566,6 +526,9 @@ namespace WaveHarmonic.Crest
if (_FillHolesCaptureHeight > 0f)
{
Graphics.ExecuteCommandBuffer(_CommandBuffer);
_CommandBuffer.Clear();
// Fill holes pass.
RenderDepthIntoProbe(k_FillKernel, _CaptureRange.y + _FillHolesCaptureHeight);
}
@@ -588,10 +551,14 @@ namespace WaveHarmonic.Crest
if (_GenerateSignedDistanceField)
{
_CommandBuffer.BeginSample("SDF");
RenderSignedDistanceField(inverted: false);
RenderSignedDistanceField(inverted: true);
_CommandBuffer.EndSample("SDF");
}
Graphics.ExecuteCommandBuffer(_CommandBuffer);
HashState(ref _RenderedStateHash);
}
@@ -621,9 +588,17 @@ namespace WaveHarmonic.Crest
backFaces = RenderTexture.GetTemporary(target.descriptor);
_Camera.targetTexture = backFaces;
// Does not work for HDRP (handled elsewhere).
var oldInvertCulling = GL.invertCulling;
GL.invertCulling = true;
#if d_UnityHDRP
if (RenderPipelineHelper.IsHighDefinition)
{
_HDAdditionalCameraData.invertFaceCulling = true;
}
#endif
// Render scene, saving depths in depth buffer.
#if d_UnityURP
if (RenderPipelineHelper.IsUniversal)
@@ -637,6 +612,14 @@ namespace WaveHarmonic.Crest
}
_Camera.targetTexture = target;
#if d_UnityHDRP
if (RenderPipelineHelper.IsHighDefinition)
{
_HDAdditionalCameraData.invertFaceCulling = false;
}
#endif
GL.invertCulling = oldInvertCulling;
}
@@ -652,7 +635,7 @@ namespace WaveHarmonic.Crest
_Camera.Render();
}
var wrapper = new PropertyWrapperComputeStandalone(WaterResources.Instance.Compute._RenderDepthProbe, kernel);
var wrapper = new PropertyWrapperCompute(_CommandBuffer, WaterResources.Instance.Compute._RenderDepthProbe, kernel);
wrapper.SetFloat(ShaderIDs.s_HeightOffset, transform.position.y);
@@ -699,13 +682,12 @@ namespace WaveHarmonic.Crest
return;
}
var buffer = _CommandBuffer;
var cameraToWorldMatrix = _Camera.cameraToWorldMatrix;
var projectionMatrix = _Camera.projectionMatrix;
var projectionToWorldMatrix = cameraToWorldMatrix * projectionMatrix.inverse;
var buffer = _CommandBuffer ??= new();
buffer.Clear();
buffer.name = "Jump Flood";
// Common uniforms.
buffer.SetComputeFloatParam(shader, DepthLodInput.ShaderIDs.s_HeightOffset, transform.position.y);
buffer.SetComputeIntParam(shader, Crest.ShaderIDs.s_TextureSize, _Resolution);
@@ -802,7 +784,6 @@ namespace WaveHarmonic.Crest
);
}
Graphics.ExecuteCommandBuffer(buffer);
buffer.ReleaseTemporaryRT(voronoiPingPong0);
buffer.ReleaseTemporaryRT(voronoiPingPong1);
}
@@ -929,6 +910,7 @@ namespace WaveHarmonic.Crest
wrapper.SetVector(Crest.ShaderIDs.s_TextureSize, _Probe.Scale);
wrapper.SetVector(Crest.ShaderIDs.s_TexturePosition, position.XZ());
wrapper.SetVector(Crest.ShaderIDs.s_TextureRotation, new Vector2(matrix.m20, matrix.m00).normalized);
wrapper.SetVector(Crest.ShaderIDs.s_Multiplier, Vector4.one);
wrapper.SetInteger(Crest.ShaderIDs.s_Blend, (int)LodInputBlend.Maximum);
wrapper.SetTexture(Crest.ShaderIDs.s_Texture, _Probe.Texture);
wrapper.SetTexture(Crest.ShaderIDs.s_Target, target);
@@ -962,9 +944,9 @@ namespace WaveHarmonic.Crest
Hash.AddBool(_EnableBackFaceInclusion, ref hash);
Hash.AddInt(_AdditionalJumpFloodRounds, ref hash);
Hash.AddBool(_GenerateSignedDistanceField, ref hash);
Hash.AddObject(Position, ref hash);
Hash.AddObject(Rotation, ref hash);
Hash.AddObject(Scale, ref hash);
Hash.AddObject(Managed ? Vector3.zero : Position, ref hash);
Hash.AddObject(Managed ? Quaternion.identity : Rotation, ref hash);
Hash.AddObject(Managed ? Vector2.zero : Scale, ref hash);
}
#if UNITY_EDITOR
@@ -992,7 +974,7 @@ namespace WaveHarmonic.Crest
void Update()
{
if (_Debug._ForceAlwaysUpdateDebug)
if (_Debug._ForceAlwaysUpdateDebug && _Type != DepthProbeMode.Baked)
{
Populate();
}

View File

@@ -1,4 +1,4 @@
// Crest Water System
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
using UnityEngine;

View File

@@ -1,4 +1,4 @@
// Crest Water System
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
using UnityEngine;

View File

@@ -1,4 +1,4 @@
// Crest Water System
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
using UnityEngine;

View File

@@ -97,6 +97,7 @@ namespace WaveHarmonic.Crest
/// <inheritdoc/>
[ForLodInput(typeof(LevelLodInput), LodInputMode.Geometry)]
[System.Serializable]
public sealed class LevelGeometryLodInputData : GeometryLodInputData
{
private protected override Shader GeometryShader => WaterResources.Instance.Shaders._LevelGeometry;
@@ -104,6 +105,7 @@ namespace WaveHarmonic.Crest
/// <inheritdoc/>
[ForLodInput(typeof(DepthLodInput), LodInputMode.Geometry)]
[System.Serializable]
public sealed class DepthGeometryLodInputData : GeometryLodInputData
{
private protected override Shader GeometryShader => WaterResources.Instance.Shaders._DepthGeometry;

View File

@@ -1,4 +1,4 @@
// Crest Water System
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
using UnityEngine;

View File

@@ -1,4 +1,4 @@
// Crest Water System
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
using System;
@@ -17,7 +17,7 @@ namespace WaveHarmonic.Crest
/// <summary>
/// Draw the input (the render target will be bound)
/// </summary>
public void Draw(Lod simulation, CommandBuffer buffer, RenderTargetIdentifier target, int pass = -1, float weight = 1f, int slice = -1);
void Draw(Lod simulation, CommandBuffer buffer, RenderTargetIdentifier target, int pass = -1, float weight = 1f, int slice = -1);
float Filter(WaterRenderer water, int slice);

View File

@@ -23,6 +23,7 @@ namespace WaveHarmonic.Crest
/// <summary>
/// Data storage for an input, pertinent to the associated input mode.
/// </summary>
[Serializable]
public abstract class LodInputData
{
[SerializeField, HideInInspector]
@@ -109,82 +110,70 @@ namespace WaveHarmonic.Crest
/// <summary>
/// Modes that inputs can use. Not all inputs support all modes. Refer to the UI.
/// </summary>
[@GenerateDoc]
public enum LodInputMode
{
/// <summary>
/// Unset is the serialization default.
/// </summary>
/// <remarks>
/// This will be replaced with the default mode automatically. Unset can also be
/// used if something is invalid.
/// </remarks>
/// <inheritdoc cref="Generated.LodInputMode.Unset"/>
[Tooltip("Unset is the serialization default.\n\nThis will be replaced with the default mode automatically. Unset can also be used if something is invalid.")]
Unset = 0,
/// <summary>
/// Hand-painted data by the user. Currently unused.
/// </summary>
/// <inheritdoc cref="Generated.LodInputMode.Paint"/>
[Tooltip("Hand-painted data by the user.")]
Paint,
/// <summary>
/// Driven by a user created spline.
/// </summary>
/// <inheritdoc cref="Generated.LodInputMode.Spline"/>
[Tooltip("Driven by a user created spline.")]
Spline,
/// <summary>
/// Attached 'Renderer' (mesh, particle or other) used to drive data.
/// </summary>
/// <inheritdoc cref="Generated.LodInputMode.Renderer"/>
[Tooltip("Attached 'Renderer' (mesh, particle or other) used to drive data.")]
Renderer,
/// <summary>
/// Driven by a mathematical primitive such as a cube or sphere.
/// </summary>
/// <inheritdoc cref="Generated.LodInputMode.Primitive"/>
[Tooltip("Driven by a mathematical primitive such as a cube or sphere.")]
Primitive,
/// <summary>
/// Covers the entire water area.
/// </summary>
/// <inheritdoc cref="Generated.LodInputMode.Global"/>
[Tooltip("Covers the entire water area.")]
Global,
/// <summary>
/// Data driven by a user provided texture.
/// </summary>
/// <inheritdoc cref="Generated.LodInputMode.Texture"/>
[Tooltip("Data driven by a user provided texture.")]
Texture,
/// <summary>
/// Renders geometry using a default material.
/// </summary>
/// <inheritdoc cref="Generated.LodInputMode.Geometry"/>
[Tooltip("Renders geometry using a default material.")]
Geometry,
}
/// <summary>
/// Blend presets for inputs.
/// </summary>
[@GenerateDoc]
public enum LodInputBlend
{
/// <summary>
/// No blending. Overwrites.
/// </summary>
/// <inheritdoc cref="Generated.LodInputBlend.Off"/>
[Tooltip("No blending. Overwrites.")]
Off,
/// <summary>
/// Additive blending.
/// </summary>
/// <inheritdoc cref="Generated.LodInputBlend.Additive"/>
[Tooltip("Additive blending.")]
Additive,
/// <summary>
/// Takes the minimum value.
/// </summary>
/// <inheritdoc cref="Generated.LodInputBlend.Minimum"/>
[Tooltip("Takes the minimum value.")]
Minimum,
/// <summary>
/// Takes the maximum value.
/// </summary>
/// <inheritdoc cref="Generated.LodInputBlend.Maximum"/>
[Tooltip("Takes the maximum value.")]
Maximum,
/// <summary>
/// Applies the inverse weight to the target.
/// </summary>
/// <remarks>
/// Basically overwrites what is already in the simulation.
/// </remarks>
/// <inheritdoc cref="Generated.LodInputBlend.Alpha"/>
[Tooltip("Applies the inverse weight to the target.\n\nBasically overwrites what is already in the simulation.")]
Alpha,
/// <summary>
/// Same as alpha except anything above zero will overwrite rather than blend.
/// </summary>
/// <inheritdoc cref="Generated.LodInputBlend.AlphaClip"/>
[Tooltip("Same as alpha except anything above zero will overwrite rather than blend.")]
AlphaClip,
}
@@ -192,21 +181,19 @@ namespace WaveHarmonic.Crest
/// Primitive shapes.
/// </summary>
// Have this match UnityEngine.PrimitiveType.
[@GenerateDoc]
public enum LodInputPrimitive
{
/// <summary>
/// Spheroid.
/// </summary>
/// <inheritdoc cref="Generated.LodInputPrimitive.Sphere"/>
[Tooltip("Spheroid.")]
Sphere = 0,
/// <summary>
/// Cuboid.
/// </summary>
/// <inheritdoc cref="Generated.LodInputPrimitive.Cube"/>
[Tooltip("Cuboid.")]
Cube = 3,
/// <summary>
/// Quad.
/// </summary>
/// <inheritdoc cref="Generated.LodInputPrimitive.Quad"/>
[Tooltip("Quad.")]
Quad = 5,
}
}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ef4f3d3c2ad88274294da9c9be0143e7
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,54 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
#if d_UnityHDRP
using System.Collections.Generic;
using UnityEngine.Rendering.HighDefinition;
namespace WaveHarmonic.Crest
{
partial class DepthProbe
{
static readonly List<FrameSettingsField> s_FrameSettingsFields = new()
{
FrameSettingsField.OpaqueObjects,
FrameSettingsField.TransparentObjects,
FrameSettingsField.TransparentPrepass,
FrameSettingsField.TransparentPostpass,
FrameSettingsField.AsyncCompute,
};
HDAdditionalCameraData _HDAdditionalCameraData;
void SetUpCameraHD()
{
var additionalCameraData = _Camera.gameObject.AddComponent<HDAdditionalCameraData>();
additionalCameraData.clearColorMode = HDAdditionalCameraData.ClearColorMode.Color;
additionalCameraData.volumeLayerMask = 0;
additionalCameraData.probeLayerMask = 0;
additionalCameraData.xrRendering = false;
// Override camera frame settings to disable most of the expensive rendering for this camera.
// Most importantly, disable custom passes and post-processing as third-party stuff might throw
// errors because of this camera. Even with excluding a lot of HDRP features, it still does a
// lit pass which is not cheap.
additionalCameraData.customRenderingSettings = true;
foreach (FrameSettingsField frameSetting in System.Enum.GetValues(typeof(FrameSettingsField)))
{
if (!s_FrameSettingsFields.Contains(frameSetting))
{
// Enable override and then disable the feature.
additionalCameraData.renderingPathCustomFrameSettingsOverrideMask.mask[(uint)frameSetting] = true;
additionalCameraData.renderingPathCustomFrameSettings.SetEnabled(frameSetting, false);
}
}
_HDAdditionalCameraData = additionalCameraData;
}
}
}
#endif // d_UnityHDRP

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 93b38270a78f94e43bfbbe01ef2e351b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 300
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,24 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
#if d_UnityURP
using UnityEngine.Rendering.Universal;
namespace WaveHarmonic.Crest
{
partial class DepthProbe
{
void SetUpCameraURP()
{
var additionalCameraData = _Camera.GetUniversalAdditionalCameraData();
additionalCameraData.renderShadows = false;
additionalCameraData.requiresColorTexture = false;
additionalCameraData.requiresDepthTexture = false;
additionalCameraData.renderPostProcessing = false;
additionalCameraData.allowXRRendering = false;
}
}
}
#endif // d_UnityURP

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c3d572236539a490aaddc647129ad141
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 300
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -11,6 +11,7 @@ namespace WaveHarmonic.Crest
/// <summary>
/// Data storage for for the Renderer input mode.
/// </summary>
[System.Serializable]
public abstract partial class RendererLodInputData : LodInputData
{
[Tooltip("The renderer to use for this input.\n\nCan be anything that inherits from <i>Renderer</i> like <i>MeshRenderer</i>, <i>TrailRenderer</i> etc.")]
@@ -47,12 +48,6 @@ namespace WaveHarmonic.Crest
#pragma warning restore 414
static class ShaderIDs
{
public static readonly int s_Time = Shader.PropertyToID("_Time");
}
// Some renderers require multiple materials like particles with trails.
// We pass this to GetSharedMaterials to avoid allocations.
internal List<Material> _Materials = new();
@@ -169,35 +164,31 @@ namespace WaveHarmonic.Crest
}
#endif
var pass = _ShaderPassIndex;
// BIRP/URP SG first pass is the right one.
// HDRP SG does not support matrix override, but users can just use BIRP instead.
var pass = 0;
if (ShapeWaves.s_RenderPassOverride > -1)
{
// Needs to use a second pass to disable blending.
pass = ShapeWaves.s_RenderPassOverride;
}
else if (!_OverrideShaderPass)
else if (_OverrideShaderPass)
{
// BIRP/URP SG first pass is the right one.
pass = 0;
// Support HDRP SG. It will always have more than one pass.
if (RenderPipelineHelper.IsHighDefinition && material.shader.passCount > 1)
{
var sgPass = material.FindPass("ForwardOnly");
if (sgPass > -1) pass = sgPass;
}
pass = _ShaderPassIndex;
}
else if (_ShaderPassIndex > material.shader.passCount - 1)
if (pass > material.shader.passCount - 1)
{
return;
}
// Time is not set for us for some reason… Use Time.timeSinceLevelLoad as per:
// https://docs.unity3d.com/Manual/SL-UnityShaderVariables.html
if (RenderPipelineHelper.IsLegacy)
if (RenderPipelineHelper.IsLegacy || RenderPipelineHelper.IsHighDefinition)
{
_Renderer.GetPropertyBlock(_MaterialPropertyBlock);
_MaterialPropertyBlock.SetVector(ShaderIDs.s_Time, new
_MaterialPropertyBlock.SetVector(ShaderIDs.Unity.s_Time, new
(
Time.timeSinceLevelLoad / 20,
Time.timeSinceLevelLoad,

View File

@@ -1,4 +1,4 @@
// Crest Water System
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
using UnityEngine;

View File

@@ -1,4 +1,4 @@
// Crest Water System
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
using UnityEngine;

View File

@@ -14,14 +14,21 @@ namespace WaveHarmonic.Crest
{
// Waves
[Tooltip("How turbulent/chaotic the waves are.")]
[@Range(0, 1, order = -3)]
[Tooltip("Whether to use the wind turbulence on this component rather than the global wind turbulence.\n\nGlobal wind turbulence comes from the Water Renderer component.")]
[@GenerateAPI]
[@InlineToggle(order = -3), SerializeField]
bool _OverrideGlobalWindTurbulence;
[Tooltip("How turbulent/chaotic the waves are.")]
[@Predicated(nameof(_OverrideGlobalWindTurbulence), hide: true)]
[@ShowComputedProperty(nameof(WindTurbulence))]
[@Range(0, 1, order = -4)]
[@GenerateAPI(Getter.Custom)]
[SerializeField]
float _WindTurbulence = 0.145f;
[Tooltip("How aligned the waves are with wind.")]
[@Range(0, 1, order = -4)]
[@Range(0, 1, order = -5)]
[@GenerateAPI]
[SerializeField]
float _WindAlignment;
@@ -51,23 +58,39 @@ namespace WaveHarmonic.Crest
[@Heading("Collision Data Baking")]
#if !d_WaveHarmonic_Crest_CPUQueries
[HideInInspector]
#endif
[Tooltip("Enable running this FFT with baked data.\n\nThis makes the FFT periodic (repeating in time).")]
[@Predicated(nameof(_Mode), inverted: true, nameof(LodInputMode.Global), hide: true)]
[@DecoratedField, SerializeField]
internal bool _EnableBakedCollision = false;
#if !d_WaveHarmonic_Crest_CPUQueries
[HideInInspector]
#endif
[Tooltip("Frames per second of baked data.\n\nLarger values may help the collision track the surface closely at the cost of more frames and increase baked data size.")]
[@Predicated(nameof(_EnableBakedCollision))]
[@Predicated(nameof(_Mode), inverted: true, nameof(LodInputMode.Global), hide: true)]
[@DecoratedField, SerializeField]
internal int _TimeResolution = 4;
#if !d_WaveHarmonic_Crest_CPUQueries
[HideInInspector]
#endif
[Tooltip("Smallest wavelength required in collision.\n\nTo preview the effect of this, disable power sliders in spectrum for smaller values than this number. Smaller values require more resolution and increase baked data size.")]
[@Predicated(nameof(_EnableBakedCollision))]
[@Predicated(nameof(_Mode), inverted: true, nameof(LodInputMode.Global), hide: true)]
[@DecoratedField, SerializeField]
internal float _SmallestWavelengthRequired = 2f;
#if !d_WaveHarmonic_Crest_CPUQueries
[HideInInspector]
#endif
[Tooltip("FFT waves will loop with a period of this many seconds.\n\nSmaller values decrease data size but can make waves visibly repetitive.")]
[@Predicated(nameof(_EnableBakedCollision))]
[@Predicated(nameof(_Mode), inverted: true, nameof(LodInputMode.Global), hide: true)]
@@ -75,21 +98,28 @@ namespace WaveHarmonic.Crest
[SerializeField]
internal float _BakedTimeLoopLength = 32f;
internal float LoopPeriod => _EnableBakedCollision ? _BakedTimeLoopLength : _TimeLoopLength;
internal float LoopPeriod =>
#if d_WaveHarmonic_Crest_CPUQueries
_EnableBakedCollision ? _BakedTimeLoopLength :
#endif
_TimeLoopLength;
private protected override int MinimumResolution => 16;
private protected override int MaximumResolution => int.MaxValue;
FFTCompute _FFTCompute;
FFTCompute.Parameters _OldFFTParameters;
internal FFTCompute.Parameters FFTParameters => new
internal FFTCompute.Parameters GetFFTParameters(float gravity) => new
(
_ActiveSpectrum,
Resolution,
_TimeLoopLength,
WindSpeedMPS,
WindDirRadForFFT,
_WindTurbulence,
_WindAlignment
WindTurbulence,
_WindAlignment,
gravity
);
private protected override void OnUpdate(WaterRenderer water)
@@ -103,7 +133,7 @@ namespace WaveHarmonic.Crest
ReportMaxDisplacement(water);
// If geometry is being used, the water input shader will rotate the waves to align to geo
var parameters = FFTParameters;
var parameters = GetFFTParameters(water.Gravity);
// Don't create tons of generators when values are varying. Notify so that existing generators may be adapted.
if (parameters.GetHashCode() != _OldFFTParameters.GetHashCode())
@@ -111,6 +141,10 @@ namespace WaveHarmonic.Crest
FFTCompute.OnGenerationDataUpdated(_OldFFTParameters, parameters);
}
#if UNITY_EDITOR
_FFTCompute = FFTCompute.GetInstance(parameters);
#endif
_OldFFTParameters = parameters;
}
@@ -118,14 +152,22 @@ namespace WaveHarmonic.Crest
{
if (_LastGenerateFrameCount != Time.frameCount)
{
// Parameters will unlikely change as our Update is called in LateUpdate with Draw
// not too far after.
var parameters = GetFFTParameters(lod.Water.Gravity);
_WaveBuffers = FFTCompute.GenerateDisplacements
(
buffer,
lod.Water.CurrentTime,
FFTParameters,
parameters,
UpdateDataEachFrame
);
#if UNITY_EDITOR
_FFTCompute = FFTCompute.GetInstance(parameters);
#endif
_LastGenerateFrameCount = Time.frameCount;
}
@@ -157,11 +199,6 @@ namespace WaveHarmonic.Crest
}
}
private protected override void DestroySharedResources()
{
FFTCompute.CleanUpAll();
}
float WindDirRadForFFT
{
get
@@ -172,36 +209,64 @@ namespace WaveHarmonic.Crest
return 0f;
}
return _WaveDirectionHeadingAngle * Mathf.Deg2Rad;
return WaveDirectionHeadingAngle * Mathf.Deg2Rad;
}
}
float GetWindTurbulence()
{
return _OverrideGlobalWindTurbulence || WaterRenderer.Instance == null ? _WindTurbulence : WaterRenderer.Instance.WindTurbulence;
}
#if UNITY_EDITOR
void OnGUI()
{
if (_DrawSlicesInEditor)
{
FFTCompute.GetInstance(FFTParameters)?.OnGUI();
_FFTCompute?.OnGUI();
}
}
internal FFTCompute GetFFTComputeInstance()
{
return FFTCompute.GetInstance(FFTParameters);
}
#endif
}
partial class ShapeFFT
{
static int s_InstanceCount;
private protected override void Awake()
{
base.Awake();
s_InstanceCount++;
}
private protected override void OnDestroy()
{
base.OnDestroy();
if (--s_InstanceCount <= 0)
{
FFTCompute.CleanUpAll();
}
}
}
partial class ShapeFFT : ISerializationCallbackReceiver
{
[SerializeField, HideInInspector]
#pragma warning disable 414
int _Version = 1;
int _Version = 2;
#pragma warning restore 414
void ISerializationCallbackReceiver.OnAfterDeserialize()
{
_Version = MigrateV1(_Version);
if (_Version < 2)
{
_OverrideGlobalWindTurbulence = true;
}
_Version = MigrateV2(_Version);
}
void ISerializationCallbackReceiver.OnBeforeSerialize()
@@ -209,4 +274,19 @@ namespace WaveHarmonic.Crest
// Empty.
}
}
#if UNITY_EDITOR
partial class ShapeFFT
{
private protected override void Reset()
{
base.Reset();
if (_Mode != LodInputMode.Global)
{
_OverrideGlobalWindTurbulence = true;
}
}
}
#endif
}

View File

@@ -17,9 +17,17 @@ namespace WaveHarmonic.Crest
{
// Waves
[Tooltip("The weight of the opposing, second pair of Gerstner waves.\n\nEach Gerstner wave is actually a pair of waves travelling in opposite directions (similar to FFT). This weight is applied to the wave travelling in against-wind direction. Set to zero to obtain simple single waves which are useful for shorelines waves.")]
[@Range(0f, 1f, order = -3)]
[@Space(10)]
[Tooltip("Use a swell spectrum as the default.\n\nUses a swell spectrum as default (when none is assigned), and disabled reverse waves.")]
[@GenerateAPI]
[@DecoratedField(order = -3), SerializeField]
bool _Swell = true;
[Tooltip("The weight of the opposing, second pair of Gerstner waves.\n\nEach Gerstner wave is actually a pair of waves travelling in opposite directions (similar to FFT). This weight is applied to the wave travelling in against-wind direction. Set to zero to obtain simple single waves which are useful for shorelines waves.")]
[Predicated(nameof(_Swell), inverted: true)]
[@Range(0f, 1f, order = -4)]
[@GenerateAPI(Getter.Custom)]
[SerializeField]
float _ReverseWaveWeight = 0.5f;
@@ -37,6 +45,11 @@ namespace WaveHarmonic.Crest
[SerializeField]
int _RandomSeed = 0;
[Tooltip("Prevent data arrays from being written to so one can provide their own.")]
[@GenerateAPI]
[SerializeField]
bool _ManualGeneration;
private protected override int MinimumResolution => 8;
private protected override int MaximumResolution => 64;
@@ -45,12 +58,39 @@ namespace WaveHarmonic.Crest
const int k_MaximumWaveComponents = 1024;
// Data for all components
float[] _Wavelengths;
float[] _Amplitudes;
/// <summary>
/// Wavelengths. Requires Manual Generation to be enabled.
/// </summary>
[System.NonSerialized]
public float[] _Wavelengths;
/// <summary>
/// Amplitudes. Requires Manual Generation to be enabled.
/// </summary>
[System.NonSerialized]
public float[] _Amplitudes;
/// <summary>
/// Powers. Requires Manual Generation to be enabled.
/// </summary>
[System.NonSerialized]
public float[] _Powers;
/// <summary>
/// Angles. Requires Manual Generation to be enabled.
/// </summary>
[System.NonSerialized]
public float[] _AngleDegrees;
/// <summary>
/// Phases. Requires Manual Generation to be enabled.
/// </summary>
[System.NonSerialized]
public float[] _Phases;
// Reverse.
float[] _Amplitudes2;
float[] _Powers;
float[] _AngleDegrees;
float[] _Phases;
float[] _Phases2;
struct GerstnerCascadeParams
@@ -82,6 +122,33 @@ namespace WaveHarmonic.Crest
ComputeShader _ShaderGerstner;
int _KernelGerstner = -1;
private protected override WaveSpectrum DefaultSpectrum => _Swell ? SwellSpectrum : WindSpectrum;
static WaveSpectrum s_SwellSpectrum;
static WaveSpectrum SwellSpectrum
{
get
{
if (s_SwellSpectrum == null)
{
s_SwellSpectrum = ScriptableObject.CreateInstance<WaveSpectrum>();
s_SwellSpectrum.name = "Swell Waves (auto)";
s_SwellSpectrum.hideFlags = HideFlags.DontSave | HideFlags.NotEditable;
s_SwellSpectrum._PowerDisabled[0] = true;
s_SwellSpectrum._PowerDisabled[1] = true;
s_SwellSpectrum._PowerDisabled[2] = true;
s_SwellSpectrum._PowerDisabled[3] = true;
s_SwellSpectrum._PowerDisabled[4] = true;
s_SwellSpectrum._PowerDisabled[5] = true;
s_SwellSpectrum._PowerDisabled[6] = true;
s_SwellSpectrum._PowerDisabled[7] = true;
s_SwellSpectrum._WaveDirectionVariance = 15f;
s_SwellSpectrum._Chop = 1.3f;
}
return s_SwellSpectrum;
}
}
static new class ShaderIDs
{
@@ -103,6 +170,11 @@ namespace WaveHarmonic.Crest
s_Instances.Clear();
}
float GetReverseWaveWeight()
{
return _Swell ? 0f : _ReverseWaveWeight;
}
void InitData()
{
if (_WaveBuffers == null)
@@ -167,7 +239,7 @@ namespace WaveHarmonic.Crest
{
UpdateGenerateWaves(buffer);
// Above changes the render target. Change it back if necessary.
if (!IsCompute) buffer.SetRenderTarget(target, 0, CubemapFace.Unknown, slice);
if (!IsCompute) CoreUtils.SetRenderTarget(buffer, target, depthSlice: slice);
}
_LastGenerateFrameCount = Time.frameCount;
@@ -196,6 +268,16 @@ namespace WaveHarmonic.Crest
var outputIdx = 0;
_CascadeParameters[0]._StartIndex = 0;
if (_ManualGeneration)
{
for (var i = 0; i < _WaveData.Length; i++)
{
_WaveData[i]._Phase2 = Vector4.zero;
_WaveData[i]._Amplitude2 = Vector4.zero;
_WaveData[i]._ChopAmplitude2 = Vector4.zero;
}
}
// Seek forward to first wavelength that is big enough to render into current cascades
var minWl = MinWavelength(cascadeIdx);
while (componentIdx < _Wavelengths.Length && _Wavelengths[componentIdx] < minWl)
@@ -230,10 +312,13 @@ namespace WaveHarmonic.Crest
_WaveData[vi]._WaveDirectionZ[ei] = 0f;
_WaveData[vi]._Omega[ei] = 0f;
_WaveData[vi]._Phase[ei] = 0f;
_WaveData[vi]._Phase2[ei] = 0f;
_WaveData[vi]._ChopAmplitude[ei] = 0f;
_WaveData[vi]._Amplitude2[ei] = 0f;
_WaveData[vi]._ChopAmplitude2[ei] = 0f;
if (!_ManualGeneration)
{
_WaveData[vi]._Phase2[ei] = 0f;
_WaveData[vi]._Amplitude2[ei] = 0f;
_WaveData[vi]._ChopAmplitude2[ei] = 0f;
}
ei = (ei + 1) % 4;
outputIdx++;
}
@@ -254,11 +339,15 @@ namespace WaveHarmonic.Crest
var ei = outputIdx - vi * 4;
_WaveData[vi]._Amplitude[ei] = _Amplitudes[componentIdx];
_WaveData[vi]._Amplitude2[ei] = _Amplitudes2[componentIdx];
var chopScale = _ActiveSpectrum._ChopScales[componentIdx / _ComponentsPerOctave];
_WaveData[vi]._ChopAmplitude[ei] = -chopScale * _ActiveSpectrum._Chop * _Amplitudes[componentIdx];
_WaveData[vi]._ChopAmplitude2[ei] = -chopScale * _ActiveSpectrum._Chop * _Amplitudes2[componentIdx];
if (!_ManualGeneration)
{
_WaveData[vi]._Amplitude2[ei] = _Amplitudes2[componentIdx];
_WaveData[vi]._ChopAmplitude2[ei] = -chopScale * _ActiveSpectrum._Chop * _Amplitudes2[componentIdx];
}
var angle = Mathf.Deg2Rad * _AngleDegrees[componentIdx];
var dx = Mathf.Cos(angle);
@@ -295,7 +384,11 @@ namespace WaveHarmonic.Crest
// Repeat every 2pi to keep angle bounded - helps precision on 16bit platforms
_WaveData[vi]._Omega[ei] = k * c;
_WaveData[vi]._Phase[ei] = Mathf.Repeat(_Phases[componentIdx], Mathf.PI * 2f);
_WaveData[vi]._Phase2[ei] = Mathf.Repeat(_Phases2[componentIdx], Mathf.PI * 2f);
if (!_ManualGeneration)
{
_WaveData[vi]._Phase2[ei] = Mathf.Repeat(_Phases2[componentIdx], Mathf.PI * 2f);
}
outputIdx++;
}
@@ -316,10 +409,13 @@ namespace WaveHarmonic.Crest
_WaveData[vi]._WaveDirectionZ[ei] = 0f;
_WaveData[vi]._Omega[ei] = 0f;
_WaveData[vi]._Phase[ei] = 0f;
_WaveData[vi]._Phase2[ei] = 0f;
_WaveData[vi]._ChopAmplitude[ei] = 0f;
_WaveData[vi]._Amplitude2[ei] = 0f;
_WaveData[vi]._ChopAmplitude2[ei] = 0f;
if (!_ManualGeneration)
{
_WaveData[vi]._Phase2[ei] = 0f;
_WaveData[vi]._Amplitude2[ei] = 0f;
_WaveData[vi]._ChopAmplitude2[ei] = 0f;
}
ei = (ei + 1) % 4;
outputIdx++;
}
@@ -340,8 +436,7 @@ namespace WaveHarmonic.Crest
void UpdateGenerateWaves(CommandBuffer buf)
{
// Clear existing waves or they could get copied.
buf.SetRenderTarget(_WaveBuffers, 0, CubemapFace.Unknown, -1);
buf.ClearRenderTarget(RTClearFlags.Color, Color.black, 0, 0);
CoreUtils.SetRenderTarget(buf, _WaveBuffers, ClearFlag.Color);
buf.SetComputeFloatParam(_ShaderGerstner, ShaderIDs.s_TextureRes, _WaveBuffers.width);
buf.SetComputeIntParam(_ShaderGerstner, ShaderIDs.s_FirstCascadeIndex, _FirstCascade);
buf.SetComputeBufferParam(_ShaderGerstner, _KernelGerstner, ShaderIDs.s_CascadeParams, _BufferCascadeParameters);
@@ -358,13 +453,23 @@ namespace WaveHarmonic.Crest
/// <param name="windSpeed">Wind speed in m/s</param>
void UpdateWaveData(WaterRenderer water, float windSpeed)
{
if (_ManualGeneration)
{
if (_Wavelengths != null)
{
SliceUpWaves(water, windSpeed);
}
return;
}
// Set random seed to get repeatable results
var randomStateBkp = Random.state;
Random.InitState(_RandomSeed);
_ActiveSpectrum.GenerateWaveData(_ComponentsPerOctave, ref _Wavelengths, ref _AngleDegrees);
UpdateAmplitudes();
UpdateAmplitudes(water);
// Won't run every time so put last in the random sequence
if (_Phases == null || _Phases.Length != _Wavelengths.Length || _Phases2 == null || _Phases2.Length != _Wavelengths.Length)
@@ -377,7 +482,7 @@ namespace WaveHarmonic.Crest
SliceUpWaves(water, windSpeed);
}
void UpdateAmplitudes()
void UpdateAmplitudes(WaterRenderer water)
{
if (_Amplitudes == null || _Amplitudes.Length != _Wavelengths.Length)
{
@@ -396,9 +501,9 @@ namespace WaveHarmonic.Crest
for (var i = 0; i < _Wavelengths.Length; i++)
{
var amp = _ActiveSpectrum.GetAmplitude(_Wavelengths[i], _ComponentsPerOctave, windSpeed, out _Powers[i]);
var amp = _ActiveSpectrum.GetAmplitude(_Wavelengths[i], _ComponentsPerOctave, windSpeed, water.Gravity, out _Powers[i]);
_Amplitudes[i] = Random.value * amp;
_Amplitudes2[i] = Random.value * amp * _ReverseWaveWeight;
_Amplitudes2[i] = Random.value * amp * ReverseWaveWeight;
}
}
@@ -490,8 +595,6 @@ namespace WaveHarmonic.Crest
}
}
private protected override void DestroySharedResources() { }
#if UNITY_EDITOR
void OnGUI()
{
@@ -503,16 +606,44 @@ namespace WaveHarmonic.Crest
#endif
}
partial class ShapeGerstner
{
static int s_InstanceCount;
private protected override void Awake()
{
base.Awake();
s_InstanceCount++;
}
private protected override void OnDestroy()
{
base.OnDestroy();
if (s_SwellSpectrum != null)
{
Helpers.Destroy(s_SwellSpectrum);
}
}
}
partial class ShapeGerstner : ISerializationCallbackReceiver
{
[SerializeField, HideInInspector]
#pragma warning disable 414
int _Version = 1;
int _Version = 2;
#pragma warning restore 414
void ISerializationCallbackReceiver.OnAfterDeserialize()
{
_Version = MigrateV1(_Version);
if (_Version < 2)
{
_Swell = false;
}
_Version = MigrateV2(_Version);
}
void ISerializationCallbackReceiver.OnBeforeSerialize()

View File

@@ -18,7 +18,7 @@ namespace WaveHarmonic.Crest
[@Heading("Waves")]
[Tooltip("The spectrum that defines the water surface shape.")]
[@Embedded]
[@Embedded(defaultPropertyName: nameof(_ActiveSpectrum))]
[@GenerateAPI]
[SerializeField]
internal WaveSpectrum _Spectrum;
@@ -35,17 +35,25 @@ namespace WaveHarmonic.Crest
[SerializeField]
float _RespectShallowWaterAttenuation = 1f;
[Tooltip("Whether to use the wind direction on this component rather than the global wind direction.\n\nGlobal wind direction comes from the Water Renderer component.")]
[@GenerateAPI]
[@InlineToggle, SerializeField]
bool _OverrideGlobalWindDirection;
[@Label("Wind Direction")]
[Tooltip("Primary wave direction heading (degrees).\n\nThis is the angle from x axis in degrees that the waves are oriented towards. If a spline is being used to place the waves, this angle is relative to the spline.")]
[@Predicated(nameof(_Mode), inverted: false, nameof(LodInputMode.Paint))]
[@Predicated(nameof(_OverrideGlobalWindDirection), hide: true)]
[@ShowComputedProperty(nameof(WaveDirectionHeadingAngle))]
[@Range(-180, 180)]
[@GenerateAPI]
[@GenerateAPI(Getter.Custom)]
[SerializeField]
private protected float _WaveDirectionHeadingAngle = 0f;
[Tooltip("Whether to use the wind speed on this component rather than the global wind speed.\n\nGlobal wind speed comes from the Water Renderer component.")]
[@GenerateAPI]
[SerializeField]
bool _OverrideGlobalWindSpeed = false;
[@InlineToggle, SerializeField]
bool _OverrideGlobalWindSpeed;
[Tooltip("Wind speed in km/h. Controls wave conditions.")]
[@ShowComputedProperty(nameof(WindSpeedKPH))]
@@ -86,19 +94,21 @@ namespace WaveHarmonic.Crest
public static readonly int s_AxisX = Shader.PropertyToID("_Crest_AxisX");
}
static WaveSpectrum s_DefaultSpectrum;
private protected static WaveSpectrum DefaultSpectrum
private protected virtual WaveSpectrum DefaultSpectrum => WindSpectrum;
static WaveSpectrum s_WindSpectrum;
private protected static WaveSpectrum WindSpectrum
{
get
{
if (s_DefaultSpectrum == null)
if (s_WindSpectrum == null)
{
s_DefaultSpectrum = ScriptableObject.CreateInstance<WaveSpectrum>();
s_DefaultSpectrum.name = "Default Waves (instance)";
s_DefaultSpectrum.hideFlags = HideFlags.DontSave | HideFlags.NotEditable;
s_WindSpectrum = ScriptableObject.CreateInstance<WaveSpectrum>();
s_WindSpectrum.name = "Wind Waves (instance)";
s_WindSpectrum.hideFlags = HideFlags.DontSave | HideFlags.NotEditable;
}
return s_DefaultSpectrum;
return s_WindSpectrum;
}
}
@@ -113,7 +123,7 @@ namespace WaveHarmonic.Crest
internal static int s_RenderPassOverride = -1;
private protected WaveSpectrum _ActiveSpectrum = null;
private protected Vector2 PrimaryWaveDirection => new(Mathf.Cos(Mathf.PI * _WaveDirectionHeadingAngle / 180f), Mathf.Sin(Mathf.PI * _WaveDirectionHeadingAngle / 180f));
private protected Vector2 PrimaryWaveDirection => new(Mathf.Cos(Mathf.PI * WaveDirectionHeadingAngle / 180f), Mathf.Sin(Mathf.PI * WaveDirectionHeadingAngle / 180f));
/// <summary>
/// The wind speed in kilometers per hour (KPH).
@@ -121,7 +131,7 @@ namespace WaveHarmonic.Crest
/// <remarks>
/// Wind speed can come from this component or the <see cref="WaterRenderer"/>.
/// </remarks>
public float WindSpeedKPH => _OverrideGlobalWindSpeed || WaterRenderer.Instance == null ? _WindSpeed : WaterRenderer.Instance.WindSpeedKPH;
public float WindSpeedKPH => _OverrideGlobalWindSpeed || WaterRenderer.Instance == null ? _WindSpeed : WaterRenderer.Instance.WindSpeed;
/// <summary>
/// The wind speed in meters per second (MPS).
@@ -301,7 +311,7 @@ namespace WaveHarmonic.Crest
// Wave generation done in Draw. Keeps track to limit to once per frame.
private protected int _LastGenerateFrameCount = -1;
internal override bool Enabled => _FirstCascade > -1 && WaterRenderer.Instance.Gravity != 0f && Mode switch
internal override bool Enabled => _FirstCascade > -1 && (WaterRenderer.Instance == null || WaterRenderer.Instance.Gravity != 0f) && Mode switch
{
LodInputMode.Global => enabled && s_TransferWavesComputeShader != null,
_ => base.Enabled,
@@ -324,7 +334,6 @@ namespace WaveHarmonic.Crest
private protected float MaximumReportedVerticalDisplacement { get; set; }
private protected float MaximumReportedWavesDisplacement { get; set; }
static int s_InstanceCount = 0;
private protected bool UpdateDataEachFrame
{
@@ -349,7 +358,6 @@ namespace WaveHarmonic.Crest
}
private protected abstract void ReportMaxDisplacement(WaterRenderer water);
private protected abstract void DestroySharedResources();
private protected override void OnUpdate(WaterRenderer water)
{
@@ -367,26 +375,6 @@ namespace WaveHarmonic.Crest
wrapper.SetFloat(ShaderIDs.s_MaximumAttenuationDepth, water._AnimatedWavesLod.ShallowsMaximumDepth);
}
private protected override void Awake()
{
base.Awake();
s_InstanceCount++;
}
private protected void OnDestroy()
{
// Since FFTCompute resources are shared we will clear after last ShapeFFT is destroyed.
if (--s_InstanceCount <= 0)
{
DestroySharedResources();
if (s_DefaultSpectrum != null)
{
Helpers.Destroy(s_DefaultSpectrum);
}
}
}
private protected override void Initialize()
{
base.Initialize();
@@ -441,6 +429,11 @@ namespace WaveHarmonic.Crest
return false;
}
float GetWaveDirectionHeadingAngle()
{
return _OverrideGlobalWindDirection || WaterRenderer.Instance == null ? _WaveDirectionHeadingAngle : WaterRenderer.Instance.WindDirection;
}
}
partial class ShapeWaves
@@ -455,6 +448,28 @@ namespace WaveHarmonic.Crest
}
}
partial class ShapeWaves
{
static int s_InstanceCount = 0;
private protected override void Awake()
{
base.Awake();
s_InstanceCount++;
}
private protected virtual void OnDestroy()
{
if (--s_InstanceCount <= 0)
{
if (s_WindSpectrum != null)
{
Helpers.Destroy(s_WindSpectrum);
}
}
}
}
partial class ShapeWaves
{
[HideInInspector, SerializeField]
@@ -486,5 +501,34 @@ namespace WaveHarmonic.Crest
return version;
}
private protected int MigrateV2(int version)
{
// Version 2
// - Global wind direction
if (version < 2)
{
_OverrideGlobalWindDirection = true;
version = 2;
}
return version;
}
}
#if UNITY_EDITOR
partial class ShapeWaves
{
private protected override void Reset()
{
base.Reset();
if (_Mode != LodInputMode.Global)
{
_OverrideGlobalWindSpeed = true;
_OverrideGlobalWindDirection = true;
}
}
}
#endif
}

View File

@@ -1,4 +1,4 @@
// Crest Water System
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
using UnityEngine;

View File

@@ -18,6 +18,11 @@ namespace WaveHarmonic.Crest
[@DecoratedField, SerializeField]
internal Texture _Texture;
[Tooltip("Multiplies the texture sample.\n\nThis is useful for normalized textures. The four components map to the four color/alpha components of the texture (if they exist).\n\nIf you just want to fade out the input, consider using weight instead.")]
[@GenerateAPI]
[@DecoratedField, SerializeField]
Vector4 _Multiplier = Vector4.one;
private protected abstract ComputeShader TextureShader { get; }
internal override bool IsEnabled => _Texture != null;
internal override bool HasHeightRange => false;
@@ -41,6 +46,7 @@ namespace WaveHarmonic.Crest
wrapper.SetVector(ShaderIDs.s_TexturePosition, transform.position.XZ());
wrapper.SetVector(ShaderIDs.s_TextureRotation, rotation);
wrapper.SetVector(ShaderIDs.s_Resolution, new(_Texture.width, _Texture.height));
wrapper.SetVector(ShaderIDs.s_Multiplier, _Multiplier);
wrapper.SetFloat(ShaderIDs.s_FeatherWidth, _Input.FeatherWidth);
wrapper.SetTexture(ShaderIDs.s_Texture, _Texture);
wrapper.SetInteger(ShaderIDs.s_Blend, (int)_Input.Blend);

View File

@@ -13,17 +13,14 @@ namespace WaveHarmonic.Crest
/// <remarks>
/// Each mode has its strengths and weaknesses.
/// </remarks>
[@GenerateDoc]
public enum WatertightHullMode
{
/// <summary>
/// Uses <see cref="AnimatedWavesLod"/> to remove water.
/// </summary>
/// <inheritdoc cref="Generated.WatertightHullMode.Displacement"/>
[Tooltip("Use displacement to remove water.\n\nUsing displacement will also affect the underwater and can nest bouyant objects. Requires the displacement layer to be enabled.")]
Displacement,
/// <summary>
/// Uses <see cref="ClipLod"/> to remove water.
/// </summary>
/// <inheritdoc cref="Generated.WatertightHullMode.Clip"/>
[Tooltip("Clips the surface to remove water.\n\nThis option is more precise and can be submerged.")]
Clip,
}