升级水插件

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

@@ -6,6 +6,7 @@ using System.Reflection;
using UnityEditor;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.HighDefinition;
using UnityEngine.Rendering.Universal;
using WaveHarmonic.Crest.Internal;
using WaveHarmonic.Crest.Watercraft;
@@ -17,7 +18,11 @@ namespace WaveHarmonic.Crest.Editor
{
static class Validators
{
// HDRP sub-shader always first.
const int k_SubShaderIndexHDRP = 0;
internal static WaterRenderer Water => Utility.Water;
static readonly System.Collections.Generic.List<Terrain> s_Terrains = new();
static readonly ShaderTagId s_RenderPipelineShaderTagID = new("RenderPipeline");
[Validator(typeof(LodInput))]
static bool ValidateTextureInput(LodInput target, ShowMessage messenger)
@@ -94,6 +99,7 @@ namespace WaveHarmonic.Crest.Editor
{
var material = materials[i];
if (material == null) continue;
if (material.shader == null) continue;
if (data._OverrideShaderPass && data._ShaderPassIndex > material.shader.passCount - 1)
{
@@ -119,6 +125,22 @@ namespace WaveHarmonic.Crest.Editor
);
}
}
#if d_UnityHDRP
if (RenderPipelineHelper.IsHighDefinition)
{
if (AssetDatabase.GetAssetPath(material.shader).EndsWith(".shadergraph") && material.shader.FindSubshaderTagValue(k_SubShaderIndexHDRP, s_RenderPipelineShaderTagID).name == "HDRenderPipeline")
{
messenger
(
"It appears you are using Shader Graph with the HDRP target. " +
"Make sure to use the Built-In target instead for your Shader Graph to work.",
"Remove the HDRP target and add the Built-In target.",
MessageType.Warning, material.shader
);
}
}
#endif
}
return isValid;
@@ -126,18 +148,18 @@ namespace WaveHarmonic.Crest.Editor
static bool ValidateRendererLayer(GameObject gameObject, ShowMessage messenger, WaterRenderer water)
{
if (water != null && gameObject.layer != water.Layer)
if (water != null && gameObject.layer != water.Surface.Layer)
{
var layerName = LayerMask.LayerToName(water.Layer);
var layerName = LayerMask.LayerToName(water.Surface.Layer);
messenger
(
$"The layer is not the same as the <i>{nameof(WaterRenderer)}.{nameof(WaterRenderer.Layer)} ({layerName})</i> which can cause problems if the <i>{layerName}</i> layer is excluded from any culling masks.",
$"The layer is not the same as the <i>{nameof(WaterRenderer)}.{nameof(WaterRenderer.Surface)}.{nameof(SurfaceRenderer.Layer)} ({layerName})</i> which can cause problems if the <i>{layerName}</i> layer is excluded from any culling masks.",
$"Set layer to <i>{layerName}</i>.",
MessageType.Warning, gameObject,
(_, _) =>
{
Undo.RecordObject(gameObject, $"Change Layer to {layerName}");
gameObject.layer = water.Layer;
gameObject.layer = water.Surface.Layer;
}
);
}
@@ -155,7 +177,7 @@ namespace WaveHarmonic.Crest.Editor
return isValid;
}
var material = water.Material;
var material = water.Surface.Material;
if (material != null)
{
@@ -231,9 +253,38 @@ namespace WaveHarmonic.Crest.Editor
return isValid;
}
if (water != null && water.Material != null)
#if !d_Crest_LegacyUnderwater
if (target.AllCameras)
{
var material = water.Material;
messenger
(
"<i>All Cameras</i> requires <i>Legacy Underwater</i> to be enabled.",
"Either disable <i>All Cameras</i> or enable <i>Project Settings > Crest > Legacy Underwater</i>.",
MessageType.Warning, water
);
}
#endif
if (target.Material != null)
{
var material = target.Material;
if (material.shader.name.StartsWithNoAlloc("Crest/") && material.shader.name != "Crest/Underwater")
{
messenger
(
$"The material {material.name} assigned to Underwater has the wrong shader ({material.shader.name}).",
"Use a material with the correct shader (Crest/Underwater).",
MessageType.Error, water
);
isValid = false;
}
}
if (water != null && water.Surface.Material != null)
{
var material = water.Surface.Material;
var cullModeName =
#if d_UnityURP
@@ -290,6 +341,34 @@ namespace WaveHarmonic.Crest.Editor
return isValid;
}
static bool Validate(Meniscus target, ShowMessage messenger, WaterRenderer water)
{
var isValid = true;
if (!target._Enabled)
{
return isValid;
}
if (target._Material == null)
{
messenger
(
"The meniscus material is missing. The meniscus will not render.",
"Add the default material or your own.",
MessageType.Warning,
water,
(so, sp) =>
{
sp.objectReferenceValue = AssetDatabase.LoadAssetAtPath<Material>(Meniscus.k_MaterialPath);
},
$"{nameof(WaterRenderer._Meniscus)}.{nameof(Meniscus._Material)}"
);
}
return isValid;
}
[Validator(typeof(WaterRenderer))]
static bool Validate(WaterRenderer target, ShowMessage messenger)
{
@@ -299,6 +378,7 @@ namespace WaveHarmonic.Crest.Editor
isValid = isValid && Validate(target._Underwater, messenger, target);
isValid = isValid && Validate(target._Reflections, messenger, target);
isValid = isValid && Validate(target._Meniscus, messenger, target);
isValid = isValid && ValidateNoRotation(target, target.transform, messenger);
isValid = isValid && ValidateNoScale(target, target.transform, messenger);
@@ -326,7 +406,7 @@ namespace WaveHarmonic.Crest.Editor
isValid = false;
}
if (target.Material == null)
if (target.Surface.Material == null)
{
messenger
(
@@ -339,33 +419,33 @@ namespace WaveHarmonic.Crest.Editor
}
else
{
isValid = ValidateWaterMaterial(target, messenger, water, target.Material) && isValid;
isValid = ValidateWaterMaterial(target, messenger, water, target.Surface.Material) && isValid;
if (RenderPipelineHelper.IsHighDefinition && target.Material.GetFloat("_RefractionModel") > 0)
if (RenderPipelineHelper.IsHighDefinition && target.Surface.Material.GetFloat("_RefractionModel") > 0)
{
messenger
(
$"<i>Refraction Model</i> is not <i>None</i> for <i>{target.Material}</i>. " +
$"<i>Refraction Model</i> is not <i>None</i> for <i>{target.Surface.Material}</i>. " +
"This is set by default so it is available in the inspector, " +
"but it incurs an overhead and will produce a dark edge at the edge of the viewport (see <i>Screen Space Refraction > Screen Weight Distance</i>). " +
"Enabling the refraction model is only useful to allow volumetric clouds to render over the water surface when view from above. " +
"The refraction model has no effect on refractions.",
$"Set <i>Refraction Model</i> to <i>None</i>.",
MessageType.Info, target.Material
MessageType.Info, target.Surface.Material
);
}
if (RenderPipelineHelper.IsHighDefinition && target.Material.HasFloat("_TransparentWritingMotionVec") && target.WriteMotionVectors != (target.Material.GetFloat("_TransparentWritingMotionVec") == 1f))
if (RenderPipelineHelper.IsHighDefinition && target.Surface.Material.HasFloat("_TransparentWritingMotionVec") && target.WriteMotionVectors != (target.Surface.Material.GetFloat("_TransparentWritingMotionVec") == 1f))
{
messenger
(
$"<i>Water Renderer > Surface Renderer > Motion Vectors</i> and <i>Transparent Writes Motion Vectors</i> on <i>{target.Material}</i> do not match. ",
$"<i>Water Renderer > Surface Renderer > Motion Vectors</i> and <i>Transparent Writes Motion Vectors</i> on <i>{target.Surface.Material}</i> do not match. ",
$"Either disable or enable both <i>Water Renderer > Surface Renderer > Motion Vectors</i> and <i>Transparent Writes Motion Vectors</i>",
MessageType.Info, target.Material
MessageType.Info, target.Surface.Material
);
}
ValidateMaterialParent(target._VolumeMaterial, target.Material, messenger);
ValidateMaterialParent(target.Surface.VolumeMaterial, target.Surface.Material, messenger);
}
if (Object.FindObjectsByType<WaterRenderer>(FindObjectsInactive.Exclude, FindObjectsSortMode.None).Length > 1)
@@ -403,13 +483,13 @@ namespace WaveHarmonic.Crest.Editor
}
// We need to find hidden probes too, but do not include assets.
if (Resources.FindObjectsOfTypeAll<ReflectionProbe>().Where(x => !EditorUtility.IsPersistent(x)).Count() > 0)
if (Resources.FindObjectsOfTypeAll<ReflectionProbe>().Count(x => !EditorUtility.IsPersistent(x)) > 0)
{
messenger
(
"There are reflection probes in the scene. These can cause tiling to appear on the water surface if not set up correctly.",
"For reflections probes that affect the water, they will either need to cover the visible water tiles or water tiles need to ignore reflection probes (can done done with <i>Water Tile Prefab</i> field). " +
$"For all reflection probles that include the <i>{LayerMask.LayerToName(target.Layer)}</i> layer, make sure they are above the water surface as underwater reflections are not supported.",
$"For all reflection probles that include the <i>{LayerMask.LayerToName(target.Surface.Layer)}</i> layer, make sure they are above the water surface as underwater reflections are not supported.",
MessageType.Info, target
);
}
@@ -470,7 +550,7 @@ namespace WaveHarmonic.Crest.Editor
}
// For safety.
if (target != null && target.Material != null)
if (target != null && target.Surface.Material != null)
{
foreach (var simulation in target.Simulations)
{
@@ -488,7 +568,7 @@ namespace WaveHarmonic.Crest.Editor
);
}
if (target.Viewer == null)
if (target.Viewer == null && !target.IsRunningWithoutGraphics)
{
messenger
(
@@ -503,7 +583,10 @@ namespace WaveHarmonic.Crest.Editor
#if d_UnityHDRP
if (RenderPipelineHelper.IsHighDefinition)
{
var hdAsset = GraphicsSettings.currentRenderPipeline as UnityEngine.Rendering.HighDefinition.HDRenderPipelineAsset;
var material = target.Surface.Material;
var camera = target._Camera != null ? target._Camera : Camera.main;
var hdCamera = camera != null ? HDCamera.GetOrCreate(camera) : null;
var hdAsset = GraphicsSettings.currentRenderPipeline as HDRenderPipelineAsset;
var mvs = hdAsset.currentPlatformRenderPipelineSettings.supportMotionVectors;
// Only check the RP asset for now.
@@ -518,6 +601,50 @@ namespace WaveHarmonic.Crest.Editor
MessageType.Info, target
);
}
if (!hdAsset.currentPlatformRenderPipelineSettings.supportCustomPass)
{
messenger
(
"Custom passes are disabled. Underwater and other features require them to work.",
"Enabled them on the global asset.",
MessageType.Error, hdCamera.camera
);
}
if (target.RenderBeforeTransparency && WaterRenderer.s_CameraMSAA)
{
messenger
(
$"The water injection point is before transparency and MSAA is enabled for a camera. This combination is not currently supported for HDRP.",
"Disable MSAA or change the water injection point.",
MessageType.Error, target
);
}
// Seems that logging is too early for these. And edit mode has false positives.
if (Application.isPlaying && messenger == ValidatedHelper.HelpBox)
{
if (hdCamera?.frameSettings.IsEnabled(FrameSettingsField.CustomPass) == false)
{
messenger
(
$"Custom passes are disabled for the primary camera ({camera}). Underwater and other features require them to work.",
"Enable them in the camera frame settings on the camera or the default frame settings in the global settings.",
MessageType.Error, hdCamera.camera
);
}
if (hdCamera?.frameSettings.IsEnabled(FrameSettingsField.Refraction) == false && material != null && SurfaceRenderer.IsTransparent(material))
{
messenger
(
"Refraction is disabled. Transparency requires it to work.",
"Enable it in the camera frame settings on the camera, or the default frame settings in the global settings.",
MessageType.Error, hdCamera.camera
);
}
}
}
#endif // d_UnityHDRP
@@ -541,11 +668,11 @@ namespace WaveHarmonic.Crest.Editor
}
#endif // d_UnityURP
if (!RenderPipelineHelper.IsHighDefinition && target.Material != null)
if (!RenderPipelineHelper.IsHighDefinition && target.Surface.Material != null)
{
if (!target.AllowRenderQueueSorting && !System.Enum.IsDefined(typeof(RenderQueue), target.Material.renderQueue))
if (!target.Surface.AllowRenderQueueSorting && !System.Enum.IsDefined(typeof(RenderQueue), target.Surface.Material.renderQueue))
{
var field = nameof(WaterRenderer.AllowRenderQueueSorting).Pretty().Italic();
var field = nameof(SurfaceRenderer.AllowRenderQueueSorting).Pretty().Italic();
messenger
(
$"The render queue has a sub-sort applied, but {field} is not enabled. Sub-sorting will not work.",
@@ -563,11 +690,13 @@ namespace WaveHarmonic.Crest.Editor
{
var isValid = true;
var water = Water;
if (Object.FindObjectsByType<WaterRenderer>(FindObjectsInactive.Include, FindObjectsSortMode.None).Length == 0)
{
messenger
(
$"Water body <i>{target.gameObject.name}</i> requires an water renderer component to be present.",
$"Water body <i>{target.gameObject.name}</i> requires a <i>{nameof(WaterRenderer)}</i> component to be present.",
$"Create a separate GameObject and add an <i>{nameof(WaterRenderer)}</i> component to it.",
MessageType.Error, target
);
@@ -595,6 +724,25 @@ namespace WaveHarmonic.Crest.Editor
isValid = isValid && ValidateNoRotation(target, target.transform, messenger);
if (target.Clipped && water != null)
{
// Validate main material, then overriden material.
ValidateLod(OptionalLod.Get(typeof(ClipLod)), messenger, water);
ValidateLod(OptionalLod.Get(typeof(ClipLod)), messenger, water, material: target._Material);
if (water.ClipLod.DefaultClippingState == DefaultClippingState.NothingClipped)
{
messenger
(
$"The {nameof(ClipLod.DefaultClippingState)} on the {nameof(WaterRenderer)} is set to {DefaultClippingState.NothingClipped}. " +
$"The {nameof(WaterBody.Clipped)} option will have no effect.",
$"Disable {nameof(WaterBody.Clipped)} or set {nameof(ClipLod.DefaultClippingState)} to {DefaultClippingState.NothingClipped}.",
MessageType.Warning,
water
);
}
}
return isValid;
}
@@ -602,7 +750,7 @@ namespace WaveHarmonic.Crest.Editor
/// <summary>
/// Does validation for a feature on the water component and on the material
/// </summary>
internal static bool ValidateLod(OptionalLod target, ShowMessage messenger, WaterRenderer water, string dependent = null)
internal static bool ValidateLod(OptionalLod target, ShowMessage messenger, WaterRenderer water, string dependent = null, Material material = null, Object context = null)
{
var isValid = true;
@@ -620,7 +768,7 @@ namespace WaveHarmonic.Crest.Editor
dependentClause = $", as {dependent} needs it.";
}
if (!simulation._Enabled)
if (!simulation._Enabled && material == null)
{
messenger
(
@@ -645,7 +793,10 @@ namespace WaveHarmonic.Crest.Editor
isValid = false;
}
var material = water.Material;
if (material == null)
{
material = water.Surface.Material;
}
if (target.HasMaterialToggle && material != null)
{
@@ -709,7 +860,7 @@ namespace WaveHarmonic.Crest.Editor
}
// These checks are not necessary for our material but there may be custom materials.
if (!water.Material.HasProperty(target.MaterialProperty))
if (!water.Surface.Material.HasProperty(target.MaterialProperty))
{
return true;
}
@@ -717,14 +868,14 @@ namespace WaveHarmonic.Crest.Editor
var feature = target.GetLod(water);
// There is only a problem if there is a mismatch.
if (feature.Enabled == (water.Material.GetFloat(target.MaterialProperty) == 1f))
if (feature._Enabled == (water.Surface.Material.GetFloat(target.MaterialProperty) == 1f))
{
return true;
}
if (feature.Enabled)
if (feature._Enabled)
{
ShowMaterialValidationMessage(target, water.Material, messenger);
ShowMaterialValidationMessage(target, water.Surface.Material, messenger);
}
else if (messenger != DebugLog)
{
@@ -756,7 +907,7 @@ namespace WaveHarmonic.Crest.Editor
);
}
if (target.Blend == LodInputBlend.AlphaClip && target.Mode is not LodInputMode.Texture or LodInputMode.Paint)
if (target.Blend == LodInputBlend.AlphaClip && target.Mode is not (LodInputMode.Texture or LodInputMode.Paint))
{
messenger
(
@@ -999,19 +1150,20 @@ namespace WaveHarmonic.Crest.Editor
{
var isValid = true;
var camera = target._Camera;
if (camera != null && camera.targetTexture != null && target.RealtimeTexture != null)
messenger
(
"If you see an error <i>RenderTexture color format cannot be set to a depth/stencil format</i> or <i>RenderTexture.Create failed</i>, this is likely a bug with Unity (grab pass) or third-party, as they may be registered to execute a custom pass to the DepthProbe camera.", "", MessageType.Info, target
);
if (target.Outdated && (messenger != DebugLog || WaterRendererEditor.ManualValidation))
{
if (target.Outdated)
{
messenger
(
"<i>Depth Probe</i> is outdated.",
"Click <i>Populate</i> or re-bake the probe to bring the probe up-to-date with component changes.",
MessageType.Warning, target,
(_, _) => target.Populate()
);
}
messenger
(
"<i>Depth Probe</i> is outdated.",
"Click <i>Populate</i> or re-bake the probe to bring the probe up-to-date with component changes.",
MessageType.Warning, target,
(_, _) => target.Populate()
);
}
if (target.Type == DepthProbeMode.Baked)
@@ -1042,12 +1194,34 @@ namespace WaveHarmonic.Crest.Editor
messenger
(
"No layers specified for rendering into depth probe.",
"Specify one or may layers using the Layers field.",
"Specify one or many layers using the Layers field.",
MessageType.Error, target
);
isValid = false;
}
#if d_Unity_Terrain
else
{
Terrain.GetActiveTerrains(s_Terrains);
foreach (var terrain in s_Terrains)
{
if (Helpers.MaskIncludesLayer(target.Layers, terrain.gameObject.layer))
{
continue;
}
messenger
(
$"There are terrains on a layer that is not in {nameof(DepthProbe)}.{nameof(DepthProbe.Layers)}.",
"This is typically mistake leading to no data (ie no shorelines). Please ignore if intentional.",
MessageType.Info, target
);
break;
}
}
#endif // d_Unity_Terrain
if (target._Debug._ForceAlwaysUpdateDebug)
{
@@ -1196,6 +1370,19 @@ namespace WaveHarmonic.Crest.Editor
{
isValid = isValid && ValidateSignedDistanceFieldsLod(messenger, water, "Generate Signed Distance Field");
}
if (water.DepthLod.IncludeTerrainHeight && Object.FindAnyObjectByType<Terrain>(FindObjectsInactive.Include) != null)
{
messenger
(
"The Water Depth data is configured to automatically include terrain height via <i>Include Terrain Height</i>. " +
"Using a DepthProbe is still valid to capture non-terrain details like rocks. " +
"But typically, if you are using a DepthProbe, it is best to capture the terrain too, as it is more accurate. " +
"One reason to use a DepthProbe together with the auto capture is for better real-time/on-demand depth capture performance.",
string.Empty,
MessageType.Info, water
);
}
}