移除水

This commit is contained in:
2025-06-21 21:58:06 +08:00
parent d61516a576
commit e9f76d0f11
1566 changed files with 9218 additions and 300913 deletions

View File

@@ -1,19 +0,0 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("WaveHarmonic.Crest.Samples.Editor")]
[assembly: InternalsVisibleTo("WaveHarmonic.Crest.CPUQueries.Editor")]
[assembly: InternalsVisibleTo("WaveHarmonic.Crest.Paint.Editor")]
[assembly: InternalsVisibleTo("WaveHarmonic.Crest.ShallowWater.Editor")]
[assembly: InternalsVisibleTo("WaveHarmonic.Crest.Splines.Editor")]
[assembly: InternalsVisibleTo("WaveHarmonic.Crest.Watercraft.Editor")]
[assembly: InternalsVisibleTo("WaveHarmonic.Crest.Whirlpool.Editor")]
// Define empty namespaces for when assemblies are not present.
namespace UnityEditor.Rendering.HighDefinition { }
namespace UnityEngine.Rendering.HighDefinition { }
namespace UnityEngine.Rendering.Universal { }
namespace WaveHarmonic.Crest.Paint { }
namespace WaveHarmonic.Crest.Splines { }

View File

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

View File

@@ -1,368 +0,0 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
using WaveHarmonic.Crest.Editor.Settings;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEditor.Build;
using UnityEditor.Build.Reporting;
using UnityEditor.Rendering;
using UnityEngine;
using UnityEngine.Rendering;
namespace WaveHarmonic.Crest.Editor.Build
{
sealed class LegacyShaderGraphProcessor : IPreprocessShaders, IPostprocessBuildWithReport
{
static readonly ShaderTagId s_ShaderGraphShaderShaderTagId = new("ShaderGraphShader");
public int callbackOrder => -1;
int _VariantCount;
int _VariantCountStripped;
bool LogVariantStripping =>
#if CREST_DEBUG
true;
#else
false;
#endif
public void OnProcessShader(Shader shader, ShaderSnippetData snippet, IList<ShaderCompilerData> data)
{
// Not one of our shaders.
if (!shader.name.StartsWithNoAlloc("Hidden/Crest/") && !shader.name.StartsWithNoAlloc("Crest/"))
{
return;
}
// Not a Shader Graph.
if (shader.GetSubShaderTag(snippet, s_ShaderGraphShaderShaderTagId) != "true")
{
return;
}
// Sub-shader is not targeting the built-in render pipeline.
if (shader.TryGetRenderPipelineTag(snippet, out _))
{
return;
}
_VariantCount += data.Count;
// Strip BIRP sub-shader if not using BIRP, as Unity only strips HDRP/URP sub-shaders.
if (!RenderPipelineHelper.IsLegacy)
{
_VariantCountStripped += data.Count;
data.Clear();
return;
}
for (var i = data.Count - 1; i >= 0; --i)
{
var keywords = data[i].shaderKeywordSet.GetShaderKeywords();
var isTransparent = keywords.Any(x => x.name == "_BUILTIN_SURFACE_TYPE_TRANSPARENT");
foreach (var keyword in keywords)
{
var name = keyword.name;
var strip =
// Main light shadows. Never used.
name.StartsWithNoAlloc("_MAIN_LIGHT_") ||
// Additional lights. Never used. Although, vertex lighting keyword is set.
name.StartsWithNoAlloc("_ADDITIONAL_LIGHT") ||
// Never used. Used in deferred pass, but not defined in deferred pass.
keyword.name is "LIGHTMAP_SHADOW_MIXING" or "SHADOWS_SHADOWMASK" ||
// Never used.
keyword.name is "_SCREEN_SPACE_OCCLUSION" or "_SHADOWS_SOFT" or "_CASTING_PUNCTUAL_LIGHT_SHADOW" ||
// BIRP does not support this feature (URP does).
keyword.name is "_GBUFFER_NORMALS_OCT" ||
// TODO: check LightMode instead of pass name.
// Shadow keywords are not enabled for transparent objects, except if casting.
isTransparent && snippet.passName == "ShadowCaster" && keyword.name.Contains("SHADOW");
if (strip)
{
_VariantCountStripped++;
data.RemoveAt(i);
break;
}
}
}
}
public void OnPostprocessBuild(BuildReport report)
{
if (LogVariantStripping)
{
Debug.Log($"Crest: {_VariantCountStripped} / {_VariantCount} stripped from Crest BIRP. Total variants: {_VariantCount - _VariantCountStripped}");
}
}
}
sealed class BuildProcessor : IPreprocessComputeShaders, IPreprocessShaders, IPostprocessBuildWithReport
{
public int callbackOrder => 0;
int _VariantCount;
int _VariantCountStripped;
ProjectSettings _Settings;
WaterResources _Resources;
void Logger(string message)
{
Debug.Log(message);
}
bool StripShader(Object shader, IList<ShaderCompilerData> data)
{
_Settings = ProjectSettings.Instance;
_Resources = WaterResources.Instance;
if (!AssetDatabase.GetAssetPath(shader).StartsWithNoAlloc("Packages/com.waveharmonic.crest"))
{
return false;
}
if (shader.name.StartsWithNoAlloc("Hidden/Crest/Samples/"))
{
return false;
}
if (_Settings.DebugEnableStrippingLogging)
{
Logger($"Shader: '{shader.name}' @ {AssetDatabase.GetAssetPath(shader)}");
}
_VariantCount += data.Count;
if (ShouldStripShader(shader))
{
if (_Settings.LogStrippedVariants)
{
Logger($"Stripping Shader: {shader.name}");
}
_VariantCountStripped += data.Count;
data.Clear();
return false;
}
return true;
}
bool ShouldStripVariant(Object shader, ShaderCompilerData data, string[] keywords)
{
return false;
}
bool ShouldStripVariant(ProjectSettings.State state, ShaderCompilerData data, string[] keywords, LocalKeyword keyword, Object shader0, Object shader1)
{
if (shader0 != shader1)
{
return false;
}
return state switch
{
ProjectSettings.State.Disabled => data.shaderKeywordSet.IsEnabled(keyword),
// Strip if keyword is not enabled and appears in one other variant.
ProjectSettings.State.Enabled => !data.shaderKeywordSet.IsEnabled(keyword) && ArrayUtility.Contains(keywords, keyword.name),
_ => false,
};
}
bool ShouldStripVariant(ProjectSettings.State state, ShaderCompilerData data, string[] keywords, ShaderKeyword keyword)
{
return state switch
{
ProjectSettings.State.Disabled => data.shaderKeywordSet.IsEnabled(keyword),
// Strip if keyword is not enabled and appears in one other variant.
ProjectSettings.State.Enabled => !data.shaderKeywordSet.IsEnabled(keyword) && ArrayUtility.Contains(keywords, keyword.name),
_ => false,
};
}
bool ShouldStripVariant(Object shader, ShaderKeyword[] keywords)
{
// Strip debug variants.
if (!EditorUserBuildSettings.development)
{
foreach (var keyword in keywords)
{
if (keyword.name.StartsWithNoAlloc("_DEBUG"))
{
if (_Settings.LogStrippedVariants)
{
Logger($"Stripping Keyword: {keyword.name}");
}
return true;
}
}
}
return false;
}
bool ShouldStripShader(Object shader)
{
if (!EditorUserBuildSettings.development)
{
if (shader.name.Contains("Debug"))
{
return true;
}
}
return false;
}
void StripKeywords(Object shader, IList<ShaderCompilerData> data)
{
// Get all keywords for this kernel/stage.
string[] keywords;
{
var set = new HashSet<ShaderKeyword>();
for (var i = 0; i < data.Count; i++)
{
// Each ShaderCompilerData is a variant which is a combination of keywords. Since each list will be
// different, simply getting a list of all keywords is not possible. This also appears to be the only
// way to get a list of keywords without trying to extract them from shader property names. Lastly,
// shader_feature will be returned only if they are enabled.
set.UnionWith(data[i].shaderKeywordSet.GetShaderKeywords());
}
keywords = set.Select(x => x.name).ToArray();
}
for (var i = data.Count - 1; i >= 0; --i)
{
if (_Settings.LogStrippedVariants)
{
Logger($"Keywords: {string.Join(", ", data[i].shaderKeywordSet.GetShaderKeywords())}");
}
if (ShouldStripVariant(shader, data[i].shaderKeywordSet.GetShaderKeywords()))
{
_VariantCountStripped++;
data.RemoveAt(i);
continue;
}
if (ShouldStripVariant(shader, data[i], keywords))
{
_VariantCountStripped++;
data.RemoveAt(i);
continue;
}
if (_Settings.LogKeptVariants)
{
Logger($"Keywords: {string.Join(", ", data[i].shaderKeywordSet.GetShaderKeywords())}");
}
}
}
bool ShouldStripSubShader(Shader shader, ShaderSnippetData snippet)
{
if (!shader.name.StartsWithNoAlloc("Crest/") && !shader.name.StartsWithNoAlloc("Hidden/Crest/"))
{
return false;
}
// There will be at least three sub-shaders if one per render pipeline.
if (shader.subshaderCount <= 2)
{
return false;
}
// Strip BIRP sub-shader if not using BIRP as Unity only strips HDRP/URP sub-shaders.
if (!RenderPipelineHelper.IsLegacy && !shader.TryGetRenderPipelineTag(snippet, out _))
{
return true;
}
return false;
}
public void OnProcessShader(Shader shader, ShaderSnippetData snippet, IList<ShaderCompilerData> data)
{
// Fixes point light cookie variant trigger shader compiler error:
// > Shader error in 'Crest/Water': call to 'texCUBE' is ambiguous at
// > Buidin/Library/PackageCache/com.unity.shadergraph/Editor/Generation/Targets/BuiltIn/Editor/ShaderGraph/Includes/PBRForwardAddPass.hlsl(58) (on gamecore_scarlett)
if (ProjectSettings.Instance.StripBrokenVariants && RenderPipelineHelper.IsLegacy && shader.name == "Crest/Water")
{
var pointCookie = new LocalKeyword(shader, "POINT_COOKIE");
for (var i = data.Count - 1; i >= 0; --i)
{
var d = data[i];
if (d.buildTarget != BuildTarget.GameCoreXboxSeries && d.shaderCompilerPlatform != ShaderCompilerPlatform.GameCoreXboxSeries)
{
continue;
}
if (d.shaderKeywordSet.IsEnabled(pointCookie))
{
_VariantCountStripped++;
data.RemoveAt(i);
Debug.Log($"Crest: Removing POINT_COOKIE {shader.name} {d.buildTarget} {d.shaderCompilerPlatform}");
continue;
}
}
}
if (!StripShader(shader, data))
{
return;
}
if (ShouldStripSubShader(shader, snippet))
{
_VariantCountStripped += data.Count;
data.Clear();
return;
}
if (_Settings.DebugEnableStrippingLogging)
{
Logger($"Pass {snippet.passName} Type {snippet.passType} Stage {snippet.shaderType}");
}
// TODO: Add stripping specific to pixel shaders here.
StripKeywords(shader, data);
}
public void OnProcessComputeShader(ComputeShader shader, string kernel, IList<ShaderCompilerData> data)
{
if (!StripShader(shader, data))
{
return;
}
if (_Settings.DebugEnableStrippingLogging)
{
Logger($"Kernel {kernel}");
}
// TODO: Add stripping specific to compute shaders here.
StripKeywords(shader, data);
}
public void OnPostprocessBuild(BuildReport report)
{
_Settings = ProjectSettings.Instance;
_Resources = WaterResources.Instance;
if (_Settings.DebugEnableStrippingLogging)
{
Debug.Log($"Crest: {_VariantCountStripped} / {_VariantCount} stripped from Crest. Total variants: {_VariantCount - _VariantCountStripped}");
}
}
}
}

View File

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

View File

@@ -1,496 +0,0 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
using UnityEditor;
using UnityEngine;
using WaveHarmonic.Crest.Internal;
namespace WaveHarmonic.Crest.Editor
{
[CustomEditor(typeof(WaterRenderer))]
sealed class WaterRendererEditor : Inspector
{
WaterRenderer _Target;
void OnEnable()
{
_Target = (WaterRenderer)target;
}
protected override void RenderInspectorGUI()
{
var target = this.target as WaterRenderer;
var currentAssignedTP = serializedObject.FindProperty(nameof(WaterRenderer._TimeProvider)).objectReferenceValue;
base.RenderInspectorGUI();
// Detect if user changed TP, if so update stack
var newlyAssignedTP = serializedObject.FindProperty(nameof(WaterRenderer._TimeProvider)).objectReferenceValue;
if (currentAssignedTP != newlyAssignedTP)
{
if (currentAssignedTP != null)
{
target.TimeProviders.Pop(currentAssignedTP as TimeProvider);
}
if (newlyAssignedTP != null)
{
target.TimeProviders.Push(newlyAssignedTP as TimeProvider);
}
}
// Display version in information box.
{
// Fix leftover nesting from drawers.
EditorGUI.indentLevel = 0;
var padding = GUI.skin.GetStyle("HelpBox").padding;
GUI.skin.GetStyle("HelpBox").padding = new(10, 10, 10, 10);
#if CREST_DEBUG
if (target._Debug._ShowDebugInformation)
{
EditorGUILayout.Space();
var baseScale = target.CalcLodScale(0);
var baseTexelSize = 2f * 2f * baseScale / target.LodResolution;
var message = "";
for (var i = 0; i < target.LodLevels; i++)
{
message += $"LOD: {i}\n";
message += $"Scale: {target.CalcLodScale(i)}\n";
message += $"Texel: {2f * 2f * target.CalcLodScale(i) / target.LodResolution}\n";
message += $"Minimum Slice: {Mathf.Floor(Mathf.Log(Mathf.Max(i / baseTexelSize, 1f), 2f))}";
if (i < target.LodLevels - 1) message += "\n\n";
}
if (target.Material.HasVector(WaterRenderer.ShaderIDs.s_Absorption))
{
message += $"\n\nDepth Fog Density: {target.Material.GetVector(WaterRenderer.ShaderIDs.s_Absorption)}";
}
EditorGUILayout.HelpBox(message, MessageType.None);
}
#endif
GUI.skin.GetStyle("HelpBox").padding = padding;
}
}
protected override void RenderBottomButtons()
{
base.RenderBottomButtons();
var target = this.target as WaterRenderer;
EditorGUILayout.Space();
#if CREST_DEBUG
if (GUILayout.Button("Change Scale"))
{
var scale = target.ScaleRange;
scale.x = scale.x == 4f ? 256f : 4f;
target.ScaleRange = scale;
EditorApplication.isPaused = false;
}
#endif
if (GUILayout.Button("Validate Setup"))
{
ValidatedHelper.ExecuteValidators(target, ValidatedHelper.DebugLog);
foreach (var component in FindObjectsByType<EditorBehaviour>(FindObjectsSortMode.None))
{
if (component is WaterRenderer) continue;
ValidatedHelper.ExecuteValidators(component, ValidatedHelper.DebugLog);
}
Debug.Log("Crest: Validation complete!", target);
}
}
}
[CustomEditor(typeof(WaveSpectrum))]
sealed class WaveSpectrumEditor : Inspector, IEmbeddableEditor
{
static readonly string[] s_ModelDescriptions = new[]
{
"Select an option to author waves using a spectrum model.",
"Fully developed sea with infinite fetch.",
};
static readonly GUIContent s_TimeScaleLabel = new("Time Scale");
System.Type _HostComponentType = null;
public void SetTypeOfHostComponent(System.Type hostComponentType)
{
_HostComponentType = hostComponentType;
}
void OnEnable()
{
Undo.undoRedoEvent -= OnUndoRedo;
Undo.undoRedoEvent += OnUndoRedo;
}
protected override void OnDisable()
{
base.OnDisable();
Undo.undoRedoEvent -= OnUndoRedo;
}
void OnUndoRedo(in UndoRedoInfo info)
{
var target = (WaveSpectrum)this.target;
if (info.undoName == "Change Spectrum")
{
target.InitializeHandControls();
}
}
protected override void RenderInspectorGUI()
{
// Display a notice if its being edited as a standalone asset (not embedded in a component) because
// it displays the FFT-interface.
if (_HostComponentType == null)
{
EditorGUILayout.HelpBox("This editor is displaying the FFT spectrum settings. " +
"To edit settings specific to the ShapeGerstner component, assign this asset to a ShapeGerstner component " +
"and edit it there by expanding the Spectrum field.", MessageType.Info);
EditorGUILayout.Space();
}
base.RenderInspectorGUI();
EditorGUI.BeginChangeCheck();
var beingEditedOnGerstnerComponent = _HostComponentType == typeof(ShapeGerstner);
var showAdvancedControls = false;
if (beingEditedOnGerstnerComponent)
{
EditorGUILayout.PropertyField(serializedObject.FindProperty(nameof(WaveSpectrum._GravityScale)));
EditorGUILayout.PropertyField(serializedObject.FindProperty(nameof(WaveSpectrum._WaveDirectionVariance)));
EditorGUILayout.PropertyField(serializedObject.FindProperty(nameof(WaveSpectrum._ShowAdvancedControls)));
showAdvancedControls = serializedObject.FindProperty(nameof(WaveSpectrum._ShowAdvancedControls)).boolValue;
}
else
{
EditorGUILayout.PropertyField(serializedObject.FindProperty(nameof(WaveSpectrum._GravityScale)), s_TimeScaleLabel);
}
var spSpectrumModel = serializedObject.FindProperty(nameof(WaveSpectrum._Model));
var spectraIndex = serializedObject.FindProperty(nameof(WaveSpectrum._Model)).enumValueIndex;
var spectrumModel = (WaveSpectrum.SpectrumModel)Mathf.Clamp(spectraIndex, 0, 1);
EditorGUILayout.Space();
var spDisabled = serializedObject.FindProperty(nameof(WaveSpectrum._PowerDisabled));
EditorGUILayout.BeginHorizontal();
var allEnabled = true;
for (var i = 0; i < spDisabled.arraySize; i++)
{
if (spDisabled.GetArrayElementAtIndex(i).boolValue) allEnabled = false;
}
var toggle = allEnabled;
if (toggle != EditorGUILayout.Toggle(toggle, GUILayout.Width(13f)))
{
for (var i = 0; i < spDisabled.arraySize; i++)
{
spDisabled.GetArrayElementAtIndex(i).boolValue = toggle;
}
}
EditorGUILayout.LabelField("Spectrum", EditorStyles.boldLabel);
EditorGUILayout.EndHorizontal();
var spec = target as WaveSpectrum;
var spPower = serializedObject.FindProperty(nameof(WaveSpectrum._PowerLogarithmicScales));
var spChopScales = serializedObject.FindProperty(nameof(WaveSpectrum._ChopScales));
var spGravScales = serializedObject.FindProperty(nameof(WaveSpectrum._GravityScales));
// Disable sliders if authoring with model.
var canEditSpectrum = spectrumModel == WaveSpectrum.SpectrumModel.None;
for (var i = 0; i < spPower.arraySize; i++)
{
EditorGUILayout.BeginHorizontal();
var spDisabled_i = spDisabled.GetArrayElementAtIndex(i);
spDisabled_i.boolValue = !EditorGUILayout.Toggle(!spDisabled_i.boolValue, GUILayout.Width(15f));
var smallWL = WaveSpectrum.SmallWavelength(i);
var spPower_i = spPower.GetArrayElementAtIndex(i);
var isPowerDisabled = spDisabled_i.boolValue;
var powerValue = isPowerDisabled ? WaveSpectrum.s_MinimumPowerLog : spPower_i.floatValue;
if (showAdvancedControls)
{
EditorGUILayout.LabelField(string.Format("{0}", smallWL), EditorStyles.boldLabel);
EditorGUILayout.EndHorizontal();
// Disable slider if authoring with model.
using (new EditorGUI.DisabledGroupScope(!canEditSpectrum || spDisabled_i.boolValue))
{
powerValue = EditorGUILayout.Slider(" Power", powerValue, WaveSpectrum.s_MinimumPowerLog, WaveSpectrum.s_MaximumPowerLog);
}
}
else
{
EditorGUILayout.LabelField(string.Format("{0}", smallWL), GUILayout.Width(50f));
// Disable slider if authoring with model.
using (new EditorGUI.DisabledGroupScope(!canEditSpectrum || spDisabled_i.boolValue))
{
powerValue = EditorGUILayout.Slider(powerValue, WaveSpectrum.s_MinimumPowerLog, WaveSpectrum.s_MaximumPowerLog);
}
EditorGUILayout.EndHorizontal();
// This will create a tooltip for slider.
GUI.Label(GUILayoutUtility.GetLastRect(), new GUIContent("", powerValue.ToString()));
}
// If the power is disabled, we are using the MIN_POWER_LOG value so we don't want to store it.
if (!isPowerDisabled)
{
spPower_i.floatValue = powerValue;
}
if (showAdvancedControls)
{
EditorGUILayout.Slider(spChopScales.GetArrayElementAtIndex(i), 0f, 4f, " Chop Scale");
EditorGUILayout.Slider(spGravScales.GetArrayElementAtIndex(i), 0f, 4f, " Grav Scale");
}
}
EditorGUILayout.Space();
EditorGUILayout.LabelField("Empirical Spectra", EditorStyles.boldLabel);
EditorGUILayout.BeginHorizontal();
spectrumModel = (WaveSpectrum.SpectrumModel)EditorGUILayout.EnumPopup(spectrumModel);
spSpectrumModel.enumValueIndex = (int)spectrumModel;
EditorGUILayout.EndHorizontal();
EditorGUILayout.Space();
EditorGUILayout.HelpBox(s_ModelDescriptions[(int)spectrumModel], MessageType.Info);
EditorGUILayout.Space();
if (spectrumModel == WaveSpectrum.SpectrumModel.None)
{
Undo.RecordObject(spec, "Change Spectrum");
}
else
{
// It doesn't seem to matter where this is called.
Undo.RecordObject(spec, $"Apply {ObjectNames.NicifyVariableName(spectrumModel.ToString())} Spectrum");
// Descriptions from this very useful paper:
// https://hal.archives-ouvertes.fr/file/index/docid/307938/filename/frechot_realistic_simulation_of_ocean_surface_using_wave_spectra.pdf
switch (spectrumModel)
{
case WaveSpectrum.SpectrumModel.PiersonMoskowitz:
spec.ApplyPiersonMoskowitzSpectrum();
break;
}
}
if (EditorGUI.EndChangeCheck())
{
// NOTE: Undo/Redo will not update for some reason.
serializedObject.ApplyModifiedProperties();
spec.InitializeHandControls();
}
if (GUI.changed)
{
// We need to call this otherwise any property which has HideInInspector won't save.
EditorUtility.SetDirty(spec);
}
}
}
[CustomEditor(typeof(LodSettings), true)]
sealed class SimSettingsBaseEditor : Inspector
{
protected override void RenderInspectorGUI()
{
EditorGUILayout.Space();
if (GUILayout.Button("Open Online Help Page"))
{
var targetType = target.GetType();
var helpAttribute = (HelpURL)System.Attribute.GetCustomAttribute(targetType, typeof(HelpURL));
Debug.AssertFormat(helpAttribute != null, "Crest: Could not get HelpURL attribute from {0}.", targetType);
Application.OpenURL(helpAttribute.URL);
}
EditorGUILayout.Space();
base.RenderInspectorGUI();
}
}
[CustomEditor(typeof(WaterChunkRenderer)), CanEditMultipleObjects]
sealed class WaterChunkRendererEditor : Inspector
{
Renderer _Renderer;
protected override void RenderInspectorGUI()
{
base.RenderInspectorGUI();
var target = this.target as WaterChunkRenderer;
if (_Renderer == null)
{
_Renderer = target.GetComponent<Renderer>();
}
GUI.enabled = false;
var boundsXZ = new Bounds(target._UnexpandedBoundsXZ.center.XNZ(), target._UnexpandedBoundsXZ.size.XNZ());
EditorGUILayout.BoundsField("Bounds XZ", boundsXZ);
EditorGUILayout.BoundsField("Expanded Bounds", _Renderer.bounds);
GUI.enabled = true;
}
}
[CustomEditor(typeof(DepthProbe))]
sealed class DepthProbeEditor : Inspector
{
[InitializeOnLoadMethod]
static void OnLoad()
{
// Allows DepthProbe to trigger a bake without referencing assembly.
DepthProbe.OnBakeRequest -= Bake;
DepthProbe.OnBakeRequest += Bake;
}
protected override void RenderBottomButtons()
{
base.RenderBottomButtons();
EditorGUILayout.Space();
var target = this.target as DepthProbe;
var isBaked = target.Type == DepthProbeMode.Baked;
var onDemand = target.Type == DepthProbeMode.RealTime && target.RefreshMode == DepthProbeRefreshMode.ViaScripting;
var canBake = !onDemand && !Application.isPlaying;
var canPopulate = Application.isPlaying ? onDemand : target.Type != DepthProbeMode.Baked;
if (target.SavedTexture != null && isBaked ? GUILayout.Button("Switch to Real-Time") : GUILayout.Button("Switch to Baked"))
{
Undo.RecordObject(target, isBaked ? "Switch to Real-Time" : "Switch to Baked");
target.Type = isBaked ? DepthProbeMode.RealTime : DepthProbeMode.Baked;
EditorUtility.SetDirty(target);
}
if (canPopulate && GUILayout.Button("Populate"))
{
target.Populate();
}
if (canBake && GUILayout.Button("Bake"))
{
Bake(target);
}
}
internal static void Bake(DepthProbe target)
{
target.ForcePopulate();
var rt = target.RealtimeTexture;
RenderTexture.active = rt;
var tex = new Texture2D(rt.width, rt.height, TextureFormat.RGBAHalf, false);
tex.ReadPixels(new(0, 0, rt.width, rt.height), 0, 0);
RenderTexture.active = null;
byte[] bytes;
bytes = tex.EncodeToEXR(Texture2D.EXRFlags.OutputAsFloat);
var path = target.SavedTexture
? AssetDatabase.GetAssetPath(target.SavedTexture)
: $"Assets/DepthProbe_{System.Guid.NewGuid()}.exr";
System.IO.File.WriteAllBytes(path, bytes);
AssetDatabase.ImportAsset(path);
if (target.SavedTexture == null)
{
var serializedObject = new SerializedObject(target);
serializedObject.FindProperty(nameof(DepthProbe._SavedTexture)).objectReferenceValue = AssetDatabase.LoadAssetAtPath<Texture2D>(path);
serializedObject.FindProperty(nameof(DepthProbe._Type)).enumValueIndex = (int)DepthProbeMode.Baked;
serializedObject.ApplyModifiedProperties();
}
var ti = AssetImporter.GetAtPath(path) as TextureImporter;
ti.textureShape = TextureImporterShape.Texture2D;
ti.sRGBTexture = false;
ti.alphaSource = TextureImporterAlphaSource.None;
ti.mipmapEnabled = false;
ti.alphaIsTransparency = false;
// Compression will clamp negative values.
ti.textureCompression = TextureImporterCompression.Uncompressed;
ti.filterMode = FilterMode.Bilinear;
ti.wrapMode = TextureWrapMode.Clamp;
// Values are slightly different with NPOT Scale applied.
ti.npotScale = TextureImporterNPOTScale.None;
// Set single component.
if (!target._GenerateSignedDistanceField)
{
ti.textureType = TextureImporterType.SingleChannel;
var settings = new TextureImporterSettings();
ti.ReadTextureSettings(settings);
settings.singleChannelComponent = TextureImporterSingleChannelComponent.Red;
ti.SetTextureSettings(settings);
}
// Set format.
{
var settings = ti.GetDefaultPlatformTextureSettings();
settings.format = target._GenerateSignedDistanceField ? TextureImporterFormat.RGFloat : TextureImporterFormat.RFloat;
ti.SetPlatformTextureSettings(settings);
}
ti.SaveAndReimport();
Debug.Log("Crest: Probe saved to " + path, AssetDatabase.LoadAssetAtPath<Object>(path));
}
}
[CustomEditor(typeof(QueryEvents))]
sealed class QueryEventsEditor : Inspector
{
protected override void RenderInspectorGUI()
{
EditorGUILayout.Space();
EditorGUILayout.HelpBox
(
"For the Above/Below Water Surface Events, whenever this game object goes below or above the water " +
"surface, the appropriate event is fired once per state change. It can be used to trigger audio to " +
"play underwater and much more. For the Distance From Water Surface event, it will pass the " +
"distance every frame (passing normalised distance to audio volume as an example).",
MessageType.Info
);
EditorGUILayout.Space();
base.RenderInspectorGUI();
}
}
[CustomEditor(typeof(NetworkedTimeProvider))]
sealed class NetworkedTimeProviderEditor : Inspector
{
public override void OnInspectorGUI()
{
EditorGUILayout.HelpBox($"Assign this component to the <i>{nameof(WaterRenderer)}</i> component and set the TimeOffsetToServer property of this component (at runtime from C#) to the delta from this client's time to the shared server time.", MessageType.Info);
}
}
}

View File

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

View File

@@ -1,410 +0,0 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
using System.Linq;
using UnityEditor;
using UnityEngine;
using WaveHarmonic.Crest.Internal;
namespace WaveHarmonic.Crest.Editor
{
static class Visualizers
{
#if CREST_DEBUG
static Material s_VisualizeMaterial;
static Material VisualizeMaterial => s_VisualizeMaterial != null ? s_VisualizeMaterial
: s_VisualizeMaterial = new(Shader.Find("Local/Debug/Visualize Signed Texture"));
#endif
[DrawGizmo(GizmoType.Selected | GizmoType.NonSelected)]
static void DrawGizmos(WaterRenderer target, GizmoType type)
{
#if CREST_DEBUG
if (target._Debug._DrawLodOutline)
{
// Each LOD could have its own position due to snapping.
foreach (var simulation in target.Simulations.Cast<Lod>())
{
if (!simulation._Enabled) continue;
for (var index = 0; index < simulation.Slices; index++)
{
Gizmos.color = simulation.GizmoColor;
var rect = simulation.Cascades[index].TexelRect;
Gizmos.DrawWireCube
(
rect.center.XNZ(target.SeaLevel),
rect.size.XNZ()
);
}
}
}
#endif
// Don't need proxy if in play mode
if (Application.isPlaying)
{
return;
}
// Create proxy if not present already, and proxy enabled
if (target._ProxyPlane == null && target._ShowWaterProxyPlane)
{
target._ProxyPlane = GameObject.CreatePrimitive(PrimitiveType.Plane);
Helpers.Destroy(target._ProxyPlane.GetComponent<Collider>());
target._ProxyPlane.hideFlags = HideFlags.HideAndDontSave;
target._ProxyPlane.transform.parent = target.transform;
target._ProxyPlane.transform.SetLocalPositionAndRotation(Vector3.zero, Quaternion.identity);
target._ProxyPlane.transform.localScale = 4000f * Vector3.one;
target._ProxyPlane.GetComponent<Renderer>().sharedMaterial = new(Shader.Find(WaterRenderer.k_ProxyShader));
}
// Change active state of proxy if necessary
if (target._ProxyPlane != null && target._ProxyPlane.activeSelf != target._ShowWaterProxyPlane)
{
target._ProxyPlane.SetActive(target._ShowWaterProxyPlane);
// Scene view doesnt automatically refresh which makes the option confusing, so force it
EditorWindow view = EditorWindow.GetWindow<SceneView>();
view.Repaint();
}
if (target.Root != null)
{
target.Root.gameObject.SetActive(!target._ShowWaterProxyPlane);
}
}
[DrawGizmo(GizmoType.Selected)]
static void DrawGizmos(LodInput target, GizmoType type)
{
if (!target.Enabled) return;
if (target._DrawBounds)
{
var rect = target.Rect;
if (rect != Rect.zero)
{
var height = WaterRenderer.Instance ? WaterRenderer.Instance.SeaLevel : target.transform.position.y;
Gizmos.color = Color.magenta;
Gizmos.DrawWireCube
(
new(rect.center.x, height, rect.center.y),
new(rect.size.x, 0, rect.size.y)
);
}
}
}
[DrawGizmo(GizmoType.Selected)]
static void DrawRendererGizmos(LodInput target, GizmoType type)
{
if (!target.Enabled) return;
if (target.Data is not RendererLodInputData data) return;
var renderer = data._Renderer;
if (renderer != null && renderer.TryGetComponent<MeshFilter>(out var mf))
{
var transform = renderer.transform;
Gizmos.color = target.GizmoColor;
Gizmos.DrawWireMesh(mf.sharedMesh, transform.position, transform.rotation, transform.lossyScale);
}
}
[DrawGizmo(GizmoType.Selected)]
static void DrawGeometryGizmos(LodInput target, GizmoType type)
{
if (!target.Enabled) return;
if (target.Data is not GeometryLodInputData data) return;
var geometry = data._Geometry;
if (geometry != null)
{
var transform = target.transform;
Gizmos.color = target.GizmoColor;
Gizmos.DrawWireMesh(geometry, transform.position, transform.rotation, transform.lossyScale);
}
}
[DrawGizmo(GizmoType.Selected)]
static void DrawWatertightHullGizmos(WatertightHull target, GizmoType type)
{
if (!target.Enabled) return;
var transform = target.transform;
Gizmos.color = ClipLod.s_GizmoColor;
Gizmos.DrawMesh(target._Mesh, submeshIndex: 0, transform.position, transform.rotation, transform.lossyScale);
Gizmos.DrawWireMesh(target._Mesh, transform.position, transform.rotation, transform.lossyScale);
if (target._Debug._DrawBounds)
{
var rect = target.Rect;
if (rect != Rect.zero)
{
var height = WaterRenderer.Instance ? WaterRenderer.Instance.SeaLevel : target.transform.position.y;
Gizmos.color = Color.magenta;
Gizmos.DrawWireCube
(
new(rect.center.x, height, rect.center.y),
new(rect.size.x, 0, rect.size.y)
);
}
}
}
[DrawGizmo(GizmoType.Selected)]
static void DrawTextureGizmos(LodInput target, GizmoType type)
{
if (!target.Enabled) return;
if (target.Data is not TextureLodInputData) return;
Gizmos.color = target.GizmoColor;
Gizmos.matrix = Matrix4x4.TRS
(
target.transform.position,
Quaternion.Euler(Vector3.up * target.transform.rotation.eulerAngles.y),
target.transform.lossyScale.XNZ()
);
Gizmos.DrawWireCube(Vector3.zero, Vector3.one);
}
[DrawGizmo(GizmoType.Selected)]
static void DrawGizmos(ClipLodInput target, GizmoType type)
{
if (!target.Enabled) return;
Gizmos.color = target.GizmoColor;
if (target.Mode == LodInputMode.Primitive)
{
Gizmos.matrix = target.transform.localToWorldMatrix;
switch (target._Primitive)
{
case LodInputPrimitive.Sphere:
// Use Unity's UV sphere mesh for gizmos as Gizmos.DrawSphere is too low resolution.
// Render mesh and wire sphere at default size (0.5m radius) which is scaled by gizmo matrix.
Gizmos.DrawMesh(Helpers.SphereMesh, submeshIndex: 0, Vector3.zero, Quaternion.identity, Vector3.one);
Gizmos.DrawWireSphere(Vector3.zero, 0.5f);
break;
case LodInputPrimitive.Cube:
// Render mesh and wire box at default size which is scaled by gizmo matrix.
Gizmos.DrawCube(Vector3.zero, Vector3.one);
Gizmos.DrawWireCube(Vector3.zero, Vector3.one);
break;
case LodInputPrimitive.Quad:
// Face quad upwards.
Gizmos.matrix *= Matrix4x4.Rotate(Quaternion.AngleAxis(90, Vector3.right));
Gizmos.DrawMesh(Helpers.QuadMesh, submeshIndex: 0, Vector3.zero, Quaternion.identity, Vector3.one);
Gizmos.DrawWireMesh(Helpers.QuadMesh, submeshIndex: 0, Vector3.zero, Quaternion.identity, Vector3.one);
break;
default:
Debug.LogError("Crest: Not a valid primitive type!");
break;
}
}
}
[DrawGizmo(GizmoType.Selected | GizmoType.NonSelected)]
static void DrawGizmos(DepthProbe target, GizmoType type)
{
#if CREST_DEBUG
if (target._Debug._ShowSimulationDataInScene)
{
VisualizeMaterial.mainTexture = target.Texture;
VisualizeMaterial.SetPass(0);
Graphics.DrawMeshNow
(
Helpers.QuadMesh,
Matrix4x4.TRS(target.Position, Quaternion.Euler(90f, 0, 0), target.Scale)
);
}
#endif
if (!type.HasFlag(GizmoType.Selected))
{
return;
}
// Lod Input gizmo.
Gizmos.matrix = Matrix4x4.TRS(target.Position, target.Rotation, target.Scale.XNZ(1f));
Gizmos.color = DepthLod.s_GizmoColor;
Gizmos.DrawWireCube(Vector3.zero, new(1f, 0f, 1f));
if (target.Type == DepthProbeMode.RealTime)
{
Gizmos.color = Color.white;
var scale = Vector3.one;
var position = Vector3.zero;
position.y = Mathf.LerpUnclamped(target._CaptureRange.x, target._CaptureRange.y, target._CaptureRange.y / scale.y);
position.y *= 0.5f;
Gizmos.DrawWireCube(position, scale.XNZ(-target._CaptureRange.x + target._CaptureRange.y));
position.y = target._CaptureRange.y + target._FillHolesCaptureHeight * 0.5f;
Gizmos.DrawWireCube(position, scale.XNZ(target._FillHolesCaptureHeight));
var size = Gizmos.matrix.lossyScale.XZ();
var offset = Vector3.one * 0.5f;
var height0 = target._CaptureRange.y;
var height1 = target._CaptureRange.x;
var height2 = target._CaptureRange.y + target._FillHolesCaptureHeight;
DrawGrid(size, offset.XNZ(-height0));
DrawGrid(size, offset.XNZ(-height1));
DrawGrid(size, offset.XNZ(-height2));
Gizmos.color = new(1f, 1f, 1f, 0.2f);
Gizmos.DrawCube(Vector3.zero.XNZ(height0), Vector3.one.XNZ());
Gizmos.DrawCube(Vector3.zero.XNZ(height1), Vector3.one.XNZ());
Gizmos.DrawCube(Vector3.zero.XNZ(height2), Vector3.one.XNZ());
}
}
[DrawGizmo(GizmoType.Selected | GizmoType.NonSelected)]
static void DrawGizmos(ShapeWaves target, GizmoType type)
{
if (!target.Enabled) return;
if (target._DrawBounds)
{
// Render bounds.
var water = WaterRenderer.Instance;
var rect = target._Rect;
if (water != null && rect != null && target.Mode != LodInputMode.Global)
{
Gizmos.DrawWireCube(new(rect.center.x, water.SeaLevel, rect.center.y), new(rect.size.x, 0, rect.size.y));
}
}
}
[DrawGizmo(GizmoType.Selected)]
static void DrawGizmos(SphereWaterInteraction target, GizmoType type)
{
Gizmos.color = DynamicWavesLod.s_GizmoColor;
Gizmos.DrawWireSphere(target.transform.position + target._VelocityOffset * target._Velocity, target._Radius);
}
[DrawGizmo(GizmoType.Selected)]
static void DrawGizmos(WaterBody target, GizmoType type)
{
var oldColor = Gizmos.color;
Gizmos.color = new(1f, 1f, 1f, 0.5f);
var center = target.AABB.center;
var size = 2f * new Vector3(target.AABB.extents.x, 1f, target.AABB.extents.z);
Gizmos.DrawCube(center, size);
Gizmos.color = Color.white;
Gizmos.DrawWireCube(center, size);
Gizmos.color = oldColor;
}
[DrawGizmo(GizmoType.Selected | GizmoType.NonSelected)]
static void DrawGizmos(WaterChunkRenderer target, GizmoType type)
{
if (target._DrawRenderBounds)
{
target.Rend.bounds.GizmosDraw();
}
if (!type.HasFlag(GizmoType.Selected))
{
return;
}
if (target.Rend != null)
{
target.Rend.bounds.GizmosDraw();
}
if (WaterBody.WaterBodies.Count > 0)
{
Gizmos.color = Color.green;
Gizmos.DrawWireCube
(
target._UnexpandedBoundsXZ.center.XNZ(target.transform.position.y),
target._UnexpandedBoundsXZ.size.XNZ()
);
}
}
[DrawGizmo(GizmoType.Selected)]
static void DrawGizmos(FloatingObject target, GizmoType type)
{
if (!target.TryGetComponent<Rigidbody>(out var physics)) return;
Gizmos.color = Color.yellow;
Gizmos.DrawCube(target.transform.TransformPoint(physics.centerOfMass), Vector3.one * 0.25f);
if (target.Model != FloatingObjectModel.Probes) return;
for (var i = 0; i < target._Probes.Length; i++)
{
var point = target._Probes[i];
var transformedPoint = target.transform.TransformPoint(point._Position + new Vector3(0, physics.centerOfMass.y, 0));
Gizmos.color = Color.red;
Gizmos.DrawCube(transformedPoint, Vector3.one * 0.5f);
}
}
static void DrawGrid(Vector2 size, Vector3 offset)
{
var xPoints = new Vector3[Mathf.FloorToInt(size.x / 2f) * 2];
var zPoints = new Vector3[Mathf.FloorToInt(size.y / 2f) * 2];
var xCellSize = 1f / size.x;
var zCellSize = 1f / size.y;
var xSize = size.x;
var zSize = size.y;
for (var x = 0; x < xPoints.Length; x += 2)
{
xPoints[x + 0] = new Vector3(x * xCellSize, 0, 0) - offset;
xPoints[x + 1] = new Vector3(x * xCellSize, 0, xSize * xCellSize) - offset;
}
Gizmos.DrawLineList(xPoints);
for (var z = 0; z < zPoints.Length; z += 2)
{
zPoints[z + 0] = new Vector3(0, 0, z * zCellSize) - offset;
zPoints[z + 1] = new Vector3(zSize * zCellSize, 0, z * zCellSize) - offset;
}
Gizmos.DrawLineList(zPoints);
}
}
static class BoundsHelper
{
internal static void GizmosDraw(this Bounds b)
{
var xmin = b.min.x;
var ymin = b.min.y;
var zmin = b.min.z;
var xmax = b.max.x;
var ymax = b.max.y;
var zmax = b.max.z;
Gizmos.DrawLine(new(xmin, ymin, zmin), new(xmin, ymin, zmax));
Gizmos.DrawLine(new(xmin, ymin, zmin), new(xmax, ymin, zmin));
Gizmos.DrawLine(new(xmax, ymin, zmax), new(xmin, ymin, zmax));
Gizmos.DrawLine(new(xmax, ymin, zmax), new(xmax, ymin, zmin));
Gizmos.DrawLine(new(xmin, ymax, zmin), new(xmin, ymax, zmax));
Gizmos.DrawLine(new(xmin, ymax, zmin), new(xmax, ymax, zmin));
Gizmos.DrawLine(new(xmax, ymax, zmax), new(xmin, ymax, zmax));
Gizmos.DrawLine(new(xmax, ymax, zmax), new(xmax, ymax, zmin));
Gizmos.DrawLine(new(xmax, ymax, zmax), new(xmax, ymin, zmax));
Gizmos.DrawLine(new(xmin, ymin, zmin), new(xmin, ymax, zmin));
Gizmos.DrawLine(new(xmax, ymin, zmin), new(xmax, ymax, zmin));
Gizmos.DrawLine(new(xmin, ymax, zmax), new(xmin, ymin, zmax));
}
}
}

View File

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

View File

@@ -1,69 +0,0 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
// Repository of custom material property drawers.
// All drawers must be prefixed with Crest as they are global.
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
namespace WaveHarmonic.Crest.Editor
{
static class MaterialAttributes
{
internal sealed class MaterialAttribute
{
public Vector2Int _IntegerRange;
}
internal static readonly Dictionary<string, MaterialAttribute> s_Common = new()
{
};
internal static readonly Dictionary<string, Dictionary<string, MaterialAttribute>> s_Grouped = new()
{
{
"Crest/Underwater", new()
{
{ "_Crest_DataSliceOffset", new() { _IntegerRange = new(0, Lod.k_MaximumSlices - 2) } },
}
},
};
}
sealed class CrestIntegerRangeDrawer : MaterialPropertyDrawer
{
// Adapted from:
// https://github.com/Unity-Technologies/UnityCsReference/blob/b44c4cc9e4ce3dfa0bab2fe4bf7efae880c5a175/Editor/Mono/Inspector/MaterialEditor.cs#L1277-L1298
public override void OnGUI(Rect position, MaterialProperty property, GUIContent label, MaterialEditor editor)
{
MaterialEditor.BeginProperty(position, property);
EditorGUI.BeginChangeCheck();
// For range properties we want to show the slider so we adjust label width to use default width (setting it to 0)
// See SetDefaultGUIWidths where we set: EditorGUIUtility.labelWidth = GUIClip.visibleRect.width - EditorGUIUtility.fieldWidth - 17;
var oldLabelWidth = EditorGUIUtility.labelWidth;
EditorGUIUtility.labelWidth = 0f;
var material = editor.target as Material;
var shader = material.shader;
var attribute = MaterialAttributes.s_Grouped.GetValueOrDefault(shader.name, null)?.GetValueOrDefault(property.name, null);
attribute ??= MaterialAttributes.s_Common[property.name];
var newValue = EditorGUI.IntSlider(position, label, property.intValue, attribute._IntegerRange.x, attribute._IntegerRange.y);
EditorGUIUtility.labelWidth = oldLabelWidth;
if (EditorGUI.EndChangeCheck())
{
property.intValue = newValue;
}
MaterialEditor.EndProperty();
}
}
}

View File

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

View File

@@ -1,330 +0,0 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEditor.Rendering;
using UnityEditor.Rendering.HighDefinition;
using UnityEngine;
namespace WaveHarmonic.Crest.Editor
{
static class MaterialTooltips
{
internal static readonly Dictionary<string, string> s_Common = new()
{
// Feather
{ "_Crest_Feather", "Feather the edges of the mesh using the texture coordinates. Easiest to understand with a plane" },
{ "_Crest_FeatherWidth", "How far from edge to feather" },
};
internal static readonly Dictionary<string, Dictionary<string, string>> s_Grouped = new()
{
{
"Crest/Inputs/Animated Waves/Add From Texture", new()
{
{ "_Crest_HeightsOnly", "Treats the texture as a heightmap and reads from the R channel" },
}
},
{
"Crest/Inputs/Flow/Add From Texture", new()
{
{ "_Crest_FlipX", "Flips the X direction (R channel)" },
{ "_Crest_FlipZ", "Flips the Z direction (Y channel)" },
{ "_Crest_NegativeValues", "Whether the texture supports negative values otherwise assumes data is packed in 0-1 range" },
}
},
{
"Crest/Inputs/All/Scale", new()
{
{ "_Crest_Scale", "Scale the water data. Zero is no data and one leaves data untouched" },
{ "_Crest_ApplyTexture", "Use the texture instead of the scale value" },
{ "_Crest_Invert", "Inverts the scale value" },
}
},
{
"Crest/Inputs/Shape Waves/Add From Geometry", new()
{
{ "_Crest_FeatherWaveStart", "Controls ramp distance over which waves grow/fade as they move forwards" },
}
},
{
"Crest/Underwater", new()
{
{ "_Crest_ExtinctionMultiplier", "Scales the depth fog density. Useful to reduce the intensity of the depth fog when underwater only" },
{ "_Crest_SunBoost", "Boost the intensity of the sun scattering" },
{ "_Crest_OutScatteringFactor", "Applied to the water depth when calculating out-scattering. Less means it gets darker deeper" },
{ "_Crest_OutScatteringExtinctionFactor", "Applied to the distance where the out-scattering gradient is calculated. Lower decreases out-scattering influence" },
{ "_Crest_DitheringEnabled", "Dithering will reduce banding" },
{ "_Crest_DitheringIntensity", "Increase if banding persists" },
{ "_Crest_MeniscusEnabled", "Add a meniscus to the boundary between water and air" },
{ "_Crest_DataSliceOffset", "How much to smooth water data such as water depth, light scattering, shadowing. Helps to smooth flickering that can occur under camera motion" },
}
},
{
WaterShaderUtility.k_ShaderName, new()
{
{ "_Crest_NormalsStrengthOverall", "Strength of the final surface normal (both wave normal and normal map)" },
{ "_Crest_NormalMapEnabled", "Whether to add normal detail from a texture. Can be used to add visual detail to the water surface" },
{ "_Crest_NormalMapTexture", "Normal map texture" },
{ "_Crest_NormalMapStrength", "Strength of normal map influence" },
{ "_Crest_NormalMapScale", "Base scale of multi-scale normal map texture" },
{ "_Crest_NormalMapScrollSpeed", "Speed of the normal maps scrolling" },
{ "_Crest_AbsorptionColor", "Works as a color (ie red adds red rather than subtracts red). This value is converted to real absorption values (proportion of light getting absorbed by water in atoms per meter). Alpha channel is for density. High alpha and darker color reduces transparency" },
{ "_Crest_Scattering", "Light scattered by the water towards the viewer (in-scattered) per meter. Brighter color reduces transparency" },
{ "_Crest_Anisotropy", "The directionality of the scattering where zero means scattered in all directions. The further towards one, the less visible soft shadows will be" },
{ "_Crest_DirectTerm", "Scale direct light contribution to volume lighting" },
{ "_Crest_AmbientTerm", "Scale ambient light contribution to volume lighting" },
{ "_Crest_SSSEnabled", "Whether to to emulate light scattering through waves" },
{ "_Crest_SSSIntensity", "Direct light contribution intensity. Applied to the scattering color. This effect is best if subtle" },
{ "_Crest_SSSPinchMinimum", "Higher the value the more scattering is towards the peaks of the waves" },
{ "_Crest_SSSPinchMaximum", "Higher the value for more scattering" },
{ "_Crest_SSSPinchFalloff", "Falloff for pinch minimum/maximum" },
{ "_Crest_SSSDirectionalFalloff", "Falloff for direct light scattering to affect directionality" },
{ "_Crest_Specular", "Strength of specular lighting response" },
{ "_Crest_Occlusion", "Strength of reflection" },
{ "_Crest_OcclusionUnderwater", "Strength of reflection when underwater. Keep this at zero to avoid skybox reflections which look incorrect when underwater, unless you want reflections from Planar Reflections or probes" },
{ "_Crest_Smoothness", "Smoothness of surface. A value of one is ideal for flat water only" },
{ "_Crest_SmoothnessFar", "Material smoothness at far distance from camera. Helps to spread out specular highlight in mid-to-background. From a theory point of view, models transfer of normal detail to microfacets in BRDF" },
{ "_Crest_SmoothnessFarDistance", "Definition of far distance" },
{ "_Crest_SmoothnessFalloff", "How smoothness varies between near and far distance" },
{ "_Crest_MinimumReflectionDirectionY", "Limits the reflection direction on the Y axis. Zero prevents reflections below the horizon. Small values above zero can be used to reduce horizon reflection contributions. Values above zero will negatively affect dynamic reflections like planar or SSR" },
{ "_Crest_PlanarReflectionsEnabled", "Dynamically rendered 'reflection plane' style reflections. Requires Reflections to be enabled on the Water Renderer" },
{ "_Crest_PlanarReflectionsIntensity", "Intensity of the planar reflections" },
{ "_Crest_PlanarReflectionsDistortion", "How much the water normal affects the planar reflection" },
{ "_Crest_PlanarReflectionsRoughness", "Controls the mipmap range" },
{ "_Crest_RefractionStrength", "How strongly light is refracted when passing through water surface" },
{ "_Crest_RefractiveIndexOfWater", "Index of refraction of water - typically left at 1.333. Changing this value can increase/decrease the size of the Snell's window" },
{ "_Crest_TotalInternalReflectionIntensity", "Zero will make the underwater reflections transparent. Slightly semi-transparency is a zero performance cost alternative to TIR" },
{ "_Crest_ShadowsEnabled", "Whether to receive shadow data. Does not affect shadow contributions from Unity" },
{ "_Crest_ShadowCasterThreshold", "Same concept as Alpha Clip Threshold but for foam casted shadows" },
{ "_Crest_FoamEnabled", "Enable foam layer on water surface" },
{ "_Crest_FoamTexture", "Foam texture" },
{ "_Crest_FoamScale", "Scale of multi-scale foam texture" },
{ "_Crest_FoamScrollSpeed", "Speed of the foam scrolling. This speed is slower than the other scroll speeds, as it scrolls in one direction only" },
{ "_Crest_FoamFeather", "Controls how gradual the transition is from full foam to no foam. Higher values look more realistic and can help mitigate flow phasing" },
{ "_Crest_FoamIntensityAlbedo", "Scale intensity of diffuse lighting" },
{ "_Crest_FoamSmoothness", "Smoothness of foam material" },
{ "_Crest_FoamNormalStrength", "Strength of the generated normals" },
{ "_Crest_CausticsEnabled", "Approximate rays being focused/defocused on underwater surfaces" },
{ "_Crest_CausticsTexture", "Caustics texture" },
{ "_Crest_CausticsStrength", "Intensity of caustics effect" },
{ "_Crest_CausticsTextureScale", "Caustics texture scale" },
{ "_Crest_CausticsScrollSpeed", "Speed of the caustics scrolling" },
{ "_Crest_CausticsTextureAverage", "The 'mid' value of the caustics texture, around which the caustic texture values are scaled. Decreasing this value will reduce the caustics darkening underwater surfaces" },
{ "_Crest_CausticsFocalDepth", "The depth at which the caustics are in focus" },
{ "_Crest_CausticsDepthOfField", "The range of depths over which the caustics are in focus" },
{ "_Crest_CausticsDistortionTexture", "Texture to distort caustics. Only applicable to underwater effect for now" },
{ "_Crest_CausticsDistortionStrength", "How much the caustics texture is distorted" },
{ "_Crest_CausticsDistortionScale", "The scale of the distortion pattern used to distort the caustics" },
{ "_Crest_CausticsMotionBlur", "How much caustics are blurred when advected by flow" },
{ "CREST_FLOW", "Flow is horizontal motion of water. Flow must be enabled on the Water Renderer to generate flow data" },
{ "_Crest_AlbedoEnabled", "Enable the Albedo simulation layer. Albedo must be enabled on the Water" },
{ "_Crest_AlbedoIgnoreFoam", "Whether Albedo renders over the top of foam or not." },
}
},
};
}
static class WaterShaderUtility
{
public const string k_ShaderName = "Crest/Water";
internal static void UpdateAbsorptionFromColor(Material material)
{
if (!material.HasProperty(WaterRenderer.ShaderIDs.s_Absorption) || !material.HasProperty(WaterRenderer.ShaderIDs.s_AbsorptionColor))
{
return;
}
// Convert an authored absorption colour to density values.
WaterRenderer.UpdateAbsorptionFromColor(material);
if (!material.IsPropertyOverriden(WaterRenderer.ShaderIDs.s_AbsorptionColor))
{
material.RevertPropertyOverride(WaterRenderer.ShaderIDs.s_Absorption);
}
}
internal static MaterialProperty[] FilterProperties(MaterialProperty[] properties)
{
return properties.Where(x => x.name != "_Crest_Absorption").ToArray();
}
}
/// <summary>
/// Supports tooltips and skips rendering render pipeline properties like "Queue".
/// </summary>
sealed class CustomShaderGUI : ShaderGUI
{
static readonly GUIContent s_Label = new();
public override void OnMaterialPreviewGUI(MaterialEditor materialEditor, Rect r, GUIStyle background)
{
}
public override void OnGUI(MaterialEditor editor, MaterialProperty[] properties)
{
var material = editor.target as Material;
var shader = material.shader;
var grouped = MaterialTooltips.s_Grouped.GetValueOrDefault(shader.name, null);
WaterShaderUtility.UpdateAbsorptionFromColor((Material)editor.target);
foreach (var property in properties)
{
if ((property.flags & MaterialProperty.PropFlags.HideInInspector) != 0) continue;
var name = property.name;
s_Label.text = property.displayName;
s_Label.tooltip = grouped?.GetValueOrDefault(name, null);
s_Label.tooltip ??= MaterialTooltips.s_Common.GetValueOrDefault(name, null);
editor.ShaderProperty(property, s_Label);
}
}
}
#if d_UnityShaderGraph
class LegacyCustomShaderGUI : UnityEditor.Rendering.BuiltIn.ShaderGraph.BuiltInBaseShaderGUI
{
MaterialEditor _Editor;
MaterialProperty[] _Properties;
protected string _ShaderName;
public override void OnMaterialPreviewGUI(MaterialEditor materialEditor, Rect r, GUIStyle background)
{
}
public override void OnGUI(MaterialEditor editor, MaterialProperty[] properties)
{
properties = properties.Where(x => x.name != "_Crest_Version").ToArray();
_Editor = editor;
_Properties = properties;
base.OnGUI(editor, properties);
}
protected override void DrawSurfaceInputs(Material material)
{
ShaderGraphPropertyDrawers.DrawShaderGraphGUI(_Editor, _Properties, MaterialTooltips.s_Grouped[_ShaderName]);
}
}
// Warning! Renaming this class is a breaking change due to users potentially
// exporting the shader to integrate with other assets.
sealed class LegacyWaterShaderGUI : LegacyCustomShaderGUI
{
public override void OnGUI(MaterialEditor editor, MaterialProperty[] properties)
{
_ShaderName = WaterShaderUtility.k_ShaderName;
properties = WaterShaderUtility.FilterProperties(properties);
base.OnGUI(editor, properties);
WaterShaderUtility.UpdateAbsorptionFromColor(editor.target as Material);
}
}
#if d_UnityURP
class UniversalCustomShaderGUI : ShaderGraphLitGUI
{
MaterialEditor _Editor;
MaterialProperty[] _Properties;
protected string _ShaderName;
public override void OnMaterialPreviewGUI(MaterialEditor materialEditor, Rect r, GUIStyle background)
{
}
public override void OnGUI(MaterialEditor editor, MaterialProperty[] properties)
{
properties = properties.Where(x => x.name != "_Crest_Version").ToArray();
_Editor = editor;
_Properties = properties;
base.OnGUI(editor, properties);
}
public override void DrawSurfaceInputs(Material material)
{
ShaderGraphPropertyDrawers.DrawShaderGraphGUI(_Editor, _Properties, MaterialTooltips.s_Grouped[_ShaderName]);
}
}
// Warning! Renaming this class is a breaking change due to users potentially
// exporting the shader to integrate with other assets.
sealed class UniversalWaterShaderGUI : UniversalCustomShaderGUI
{
public override void OnGUI(MaterialEditor editor, MaterialProperty[] properties)
{
_ShaderName = WaterShaderUtility.k_ShaderName;
properties = WaterShaderUtility.FilterProperties(properties);
base.OnGUI(editor, properties);
WaterShaderUtility.UpdateAbsorptionFromColor(editor.target as Material);
}
}
#endif // d_UnityURP
#if d_UnityHDRP
sealed class CustomShaderGraphUIBlock : MaterialUIBlock
{
public override void LoadMaterialProperties() { }
public override void OnGUI()
{
using var header = new MaterialHeaderScope("Exposed Properties", (uint)ExpandableBit.ShaderGraph, materialEditor);
if (!header.expanded)
{
return;
}
var name = (materialEditor.customShaderGUI as HighDefinitionCustomShaderGUI)._ShaderName;
ShaderGraphPropertyDrawers.DrawShaderGraphGUI(materialEditor, properties, MaterialTooltips.s_Grouped[name]);
}
}
class HighDefinitionCustomShaderGUI : LightingShaderGraphGUI
{
internal string _ShaderName;
public HighDefinitionCustomShaderGUI()
{
// Add refraction block.
uiBlocks.Insert(1, new TransparencyUIBlock(MaterialUIBlock.ExpandableBit.Transparency, TransparencyUIBlock.Features.Refraction));
// Remove the ShaderGraphUIBlock to avoid having duplicated properties in the UI.
uiBlocks.RemoveAll(x => x is ShaderGraphUIBlock);
// Insert the custom block just after the Surface Option block.
uiBlocks.Insert(1, new CustomShaderGraphUIBlock());
}
protected override void OnMaterialGUI(MaterialEditor editor, MaterialProperty[] properties)
{
properties = properties.Where(x => x.name != "_Crest_Version").ToArray();
base.OnMaterialGUI(editor, properties);
WaterShaderUtility.UpdateAbsorptionFromColor(editor.target as Material);
}
}
// Warning! Renaming this class is a breaking change due to users potentially
// exporting the shader to integrate with other assets.
sealed class HighDefinitionWaterShaderGUI : HighDefinitionCustomShaderGUI
{
protected override void OnMaterialGUI(MaterialEditor editor, MaterialProperty[] properties)
{
_ShaderName = WaterShaderUtility.k_ShaderName;
properties = WaterShaderUtility.FilterProperties(properties);
base.OnMaterialGUI(editor, properties);
WaterShaderUtility.UpdateAbsorptionFromColor(editor.target as Material);
}
}
#endif // d_UnityHDRP
#endif // d_UnityShaderGraph
}

View File

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

View File

@@ -1,164 +0,0 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
using System.Collections.Generic;
namespace WaveHarmonic.Crest.Editor
{
class OptionalLod
{
internal string _MaterialProperty;
internal string PropertyName { get; private set; }
internal string PropertyLabel { get; private set; }
internal System.Type Dependency { get; private set; }
internal Lod GetLod(WaterRenderer water) => PropertyName switch
{
nameof(WaterRenderer._AbsorptionLod) => water.AbsorptionLod,
nameof(WaterRenderer._AlbedoLod) => water.AlbedoLod,
nameof(WaterRenderer._AnimatedWavesLod) => water.AnimatedWavesLod,
nameof(WaterRenderer._ClipLod) => water.ClipLod,
nameof(WaterRenderer._DepthLod) => water.DepthLod,
nameof(WaterRenderer._DynamicWavesLod) => water.DynamicWavesLod,
nameof(WaterRenderer._FlowLod) => water.FlowLod,
nameof(WaterRenderer._FoamLod) => water.FoamLod,
nameof(WaterRenderer._LevelLod) => water.LevelLod,
nameof(WaterRenderer._ScatteringLod) => water.ScatteringLod,
nameof(WaterRenderer._ShadowLod) => water.ShadowLod,
_ => throw new System.NotImplementedException(),
};
// Optional. Not all simulations will have a corresponding keyword.
internal bool HasMaterialToggle => !string.IsNullOrEmpty(MaterialProperty);
// Needed as clip surface material toggle is Alpha Clipping.
internal virtual string MaterialProperty => _MaterialProperty;
internal virtual string MaterialPropertyPath => $"{PropertyLabel} > Enabled";
internal virtual string MaterialKeyword => $"{MaterialProperty}_ON";
internal static OptionalLod Get(System.Type type)
{
return s_Lods.GetValueOrDefault(s_Mapping.GetValueOrDefault(type, type), null);
}
static readonly Dictionary<System.Type, OptionalLod> s_Lods = new()
{
{
typeof(AbsorptionLod), new ColorOptionLod()
{
PropertyLabel = "Absorption",
PropertyName = nameof(WaterRenderer._AbsorptionLod),
}
},
{
typeof(AlbedoLod), new()
{
PropertyLabel = "Albedo",
PropertyName = nameof(WaterRenderer._AlbedoLod),
_MaterialProperty = "_Crest_AlbedoEnabled",
}
},
{
typeof(AnimatedWavesLod), new()
{
PropertyLabel = "Animate Waves",
PropertyName = nameof(WaterRenderer._AnimatedWavesLod),
}
},
{
typeof(ClipLod), new ClipOptionalLod()
{
PropertyLabel = "Clip Surface",
PropertyName = nameof(WaterRenderer._ClipLod),
}
},
{
typeof(DepthLod), new()
{
PropertyLabel = "Water Depth",
PropertyName = nameof(WaterRenderer._DepthLod),
}
},
{
typeof(DynamicWavesLod), new()
{
PropertyLabel = "Dynamic Waves",
PropertyName = nameof(WaterRenderer._DynamicWavesLod),
Dependency = typeof(AnimatedWavesLod),
}
},
{
typeof(FlowLod), new()
{
PropertyLabel = "Flow",
PropertyName = nameof(WaterRenderer._FlowLod),
_MaterialProperty = "CREST_FLOW",
}
},
{
typeof(FoamLod), new()
{
PropertyLabel = "Foam",
PropertyName = nameof(WaterRenderer._FoamLod),
_MaterialProperty = "_Crest_FoamEnabled",
}
},
{
typeof(LevelLod), new()
{
PropertyLabel = "Water Level",
PropertyName = nameof(WaterRenderer._LevelLod),
_MaterialProperty = "_Crest_LevelEnabled",
Dependency = typeof(AnimatedWavesLod),
}
},
{
typeof(ScatteringLod), new ColorOptionLod()
{
PropertyLabel = "Scattering",
PropertyName = nameof(WaterRenderer._ScatteringLod),
}
},
{
typeof(ShadowLod), new()
{
PropertyLabel = "Shadow",
PropertyName = nameof(WaterRenderer._ShadowLod),
_MaterialProperty = "_Crest_ShadowsEnabled",
}
},
};
static readonly Dictionary<System.Type, System.Type> s_Mapping = new()
{
{ typeof(AbsorptionLodInput), typeof(AbsorptionLod) },
{ typeof(AlbedoLodInput), typeof(AlbedoLod) },
{ typeof(AnimatedWavesLodInput), typeof(AnimatedWavesLod) },
{ typeof(ClipLodInput), typeof(ClipLod) },
{ typeof(DepthLodInput), typeof(DepthLod) },
{ typeof(DynamicWavesLodInput), typeof(DynamicWavesLod) },
{ typeof(FlowLodInput), typeof(FlowLod) },
{ typeof(FoamLodInput), typeof(FoamLod) },
{ typeof(LevelLodInput), typeof(LevelLod) },
{ typeof(ScatteringLodInput), typeof(ScatteringLod) },
{ typeof(ShadowLodInput), typeof(ShadowLod) },
};
}
sealed class ClipOptionalLod : OptionalLod
{
// BIRP SG has prefixes for Unity properties but other RPs do not. These prefixes
// are for serialisation only and are not used in the shader.
internal override string MaterialPropertyPath => "Alpha Clipping";
internal override string MaterialProperty => (RenderPipelineHelper.IsLegacy ? "_BUILTIN" : "") + "_AlphaClip";
internal override string MaterialKeyword => (RenderPipelineHelper.IsLegacy ? "_BUILTIN" : "") + "_ALPHATEST_ON";
}
sealed class ColorOptionLod : OptionalLod
{
internal override string MaterialPropertyPath => $"Volume Lighting > Sample {PropertyLabel} Simulation";
}
}

View File

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

View File

@@ -1,265 +0,0 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
using UnityEditor;
using UnityEngine;
namespace WaveHarmonic.Crest.Editor
{
//
// Lod
//
abstract class LodPreview : TexturePreview
{
protected abstract Lod Lod { get; }
protected abstract bool VisualizeNegatives { get; }
protected virtual bool ForceAlpha => false;
public override GUIContent GetPreviewTitle() => new(Lod.Name);
protected RenderTexture _TemporaryTexture;
protected override Texture OriginalTexture
{
get
{
if (!Lod.Enabled || !((WaterRenderer)target).isActiveAndEnabled) return null;
var texture = Lod.DataTexture;
if (texture == null)
{
return null;
}
return texture;
}
}
protected override Texture ModifiedTexture => _TemporaryTexture;
public override void OnPreviewSettings()
{
base.OnPreviewSettings();
// OnPreviewSettings is called after OnPreviewGUI so release here.
RenderTexture.ReleaseTemporary(_TemporaryTexture);
_TemporaryTexture = null;
}
public override void OnPreviewGUI(Rect rect, GUIStyle background)
{
var texture = Lod.DataTexture;
var descriptor = texture.descriptor;
_TemporaryTexture = RenderTexture.GetTemporary(descriptor);
_TemporaryTexture.name = "Crest Preview (Temporary)";
Graphics.CopyTexture(texture, _TemporaryTexture);
if (VisualizeNegatives)
{
var wrapper = new PropertyWrapperComputeStandalone(EditorHelpers.VisualizeNegativeValuesShader, 1);
wrapper.SetTexture(ShaderIDs.s_Target, _TemporaryTexture);
wrapper.Dispatch
(
Lod.Resolution / Lod.k_ThreadGroupSizeX,
Lod.Resolution / Lod.k_ThreadGroupSizeY,
Lod.Slices
);
}
ModifyTexture();
if (ForceAlpha)
{
// Set alpha to one otherwise it shows nothing when set to RGB.
var clear = WaterResources.Instance.Compute._Clear;
if (clear != null)
{
clear.SetTexture(0, ShaderIDs.s_Target, _TemporaryTexture);
clear.SetVector(ShaderIDs.s_ClearMask, Color.black);
clear.SetVector(ShaderIDs.s_ClearColor, Color.black);
clear.Dispatch
(
0,
Lod.Resolution / Lod.k_ThreadGroupSizeX,
Lod.Resolution / Lod.k_ThreadGroupSizeY,
Lod.Slices
);
}
}
base.OnPreviewGUI(rect, background);
}
protected virtual void ModifyTexture()
{
}
public override void Cleanup()
{
base.Cleanup();
RenderTexture.ReleaseTemporary(_TemporaryTexture);
}
// FIXME: Without constructor Unity complains:
// WaveHarmonic.Crest.Editor.LodPreview does not contain a default constructor, it
// will not be registered as a preview handler. Use the Initialize function to set
// up your object instead.
public LodPreview() { }
}
[CustomPreview(typeof(WaterRenderer))]
sealed class AbsorptionLodPreview : LodPreview
{
protected override Lod Lod => (target as WaterRenderer)._AbsorptionLod;
protected override bool VisualizeNegatives => false;
}
[CustomPreview(typeof(WaterRenderer))]
sealed class AlbedoLodPreview : LodPreview
{
protected override Lod Lod => (target as WaterRenderer)._AlbedoLod;
protected override bool VisualizeNegatives => false;
}
[CustomPreview(typeof(WaterRenderer))]
sealed class AnimatedWavesLodPreview : LodPreview
{
protected override Lod Lod => (target as WaterRenderer)._AnimatedWavesLod;
protected override bool VisualizeNegatives => true;
protected override bool ForceAlpha => true;
}
[CustomPreview(typeof(WaterRenderer))]
sealed class ClipLodPreview : LodPreview
{
protected override Lod Lod => (target as WaterRenderer)._ClipLod;
protected override bool VisualizeNegatives => false;
}
[CustomPreview(typeof(WaterRenderer))]
sealed class DepthLodPreview : LodPreview
{
protected override Lod Lod => (target as WaterRenderer)._DepthLod;
protected override bool VisualizeNegatives => true;
}
[CustomPreview(typeof(WaterRenderer))]
sealed class DynamicWavesLodPreview : LodPreview
{
protected override Lod Lod => (target as WaterRenderer)._DynamicWavesLod;
// Negatives do not visualize well, and obscure positives too much.
protected override bool VisualizeNegatives => false;
}
[CustomPreview(typeof(WaterRenderer))]
sealed class FlowLodPreview : LodPreview
{
protected override Lod Lod => (target as WaterRenderer)._FlowLod;
protected override bool VisualizeNegatives => true;
}
[CustomPreview(typeof(WaterRenderer))]
sealed class FoamLodPreview : LodPreview
{
protected override Lod Lod => (target as WaterRenderer)._FoamLod;
protected override bool VisualizeNegatives => false;
}
[CustomPreview(typeof(WaterRenderer))]
sealed class LevelLodPreview : LodPreview
{
protected override Lod Lod => (target as WaterRenderer)._LevelLod;
protected override bool VisualizeNegatives => true;
}
[CustomPreview(typeof(WaterRenderer))]
sealed class ScatteringLodPreview : LodPreview
{
protected override Lod Lod => (target as WaterRenderer)._ScatteringLod;
protected override bool VisualizeNegatives => false;
protected override bool ForceAlpha => true;
}
[CustomPreview(typeof(WaterRenderer))]
sealed class ShadowLodPreview : LodPreview
{
protected override Lod Lod => (target as WaterRenderer)._ShadowLod;
protected override bool VisualizeNegatives => false;
}
//
// LodInput
//
// Adding abstract causes exception:
// does not contain a default constructor, it will not be registered as a preview
// handler. Use the Initialize function to set up your object instead.
class ShapeWavesPreview : TexturePreview
{
public override GUIContent GetPreviewTitle() => new($"{target.GetType().Name}: Wave Buffer");
protected override Texture OriginalTexture => (target as ShapeWaves).WaveBuffer;
}
[CustomPreview(typeof(ShapeFFT))]
sealed class ShapeFFTPreview : ShapeWavesPreview
{
}
[CustomPreview(typeof(ShapeGerstner))]
sealed class ShapeGerstnerPreview : ShapeWavesPreview
{
}
[CustomPreview(typeof(DepthProbe))]
sealed class DepthProbePreview : TexturePreview
{
public override GUIContent GetPreviewTitle() => new("Depth Probe");
protected override Texture OriginalTexture => (target as DepthProbe).Texture;
}
#if CREST_DEBUG
[CustomPreview(typeof(DepthProbe))]
sealed class DepthProbeCameraPreview : TexturePreview
{
public override GUIContent GetPreviewTitle() => new("Depth Probe: Camera");
protected override Texture OriginalTexture
{
get
{
var target = this.target as DepthProbe;
if (target._Camera == null) return null;
return target._Camera.targetTexture;
}
}
}
#endif
//
// Other
//
#if CREST_DEBUG
[CustomPreview(typeof(WaterRenderer))]
sealed class WaterLevelDepthPreview : TexturePreview
{
public override GUIContent GetPreviewTitle() => new("Water Level Screen-Space Depth");
protected override Texture OriginalTexture => (target as WaterRenderer).WaterLevelDepthTexture;
}
[CustomPreview(typeof(WaterRenderer))]
sealed class WaterVolumeMaskPreview : TexturePreview
{
public override GUIContent GetPreviewTitle() => new("Water Volume Mask");
protected override Texture OriginalTexture => RenderPipelineHelper.IsLegacy && (target as WaterRenderer)._Underwater._MaskRT && (target as WaterRenderer)._Underwater._MaskRT.width > 0 ? (target as WaterRenderer)._Underwater._MaskRT : null;
}
#endif
[CustomPreview(typeof(WaterRenderer))]
sealed class ReflectionPreview : TexturePreview
{
public override GUIContent GetPreviewTitle() => new("Water Reflections");
protected override Texture OriginalTexture => (target as WaterRenderer)._Reflections._Enabled ? (target as WaterRenderer)._Reflections.ReflectionTexture : null;
}
}

View File

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

View File

@@ -1,197 +0,0 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
using System.IO;
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
namespace WaveHarmonic.Crest.Editor.Settings
{
[FilePath(k_Path, FilePathAttribute.Location.ProjectFolder)]
sealed class ProjectSettings : ScriptableSingleton<ProjectSettings>
{
[SerializeField, HideInInspector]
#pragma warning disable 414
int _Version = 0;
#pragma warning restore 414
#pragma warning disable IDE0032 // Use auto property
[@Heading("Variant Stripping", Heading.Style.Settings)]
[@Group]
[@DecoratedField, SerializeField]
bool _DebugEnableStrippingLogging;
[@Predicated(nameof(_DebugEnableStrippingLogging))]
[@DecoratedField, SerializeField]
bool _DebugOnlyLogRemainingVariants;
[Tooltip("Whether to strip broken variants.\n\nCurrently, the only known case is the point cookie variant being broken on Xbox.")]
[@DecoratedField, SerializeField]
bool _StripBrokenVariants = true;
[@Heading("Features", Heading.Style.Settings)]
[@Group]
[Tooltip("Whether to use full precision sampling for half precision platforms (typically mobile).\n\nThis will solve rendering artifacts like minor bumps and staircasing.")]
[@DecoratedField, SerializeField]
bool _FullPrecisionDisplacementOnHalfPrecisionPlatforms = true;
[Tooltip("Whether to sample shadow maps for built-in renderer.")]
[@DecoratedField, SerializeField]
bool _BuiltInRendererSampleShadowMaps = true;
#pragma warning restore IDE0032 // Use auto property
internal const string k_Path = "ProjectSettings/Packages/com.waveharmonic.crest/Settings.asset";
internal enum State
{
Dynamic,
Disabled,
Enabled,
}
internal static ProjectSettings Instance => instance;
internal bool StripBrokenVariants => _StripBrokenVariants;
internal bool DebugEnableStrippingLogging => _DebugEnableStrippingLogging;
internal bool LogStrippedVariants => _DebugEnableStrippingLogging && !_DebugOnlyLogRemainingVariants;
internal bool LogKeptVariants => _DebugEnableStrippingLogging && _DebugOnlyLogRemainingVariants;
internal bool FullPrecisionDisplacementOnHalfPrecisionPlatforms => _FullPrecisionDisplacementOnHalfPrecisionPlatforms;
internal bool BuiltInRendererSampleShadowMaps => _BuiltInRendererSampleShadowMaps;
void OnEnable()
{
// Fixes not being editable.
hideFlags = HideFlags.HideAndDontSave & ~HideFlags.NotEditable;
}
internal static void Save()
{
instance.Save(saveAsText: true);
}
[@OnChange(skipIfInactive: false)]
void OnChange(string path, object previous)
{
switch (path)
{
case nameof(_BuiltInRendererSampleShadowMaps):
case nameof(_FullPrecisionDisplacementOnHalfPrecisionPlatforms):
ShaderSettingsGenerator.Generate();
break;
}
}
}
sealed class SettingsProvider : UnityEditor.SettingsProvider
{
static readonly string[] s_ShaderGraphs = new string[]
{
"Packages/com.waveharmonic.crest/Runtime/Shaders/Surface/Water.shadergraph",
"Packages/com.waveharmonic.crest/Shared/Shaders/Lit.shadergraph",
"Packages/com.waveharmonic.crest.paint/Samples/Colorado/Shaders/SpeedTree8_PBRLit.shadergraph",
"Packages/com.waveharmonic.crest.paint/Samples/Colorado/Shaders/Environment (Splat Map).shadergraph",
};
UnityEditor.Editor _Editor;
SettingsProvider(string path, SettingsScope scope = SettingsScope.User) : base(path, scope)
{
// Empty
}
static bool IsSettingsAvailable()
{
return File.Exists(ProjectSettings.k_Path);
}
public override void OnActivate(string searchContext, VisualElement rootElement)
{
base.OnActivate(searchContext, rootElement);
_Editor = UnityEditor.Editor.CreateEditor(ProjectSettings.Instance);
Undo.undoRedoPerformed -= OnUndoRedo;
Undo.undoRedoPerformed += OnUndoRedo;
}
public override void OnDeactivate()
{
base.OnDeactivate();
Helpers.Destroy(_Editor);
Undo.undoRedoPerformed -= OnUndoRedo;
}
void OnUndoRedo()
{
ProjectSettings.Save();
}
public override void OnGUI(string searchContext)
{
if (_Editor.target == null)
{
Helpers.Destroy(_Editor);
_Editor = UnityEditor.Editor.CreateEditor(ProjectSettings.Instance);
return;
}
// Reset foldout values.
DecoratedDrawer.s_IsFoldout = false;
DecoratedDrawer.s_IsFoldoutOpen = false;
EditorGUI.BeginChangeCheck();
// Pad similar to settings header.
var style = new GUIStyle();
style.padding.left = 8;
// Same label with as other settings.
EditorGUIUtility.labelWidth = 251;
EditorGUILayout.BeginVertical(style);
_Editor.OnInspectorGUI();
EditorGUILayout.EndVertical();
// Commit all changes. Normally settings are written when user hits save or exits
// without any undo/redo entry and dirty state. No idea how to do the same.
// SaveChanges and hasUnsavedChanges on custom editor did not work.
// Not sure if hooking into EditorSceneManager.sceneSaving is correct.
if (EditorGUI.EndChangeCheck())
{
ProjectSettings.Save();
}
GUILayout.Space(10 * 2);
if (GUILayout.Button("Repair Shaders"))
{
foreach (var path in s_ShaderGraphs)
{
if (!File.Exists(path)) continue;
AssetDatabase.ImportAsset(path);
}
}
}
[SettingsProvider]
static UnityEditor.SettingsProvider Create()
{
if (ProjectSettings.Instance)
{
var provider = new SettingsProvider("Project/Crest", SettingsScope.Project);
provider.keywords = GetSearchKeywordsFromSerializedObject(new(ProjectSettings.Instance));
return provider;
}
// Settings Asset doesn't exist yet; no need to display anything in the Settings window.
return null;
}
}
}

View File

@@ -1,12 +0,0 @@
fileFormatVersion: 2
guid: 8ac118e410ac44d44a323c7c14819660
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences:
- _ArtifactsShader: {fileID: 7200000, guid: 08549c36146ad4899a07193754b21ea2, type: 3}
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

@@ -1,34 +0,0 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
#if !UNITY_2022_3_OR_NEWER
#error "Crest requires Unity version 2022.3 at a minimum."
#endif
#if d_UpdateCPUQueries
#error "Your <i>Crest - CPU Queries</i> package needs to be updated to be compatible with this version of Crest."
#endif
#if d_UpdatePaint
#error "Your <i>Crest - Paint</i> package needs to be updated to be compatible with this version of Crest."
#endif
#if d_UpdatePortals
#error "Your <i>Crest - Portals</i> package needs to be updated to be compatible with this version of Crest."
#endif
#if d_UpdateShallowWater
#error "Your <i>Crest - Shallow Water</i> package needs to be updated to be compatible with this version of Crest."
#endif
#if d_UpdateShiftingOrigin
#error "Your <i>Crest - Shifting Origin</i> package needs to be updated to be compatible with this version of Crest."
#endif
#if d_UpdateSplines
#error "Your <i>Crest - Splines</i> package needs to be updated to be compatible with this version of Crest."
#endif
#if d_UpdateWhirlpool
#error "Your <i>Crest - Whirlpool</i> package needs to be updated to be compatible with this version of Crest."
#endif

View File

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

View File

@@ -1,52 +0,0 @@
{
"name": "WaveHarmonic.Crest.Editor.Dependents",
"rootNamespace": "",
"references": [],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": false,
"defineConstraints": [],
"versionDefines": [
{
"name": "com.waveharmonic.crest.cpu-queries",
"expression": "(,1.0.4)",
"define": "d_UpdateCPUQueries"
},
{
"name": "com.waveharmonic.crest.paint",
"expression": "(,1.2.0)",
"define": "d_UpdatePaint"
},
{
"name": "com.waveharmonic.crest.portals",
"expression": "(,1.2.1)",
"define": "d_UpdatePortals"
},
{
"name": "com.waveharmonic.crest.shallow-water",
"expression": "(,1.3.1)",
"define": "d_UpdateShallowWater"
},
{
"name": "com.waveharmonic.crest.shifting-origin",
"expression": "(,1.2.1)",
"define": "d_UpdateShiftingOrigin"
},
{
"name": "com.waveharmonic.crest.splines",
"expression": "(,1.3.2)",
"define": "d_UpdateSplines"
},
{
"name": "com.waveharmonic.crest.whirlpool",
"expression": "(,1.0.1)",
"define": "d_UpdateWhirlpool"
}
],
"noEngineReferences": false
}

View File

@@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: f9ef151b7b8ca4034bc5cdd0c2a6465d
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,92 +0,0 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
using System.Linq;
using UnityEditor;
using UnityEditor.Callbacks;
using UnityEditor.Compilation;
using UnityEngine.Rendering;
using WaveHarmonic.Crest.Editor.Settings;
namespace WaveHarmonic.Crest.Editor
{
static class ShaderSettingsGenerator
{
[DidReloadScripts]
static void OnReloadScripts()
{
EditorApplication.update -= GenerateAfterReloadScripts;
EditorApplication.update += GenerateAfterReloadScripts;
}
static async void GenerateAfterReloadScripts()
{
if (EditorApplication.isCompiling)
{
return;
}
EditorApplication.update -= GenerateAfterReloadScripts;
// Generate HLSL from C#. Only targets WaveHarmonic.Crest assemblies.
await ShaderGeneratorUtility.GenerateAll();
AssetDatabase.Refresh();
}
internal static void Generate()
{
if (EditorApplication.isCompiling)
{
return;
}
// Could not ShaderGeneratorUtility.GenerateAll to work without recompiling…
CompilationPipeline.RequestScriptCompilation();
}
sealed class AssetPostProcessor : AssetPostprocessor
{
static async void OnPostprocessAllAssets(string[] imported, string[] deleted, string[] movedTo, string[] movedFrom, bool domainReload)
{
// Unused.
_ = deleted; _ = movedTo; _ = movedFrom; _ = domainReload;
if (EditorApplication.isCompiling)
{
return;
}
// Regenerate if file changed like re-importing.
if (imported.Contains("Packages/com.waveharmonic.crest/Runtime/Shaders/Library/Settings.Crest.hlsl"))
{
// Generate HLSL from C#. Only targets WaveHarmonic.Crest assemblies.
await ShaderGeneratorUtility.GenerateAll();
AssetDatabase.Refresh();
}
}
}
}
[GenerateHLSL(sourcePath = "Packages/com.waveharmonic.crest/Runtime/Shaders/Library/Settings.Crest")]
sealed class ShaderSettings
{
public static int s_CrestPortals =
#if d_CrestPortals
1
#else
0
#endif
;
public static int s_CrestShiftingOrigin =
#if d_WaveHarmonic_Crest_ShiftingOrigin
1
#else
0
#endif
;
public static int s_CrestShadowsBuiltInRenderPipeline = ProjectSettings.Instance.BuiltInRendererSampleShadowMaps ? 1 : 0;
public static int s_CrestFullPrecisionDisplacement = ProjectSettings.Instance.FullPrecisionDisplacementOnHalfPrecisionPlatforms ? 1 : 0;
}
}

View File

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

View File

@@ -1,39 +0,0 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
using System.Linq;
using UnityEditor;
using UnityEngine;
namespace WaveHarmonic.Crest.Editor
{
static class Utility
{
static int s_LastCheckedForWater = -1;
static WaterRenderer s_Water;
public static WaterRenderer Water
{
get
{
if (s_LastCheckedForWater == Time.frameCount)
{
return s_Water;
}
s_LastCheckedForWater = Time.frameCount;
// Gets the water from the current stage.
return s_Water = UnityEditor.SceneManagement.StageUtility
.GetCurrentStageHandle()
.FindComponentsOfType<WaterRenderer>()
.FirstOrDefault();
}
}
[InitializeOnEnterPlayMode]
static void OnEnterPlayMode()
{
s_LastCheckedForWater = -1;
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,7 +0,0 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("WaveHarmonic.Crest.Editor")]
[assembly: InternalsVisibleTo("WaveHarmonic.Crest.Shared.Editor")]

View File

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

View File

@@ -1,3 +0,0 @@
{
"reference": "GUID:3eae0364be2026648bf74846acb8a731"
}

View File

@@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 5373b95f6a6234bd9acf598c2856b1c9
AssemblyDefinitionReferenceImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

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

View File

@@ -1,6 +0,0 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("WaveHarmonic.Crest.Editor")]

View File

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

View File

@@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: b4e50428db6234431b8ab6d9fe9af9ad
AssemblyDefinitionReferenceImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

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

View File

@@ -1,6 +0,0 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("WaveHarmonic.Crest.Shared.Editor")]

View File

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

View File

@@ -1,3 +0,0 @@
{
"reference": "GUID:be0903cd8e1546f498710afdc59db5eb"
}

View File

@@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: d1d2e8dc2707d4ea38f4e65cafa7ab83
AssemblyDefinitionReferenceImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

@@ -1,24 +0,0 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("WaveHarmonic.Crest")]
[assembly: InternalsVisibleTo("WaveHarmonic.Crest.Editor")]
[assembly: InternalsVisibleTo("WaveHarmonic.Crest.Samples")]
[assembly: InternalsVisibleTo("WaveHarmonic.Crest.Samples.Editor")]
[assembly: InternalsVisibleTo("WaveHarmonic.Crest.Samples.Examples")]
[assembly: InternalsVisibleTo("WaveHarmonic.Crest.CPUQueries")]
[assembly: InternalsVisibleTo("WaveHarmonic.Crest.CPUQueries.Editor")]
[assembly: InternalsVisibleTo("WaveHarmonic.Crest.Paint")]
[assembly: InternalsVisibleTo("WaveHarmonic.Crest.Paint.Editor")]
[assembly: InternalsVisibleTo("WaveHarmonic.Crest.ShallowWater")]
[assembly: InternalsVisibleTo("WaveHarmonic.Crest.ShallowWater.Editor")]
[assembly: InternalsVisibleTo("WaveHarmonic.Crest.ShiftingOrigin")]
[assembly: InternalsVisibleTo("WaveHarmonic.Crest.ShiftingOrigin.Editor")]
[assembly: InternalsVisibleTo("WaveHarmonic.Crest.Splines")]
[assembly: InternalsVisibleTo("WaveHarmonic.Crest.Splines.Editor")]
[assembly: InternalsVisibleTo("WaveHarmonic.Crest.Watercraft")]
[assembly: InternalsVisibleTo("WaveHarmonic.Crest.Watercraft.Editor")]
[assembly: InternalsVisibleTo("WaveHarmonic.Crest.Whirlpool")]
[assembly: InternalsVisibleTo("WaveHarmonic.Crest.Whirlpool.Editor")]

View File

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

View File

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

View File

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

View File

@@ -1,29 +0,0 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
using UnityEditor;
using UnityEngine;
using WaveHarmonic.Crest.Attributes;
using WaveHarmonic.Crest.Editor;
namespace WaveHarmonic.Crest
{
sealed class Embedded : DecoratedProperty
{
internal EmbeddedAssetEditor _Editor;
readonly int _BottomMargin;
public Embedded(int margin = 0)
{
_Editor = new();
_BottomMargin = margin;
}
internal override void OnGUI(Rect position, SerializedProperty property, GUIContent label, DecoratedDrawer drawer)
{
_Editor.DrawEditorCombo(label, drawer, property, "asset", _BottomMargin);
}
internal override bool NeedsControlRectangle(SerializedProperty property) => false;
}
}

View File

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

View File

@@ -1,72 +0,0 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
using UnityEditor;
using UnityEngine;
using WaveHarmonic.Crest.Attributes;
using WaveHarmonic.Crest.Editor;
namespace WaveHarmonic.Crest
{
sealed class HelpBox : Decorator
{
// Define our own as Unity's won't be available in builds.
public enum MessageType
{
Info,
Warning,
Error,
}
public string _Message;
public MessageType _MessageType;
public Visibility _Visibility;
public enum Visibility
{
Always,
PropertyEnabled,
PropertyDisabled,
}
readonly GUIContent _GuiContent;
public override bool AlwaysVisible => false;
public HelpBox(string message, MessageType messageType = MessageType.Info, Visibility visibility = Visibility.Always)
{
_Message = message;
_MessageType = messageType;
_Visibility = visibility;
_GuiContent = new(message);
}
internal override void Decorate(Rect position, SerializedProperty property, GUIContent label, DecoratedDrawer drawer)
{
if (_Visibility == Visibility.PropertyEnabled && !GUI.enabled || _Visibility == Visibility.PropertyDisabled && GUI.enabled)
{
return;
}
// Enable rich text in help boxes. Store original so we can revert since this might be a "hack".
var style = GUI.skin.GetStyle("HelpBox");
var styleRichText = style.richText;
style.richText = true;
var height = style.CalcHeight(_GuiContent, EditorGUIUtility.currentViewWidth);
if (height <= EditorGUIUtility.singleLineHeight)
{
// This gets internal layout of the help box right but breaks down if multiline.
height += style.padding.horizontal + style.lineHeight;
}
// Always get a new control rect so we don't have to deal with positions and offsets.
position = EditorGUILayout.GetControlRect(true, height, style);
// + 1 maps our MessageType to Unity's.
EditorGUI.HelpBox(position, _Message, (UnityEditor.MessageType)_MessageType + 1);
// Revert skin since it persists.
style.richText = styleRichText;
}
}
}

View File

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

View File

@@ -1,25 +0,0 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
using System;
using UnityEngine;
namespace WaveHarmonic.Crest
{
/// <summary>
/// Constructs a custom link to Crest's documentation for the help URL button.
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Enum, AllowMultiple = false)]
sealed class HelpURL : HelpURLAttribute
{
public HelpURL(string path = "") : base(GetPageLink(path))
{
// Blank.
}
public static string GetPageLink(string path)
{
return "https://docs.crest.waveharmonic.com/" + path;
}
}
}

View File

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

View File

@@ -1,18 +0,0 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
using UnityEditor;
using UnityEngine;
using WaveHarmonic.Crest.Attributes;
using WaveHarmonic.Crest.Editor;
namespace WaveHarmonic.Crest
{
sealed class Layer : DecoratedProperty
{
internal override void OnGUI(Rect position, SerializedProperty property, GUIContent label, DecoratedDrawer drawer)
{
property.intValue = EditorGUI.LayerField(position, label, property.intValue);
}
}
}

View File

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

View File

@@ -1,33 +0,0 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
using System;
namespace WaveHarmonic.Crest
{
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
sealed class OnChange : Attribute
{
public Type Type { get; }
public bool SkipIfInactive { get; }
/// <summary>
/// Register an instance method as an OnChange handler.
/// </summary>
public OnChange(bool skipIfInactive = true)
{
SkipIfInactive = skipIfInactive;
}
/// <summary>
/// Register a static method as an OnChange handler.
/// </summary>
/// <param name="type">The type to target.</param>
/// <param name="skipIfInactive">Skip this handler if component is inactive.</param>
public OnChange(Type type, bool skipIfInactive = true)
{
Type = type;
SkipIfInactive = skipIfInactive;
}
}
}

View File

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

View File

@@ -1,213 +0,0 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
using System;
using System.Reflection;
using UnityEditor;
using UnityEngine;
using WaveHarmonic.Crest.Attributes;
using WaveHarmonic.Crest.Editor;
namespace WaveHarmonic.Crest
{
sealed class Predicated : Decorator
{
enum Mode
{
Property,
Member,
Type,
RenderPipeline,
}
readonly Mode _Mode;
readonly bool _Inverted;
readonly bool _Hide;
readonly string _PropertyName;
readonly object _DisableValue;
readonly Type _Type;
readonly MemberInfo _Member;
readonly RenderPipeline _RenderPipeline;
/// <summary>
/// The field with this attribute will be drawn enabled/disabled based on return of method.
/// </summary>
/// <param name="type">The type to call the method on. Must be either a static type or the type the field is defined on.</param>
/// <param name="member">Member name. Method must match signature: bool MethodName(Component component). Can be any visibility and static or instance.</param>
/// <param name="disableValue"></param>
/// <param name="inverted">Flip behaviour - for example disable if a bool field is set to true (instead of false).</param>
/// <param name="hide">Hide this component in the inspector.</param>
public Predicated(Type type, string member, object disableValue, bool inverted = false, bool hide = false)
{
_Mode = Mode.Member;
_Inverted = inverted;
_Hide = hide;
_Type = type;
_DisableValue = disableValue;
_Member = _Type.GetMember(member, Helpers.s_AnyMethod)[0];
}
/// <summary>
/// Enable/Disable field depending on the current type of the component.
/// </summary>
/// <param name="type">If a component of this type is not attached to this GameObject, disable the GUI (or enable if inverted is true).</param>
/// <param name="inverted">Flip behaviour - for example disable if a bool field is set to true (instead of false).</param>
/// <param name="hide">Hide this component in the inspector.</param>
public Predicated(Type type, bool inverted = false, bool hide = false)
{
_Mode = Mode.Type;
_Inverted = inverted;
_Hide = hide;
_Type = type;
}
/// <summary>
/// The field with this attribute will be drawn enabled/disabled based on another field. For example can be used
/// to disable a field if a toggle is false.
/// </summary>
/// <param name="property">The name of the other property whose value dictates whether this field is enabled or not.</param>
/// <param name="inverted">Flip behaviour - for example disable if a bool field is set to true (instead of false).</param>
/// <param name="disableValue">If the field has this value, disable the GUI (or enable if inverted is true).</param>
/// <param name="hide">Hide this component in the inspector.</param>
public Predicated(string property, bool inverted = false, object disableValue = null, bool hide = false)
{
_Mode = Mode.Property;
_Inverted = inverted;
_Hide = hide;
_PropertyName = property;
_DisableValue = disableValue;
}
/// <summary>
/// Field is predicated (enabled/shown) on which render pipeline is active.
/// </summary>
/// <param name="rp">Enable if this render pipeline is active.</param>
/// <param name="inverted">Invert behaviour.</param>
/// <param name="hide">Hide instead of disable.</param>
public Predicated(RenderPipeline rp, bool inverted = false, bool hide = false)
{
_Mode = Mode.RenderPipeline;
_Inverted = inverted;
_Hide = hide;
_RenderPipeline = rp;
}
public override bool AlwaysVisible => true;
public bool GUIEnabled(SerializedProperty property)
{
return _Inverted != property.propertyType switch
{
// Enable GUI if int value of field is not equal to 0, or whatever the disable-value is set to
SerializedPropertyType.Integer => property.intValue != ((int?)_DisableValue ?? 0),
// Enable GUI if disable-value is 0 and field is true, or disable-value is not 0 and field is false
SerializedPropertyType.Boolean => property.boolValue ^ (((int?)_DisableValue ?? 0) != 0),
SerializedPropertyType.Float => property.floatValue != ((float?)_DisableValue ?? 0),
SerializedPropertyType.String => property.stringValue != ((string)_DisableValue ?? ""),
// Must pass nameof enum entry as we are comparing names because index != value.
SerializedPropertyType.Enum => property.enumNames[property.enumValueIndex] != ((string)_DisableValue ?? ""),
SerializedPropertyType.ObjectReference => property.objectReferenceValue != null,
SerializedPropertyType.ManagedReference => property.managedReferenceValue != null,
_ => throw new ArgumentException($"Crest.Predicated: property type on <i>{property.serializedObject.targetObject}</i> not implemented yet: {property.propertyType}."),
};
}
internal override void Decorate(Rect position, SerializedProperty property, GUIContent label, DecoratedDrawer drawer)
{
var enabled = true;
if (_Mode == Mode.Property)
{
var propertyPath = _PropertyName;
if (property.depth > 0)
{
// Get the property path so we can find it from the serialized object.
propertyPath = $"{string.Join(".", property.propertyPath.Split(".", StringSplitOptions.None)[0..^1])}.{propertyPath}";
}
// Get the other property to be the predicate for the enabled/disabled state of this property.
// Do not use property.FindPropertyRelative as it does not work with nested properties.
// Try and find the nested property first and then fallback to the root object.
var otherProperty = property.serializedObject.FindProperty(propertyPath) ?? property.serializedObject.FindProperty(_PropertyName);
Debug.AssertFormat(otherProperty != null, "Crest.Predicated: {0} or {1} property on {2} ({3}) could not be found!", propertyPath, _PropertyName, property.serializedObject.targetObject.GetType(), property.name);
if (otherProperty != null)
{
enabled = GUIEnabled(otherProperty);
}
}
else if (_Mode == Mode.Member)
{
// Static is both abstract and sealed: https://stackoverflow.com/a/1175950
object @object = _Type.IsAbstract && _Type.IsSealed ? null : property.serializedObject.targetObject;
// If this is a nested type, grab that type. This may not be suitable in all cases.
if (property.depth > 0)
{
// Get the property path so we can find it from the serialized object.
var propertyPath = string.Join(".", property.propertyPath.Split(".", StringSplitOptions.None)[0..^1]);
var otherProperty = property.serializedObject.FindProperty(propertyPath);
@object = otherProperty.propertyType switch
{
SerializedPropertyType.ManagedReference => otherProperty.managedReferenceValue,
SerializedPropertyType.Generic => otherProperty.boxedValue,
_ => @object,
};
}
if (_Member is PropertyInfo autoProperty)
{
// == operator does not work.
enabled = autoProperty.GetValue(@object).Equals(_DisableValue);
}
else if (_Member is MethodInfo method)
{
enabled = method.Invoke(@object, new object[] { }).Equals(_DisableValue);
}
if (_Inverted) enabled = !enabled;
}
else if (_Mode == Mode.Type)
{
var type = property.serializedObject.targetObject.GetType();
// If this is a nested type, grab that type. This may not be suitable in all cases.
if (property.depth > 0)
{
// Get the property path so we can find it from the serialized object.
var propertyPath = string.Join(".", property.propertyPath.Split(".", StringSplitOptions.None)[0..^1]);
var otherProperty = property.serializedObject.FindProperty(propertyPath);
type = otherProperty.propertyType switch
{
SerializedPropertyType.ManagedReference => otherProperty.managedReferenceValue.GetType(),
SerializedPropertyType.Generic => otherProperty.boxedValue.GetType(),
_ => type,
};
}
var enabledByTypeCheck = _Type.IsAssignableFrom(type);
if (_Inverted) enabledByTypeCheck = !enabledByTypeCheck;
enabled = enabledByTypeCheck && enabled;
}
else if (_Mode == Mode.RenderPipeline)
{
enabled = RenderPipelineHelper.RenderPipeline == _RenderPipeline != _Inverted;
}
// Keep current disabled state.
GUI.enabled &= enabled;
// Keep previous hidden state.
DecoratedDrawer.s_HideInInspector = DecoratedDrawer.s_HideInInspector || (_Hide && !enabled);
}
}
}

View File

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

View File

@@ -1,217 +0,0 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
// Adapted from:
// https://forum.unity.com/threads/drawing-a-field-using-multiple-property-drawers.479377/
// This class draws all the attributes which inherit from DecoratedProperty. This class may need to be
// extended to handle reseting GUI states as we need them.
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEditor;
using UnityEngine;
using UnityEngine.Events;
using WaveHarmonic.Crest.Attributes;
namespace WaveHarmonic.Crest.Editor
{
[CustomPropertyDrawer(typeof(DecoratedProperty), true)]
sealed class DecoratedDrawer : PropertyDrawer
{
internal static bool s_HideInInspector = false;
public static bool s_IsFoldout = false;
public static bool s_IsFoldoutOpen = false;
public static bool s_TemporaryColor;
public static Color s_PreviousColor;
List<object> _Decorators = null;
List<object> Decorators
{
get
{
// Populate list with decorators.
_Decorators ??= fieldInfo
.GetCustomAttributes(typeof(Decorator), false)
.ToList();
return _Decorators;
}
}
List<Attributes.Validator> _Validators = null;
List<Attributes.Validator> Validators => _Validators ??= fieldInfo
.GetCustomAttributes(typeof(Attributes.Validator), false)
.Cast<Attributes.Validator>()
.ToList();
static List<(MethodInfo, OnChange)> s_OnChangeHandlers;
static List<(MethodInfo, OnChange)> OnChangeHandlers => s_OnChangeHandlers ??= TypeCache
.GetMethodsWithAttribute<OnChange>()
.Select(x => (x, x.GetCustomAttribute<OnChange>()))
.ToList();
[InitializeOnLoadMethod]
static void OnDomainReload()
{
s_OnChangeHandlers = null;
}
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
// Make original control rectangle be invisible because we always create our own. Zero still adds a little
// height which becomes noticeable once multiple properties are hidden. This could be some GUI style
// property but could not find which one.
return -2f;
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
// Store the original GUI state so it can be reset later.
var originalColor = GUI.color;
var originalEnabled = GUI.enabled;
if (s_TemporaryColor) GUI.color = s_PreviousColor;
// Execute all non visual attributes like Predicated. If these change any IMGUI
// properties they might be overriden with EditorGUI.GetPropertyHeight call.
for (var index = 0; index < Decorators.Count; index++)
{
var attribute = (Decorator)Decorators[index];
if (!attribute.AlwaysVisible) continue;
attribute.Decorate(position, property, label, this);
}
if (!s_HideInInspector && (!s_IsFoldout || s_IsFoldoutOpen))
{
// Execute all visual attributes.
for (var index = 0; index < Decorators.Count; index++)
{
var attribute = (Decorator)Decorators[index];
if (attribute.AlwaysVisible) continue;
label = attribute.BuildLabel(label);
attribute.Decorate(position, property, label, this);
}
var a = (DecoratedProperty)attribute;
position = a.NeedsControlRectangle(property)
? EditorGUILayout.GetControlRect(true, EditorGUI.GetPropertyHeight(property, label, true))
: position;
// Call for labels again as EditorGUI.GetPropertyHeight will revert them.
for (var index = 0; index < Decorators.Count; index++)
{
var attribute = (Decorator)Decorators[index];
if (attribute.AlwaysVisible) continue;
label = attribute.BuildLabel(label);
}
var skipChange = fieldInfo.FieldType == typeof(UnityEvent);
if (!skipChange)
{
EditorGUI.BeginChangeCheck();
}
var oldValue = skipChange ? null : property.boxedValue;
a.OnGUI(position, property, label, this);
for (var index = 0; index < Validators.Count; index++)
{
Validators[index].Validate(position, property, label, this, oldValue);
}
// Guard against foldouts triggering change check due to changing isExpanded.
if (!skipChange && EditorGUI.EndChangeCheck() && oldValue != property.boxedValue)
{
// Apply any changes.
property.serializedObject.ApplyModifiedProperties();
OnChange(property, oldValue);
}
for (var index = 0; index < Decorators.Count; index++)
{
var attribute = (Decorator)Decorators[index];
if (attribute.AlwaysVisible) continue;
attribute.DecorateAfter(position, property, label, this);
}
}
if (!s_IsFoldout || s_IsFoldoutOpen)
{
for (var index = 0; index < Decorators.Count; index++)
{
var attribute = (Decorator)Decorators[index];
if (!attribute.AlwaysVisible) continue;
attribute.DecorateAfter(position, property, label, this);
}
}
// Handle resetting the GUI state.
s_HideInInspector = false;
GUI.color = originalColor;
GUI.enabled = originalEnabled;
}
public static void OnChange(SerializedProperty property, object oldValue)
{
var target = property.serializedObject.targetObject;
// This is the type the field is declared on so it will work for nested objects.
var targetType = target.GetType();
var isActive = true;
if (property.serializedObject.targetObject is Internal.EditorBehaviour c && !c.isActiveAndEnabled)
{
isActive = false;
}
// Send event to target.
foreach (var (method, attribute) in OnChangeHandlers)
{
if (attribute.SkipIfInactive && !isActive) continue;
var type = attribute.Type ?? method.DeclaringType;
if (!type.IsAssignableFrom(targetType)) continue;
if (attribute.Type == null)
{
method.Invoke(target, new object[] { property.propertyPath, oldValue });
}
else
{
method.Invoke(null, new object[] { target, property.propertyPath, oldValue, property });
}
}
// Propagate event to nested classes.
for (var i = 0; i < property.depth; i++)
{
var chunks = property.propertyPath.Split(".");
var nestedProperty = property.serializedObject.FindProperty(string.Join(".", chunks[..(i + 1)]));
if (nestedProperty.propertyType != SerializedPropertyType.ManagedReference) continue;
var relativePath = string.Join(".", chunks[(i + 1)..]);
var nestedTarget = nestedProperty.managedReferenceValue;
var nestedTargetType = nestedTarget.GetType();
foreach (var (method, attribute) in OnChangeHandlers)
{
if (attribute.SkipIfInactive && !isActive) continue;
var type = attribute.Type ?? method.DeclaringType;
if (!type.IsAssignableFrom(nestedTargetType)) continue;
if (attribute.Type == null)
{
method.Invoke(nestedTarget, new object[] { relativePath, oldValue });
}
else
{
method.Invoke(null, new object[] { nestedTarget, relativePath, oldValue, property });
}
}
}
}
}
}

View File

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

View File

@@ -1,246 +0,0 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
// This file is subject to the Unity Companion License:
// https://github.com/Unity-Technologies/com.unity.cinemachine/blob/593fa283bee378322337e5d9f5a7b91331a45799/LICENSE.md
// Lovingly adapted from Cinemachine:
// https://github.com/Unity-Technologies/com.unity.cinemachine/blob/593fa283bee378322337e5d9f5a7b91331a45799/Editor/Utility/EmbeddedAssetHelpers.cs
using UnityEngine;
using UnityEditor;
using UnityEditor.VersionControl;
namespace WaveHarmonic.Crest.Editor
{
/// <summary>
/// Interface for editors that receive an argument
/// </summary>
interface IEmbeddableEditor
{
void SetTypeOfHostComponent(System.Type hostType);
}
/// <summary>
/// Helper for drawing embedded asset editors
/// </summary>
sealed class EmbeddedAssetEditor
{
/// <summary>
/// Create in OnEnable()
/// </summary>
public EmbeddedAssetEditor()
{
_CreateButtonGUIContent = new("Create Asset", "Create a new shared settings asset");
}
/// <summary>
/// Called after the asset editor is created, in case it needs
/// to be customized
/// </summary>
public OnCreateEditorDelegate _OnCreateEditor;
public delegate void OnCreateEditorDelegate(UnityEditor.Editor editor);
/// <summary>
/// Called when the asset being edited was changed by the user.
/// </summary>
public OnChangedDelegate _OnChanged;
public delegate void OnChangedDelegate(System.Type type, Object obj);
/// <summary>
/// Free the resources in OnDisable()
/// </summary>
public void OnDisable()
{
DestroyEditor();
Helpers.Destroy(_DefaultTarget);
}
/// <summary>
/// Customize this after creation if you want
/// </summary>
public GUIContent _CreateButtonGUIContent;
UnityEditor.Editor _Editor = null;
System.Type _Type;
Object _DefaultTarget;
const int k_IndentOffset = 3;
public void DrawEditorCombo(GUIContent label, PropertyDrawer drawer, SerializedProperty property, string extension, int bottomMargin = 0)
{
_Type = drawer.fieldInfo.FieldType;
DrawEditorCombo
(
label,
$"Create {property.displayName} Asset",
$"{property.displayName.Replace(' ', '_')}",
extension,
string.Empty,
false,
property,
bottomMargin
);
}
/// <summary>
/// Call this from OnInspectorGUI. Will draw the asset reference field, and
/// the embedded editor, or a Create Asset button, if no asset is set.
/// </summary>
public void DrawEditorCombo
(
GUIContent label,
string title,
string defaultName,
string extension,
string message,
bool indent,
SerializedProperty property,
int bottomMargin
)
{
UpdateEditor(property);
EditorGUI.BeginChangeCheck();
var rect = AssetField(label, property, title, defaultName, extension, message);
if (EditorGUI.EndChangeCheck())
{
property.serializedObject.ApplyModifiedProperties();
UpdateEditor(property);
}
// Display embedded editor.
if (_Editor != null)
{
var foldoutRect = new Rect(rect.x - k_IndentOffset, rect.y, rect.width + k_IndentOffset, EditorGUIUtility.singleLineHeight);
property.isExpanded = EditorGUI.Foldout(foldoutRect, property.isExpanded, GUIContent.none, true);
var canEditAsset = AssetDatabase.IsOpenForEdit(_Editor.target, StatusQueryOptions.UseCachedIfPossible);
// We take the current GUI state into account to support attribute stacking.
var guiEnabled = GUI.enabled;
GUI.enabled = guiEnabled && canEditAsset;
if (property.isExpanded)
{
var level = EditorGUI.indentLevel;
EditorGUI.indentLevel = 0;
EditorGUILayout.BeginHorizontal();
// NOTE: Tweaked for current usage but probably will not work everywhere.
if (level > 0) GUILayout.Space(8 * (level + 2));
EditorGUILayout.BeginVertical(GUI.skin.box);
if ((_Editor.target.hideFlags & HideFlags.NotEditable) == 0)
{
EditorGUILayout.HelpBox("This is a shared asset. Changes made here will apply to all users of this asset.", MessageType.Info);
}
EditorGUI.BeginChangeCheck();
_Editor.OnInspectorGUI();
if (EditorGUI.EndChangeCheck() && (_OnChanged != null))
_OnChanged(_Type, property.objectReferenceValue);
EditorGUILayout.EndVertical();
EditorGUILayout.EndHorizontal();
if (bottomMargin > 0)
{
EditorGUILayout.Space(bottomMargin);
}
}
// Enable GUI so the checkout button works.
GUI.enabled = true;
if (_Editor.target != null)
{
if (!canEditAsset && GUILayout.Button("Check out"))
{
var task = Provider.Checkout(AssetDatabase.GetAssetPath(_Editor.target), CheckoutMode.Asset);
task.Wait();
}
}
// Restore stacked GUI enabled state.
GUI.enabled = guiEnabled;
}
}
Rect AssetField
(
GUIContent label,
SerializedProperty property,
string title,
string defaultName,
string extension,
string message
)
{
return EditorHelpers.AssetField
(
_Type,
label,
property,
EditorGUILayout.GetControlRect(true),
title,
defaultName,
extension,
message,
x => ScriptableObject.CreateInstance(_Type)
);
}
public void DestroyEditor()
{
if (_Editor != null)
{
Object.DestroyImmediate(_Editor);
_Editor = null;
}
}
public void UpdateEditor(SerializedProperty property)
{
var target = property.objectReferenceValue;
if (target == null && _DefaultTarget == null)
{
_DefaultTarget = ScriptableObject.CreateInstance(_Type);
_DefaultTarget.hideFlags = HideFlags.DontSave | HideFlags.NotEditable;
}
if (target == null)
{
target = _DefaultTarget;
}
// Destroy the editor if target has changed.
if (_Editor != null && _Editor.target != target)
{
DestroyEditor();
}
if (_Editor != null)
{
return;
}
// NOTE: This is triggered twice on asset switch for some reason.
// Create editor if need one.
if (target != null)
{
_Editor = UnityEditor.Editor.CreateEditor(target);
// Pass through argument for editors that receive it
if (property.serializedObject.targetObject != null)
{
(_Editor as IEmbeddableEditor)?.SetTypeOfHostComponent(property.serializedObject.targetObject.GetType());
}
_OnCreateEditor?.Invoke(_Editor);
}
}
}
}

View File

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

View File

@@ -1,272 +0,0 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Reflection;
using System.Text.RegularExpressions;
using UnityEditor;
using UnityEditor.Rendering;
using UnityEngine;
using UnityEngine.Rendering;
namespace WaveHarmonic.Crest.Editor
{
/// <summary>
/// Provides general helper functions for the editor.
/// </summary>
static class EditorHelpers
{
internal static ComputeShader s_VisualizeNegativeValuesShader;
internal static ComputeShader VisualizeNegativeValuesShader
{
get
{
if (s_VisualizeNegativeValuesShader == null)
{
s_VisualizeNegativeValuesShader = AssetDatabase.LoadAssetAtPath<ComputeShader>("Packages/com.waveharmonic.crest/Editor/Shaders/VisualizeNegativeValues.compute");
}
return s_VisualizeNegativeValuesShader;
}
}
public static LayerMask LayerMaskField(string label, LayerMask layerMask)
{
// Adapted from: http://answers.unity.com/answers/1387522/view.html
var temporary = EditorGUILayout.MaskField(
label,
UnityEditorInternal.InternalEditorUtility.LayerMaskToConcatenatedLayersMask(layerMask),
UnityEditorInternal.InternalEditorUtility.layers);
return UnityEditorInternal.InternalEditorUtility.ConcatenatedLayersMaskToLayerMask(temporary);
}
/// <summary>Attempts to get the scene view this camera is rendering.</summary>
/// <returns>The scene view or null if not found.</returns>
public static SceneView GetSceneViewFromSceneCamera(Camera camera)
{
foreach (SceneView sceneView in SceneView.sceneViews)
{
if (sceneView.camera == camera)
{
return sceneView;
}
}
return null;
}
/// <summary>Get time passed to animated materials.</summary>
public static float GetShaderTime()
{
// When "Always Refresh" is disabled, Unity passes zero. Also uses realtimeSinceStartup:
// https://github.com/Unity-Technologies/Graphics/blob/5743e39cdf0795cf7cbeb7ba8ffbbcc7ca200709/Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariablesGlobal.cs#L116
return !Application.isPlaying && SceneView.lastActiveSceneView != null &&
!SceneView.lastActiveSceneView.sceneViewState.alwaysRefresh ? 0f : Time.realtimeSinceStartup;
}
public static GameObject GetGameObject(SerializedObject serializedObject)
{
// We will either get the component or the GameObject it is attached to.
return serializedObject.targetObject is GameObject
? serializedObject.targetObject as GameObject
: (serializedObject.targetObject as Component).gameObject;
}
public static Material CreateSerializedMaterial(string shaderPath, string message)
{
var shader = Shader.Find(shaderPath);
Debug.Assert(shader != null, "Crest: Cannot create required material because shader is null");
var material = new Material(shader);
// Record the material and any subsequent changes.
Undo.RegisterCreatedObjectUndo(material, message);
Undo.RegisterCompleteObjectUndo(material, message);
return material;
}
public static Material CreateSerializedMaterial(string shaderPath)
{
return CreateSerializedMaterial(shaderPath, Undo.GetCurrentGroupName());
}
public static Object GetDefaultReference(this SerializedObject self, string property)
{
var path = AssetDatabase.GetAssetPath(MonoScript.FromMonoBehaviour(self.targetObject as MonoBehaviour));
var importer = AssetImporter.GetAtPath(path) as MonoImporter;
return importer.GetDefaultReference(property);
}
public static object GetDefiningBoxedObject(this SerializedProperty property)
{
object target = property.serializedObject.targetObject;
if (property.depth > 0)
{
// Get the property path so we can find it from the serialized object.
var path = string.Join(".", property.propertyPath.Split(".", System.StringSplitOptions.None)[0..^1]);
var other = property.serializedObject.FindProperty(path);
// Boxed value can handle both managed and generic with caveats:
// https://docs.unity3d.com/ScriptReference/SerializedProperty-boxedValue.html
// Not sure if it will be a new or same instance as in the scene.
target = other.boxedValue;
}
return target;
}
internal delegate Object CreateInstance(SerializedProperty property);
internal static Rect AssetField
(
System.Type type,
GUIContent label,
SerializedProperty property,
Rect rect,
string title,
string defaultName,
string extension,
string message,
CreateInstance create
)
{
var hSpace = 5;
var buttonWidth = 45;
var buttonCount = 2;
rect.width -= buttonWidth * buttonCount + hSpace;
EditorGUI.PropertyField(rect, property, label);
var r = new Rect(rect);
r.x += r.width + hSpace;
r.width = buttonWidth;
if (GUI.Button(r, "New", EditorStyles.miniButtonLeft))
{
var path = EditorUtility.SaveFilePanelInProject(title, defaultName, extension, message);
if (!string.IsNullOrEmpty(path))
{
var asset = create(property);
if (asset != null)
{
if (extension == "prefab")
{
PrefabUtility.SaveAsPrefabAsset(asset as GameObject, path);
}
else
{
AssetDatabase.CreateAsset(asset, path);
}
property.objectReferenceValue = AssetDatabase.LoadAssetAtPath<Object>(path);
property.serializedObject.ApplyModifiedProperties();
}
else
{
Debug.LogError($"Crest: Could not create file");
}
}
}
// Only allow cloning if extensions match. Guards against cloning Shader Graph if
// using its embedded material.
var cloneable = property.objectReferenceValue != null;
cloneable = cloneable && Path.GetExtension(AssetDatabase.GetAssetPath(property.objectReferenceValue)) == $".{extension}";
EditorGUI.BeginDisabledGroup(!cloneable);
r.x += r.width;
if (GUI.Button(r, "Clone", EditorStyles.miniButtonRight))
{
var oldPath = AssetDatabase.GetAssetPath(property.objectReferenceValue);
var newPath = oldPath;
if (!newPath.StartsWithNoAlloc("Assets")) newPath = Path.Join("Assets", Path.GetFileName(newPath));
newPath = AssetDatabase.GenerateUniqueAssetPath(newPath);
AssetDatabase.CopyAsset(oldPath, newPath);
property.objectReferenceValue = AssetDatabase.LoadAssetAtPath<Object>(newPath);
}
EditorGUI.EndDisabledGroup();
return rect;
}
internal static void RichTextHelpBox(string message, MessageType type)
{
var styleRichText = GUI.skin.GetStyle("HelpBox").richText;
GUI.skin.GetStyle("HelpBox").richText = true;
EditorGUILayout.HelpBox(message, type);
// Revert skin since it persists.
GUI.skin.GetStyle("HelpBox").richText = styleRichText;
}
// Prettify nameof.
internal static string Pretty(this string text)
{
// Regular expression to split on transitions from lower to upper case and keep acronyms together
return Regex.Replace(text, @"([a-z])([A-Z])|([A-Z])([A-Z][a-z])", "$1$3 $2$4").Replace("_", "");
}
internal static string Italic(this string text)
{
return $"<i>{text}</i>";
}
public static void MarkCurrentStageAsDirty()
{
var stage = UnityEditor.SceneManagement.PrefabStageUtility.GetCurrentPrefabStage();
if (stage != null)
{
UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(stage.scene);
}
else
{
UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(UnityEngine.SceneManagement.SceneManager.GetActiveScene());
}
}
static readonly MethodInfo s_ButtonWithDropdownList = typeof(EditorGUI).GetMethod
(
"ButtonWithDropdownList",
BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public,
null,
new System.Type[] { typeof(GUIContent), typeof(string[]), typeof(GenericMenu.MenuFunction2), typeof(GUILayoutOption[]) },
null
);
static readonly GUILayoutOption[] s_GUILayoutOptionsZero;
public static bool ButtonWithDropdownList(GUIContent name, string[] names, GenericMenu.MenuFunction2 callback)
{
if (names == null)
{
return GUILayout.Button(name);
}
return (bool)s_ButtonWithDropdownList.Invoke(null, new object[] { name, names, callback, s_GUILayoutOptionsZero });
}
}
static partial class Extensions
{
internal static string GetSubShaderTag([DisallowNull] this Shader shader, ShaderSnippetData snippet, ShaderTagId id)
{
var data = ShaderUtil.GetShaderData(shader);
if (data == null) return null;
var index = (int)snippet.pass.SubshaderIndex;
if (index < 0 || index >= shader.subshaderCount) return null;
var subShader = data.GetSerializedSubshader(index);
if (subShader == null) return null;
var tag = subShader.FindTagValue(id);
if (string.IsNullOrEmpty(tag.name)) return null;
return tag.name;
}
}
}

View File

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

View File

@@ -1,158 +0,0 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
using System.Text.RegularExpressions;
using UnityEditor;
using UnityEngine;
namespace WaveHarmonic.Crest.Editor
{
partial class Inspector
{
static readonly bool s_GroupMessages = false;
static GUIContent s_JumpButtonContent = null;
static GUIContent s_FixButtonContent = null;
protected virtual void RenderValidationMessages()
{
// Enable rich text in help boxes. Store original so we can revert since this might be a "hack".
var styleRichText = GUI.skin.GetStyle("HelpBox").richText;
GUI.skin.GetStyle("HelpBox").richText = true;
// This is a static list so we need to clear it before use. Not sure if this will ever be a threaded
// operation which would be an issue.
foreach (var messages in ValidatedHelper.s_Messages)
{
messages.Clear();
}
ValidatedHelper.ExecuteValidators(target, ValidatedHelper.HelpBox);
// We only want space before and after the list of help boxes. We don't want space between.
var needsSpaceAbove = true;
var needsSpaceBelow = false;
// We loop through in reverse order so errors appears at the top.
for (var messageTypeIndex = 0; messageTypeIndex < ValidatedHelper.s_Messages.Length; messageTypeIndex++)
{
var messages = ValidatedHelper.s_Messages[messageTypeIndex];
if (messages.Count > 0)
{
if (needsSpaceAbove)
{
// Double space looks good at top.
EditorGUILayout.Space();
// EditorGUILayout.Space();
needsSpaceAbove = false;
}
needsSpaceBelow = true;
// Map Validated.MessageType to HelpBox.MessageType.
var messageType = (MessageType)ValidatedHelper.s_Messages.Length - messageTypeIndex;
if (s_GroupMessages)
{
// We join the messages together to reduce vertical space since HelpBox has padding, borders etc.
var joinedMessage = messages[0]._Message;
// Format as list if we have more than one message.
if (messages.Count > 1) joinedMessage = $"- {joinedMessage}";
for (var messageIndex = 1; messageIndex < messages.Count; messageIndex++)
{
joinedMessage += $"\n- {messages[messageIndex]}";
}
EditorGUILayout.HelpBox(joinedMessage, messageType);
}
else
{
foreach (var message in messages)
{
EditorGUILayout.BeginHorizontal();
var fixDescription = message._FixDescription;
var originalGUIEnabled = GUI.enabled;
if (message._Action != null)
{
fixDescription += " Click the fix/repair button on the right to fix.";
if ((message._Action == ValidatedHelper.FixAddMissingMathPackage || message._Action == ValidatedHelper.FixAddMissingBurstPackage) && PackageManagerHelpers.IsBusy)
{
GUI.enabled = false;
}
}
EditorGUILayout.HelpBox($"{message._Message} {fixDescription}", messageType);
// Jump to object button.
if (message._Object != null)
{
// Selection.activeObject can be message._object.gameObject instead of the component
// itself. We soft cast to MonoBehaviour to get the gameObject for comparison.
// Alternatively, we could always pass gameObject instead of "this".
var casted = message._Object as MonoBehaviour;
if (Selection.activeObject != message._Object && (casted == null || casted.gameObject != Selection.activeObject))
{
s_JumpButtonContent ??= new(EditorGUIUtility.FindTexture("scenepicking_pickable_hover@2x"), "Jump to object to resolve issue");
if (GUILayout.Button(s_JumpButtonContent, GUILayout.ExpandWidth(false), GUILayout.ExpandHeight(true)))
{
Selection.activeObject = message._Object;
}
}
}
// Fix the issue button.
if (message._Action != null)
{
s_FixButtonContent ??= new(EditorGUIUtility.FindTexture("SceneViewTools"));
if (message._FixDescription != null)
{
var sanitisedFixDescr = Regex.Replace(message._FixDescription, @"<[^<>]*>", "'");
s_FixButtonContent.tooltip = $"Apply fix: {sanitisedFixDescr}";
}
else
{
s_FixButtonContent.tooltip = "Fix issue";
}
if (GUILayout.Button(s_FixButtonContent, GUILayout.ExpandWidth(false), GUILayout.ExpandHeight(true)))
{
// Run fix function
var serializedObject = new SerializedObject(message._Object);
// Property is optional.
var property = message._PropertyPath != null ? serializedObject?.FindProperty(message._PropertyPath) : null;
var oldValue = property?.boxedValue;
message._Action.Invoke(serializedObject, property);
if (serializedObject.ApplyModifiedProperties())
{
// SerializedObject does this for us, but gives the history item a nicer label.
Undo.RecordObject(message._Object, s_FixButtonContent.tooltip);
DecoratedDrawer.OnChange(property, oldValue);
}
}
}
GUI.enabled = originalGUIEnabled;
EditorGUILayout.EndHorizontal();
}
}
}
}
if (needsSpaceBelow)
{
// EditorGUILayout.Space();
}
// Revert skin since it persists.
GUI.skin.GetStyle("HelpBox").richText = styleRichText;
}
}
}

View File

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

View File

@@ -1,218 +0,0 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
using System;
using System.Collections.Generic;
using System.Reflection;
using UnityEditor;
using UnityEngine;
using WaveHarmonic.Crest.Internal;
namespace WaveHarmonic.Crest.Editor
{
/// <summary>
/// Base editor. Needed as custom drawers require a custom editor to work.
/// </summary>
[CustomEditor(typeof(EditorBehaviour), editorForChildClasses: true)]
partial class Inspector : UnityEditor.Editor
{
public const int k_FieldGroupOrder = 1000;
const string k_NoPrefabModeSupportWarning = "Prefab mode is not supported. Changes made in prefab mode will not be reflected in the scene view. Save this prefab to see changes.";
internal static Inspector Current { get; private set; }
readonly Dictionary<Material, MaterialEditor> _MaterialEditors = new();
public override bool RequiresConstantRepaint() => TexturePreview.AnyActivePreviews;
// Set this from a decorator to enable the material editor.
internal List<Material> _Materials = new();
readonly Utility.SortedList<int, SerializedProperty> _Properties = new(Helpers.DuplicateComparison);
public override void OnInspectorGUI()
{
// Reset foldout values.
DecoratedDrawer.s_IsFoldout = false;
DecoratedDrawer.s_IsFoldoutOpen = false;
// Make sure we adhere to flags.
var enabled = GUI.enabled;
GUI.enabled = (target.hideFlags & HideFlags.NotEditable) == 0;
RenderBeforeInspectorGUI();
RenderInspectorGUI();
RenderValidationMessages();
EditorGUI.BeginDisabledGroup(target is Behaviour component && !component.isActiveAndEnabled);
RenderBottomButtons();
EditorGUI.EndDisabledGroup();
RenderAfterInspectorGUI();
GUI.enabled = enabled;
}
protected virtual void OnDisable()
{
foreach (var (_, editor) in _MaterialEditors)
{
Helpers.Destroy(editor);
}
}
protected void RenderBeforeInspectorGUI()
{
if (this.target is EditorBehaviour target && target._IsPrefabStageInstance)
{
EditorGUILayout.Space();
EditorGUILayout.HelpBox(k_NoPrefabModeSupportWarning, MessageType.Warning);
EditorGUILayout.Space();
}
}
protected virtual void RenderInspectorGUI()
{
var previous = Current;
Current = this;
_Properties.Clear();
serializedObject.Update();
using var iterator = serializedObject.GetIterator();
if (iterator.NextVisible(true))
{
var index = 0;
var group = 0;
Type type = null;
do
{
var property = serializedObject.FindProperty(iterator.name);
if (iterator.name == "m_Script")
{
#if CREST_DEBUG
_Properties.Add(index++, property);
#endif
continue;
}
var field = property.GetFieldInfo(out _);
// If field is on a new type (inheritance) then reset the group to prevent group leakage.
if (field.DeclaringType != type)
{
group = 0;
type = field.DeclaringType;
}
// Null checking but there should always be one DecoratedProperty.
var order = field.GetCustomAttribute<Attributes.DecoratedProperty>()?.order ?? 0;
group = field.GetCustomAttribute<Group>()?.order * k_FieldGroupOrder ?? group;
_Properties.Add(order + group + index++, property);
}
while (iterator.NextVisible(false));
}
foreach (var (_, property) in _Properties)
{
#if CREST_DEBUG
using (new EditorGUI.DisabledGroupScope(property.name == "m_Script"))
#endif
{
// Only support top level ordering for now.
EditorGUILayout.PropertyField(property, includeChildren: true);
}
}
// Need to call just in case there is no decorated property.
serializedObject.ApplyModifiedProperties();
// Restore previous in case this is a nested editor.
Current = previous;
// Fixes indented validation etc.
EditorGUI.indentLevel = 0;
}
protected virtual void RenderBottomButtons()
{
}
protected virtual void RenderAfterInspectorGUI()
{
foreach (var material in _Materials)
{
if (material == null) continue;
DrawMaterialEditor(material);
}
_Materials.Clear();
}
// Adapted from: http://answers.unity.com/answers/975894/view.html
void DrawMaterialEditor(Material material)
{
MaterialEditor editor;
if (!_MaterialEditors.ContainsKey(material))
{
editor = (MaterialEditor)CreateEditor(material);
_MaterialEditors[material] = editor;
}
else
{
editor = _MaterialEditors[material];
}
// Check material again as sometimes null.
if (editor != null && material != null)
{
EditorGUILayout.Space();
// Draw the material's foldout and the material shader field. Required to call OnInspectorGUI.
editor.DrawHeader();
var path = AssetDatabase.GetAssetPath(material);
// We need to prevent the user from editing Unity's default materials.
// Check Editor.IsEnabled in Editor.cs for further filtering.
var isEditable = (material.hideFlags & HideFlags.NotEditable) == 0;
#if !CREST_DEBUG
// Do not allow editing of our assets.
isEditable &= !path.StartsWithNoAlloc("Packages/com.waveharmonic");
#endif
using (new EditorGUI.DisabledGroupScope(!isEditable))
{
// Draw the material properties. Works only if the foldout of DrawHeader is open.
editor.OnInspectorGUI();
}
}
}
}
// Adapted from:
// https://gist.github.com/thebeardphantom/1ad9aee0ef8de6271fff39f1a6a3d66d
static partial class Extensions
{
static readonly MethodInfo s_GetFieldInfoFromProperty;
static Extensions()
{
var utility = typeof(UnityEditor.Editor).Assembly.GetType("UnityEditor.ScriptAttributeUtility");
Debug.Assert(utility != null, "Crest: ScriptAttributeUtility not found!");
s_GetFieldInfoFromProperty = utility.GetMethod("GetFieldInfoFromProperty", BindingFlags.NonPublic | BindingFlags.Static);
Debug.Assert(s_GetFieldInfoFromProperty != null, "Crest: GetFieldInfoFromProperty not found!");
}
public static FieldInfo GetFieldInfo(this SerializedProperty property, out Type type)
{
type = null;
return (FieldInfo)s_GetFieldInfoFromProperty.Invoke(null, new object[] { property, type });
}
}
}

View File

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

View File

@@ -1,54 +0,0 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
using UnityEditor;
using UnityEngine;
namespace WaveHarmonic.Crest.Editor
{
/// <summary>
/// Adds a deprecated message to shaders.
///
/// USAGE
/// Add to bottom of Shader block:
/// CustomEditor "Crest.ObsoleteShaderGUI"
/// Optionally add to Properties block:
/// [HideInInspector] _ObsoleteMessage("The additional message.", Float) = 0
/// </summary>
sealed class ObsoleteShaderGUI : ShaderGUI
{
public override void OnGUI(MaterialEditor editor, MaterialProperty[] properties)
{
// Enable rich text in help boxes. Store original so we can revert since this might be a "hack".
var styleRichText = GUI.skin.GetStyle("HelpBox").richText;
GUI.skin.GetStyle("HelpBox").richText = true;
var message = "";
{
var property = FindProperty("_Message", properties, propertyIsMandatory: false);
if (property != null)
{
message += property.displayName;
}
}
{
var property = FindProperty("_ObsoleteMessage", properties, propertyIsMandatory: false);
if (property != null)
{
message += "This shader is deprecated and will be removed in a future version. " + property.displayName;
}
}
EditorGUILayout.HelpBox(message, MessageType.Warning);
EditorGUILayout.Space(3f);
// Revert skin since it persists.
GUI.skin.GetStyle("HelpBox").richText = styleRichText;
// Render the default GUI.
base.OnGUI(editor, properties);
}
}
}

View File

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

View File

@@ -1,41 +0,0 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
// Helpers for using the Unity Package Manager.
using UnityEditor;
using UnityEditor.PackageManager;
using UnityEditor.PackageManager.Requests;
using UnityEngine;
namespace WaveHarmonic.Crest.Editor
{
static class PackageManagerHelpers
{
static AddRequest s_Request;
public static bool IsBusy => s_Request?.IsCompleted == false;
public static void AddMissingPackage(string packageName)
{
s_Request = Client.Add(packageName);
EditorApplication.update += AddMissingPackageProgress;
}
static void AddMissingPackageProgress()
{
if (s_Request.IsCompleted)
{
if (s_Request.Status == StatusCode.Success)
{
Debug.Log("Installed: " + s_Request.Result.packageId);
}
else if (s_Request.Status >= StatusCode.Failure)
{
Debug.Log(s_Request.Error.message);
}
EditorApplication.update -= AddMissingPackageProgress;
}
}
}
}

View File

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

View File

@@ -1,79 +0,0 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
// Exposes a customized version of Unity's shader generator task to only generate
// our source files.
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using UnityEditor;
using UnityEditor.Rendering;
using UnityEngine.Rendering;
namespace WaveHarmonic.Crest.Editor
{
static class ShaderGeneratorUtility
{
static MethodInfo s_GenerateAsync;
static MethodInfo GenerateAsync => s_GenerateAsync ??= typeof(CSharpToHLSL)
.GetMethod("GenerateAsync", BindingFlags.Static | BindingFlags.NonPublic);
static async Task InvokeAsync(this MethodInfo methodInfo, object obj, params object[] parameters)
{
dynamic awaitable = methodInfo.Invoke(obj, parameters);
await awaitable;
}
// Adapted from:
// https://github.com/Unity-Technologies/Graphics/blob/96ba978a240e96adcb2abceb21e90b24caa484a3/Packages/com.unity.render-pipelines.core/Editor/ShaderGenerator/CSharpToHLSL.cs#L18L53
internal static async Task GenerateAll()
{
Dictionary<string, List<ShaderTypeGenerator>> sourceGenerators = null;
try
{
// Store per source file path the generator definitions
sourceGenerators = DictionaryPool<string, List<ShaderTypeGenerator>>.Get();
// Extract all types with the GenerateHLSL tag
foreach (var type in TypeCache.GetTypesWithAttribute<GenerateHLSL>())
{
// Only generate our sources as Unity's will trigger a package refresh.
if (!type.FullName.StartsWith("WaveHarmonic.Crest")) continue;
var attr = type.GetCustomAttributes(typeof(GenerateHLSL), false).First() as GenerateHLSL;
if (!sourceGenerators.TryGetValue(attr.sourcePath, out var generators))
{
generators = ListPool<ShaderTypeGenerator>.Get();
sourceGenerators.Add(attr.sourcePath, generators);
}
generators.Add(new(type, attr));
}
// We need to force the culture to invariant, otherwise generated code can replace characters.
// For example, Turkish will replace "I" with "İ". This is a bug with GenerateAsync.
var culture = System.Globalization.CultureInfo.CurrentCulture;
System.Globalization.CultureInfo.CurrentCulture = System.Globalization.CultureInfo.InvariantCulture;
// Generate all files
await Task.WhenAll(sourceGenerators.Select(async it => await GenerateAsync
.InvokeAsync(null, new object[] { $"{it.Key}.hlsl", $"{Path.ChangeExtension(it.Key, "custom")}.hlsl", it.Value })));
System.Globalization.CultureInfo.CurrentCulture = culture;
}
finally
{
// Make sure we always release pooled resources
if (sourceGenerators != null)
{
foreach (var pair in sourceGenerators)
ListPool<ShaderTypeGenerator>.Release(pair.Value);
DictionaryPool<string, List<ShaderTypeGenerator>>.Release(sourceGenerators);
}
}
}
}
}

View File

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

View File

@@ -1,199 +0,0 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
#pragma warning disable
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using UnityEditor;
using UnityEditor.ShaderGraph;
using UnityEditor.ShaderGraph.Internal;
using UnityEngine;
[assembly: InternalsVisibleTo("WaveHarmonic.Crest.Editor")]
namespace WaveHarmonic.Crest.Editor
{
internal static class ShaderGraphPropertyDrawers
{
static Dictionary<GraphInputData, bool> s_CompoundPropertyFoldoutStates = new();
static Dictionary<string, string> s_Tooltips;
static readonly GUIContent s_Label = new();
public static void DrawShaderGraphGUI(MaterialEditor materialEditor, IEnumerable<MaterialProperty> properties, Dictionary<string, string> tooltips)
{
s_Tooltips = tooltips;
Material m = materialEditor.target as Material;
Shader s = m.shader;
string path = AssetDatabase.GetAssetPath(s);
ShaderGraphMetadata metadata = null;
foreach (var obj in AssetDatabase.LoadAllAssetsAtPath(path))
{
if (obj is ShaderGraphMetadata meta)
{
metadata = meta;
break;
}
}
if (metadata != null)
DrawShaderGraphGUI(materialEditor, properties, metadata.categoryDatas);
else
PropertiesDefaultGUI(materialEditor, properties);
}
static void PropertiesDefaultGUI(MaterialEditor materialEditor, IEnumerable<MaterialProperty> properties)
{
foreach (var property in properties)
{
if ((property.flags & (MaterialProperty.PropFlags.HideInInspector | MaterialProperty.PropFlags.PerRendererData)) != 0)
continue;
float h = materialEditor.GetPropertyHeight(property, property.displayName);
Rect r = EditorGUILayout.GetControlRect(true, h, EditorStyles.layerMaskField);
s_Label.text = property.displayName;
s_Label.tooltip = s_Tooltips.GetValueOrDefault(property.name, null);
materialEditor.ShaderProperty(r, property, s_Label);
}
}
static Rect GetRect(MaterialProperty prop)
{
return EditorGUILayout.GetControlRect(true, MaterialEditor.GetDefaultPropertyHeight(prop));
}
static MaterialProperty FindProperty(string propertyName, IEnumerable<MaterialProperty> properties)
{
foreach (var prop in properties)
{
if (prop.name == propertyName)
{
return prop;
}
}
return null;
}
public static void DrawShaderGraphGUI(MaterialEditor materialEditor, IEnumerable<MaterialProperty> properties, IEnumerable<MinimalCategoryData> categoryDatas)
{
foreach (MinimalCategoryData mcd in categoryDatas)
{
DrawCategory(materialEditor, properties, mcd);
}
}
static void DrawCategory(MaterialEditor materialEditor, IEnumerable<MaterialProperty> properties, MinimalCategoryData minimalCategoryData)
{
if (minimalCategoryData.categoryName.Length > 0)
{
minimalCategoryData.expanded = EditorGUILayout.BeginFoldoutHeaderGroup(minimalCategoryData.expanded, minimalCategoryData.categoryName);
}
else
{
// force draw if no category name to do foldout on
minimalCategoryData.expanded = true;
}
if (minimalCategoryData.expanded)
{
foreach (var propData in minimalCategoryData.propertyDatas)
{
if (propData.isCompoundProperty == false)
{
MaterialProperty prop = FindProperty(propData.referenceName, properties);
if (prop == null) continue;
DrawMaterialProperty(materialEditor, prop, propData.propertyType, propData.isKeyword, propData.keywordType);
}
else
{
DrawCompoundProperty(materialEditor, properties, propData);
}
}
}
EditorGUILayout.EndFoldoutHeaderGroup();
}
static void DrawCompoundProperty(MaterialEditor materialEditor, IEnumerable<MaterialProperty> properties, GraphInputData compoundPropertyData)
{
EditorGUI.indentLevel++;
bool foldoutState = true;
var exists = s_CompoundPropertyFoldoutStates.ContainsKey(compoundPropertyData);
if (!exists)
s_CompoundPropertyFoldoutStates.Add(compoundPropertyData, true);
else
foldoutState = s_CompoundPropertyFoldoutStates[compoundPropertyData];
foldoutState = EditorGUILayout.Foldout(foldoutState, compoundPropertyData.referenceName);
if (foldoutState)
{
EditorGUI.indentLevel++;
foreach (var subProperty in compoundPropertyData.subProperties)
{
var property = FindProperty(subProperty.referenceName, properties);
if (property == null) continue;
DrawMaterialProperty(materialEditor, property, subProperty.propertyType);
}
EditorGUI.indentLevel--;
}
if (exists)
s_CompoundPropertyFoldoutStates[compoundPropertyData] = foldoutState;
EditorGUI.indentLevel--;
}
static void DrawMaterialProperty(MaterialEditor materialEditor, MaterialProperty property, PropertyType propertyType, bool isKeyword = false, KeywordType keywordType = KeywordType.Boolean)
{
if (!isKeyword)
{
switch (propertyType)
{
case PropertyType.SamplerState:
case PropertyType.Matrix4:
case PropertyType.Matrix3:
case PropertyType.Matrix2:
case PropertyType.VirtualTexture:
case PropertyType.Gradient:
return;
case PropertyType.Vector3:
DrawVector3Property(materialEditor, property);
return;
case PropertyType.Vector2:
DrawVector2Property(materialEditor, property);
return;
}
}
s_Label.text = property.displayName;
s_Label.tooltip = s_Tooltips.GetValueOrDefault(property.name, null);
materialEditor.ShaderProperty(property, s_Label);
}
static void DrawVector2Property(MaterialEditor materialEditor, MaterialProperty property)
{
EditorGUI.BeginChangeCheck();
EditorGUI.showMixedValue = property.hasMixedValue;
Vector2 newValue = EditorGUI.Vector2Field(GetRect(property), property.displayName, new Vector2(property.vectorValue.x, property.vectorValue.y));
EditorGUI.showMixedValue = false;
if (EditorGUI.EndChangeCheck())
{
property.vectorValue = newValue;
}
}
static void DrawVector3Property(MaterialEditor materialEditor, MaterialProperty property)
{
EditorGUI.BeginChangeCheck();
EditorGUI.showMixedValue = property.hasMixedValue;
Vector3 newValue = EditorGUI.Vector3Field(GetRect(property), property.displayName, new Vector3(property.vectorValue.x, property.vectorValue.y, property.vectorValue.z));
EditorGUI.showMixedValue = false;
if (EditorGUI.EndChangeCheck())
{
property.vectorValue = newValue;
}
}
}
}

View File

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

View File

@@ -1,131 +0,0 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
using UnityEditor;
using UnityEngine;
using UnityEngine.Experimental.Rendering;
using WaveHarmonic.Crest.Internal;
namespace WaveHarmonic.Crest.Editor
{
abstract class TexturePreview : ObjectPreview
{
public static bool AnyActivePreviews { get; private set; }
UnityEditor.Editor _Editor;
RenderTexture _RenderTexture;
RenderTextureDescriptor _OriginalDescriptor = new();
Texture _Current;
protected int _Slice;
bool _First = true;
protected abstract Texture OriginalTexture { get; }
protected virtual Texture ModifiedTexture { get; }
Texture Texture => ModifiedTexture != null ? ModifiedTexture : OriginalTexture;
protected virtual bool Flipped => false;
// Preview complains if not a certain set of formats.
bool Incompatible => !(GraphicsFormatUtility.IsIEEE754Format(Texture.graphicsFormat)
|| GraphicsFormatUtility.IsNormFormat(Texture.graphicsFormat));
public TexturePreview() { }
public override bool HasPreviewGUI()
{
AnyActivePreviews = false;
return OriginalTexture;
}
public override void Cleanup()
{
base.Cleanup();
Object.DestroyImmediate(_Editor);
if (_RenderTexture != null) _RenderTexture.Release();
Object.DestroyImmediate(_RenderTexture);
}
public override void OnPreviewSettings()
{
if (_First && Event.current.type == EventType.Repaint && !Application.isPlaying)
{
// Solves on enter edit mode:
// ArgumentException: Getting control 2's position in a group with only 2 controls when doing repaint
_First = false;
return;
}
if (_Editor != null) _Editor.OnPreviewSettings();
}
public override void OnPreviewGUI(Rect rect, GUIStyle background)
{
AnyActivePreviews = true;
// This check is in original.
if (Event.current.type == EventType.Repaint)
{
background.Draw(rect, false, false, false, false);
}
var descriptor = Texture.GetDescriptor();
if (Helpers.RenderTextureNeedsUpdating(descriptor, _OriginalDescriptor))
{
_OriginalDescriptor = descriptor;
if (Incompatible)
{
descriptor.graphicsFormat = GraphicsFormat.R32G32B32A32_SFloat;
}
Helpers.SafeCreateRenderTexture(ref _RenderTexture, descriptor);
_RenderTexture.Create();
Object.DestroyImmediate(_Editor);
_Editor = UnityEditor.Editor.CreateEditor(_RenderTexture);
// Reset for incompatible copy.
descriptor = _OriginalDescriptor;
}
// Name may change without texture changing (see SWS).
_RenderTexture.name = Texture.name + " (Preview)";
if (Incompatible)
{
var temporary = RenderTexture.GetTemporary(descriptor);
Graphics.CopyTexture(Texture, temporary);
Helpers.Blit(temporary, _RenderTexture);
RenderTexture.ReleaseTemporary(temporary);
}
else
{
Graphics.CopyTexture(Texture, _RenderTexture);
}
_Editor.DrawPreview(rect);
}
#if CREST_DEBUG
public override void OnInteractivePreviewGUI(Rect rect, GUIStyle background)
{
OnPreviewGUI(rect, background);
// Show pixel value in preview.
_Slice = Development.Utility.GetPreviewSlice(_Editor, Texture);
var color = Development.Utility.InspectPixel(rect, OriginalTexture, Flipped, _Slice);
var text = Development.Utility.GetInspectPixelString(color, OriginalTexture);
EditorGUI.DropShadowLabel(new Rect(rect.x, rect.y, rect.width, 40), text);
}
#endif
void Allocate(Texture texture)
{
// LOD with buffered data like foam will recreate every frame freezing controls.
if (_Editor != null && _Current == Texture) return;
_Current = texture;
Object.DestroyImmediate(_Editor);
_Editor = UnityEditor.Editor.CreateEditor(texture);
}
}
}

View File

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

View File

@@ -1,302 +0,0 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
// How to use:
// Use or inherit from Crest.Editor.Inspector to support validation messages.
// Then create a static method with Validator attribute.
using System.Collections.Generic;
using System.Reflection;
using UnityEditor;
using UnityEngine;
using WaveHarmonic.Crest.Internal;
namespace WaveHarmonic.Crest.Editor
{
using FixValidation = System.Action<SerializedObject, SerializedProperty>;
[System.AttributeUsage(System.AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
sealed class Validator : System.Attribute
{
public readonly System.Type _Type;
public Validator(System.Type type)
{
_Type = type;
}
}
// Holds the shared list for messages
static class ValidatedHelper
{
public enum MessageType
{
Error,
Warning,
Info,
}
public struct HelpBoxMessage
{
public string _Message;
public string _FixDescription;
public Object _Object;
public FixValidation _Action;
public string _PropertyPath;
}
// This is a shared resource. It will be cleared before use. It is only used by the HelpBox delegate since we
// want to group them by severity (MessageType). Make sure length matches MessageType length.
public static readonly List<HelpBoxMessage>[] s_Messages = new List<HelpBoxMessage>[]
{
new(),
new(),
new(),
};
public delegate void ShowMessage(string message, string fixDescription, MessageType type, Object @object = null, FixValidation action = null, string property = null);
public static void DebugLog(string message, string fixDescription, MessageType type, Object @object = null, FixValidation action = null, string property = null)
{
// Never log info validation to console.
if (type == MessageType.Info)
{
return;
}
message = $"Crest Validation: {message} {fixDescription} Click this message to highlight the problem object.";
switch (type)
{
case MessageType.Error: Debug.LogError(message, @object); break;
case MessageType.Warning: Debug.LogWarning(message, @object); break;
default: Debug.Log(message, @object); break;
}
}
public static void HelpBox(string message, string fixDescription, MessageType type, Object @object = null, FixValidation action = null, string property = null)
{
s_Messages[(int)type].Add(new() { _Message = message, _FixDescription = fixDescription, _Object = @object, _Action = action, _PropertyPath = property });
}
public static void Suppressed(string message, string fixDescription, MessageType type, Object @object = null, FixValidation action = null, string property = null)
{
}
public static T FixAttachComponent<T>(SerializedObject componentOrGameObject)
where T : Component
{
return Undo.AddComponent<T>(EditorHelpers.GetGameObject(componentOrGameObject));
}
internal static void FixSetMaterialOptionEnabled(SerializedObject material, string keyword, string floatParam, bool enabled)
{
var mat = material.targetObject as Material;
Undo.RecordObject(mat, $"Enable keyword {keyword}");
mat.SetBoolean(Shader.PropertyToID(floatParam), enabled);
if (ArrayUtility.Contains(mat.shader.keywordSpace.keywordNames, keyword))
{
mat.SetKeyword(keyword, enabled);
}
}
internal static void FixSetMaterialIntProperty(SerializedObject material, string label, string intParam, int value)
{
var mat = material.targetObject as Material;
Undo.RecordObject(mat, $"change {label}");
mat.SetInteger(intParam, value);
}
public static void FixAddMissingMathPackage(SerializedObject _0, SerializedProperty _1)
{
PackageManagerHelpers.AddMissingPackage("com.unity.mathematics");
}
public static void FixAddMissingBurstPackage(SerializedObject _0, SerializedProperty _1)
{
PackageManagerHelpers.AddMissingPackage("com.unity.burst");
}
public static bool ValidateNoScale(Object @object, Transform transform, ShowMessage showMessage)
{
if (transform.lossyScale != Vector3.one)
{
showMessage
(
$"There must be no scale on the <i>{@object.GetType().Name}</i> Transform or any of its parents." +
$"The current scale is <i>{transform.lossyScale}</i>.",
"Reset the scale on this Transform and all parents to one.",
MessageType.Error, @object
);
return false;
}
return true;
}
public static bool ValidateNoRotation(Object @object, Transform transform, ShowMessage showMessage)
{
if (transform.eulerAngles.magnitude > 0.0001f)
{
showMessage
(
$"There must be no rotation on the <i>{@object.GetType().Name}</i> Transform or any of its parents." +
$"The current rotation is <i>{transform.eulerAngles}.</i>",
"Reset the rotation on this Transform and all parents to zero.",
MessageType.Error, @object
);
return false;
}
return true;
}
public static bool ValidateRenderer<T>
(
Component component,
Renderer renderer,
ShowMessage showMessage,
bool checkShaderPasses,
string shaderPrefix = null
)
where T : Renderer
{
if (renderer == null)
{
var type = typeof(T);
var name = type.Name;
// Give users a hint as to what "Renderer" really means.
if (type == typeof(Renderer))
{
name += " (Mesh, Trail etc)";
}
showMessage
(
$"A <i>{name}</i> component is required but none is assigned.",
"Provide a renderer.",
MessageType.Error, component
);
return false;
}
var materials = renderer.sharedMaterials;
for (var i = 0; i < materials.Length; i++)
{
// Empty material slots is a user error. Unity complains about it so we should too.
if (materials[i] == null)
{
showMessage
(
$"<i>{renderer.GetType().Name}</i> used by this input (<i>{component.GetType().Name}</i>) has empty material slots.",
"Remove these slots or fill them with a material.",
MessageType.Error, renderer
);
}
}
if (renderer is MeshRenderer)
{
renderer.gameObject.TryGetComponent<MeshFilter>(out var mf);
if (mf == null)
{
showMessage
(
$"A <i>MeshRenderer</i> component is being used by this input but no <i>MeshFilter</i> component was found so there may not be any valid geometry to render.",
"Attach a <i>MeshFilter</i> component.",
MessageType.Error, renderer.gameObject,
(_, _) => Undo.AddComponent<MeshFilter>(renderer.gameObject)
);
return false;
}
else if (mf.sharedMesh == null)
{
showMessage
(
$"A <i>MeshRenderer</i> component is being used by this input but no mesh is assigned to the <i>MeshFilter</i> component.",
"Assign the geometry to be rendered to the <i>MeshFilter</i> component.",
MessageType.Error, renderer.gameObject
);
return false;
}
}
if (!ValidateMaterial(renderer.gameObject, showMessage, renderer.sharedMaterial, shaderPrefix, checkShaderPasses))
{
return false;
}
return true;
}
public static bool ValidateMaterial(GameObject gameObject, ShowMessage showMessage, Material material, string shaderPrefix, bool checkShaderPasses)
{
if (shaderPrefix == null && material == null)
{
showMessage
(
$"<i>Mesh Renderer</i> requires a material.",
"Assign a material.",
MessageType.Error, gameObject
);
return false;
}
if (!material || material.shader && (!material.shader.name.StartsWithNoAlloc(shaderPrefix) && !material.shader.name.StartsWithNoAlloc($"Hidden/{shaderPrefix}") && !material.shader.name.Contains("/All/")))
{
showMessage
(
$"Shader assigned to water input expected to be of type <i>{shaderPrefix}</i>.",
"Assign a material that uses a shader of this type.",
MessageType.Error, gameObject
);
return false;
}
if (checkShaderPasses && material.passCount > 1)
{
showMessage
(
$"The shader <i>{material.shader.name}</i> for material <i>{material.name}</i> has multiple passes which might not work as expected as only the first pass is executed. " +
"This can be ignored in most cases, like Shader Graph, as only one pass is often required.",
"To have all passes execute then set <i>Shader Pass Index</i> to <i>-1</i>.",
MessageType.Info, gameObject
);
}
return true;
}
public static bool ExecuteValidators(object target, ShowMessage messenger)
{
var isValid = true;
var type = target.GetType();
var validators = TypeCache.GetMethodsWithAttribute<Validator>();
foreach (var validator in validators)
{
var attribute = validator.GetCustomAttribute<Validator>();
if (attribute._Type.IsAssignableFrom(type))
{
isValid = (bool)validator.Invoke(null, new object[] { target, messenger }) && isValid;
}
}
return isValid;
}
public static bool ExecuteValidators(Object target)
{
return ExecuteValidators(target, DebugLog);
}
}
}

View File

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

View File

@@ -1,23 +0,0 @@
{
"name": "WaveHarmonic.Crest.Shared.Editor",
"rootNamespace": "",
"references": [
"GUID:056ff2a5b2f124d468c6655552acdca5",
"GUID:3eae0364be2026648bf74846acb8a731",
"GUID:df380645f10b7bc4b97d4f5eb6303d95",
"GUID:be0903cd8e1546f498710afdc59db5eb"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": false,
"defineConstraints": [
"UNITY_2022_3_OR_NEWER"
],
"versionDefines": [],
"noEngineReferences": false
}

View File

@@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 1ab2a6c2a51cd4b43867788dbaee1a55
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -1,86 +0,0 @@
{
"name": "WaveHarmonic.Crest.Editor",
"rootNamespace": "",
"references": [
"GUID:d60799ab2a985554ea1a39cd38695018",
"GUID:3eae0364be2026648bf74846acb8a731",
"GUID:df380645f10b7bc4b97d4f5eb6303d95",
"GUID:78bd2ddd6e276394a9615c203e574844",
"GUID:457756d89b35d2941b3e7b37b4ece6f1",
"GUID:c579267770062bf448e75eb160330b7f",
"GUID:15fc0a57446b3144c949da3e2b9737a9",
"GUID:be0903cd8e1546f498710afdc59db5eb",
"GUID:7c347618730f5467f86a58f333ce21df",
"GUID:056ff2a5b2f124d468c6655552acdca5",
"GUID:1ab2a6c2a51cd4b43867788dbaee1a55"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": false,
"defineConstraints": [
"UNITY_2022_3_OR_NEWER"
],
"versionDefines": [
{
"name": "com.unity.modules.director",
"expression": "",
"define": "d_ModuleUnityDirector"
},
{
"name": "com.unity.postprocessing",
"expression": "",
"define": "d_UnityPostProcessing"
},
{
"name": "com.unity.postprocessing",
"expression": "(,3.4.0)",
"define": "d_UnityPostProcessingBroken"
},
{
"name": "com.unity.render-pipelines.high-definition",
"expression": "",
"define": "d_UnityHDRP"
},
{
"name": "com.unity.render-pipelines.universal",
"expression": "",
"define": "d_UnityURP"
},
{
"name": "com.unity.shadergraph",
"expression": "",
"define": "d_UnityShaderGraph"
},
{
"name": "com.waveharmonic.crest.cpu-queries",
"expression": "",
"define": "d_CrestCPUQueries"
},
{
"name": "com.waveharmonic.crest.paint",
"expression": "",
"define": "d_CrestPaint"
},
{
"name": "com.waveharmonic.crest.splines",
"expression": "",
"define": "d_CrestSpline"
},
{
"name": "com.waveharmonic.crest.portals",
"expression": "",
"define": "d_CrestPortals"
},
{
"name": "com.waveharmonic.crest.shifting-origin",
"expression": "",
"define": "d_WaveHarmonic_Crest_ShiftingOrigin"
}
],
"noEngineReferences": false
}

View File

@@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 125c216bac85c4443bfd4de6bc7eda99
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant: