升级水插件

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

View File

@@ -1,4 +1,4 @@
// Crest Water System
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
// Adapted from:
@@ -619,6 +619,44 @@ namespace WaveHarmonic.Crest
}
}
sealed class InlineToggle : DecoratedProperty
{
// Add extra y offset. Needed for foldouts in foldouts so far.
readonly bool _Fix;
public InlineToggle(bool fix = false)
{
_Fix = fix;
}
internal override bool NeedsControlRectangle(SerializedProperty property)
{
return false;
}
internal override void OnGUI(Rect position, SerializedProperty property, GUIContent label, DecoratedDrawer drawer)
{
var r = position;
r.x -= 16f;
// Align with Space offset.
if (drawer.Space > 0) r.y += drawer.Space + 2f;
if (_Fix) r.y += EditorGUIUtility.singleLineHeight + 2f;
// Seems to be needed.
r.width = 16f * (1f + EditorGUI.indentLevel);
r.height = EditorGUIUtility.singleLineHeight;
label.text = "";
using (new EditorGUI.PropertyScope(r, label, property))
{
EditorGUI.BeginProperty(r, label, property);
// Passing a tooltip to Toggle does nothing.
GUI.Label(r, label);
property.boolValue = EditorGUI.Toggle(r, property.boolValue);
EditorGUI.EndProperty();
}
}
}
/// <summary>
/// Allows an enum to render only a subset of options in subclasses.
/// </summary>
@@ -793,6 +831,9 @@ namespace WaveHarmonic.Crest
case SerializedPropertyType.Float:
EditorGUILayout.FloatField(label, (float)_PropertyInfo.GetValue(_Target));
break;
case SerializedPropertyType.Integer:
EditorGUILayout.IntField(label, (int)_PropertyInfo.GetValue(_Target));
break;
case SerializedPropertyType.Enum:
_EnumValues ??= Enum.GetValues(_PropertyInfo.PropertyType);
EditorGUILayout.Popup(label, Array.IndexOf(_EnumValues, _PropertyInfo.GetValue(_Target)), property.enumDisplayNames);
@@ -886,7 +927,7 @@ namespace WaveHarmonic.Crest
/// </summary>
sealed class Space : Decorator
{
readonly float _Height;
public readonly float _Height;
readonly bool _AlwaysVisible;
public Space(float height, bool isAlwaysVisible = false)
@@ -978,13 +1019,13 @@ namespace WaveHarmonic.Crest
}
}
sealed class AttachMaterialEditor : Decorator
sealed class AttachMaterialEditor : Attribute
{
public override bool AlwaysVisible => true;
public int Order { get; private set; }
internal override void Decorate(Rect position, SerializedProperty property, GUIContent label, DecoratedDrawer drawer)
public AttachMaterialEditor(int order = 0)
{
Inspector.Current._Materials.Add((Material)property.objectReferenceValue);
Order = order;
}
}

View File

@@ -1,4 +1,4 @@
// Crest Water System
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
using UnityEditor;
@@ -11,17 +11,19 @@ namespace WaveHarmonic.Crest
sealed class Embedded : DecoratedProperty
{
internal EmbeddedAssetEditor _Editor;
readonly int _BottomMargin;
public int BottomMargin { get; private set; }
public string DefaultPropertyName { get; private set; }
public Embedded(int margin = 0)
public Embedded(int margin = 0, string defaultPropertyName = null)
{
_Editor = new();
_BottomMargin = margin;
BottomMargin = margin;
DefaultPropertyName = defaultPropertyName;
}
internal override void OnGUI(Rect position, SerializedProperty property, GUIContent label, DecoratedDrawer drawer)
{
_Editor.DrawEditorCombo(label, drawer, property, "asset", _BottomMargin);
_Editor.DrawEditorCombo(this, label, drawer, property, "asset");
}
internal override bool NeedsControlRectangle(SerializedProperty property) => false;

View File

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

View File

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

View File

@@ -1,4 +1,4 @@
// Crest Water System
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
using System;
@@ -51,6 +51,12 @@ namespace WaveHarmonic.Crest
_Member = _Type.GetMember(member, Helpers.s_AnyMethod)[0];
}
/// <inheritdoc cref="Predicated(Type, string, object, bool, bool)"/>
public Predicated(Type type, string member, bool inverted = false, bool hide = false) : this(type, member, true, inverted, hide)
{
}
/// <summary>
/// Enable/Disable field depending on the current type of the component.
/// </summary>
@@ -165,11 +171,11 @@ namespace WaveHarmonic.Crest
if (_Member is PropertyInfo autoProperty)
{
// == operator does not work.
enabled = autoProperty.GetValue(@object).Equals(_DisableValue);
enabled = !autoProperty.GetValue(@object).Equals(_DisableValue);
}
else if (_Member is MethodInfo method)
{
enabled = method.Invoke(@object, new object[] { }).Equals(_DisableValue);
enabled = !method.Invoke(@object, new object[] { }).Equals(_DisableValue);
}
if (_Inverted) enabled = !enabled;

View File

@@ -1,4 +1,4 @@
// Crest Water System
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
// Adapted from:
@@ -27,6 +27,8 @@ namespace WaveHarmonic.Crest.Editor
public static bool s_TemporaryColor;
public static Color s_PreviousColor;
public float Space { get; private set; }
List<object> _Decorators = null;
List<object> Decorators
{
@@ -84,6 +86,16 @@ namespace WaveHarmonic.Crest.Editor
attribute.Decorate(position, property, label, this);
}
Space = 0;
// Execute all labels
for (var index = 0; index < Decorators.Count; index++)
{
var attribute = (Decorator)Decorators[index];
if (attribute is Space space) Space = space._Height;
label = attribute.BuildLabel(label);
}
if (!s_HideInInspector && (!s_IsFoldout || s_IsFoldoutOpen))
{
// Execute all visual attributes.
@@ -91,7 +103,6 @@ namespace WaveHarmonic.Crest.Editor
{
var attribute = (Decorator)Decorators[index];
if (attribute.AlwaysVisible) continue;
label = attribute.BuildLabel(label);
attribute.Decorate(position, property, label, this);
}
@@ -101,6 +112,7 @@ namespace WaveHarmonic.Crest.Editor
: position;
// Call for labels again as EditorGUI.GetPropertyHeight will revert them.
// Specifically for nested classes where the label will revert once opened.
for (var index = 0; index < Decorators.Count; index++)
{
var attribute = (Decorator)Decorators[index];
@@ -163,7 +175,7 @@ namespace WaveHarmonic.Crest.Editor
var targetType = target.GetType();
var isActive = true;
if (property.serializedObject.targetObject is Internal.EditorBehaviour c && !c.isActiveAndEnabled)
if (property.serializedObject.targetObject is Crest.Internal.EditorBehaviour c && !c.isActiveAndEnabled)
{
isActive = false;
}

View File

@@ -7,9 +7,10 @@
// Lovingly adapted from Cinemachine:
// https://github.com/Unity-Technologies/com.unity.cinemachine/blob/593fa283bee378322337e5d9f5a7b91331a45799/Editor/Utility/EmbeddedAssetHelpers.cs
using UnityEngine;
using System.Reflection;
using UnityEditor;
using UnityEditor.VersionControl;
using UnityEngine;
namespace WaveHarmonic.Crest.Editor
{
@@ -66,23 +67,24 @@ namespace WaveHarmonic.Crest.Editor
System.Type _Type;
Object _DefaultTarget;
FieldInfo _DefaultTargetField;
const int k_IndentOffset = 3;
public void DrawEditorCombo(GUIContent label, PropertyDrawer drawer, SerializedProperty property, string extension, int bottomMargin = 0)
public void DrawEditorCombo(Embedded embedded, GUIContent label, PropertyDrawer drawer, SerializedProperty property, string extension)
{
_Type = drawer.fieldInfo.FieldType;
DrawEditorCombo
(
embedded,
label,
$"Create {property.displayName} Asset",
$"{property.displayName.Replace(' ', '_')}",
extension,
string.Empty,
false,
property,
bottomMargin
property
);
}
@@ -92,17 +94,17 @@ namespace WaveHarmonic.Crest.Editor
/// </summary>
public void DrawEditorCombo
(
Embedded embedded,
GUIContent label,
string title,
string defaultName,
string extension,
string message,
bool indent,
SerializedProperty property,
int bottomMargin
SerializedProperty property
)
{
UpdateEditor(property);
UpdateEditor(property, embedded);
EditorGUI.BeginChangeCheck();
var rect = AssetField(label, property, title, defaultName, extension, message);
@@ -110,7 +112,7 @@ namespace WaveHarmonic.Crest.Editor
if (EditorGUI.EndChangeCheck())
{
property.serializedObject.ApplyModifiedProperties();
UpdateEditor(property);
UpdateEditor(property, embedded);
}
// Display embedded editor.
@@ -145,9 +147,9 @@ namespace WaveHarmonic.Crest.Editor
EditorGUILayout.EndVertical();
EditorGUILayout.EndHorizontal();
if (bottomMargin > 0)
if (embedded.BottomMargin > 0)
{
EditorGUILayout.Space(bottomMargin);
EditorGUILayout.Space(embedded.BottomMargin);
}
}
@@ -201,14 +203,31 @@ namespace WaveHarmonic.Crest.Editor
}
}
public void UpdateEditor(SerializedProperty property)
public void UpdateEditor(SerializedProperty property, Embedded embedded)
{
var target = property.objectReferenceValue;
var hasDefaultField = !string.IsNullOrEmpty(embedded.DefaultPropertyName);
if (target == null && _DefaultTarget == null)
if (target == null)
{
_DefaultTarget = ScriptableObject.CreateInstance(_Type);
_DefaultTarget.hideFlags = HideFlags.DontSave | HideFlags.NotEditable;
if (!hasDefaultField)
{
if (_DefaultTarget == null)
{
_DefaultTarget = ScriptableObject.CreateInstance(_Type);
_DefaultTarget.hideFlags = HideFlags.DontSave | HideFlags.NotEditable;
}
}
else
{
if (_DefaultTargetField == null)
{
_DefaultTargetField = property.serializedObject.targetObject.GetType().GetField(embedded.DefaultPropertyName, Helpers.s_AnyMethod);
}
// Always call, as it is dynamic.
_DefaultTarget = (Object)_DefaultTargetField.GetValue(property.serializedObject.targetObject);
}
}
if (target == null)

View File

@@ -1,4 +1,4 @@
// Crest Water System
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
using System.Diagnostics.CodeAnalysis;
@@ -15,7 +15,7 @@ namespace WaveHarmonic.Crest.Editor
/// <summary>
/// Provides general helper functions for the editor.
/// </summary>
static class EditorHelpers
static partial class EditorHelpers
{
internal static ComputeShader s_VisualizeNegativeValuesShader;
internal static ComputeShader VisualizeNegativeValuesShader
@@ -227,27 +227,6 @@ namespace WaveHarmonic.Crest.Editor
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
@@ -269,4 +248,241 @@ namespace WaveHarmonic.Crest.Editor
return tag.name;
}
}
static partial class EditorHelpers
{
const int k_ButtonDropDownWidth = 15;
static readonly GUIContent s_ButtonDropDownIcon = new(EditorGUIUtility.FindTexture("icon dropdown@2x"));
static readonly PropertyInfo s_TopLevel = typeof(GUILayoutUtility).GetProperty("topLevel", BindingFlags.NonPublic | BindingFlags.Static);
static readonly MethodInfo s_GetLast = typeof(GUILayoutUtility).Assembly.GetType("UnityEngine.GUILayoutGroup").GetMethod("GetLast", BindingFlags.Public | BindingFlags.Instance);
// Only way to identify the caller is its rect.
static Rect s_ButtonChooser;
static int s_ButtonChoice = -2;
// Normal button or split button with dropdown.
public static bool Button
(
GUIContent label,
out int choice,
string[] labels,
bool disableMain = false,
bool disableDropDown = false,
bool centerLabel = false,
bool expandWidth = true,
int minimumWidth = 0
)
{
choice = -2;
var chosen = false;
var hasDropDown = labels?.Length > 0;
var skin = GUI.skin.button;
using (new EditorGUI.DisabledGroupScope(disableMain))
{
var style = new GUIStyle(hasDropDown ? EditorStyles.miniButtonLeft : EditorStyles.miniButton)
{
padding = skin.padding,
stretchHeight = skin.stretchHeight,
fixedHeight = skin.fixedHeight
};
var width = style.CalcSize(label).x + style.padding.left +
style.padding.right + style.border.left + style.border.right;
width = Mathf.Max(width, minimumWidth);
// TODO: Add option to disable this (consistent width).
if (!hasDropDown && minimumWidth > 0) width += k_ButtonDropDownWidth;
if (centerLabel && hasDropDown) style.padding.left += k_ButtonDropDownWidth;
if (GUILayout.Button(label, style, expandWidth ? GUILayout.ExpandWidth(true) : GUILayout.Width(width)))
{
choice = -1;
chosen = true;
}
}
if (hasDropDown)
{
using (new EditorGUI.DisabledGroupScope(disableDropDown))
{
// TODO: color interior border same as exterior (lighten).
var style = new GUIStyle(EditorStyles.miniButtonRight)
{
padding = new(1, 1, 3, 3),
stretchHeight = skin.stretchHeight,
fixedHeight = skin.fixedHeight
};
var rect = (Rect)s_GetLast.Invoke(s_TopLevel.GetValue(null), null);
rect.width += k_ButtonDropDownWidth;
if (s_ButtonChoice > -1 && s_ButtonChooser == rect)
{
choice = s_ButtonChoice;
chosen = true;
s_ButtonChoice = -2;
s_ButtonChooser = Rect.zero;
}
if (GUILayout.Button(s_ButtonDropDownIcon, style, GUILayout.Width(k_ButtonDropDownWidth), GUILayout.ExpandHeight(true)))
{
var menu = new GenericMenu();
for (var i = 0; i < labels.Length; i++)
{
menu.AddItem(new(labels[i]), false, x => { s_ButtonChoice = (int)x; s_ButtonChooser = rect; }, i);
}
menu.DropDown(rect);
}
}
}
return chosen;
}
}
static partial class EditorHelpers
{
// Adapted from (public API may support this in future):
// com.unity.splines@2.7.2/Editor/Components/SplineContainerEditor.cs
static GUIStyle s_HelpLabelStyle;
static GUIStyle HelpLabelStyle => s_HelpLabelStyle ??= new(EditorStyles.label)
{
wordWrap = EditorStyles.helpBox.wordWrap,
fontSize = EditorStyles.helpBox.fontSize,
padding = new(-2, 0, 0, 0),
richText = true,
};
static readonly MethodInfo s_GetHelpIcon = typeof(EditorGUIUtility).GetMethod("GetHelpIcon", BindingFlags.Static | BindingFlags.NonPublic);
internal static int? HelpBox
(
GUIContent message,
MessageType type,
GUIContent button = null,
string[] buttons = null,
bool buttonCenterLabel = false,
int buttonMinimumWidth = 0
)
{
return HelpBox
(
message,
new GUIContent((Texture2D)s_GetHelpIcon.Invoke(null, new object[] { type })),
button,
buttons,
buttonCenterLabel,
buttonMinimumWidth
);
}
internal static int? HelpBox
(
GUIContent message,
GUIContent icon,
GUIContent button = null,
string[] buttons = null,
bool buttonCenterLabel = false,
int buttonMinimumWidth = 0
)
{
int? result = null;
// Box
EditorGUILayout.BeginHorizontal(EditorStyles.helpBox);
// Icon
EditorGUIUtility.SetIconSize(new(32f, 32f));
EditorGUILayout.LabelField(icon, GUILayout.Width(34), GUILayout.MinHeight(34), GUILayout.ExpandHeight(true));
EditorGUIUtility.SetIconSize(Vector2.zero);
// Text
EditorGUILayout.LabelField(message, HelpLabelStyle, GUILayout.ExpandWidth(true), GUILayout.ExpandHeight(true));
// Button
if (button != null)
{
GUILayout.FlexibleSpace();
EditorGUILayout.BeginVertical();
GUILayout.FlexibleSpace();
EditorGUILayout.BeginHorizontal();
if (Button(button, out var choice, buttons, centerLabel: buttonCenterLabel, minimumWidth: buttonMinimumWidth, expandWidth: false))
{
result = choice;
}
EditorGUILayout.EndHorizontal();
GUILayout.FlexibleSpace();
EditorGUILayout.EndVertical();
}
EditorGUILayout.EndHorizontal();
return result;
}
}
namespace Internal
{
static class Extensions
{
// Recursively find the field owner (instance).
public static bool FindOwner(this FieldInfo field, ref object target)
{
if (field.DeclaringType.IsAssignableFrom(target.GetType()))
{
return true;
}
return field.FindOwnerInFields(ref target);
}
public static bool FindOwnerInFields(this FieldInfo targetField, ref object target)
{
if (target == null)
{
return false;
}
var fields = target.GetType()
.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
foreach (var field in fields)
{
if (field.GetCustomAttribute<SerializeReference>() == null)
{
continue;
}
var value = field.GetValue(target);
if (value == null)
{
continue;
}
if (targetField.DeclaringType.IsAssignableFrom(value.GetType()))
{
target = value;
return true;
}
if (FindOwnerInFields(targetField, ref value))
{
return true;
}
}
return false;
}
}
}
}

View File

@@ -9,16 +9,12 @@ namespace WaveHarmonic.Crest.Editor
{
partial class Inspector
{
static readonly bool s_GroupMessages = false;
static GUIContent s_JumpButtonContent = null;
static GUIContent s_FixButtonContent = null;
static readonly string[] s_FixButtonDropDown = { "Inspect" };
static readonly GUIContent s_FixButtonContent = new("Fix");
static readonly GUIContent s_InspectButtonContent = new("Inspect", "Jump to object to resolve issue.");
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)
@@ -30,7 +26,43 @@ namespace WaveHarmonic.Crest.Editor
// We only want space before and after the list of help boxes. We don't want space between.
var needsSpaceAbove = true;
var needsSpaceBelow = false;
// Work out if button label needs aligning.
var needsAlignment = false;
var hasBoth = false;
var hasEither = false;
for (var messageTypeIndex = 0; messageTypeIndex < ValidatedHelper.s_Messages.Length; messageTypeIndex++)
{
var messages = ValidatedHelper.s_Messages[messageTypeIndex];
if (messages.Count > 0)
{
var messageType = (MessageType)ValidatedHelper.s_Messages.Length - messageTypeIndex;
foreach (var message in messages)
{
var hasFix = message._Action != null;
var hasInspect = false;
if (message._Object != null)
{
var casted = message._Object as MonoBehaviour;
if (Selection.activeObject != message._Object && (casted == null || casted.gameObject != Selection.activeObject))
{
hasInspect = true;
}
}
if (hasFix && hasInspect) hasBoth = true;
if (hasInspect != hasFix) hasEither = true;
if (hasBoth && hasEither) goto exit;
}
}
}
exit:
needsAlignment = hasBoth && hasEither;
// We loop through in reverse order so errors appears at the top.
for (var messageTypeIndex = 0; messageTypeIndex < ValidatedHelper.s_Messages.Length; messageTypeIndex++)
@@ -41,118 +73,87 @@ namespace WaveHarmonic.Crest.Editor
{
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)
foreach (var message in messages)
{
// 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}";
EditorGUILayout.BeginHorizontal();
for (var messageIndex = 1; messageIndex < messages.Count; messageIndex++)
var originalGUIEnabled = GUI.enabled;
if ((message._Action == ValidatedHelper.FixAddMissingMathPackage || message._Action == ValidatedHelper.FixAddMissingBurstPackage) && PackageManagerHelpers.IsBusy)
{
joinedMessage += $"\n- {messages[messageIndex]}";
GUI.enabled = false;
}
EditorGUILayout.HelpBox(joinedMessage, messageType);
}
else
{
foreach (var message in messages)
if (message._FixDescription != null)
{
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();
var sanitized = Regex.Replace(message._FixDescription, @"<[^<>]*>", "'");
s_FixButtonContent.tooltip = $"Fix: {sanitized}";
}
else
{
s_FixButtonContent.tooltip = "Fix issue";
}
var canFix = message._Action != null;
var canInspect = false;
// 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))
{
canInspect = true;
}
}
var result = EditorHelpers.HelpBox
(
new($"{message._Message} {message._FixDescription}"),
messageType,
canFix ? s_FixButtonContent : canInspect ? s_InspectButtonContent : null,
buttons: canInspect && canFix ? s_FixButtonDropDown : null,
buttonCenterLabel: needsAlignment,
buttonMinimumWidth: 72
);
if (canFix && result == -1)
{
// 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);
}
}
else if (canInspect && result != null)
{
Selection.activeObject = message._Object;
}
GUI.enabled = originalGUIEnabled;
EditorGUILayout.EndHorizontal();
}
}
}
if (needsSpaceBelow)
{
// EditorGUILayout.Space();
}
// Revert skin since it persists.
GUI.skin.GetStyle("HelpBox").richText = styleRichText;
}
}
}

View File

@@ -3,9 +3,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEditor;
using UnityEngine;
using WaveHarmonic.Crest.Editor.Internal;
using WaveHarmonic.Crest.Internal;
namespace WaveHarmonic.Crest.Editor
@@ -21,15 +23,32 @@ namespace WaveHarmonic.Crest.Editor
internal static Inspector Current { get; private set; }
readonly Dictionary<FieldInfo, object> _MaterialOwners = new();
readonly Dictionary<Material, MaterialEditor> _MaterialEditors = new();
public override bool RequiresConstantRepaint() => TexturePreview.AnyActivePreviews;
public override bool RequiresConstantRepaint() => TexturePreview.s_ActiveInstance?.Open == true;
// Set this from a decorator to enable the material editor.
internal List<Material> _Materials = new();
static readonly IOrderedEnumerable<FieldInfo> s_AttachMaterialEditors = TypeCache
.GetFieldsWithAttribute<AttachMaterialEditor>()
.OrderBy(x => x.GetCustomAttribute<AttachMaterialEditor>().Order);
readonly Utility.SortedList<int, SerializedProperty> _Properties = new(Helpers.DuplicateComparison);
protected virtual void OnEnable()
{
_MaterialOwners.Clear();
foreach (var field in s_AttachMaterialEditors)
{
var target = (object)this.target;
if (field.FindOwner(ref target))
{
_MaterialOwners.Add(field, target);
}
}
}
public override void OnInspectorGUI()
{
// Reset foldout values.
@@ -61,7 +80,7 @@ namespace WaveHarmonic.Crest.Editor
}
}
protected void RenderBeforeInspectorGUI()
protected virtual void RenderBeforeInspectorGUI()
{
if (this.target is EditorBehaviour target && target._IsPrefabStageInstance)
{
@@ -143,13 +162,14 @@ namespace WaveHarmonic.Crest.Editor
protected virtual void RenderAfterInspectorGUI()
{
foreach (var material in _Materials)
foreach (var mapping in _MaterialOwners)
{
if (material == null) continue;
DrawMaterialEditor(material);
var material = (Material)mapping.Key.GetValue(mapping.Value);
if (material != null)
{
DrawMaterialEditor(material);
}
}
_Materials.Clear();
}
// Adapted from: http://answers.unity.com/answers/975894/view.html

View File

@@ -0,0 +1,75 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
using System.Linq;
using UnityEditor;
using UnityEditor.Build;
namespace WaveHarmonic.Crest.Editor.Settings
{
static class ScriptingSymbols
{
static NamedBuildTarget CurrentNamedBuildTarget
{
get
{
#if UNITY_SERVER
return NamedBuildTarget.Server;
#else
return NamedBuildTarget.FromBuildTargetGroup(BuildPipeline.GetBuildTargetGroup(EditorUserBuildSettings.activeBuildTarget));
#endif
}
}
public static string[] Symbols => PlayerSettings.GetScriptingDefineSymbols(CurrentNamedBuildTarget).Split(';');
public static void Add(string[] symbols)
{
// We remove our symbols from the list first to prevent duplicates - just to be safe.
SetScriptingDefineSymbols(Symbols.Except(symbols).Concat(symbols).ToArray());
}
public static void Add(string symbol)
{
Add(new string[] { symbol });
}
public static void Remove(string[] symbols)
{
SetScriptingDefineSymbols(Symbols.Except(symbols).ToArray());
}
public static void Remove(string symbol)
{
Remove(new string[] { symbol });
}
public static void Set(string[] symbols, bool enable)
{
if (enable)
{
Add(symbols);
}
else
{
Remove(symbols);
}
}
public static void Set(string symbol, bool enable)
{
Set(new string[] { symbol }, enable);
}
static void SetScriptingDefineSymbols(string[] symbols)
{
SetScriptingDefineSymbols(string.Join(";", symbols));
}
static void SetScriptingDefineSymbols(string symbols)
{
PlayerSettings.SetScriptingDefineSymbols(CurrentNamedBuildTarget, symbols);
}
}
}

View File

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

View File

@@ -0,0 +1,739 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEditor;
using UnityEditor.Rendering.BuiltIn;
using UnityEditor.Rendering.BuiltIn.ShaderGraph;
using UnityEditor.ShaderGraph;
using UnityEngine;
using UnityEngine.Rendering;
#if UNITY_2022_3_OR_NEWER
using UnityEngine.UIElements;
#else
using UnityEngine.UIElements;
using UnityEditor.UIElements;
#endif
using UnityBuiltInLitSubTarget = UnityEditor.Rendering.BuiltIn.ShaderGraph.BuiltInLitSubTarget;
namespace WaveHarmonic.Crest.Editor.ShaderGraph
{
sealed class MaterialModificationProcessor : AssetModificationProcessor
{
static void OnWillCreateAsset(string asset)
{
if (!asset.ToLowerInvariant().EndsWith(".mat"))
{
return;
}
MaterialPostProcessor.s_CreatedAssets.Add(asset);
}
}
sealed class MaterialPostProcessor : AssetPostprocessor
{
public override int GetPostprocessOrder()
{
return 1;
}
internal static readonly List<string> s_CreatedAssets = new();
static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
{
foreach (var asset in importedAssets)
{
// We only care about materials
if (!asset.EndsWith(".mat", System.StringComparison.InvariantCultureIgnoreCase))
{
continue;
}
// Load the material and look for it's BuiltIn ShaderID.
// We only care about versioning materials using a known BuiltIn ShaderID.
// This skips any materials that only target other render pipelines, are user shaders,
// or are shaders we don't care to version
var material = (Material)AssetDatabase.LoadAssetAtPath(asset, typeof(Material));
var shaderID = ShaderUtils.GetShaderID(material.shader);
if (shaderID == ShaderUtils.ShaderID.Unknown)
{
continue;
}
if (material.shader == null || material.shader.name != "Crest/Water")
{
continue;
}
// Look for the BuiltIn AssetVersion
AssetVersion assetVersion = null;
var allAssets = AssetDatabase.LoadAllAssetsAtPath(asset);
foreach (var subAsset in allAssets)
{
if (subAsset is AssetVersion sub)
{
assetVersion = sub;
}
}
if (!assetVersion)
{
if (s_CreatedAssets.Contains(asset))
{
s_CreatedAssets.Remove(asset);
CustomBuiltInLitGUI.UpdateMaterial(material);
}
}
}
}
}
class CustomBuiltInLitGUI : BuiltInLitGUI
{
MaterialEditor _MaterialEditor;
MaterialProperty[] _Properties;
static readonly GUIContent s_WorkflowModeText = EditorGUIUtility.TrTextContent
(
"Workflow Mode",
"Select a workflow that fits your textures. Choose between Metallic or Specular."
);
static readonly GUIContent s_TransparentReceiveShadowsText = EditorGUIUtility.TrTextContent
(
"Receives Shadows",
"When enabled, other GameObjects can cast shadows onto this GameObject."
);
public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] properties)
{
_MaterialEditor = materialEditor;
_Properties = properties;
base.OnGUI(materialEditor, properties);
}
public override void ValidateMaterial(Material material)
{
base.ValidateMaterial(material);
UpdateMaterial(material);
}
public override void AssignNewShaderToMaterial(Material material, Shader oldShader, Shader newShader)
{
base.AssignNewShaderToMaterial(material, oldShader, newShader);
UpdateMaterial(material);
}
protected override void DrawSurfaceOptions(Material material)
{
var materialEditor = _MaterialEditor;
var properties = _Properties;
var workflowProperty = FindProperty(Property.SpecularWorkflowMode(), properties, false);
if (workflowProperty != null)
{
DoPopup(s_WorkflowModeText, materialEditor, workflowProperty, System.Enum.GetNames(typeof(WorkflowMode)));
}
base.DrawSurfaceOptions(material);
var surfaceTypeProp = FindProperty(Property.Surface(), properties, false);
if (surfaceTypeProp != null && (SurfaceType)surfaceTypeProp.floatValue == SurfaceType.Transparent)
{
var trsProperty = FindProperty(BuiltInLitSubTarget.s_TransparentReceiveShadowsProperty, properties, false);
DrawFloatToggleProperty(s_TransparentReceiveShadowsText, trsProperty);
}
}
// Should be called by ShaderGraphMaterialsUpdater, but we will never upgrade.
public static new void UpdateMaterial(Material material)
{
if (material.HasProperty(Property.SpecularWorkflowMode()))
{
var workflow = (WorkflowMode)material.GetFloat(Property.SpecularWorkflowMode());
CoreUtils.SetKeyword(material, BuiltInLitSubTarget.LitDefines.s_SpecularSetup.referenceName, workflow == WorkflowMode.Specular);
}
if (material.HasProperty(BuiltInLitSubTarget.s_TransparentReceiveShadowsProperty))
{
var receive = material.GetFloat(BuiltInLitSubTarget.s_TransparentReceiveShadowsProperty) == 1f;
CoreUtils.SetKeyword(material, BuiltInLitSubTarget.LitDefines.s_TransparentReceivesShadows.referenceName, receive);
}
}
}
sealed class BuiltInLitSubTarget : BuiltInSubTarget
{
const string k_ShaderPath = "Packages/com.waveharmonic.crest/Runtime/Shaders/Library/Utility/Legacy";
const string k_TemplatePath = "Packages/com.waveharmonic.crest/Editor/Shaders/Templates";
readonly UnityBuiltInLitSubTarget _BuiltInLitSubTarget;
#pragma warning disable IDE0032, IDE1006
[SerializeField]
WorkflowMode m_WorkflowMode = WorkflowMode.Metallic;
[SerializeField]
NormalDropOffSpace m_NormalDropOffSpace = NormalDropOffSpace.Tangent;
[SerializeField]
bool m_TransparentReceiveShadows = true;
#pragma warning restore IDE0032, IDE1006
public static readonly string s_TransparentReceiveShadowsProperty = "_BUILTIN_TransparentReceiveShadows";
public BuiltInLitSubTarget()
{
_BuiltInLitSubTarget = new();
displayName = _BuiltInLitSubTarget.displayName;
}
protected override ShaderUtils.ShaderID shaderID => ShaderUtils.ShaderID.SG_Lit;
public override bool IsActive() => true;
WorkflowMode WorkflowMode
{
get => m_WorkflowMode;
set => m_WorkflowMode = value;
}
NormalDropOffSpace NormalDropOffSpace
{
get => m_NormalDropOffSpace;
set => m_NormalDropOffSpace = value;
}
bool TransparentReceiveShadows
{
get => m_TransparentReceiveShadows;
set => m_TransparentReceiveShadows = value;
}
#if UNITY_2022_3_OR_NEWER
static FieldInfo s_CustomEditorForRenderPipelines;
static FieldInfo CustomEditorForRenderPipelines => s_CustomEditorForRenderPipelines ??= typeof(TargetSetupContext).GetField("customEditorForRenderPipelines", BindingFlags.NonPublic | BindingFlags.Instance);
#endif
public override void Setup(ref TargetSetupContext context)
{
_BuiltInLitSubTarget.target = target;
_BuiltInLitSubTarget.normalDropOffSpace = NormalDropOffSpace;
_BuiltInLitSubTarget.Setup(ref context);
// Caused a crash: !context.HasCustomEditorForRenderPipeline(null)
if (string.IsNullOrEmpty(target.customEditorGUI))
{
#if UNITY_2022_3_OR_NEWER
var editors = (List<ShaderCustomEditor>)CustomEditorForRenderPipelines.GetValue(context);
if (editors.Count > 0)
{
editors.RemoveAt(editors.Count - 1);
}
context.AddCustomEditorForRenderPipeline(typeof(CustomBuiltInLitGUI).FullName, "");
#else
if (context.customEditorForRenderPipelines.Count > 0)
{
context.customEditorForRenderPipelines.RemoveAt(context.customEditorForRenderPipelines.Count - 1);
}
context.customEditorForRenderPipelines.Add((typeof(CustomBuiltInLitGUI).FullName, ""));
#endif
}
context.subShaders.RemoveAt(0);
context.AddSubShader(SubShaders.Lit(this));
}
public override void ProcessPreviewMaterial(Material material)
{
_BuiltInLitSubTarget.target = target;
_BuiltInLitSubTarget.normalDropOffSpace = NormalDropOffSpace;
_BuiltInLitSubTarget.ProcessPreviewMaterial(material);
CustomBuiltInLitGUI.UpdateMaterial(material);
}
public override void GetFields(ref TargetFieldContext context)
{
_BuiltInLitSubTarget.target = target;
_BuiltInLitSubTarget.normalDropOffSpace = NormalDropOffSpace;
_BuiltInLitSubTarget.GetFields(ref context);
// Do not use this, as we handle this properly.
context.AddField(BuiltInFields.SpecularSetup, false);
}
public override void GetActiveBlocks(ref TargetActiveBlockContext context)
{
_BuiltInLitSubTarget.target = target;
_BuiltInLitSubTarget.normalDropOffSpace = NormalDropOffSpace;
_BuiltInLitSubTarget.GetActiveBlocks(ref context);
context.activeBlocks.Remove(BlockFields.SurfaceDescription.Metallic);
var insertion = context.activeBlocks.FindIndex(x => x == BlockFields.SurfaceDescription.Occlusion) + 1;
if ((WorkflowMode == WorkflowMode.Specular) || target.allowMaterialOverride)
{
context.activeBlocks.Insert(insertion, BlockFields.SurfaceDescription.Specular);
}
if ((WorkflowMode == WorkflowMode.Metallic) || target.allowMaterialOverride)
{
context.activeBlocks.Insert(insertion, BlockFields.SurfaceDescription.Metallic);
}
}
public override void CollectShaderProperties(PropertyCollector collector, GenerationMode generationMode)
{
if (target.allowMaterialOverride)
{
collector.AddFloatProperty(Property.SpecularWorkflowMode(), (float)WorkflowMode);
}
_BuiltInLitSubTarget.target = target;
_BuiltInLitSubTarget.normalDropOffSpace = NormalDropOffSpace;
_BuiltInLitSubTarget.CollectShaderProperties(collector, generationMode);
if (target.allowMaterialOverride)
{
collector.AddFloatProperty(s_TransparentReceiveShadowsProperty, TransparentReceiveShadows ? 1f : 0f);
}
// LEqual
collector.AddFloatProperty(SubShaders.k_ShadowCasterZTest, 4, UnityEditor.ShaderGraph.Internal.HLSLDeclaration.UnityPerMaterial);
}
public override void GetPropertiesGUI(ref TargetPropertyGUIContext context, System.Action onChange, System.Action<string> registerUndo)
{
target.AddDefaultMaterialOverrideGUI(ref context, onChange, registerUndo);
context.AddProperty("Workflow", new EnumField(WorkflowMode.Metallic) { value = WorkflowMode }, (evt) =>
{
if (Equals(WorkflowMode, evt.newValue))
return;
registerUndo("Change Workflow");
WorkflowMode = (WorkflowMode)evt.newValue;
onChange();
});
target.GetDefaultSurfacePropertiesGUI(ref context, onChange, registerUndo);
context.AddProperty("Transparent Receives Shadows", new Toggle() { value = TransparentReceiveShadows }, (evt) =>
{
if (Equals(TransparentReceiveShadows, evt.newValue))
return;
registerUndo("Change Transparent Receives Shadows");
TransparentReceiveShadows = evt.newValue;
onChange();
});
context.AddProperty("Fragment Normal Space", new EnumField(NormalDropOffSpace.Tangent) { value = NormalDropOffSpace }, (evt) =>
{
if (Equals(NormalDropOffSpace, evt.newValue))
return;
registerUndo("Change Fragment Normal Space");
NormalDropOffSpace = (NormalDropOffSpace)evt.newValue;
_BuiltInLitSubTarget.normalDropOffSpace = NormalDropOffSpace;
onChange();
});
}
static class SubShaders
{
static readonly string s_ShaderPathDefines = $"{k_ShaderPath}/Defines.hlsl";
static readonly string s_ShaderPathBuilding = $"{k_ShaderPath}/LegacyBuilding.hlsl";
// SetShaderPassEnabled on ShadowCaster pass does not work for BIRP. We set ZTest
// to Never which is the best we can do. We are still incurring the draw call cost.
// This is an issue because of the way we trigger motion vectors, but is a bug with
// Unity and should be reported.
internal const string k_ShadowCasterZTest = "_Crest_BUILTIN_ShadowCasterZTest";
internal static System.Type s_SubShadersType;
internal static System.Type SubShadersType => s_SubShadersType ??= typeof(UnityBuiltInLitSubTarget).GetNestedType("SubShaders", BindingFlags.Static | BindingFlags.NonPublic);
internal static MethodInfo s_LitMethod;
internal static MethodInfo LitMethod => s_LitMethod ??= SubShadersType.GetMethod("Lit", BindingFlags.Static | BindingFlags.Public);
static void PatchIncludes(ref PassDescriptor result)
{
var includes = new IncludeCollection();
includes.Add(s_ShaderPathDefines, IncludeLocation.Pregraph);
includes.Add("Packages/com.unity.shadergraph/Editor/Generation/Targets/BuiltIn/Editor/ShaderGraph/Includes/ShaderPass.hlsl", IncludeLocation.Pregraph);
foreach (var include in result.includes)
{
includes.AddInternal(include.guid, include.path, include.location, include.fieldConditions);
}
result.includes = includes;
}
static void PatchSpecularIncludes(ref PassDescriptor result, string file)
{
var ic = new IncludeCollection();
foreach (var include in result.includes)
{
if (include.path.EndsWith(file))
{
ic.Add(s_ShaderPathBuilding, include.location);
ic.AddInternal(include.guid, include.path, include.location, include.fieldConditions);
}
else
{
ic.AddInternal(include.guid, include.path, include.location, include.fieldConditions);
}
}
result.includes = ic;
}
static readonly Dictionary<string, string> s_Mappings = new()
{
{ "SHADERPASS_FORWARD", "PBRForwardPass.hlsl" },
{ "SHADERPASS_FORWARD_ADD", "PBRForwardAddPass.hlsl" },
{ "SHADERPASS_DEFERRED", "PBRDeferredPass.hlsl" },
};
static readonly string[] s_SkipVariants = new string[]
{
"LIGHTMAP_ON",
"LIGHTMAP_SHADOW_MIXING",
"DIRLIGHTMAP_COMBINED",
"DYNAMICLIGHTMAP_ON",
"SHADOWS_SHADOWMASK",
};
public static SubShaderDescriptor Lit(BuiltInLitSubTarget subtarget)
{
var target = subtarget.target;
var ssd = (SubShaderDescriptor)LitMethod.Invoke(null, new object[] { target, target.renderType, target.renderQueue });
PassCollection passes = new();
foreach (var item in ssd.passes)
{
// Many artifacts in U6 if our Write Depth enabled.
// Caused by _SURFACE_TYPE_TRANSPARENT in m_ValidKeywords.
if (item.descriptor.referenceName == "SceneSelectionPass")
{
continue;
}
var result = item.descriptor;
var keywords = new KeywordCollection();
foreach (var keyword in result.keywords)
{
// All others are either duplicate or unused.
if (!keyword.descriptor.referenceName.StartsWith("_BUILTIN_"))
{
continue;
}
keywords.Add(keyword.descriptor, keyword.fieldConditions);
}
result.keywords = keywords;
switch (item.descriptor.referenceName)
{
case "SHADERPASS_FORWARD":
case "SHADERPASS_FORWARD_ADD":
case "SHADERPASS_DEFERRED":
AddWorkflowModeControlToPass(ref result, target, subtarget.WorkflowMode);
PatchSpecularIncludes(ref result, s_Mappings[item.descriptor.referenceName]);
var pragmas = new PragmaCollection();
foreach (var pragma in result.pragmas)
{
// For UAVs (RWStructuredBuffer).
if (pragma.descriptor.value.StartsWithNoAlloc("target"))
{
pragmas.Add(Pragma.Target(ShaderModel.Target45));
continue;
}
if (pragma.descriptor.value.StartsWithNoAlloc("vertex"))
{
pragmas.Add(Pragma.SkipVariants(s_SkipVariants));
}
pragmas.Add(pragma.descriptor, pragma.fieldConditions);
}
result.pragmas = pragmas;
goto default;
default:
PatchIncludes(ref result);
break;
}
switch (item.descriptor.referenceName)
{
case "SHADERPASS_FORWARD":
case "SHADERPASS_FORWARD_ADD":
AddReceivesShadowsControlToPass(ref result, target, subtarget.TransparentReceiveShadows);
break;
case "SHADERPASS_SHADOWCASTER":
var states = new RenderStateCollection();
foreach (var state in result.renderStates)
{
if (state.descriptor.type == RenderStateType.ZTest)
{
states.Add(RenderState.ZTest($"[{k_ShadowCasterZTest}]"));
continue;
}
states.Add(state.descriptor, state.fieldConditions);
}
result.renderStates = states;
break;
}
// Add missing cull render state.
if (item.descriptor.referenceName == "SHADERPASS_FORWARD_ADD")
{
CoreRenderStates.AddUberSwitchedCull(target, result.renderStates);
}
// Inject MV before DO pass.
if (item.descriptor.referenceName == "SHADERPASS_DEPTHONLY")
{
var mv = LitPasses.MotionVectors(target);
PatchIncludes(ref mv);
passes.Add(mv);
}
// Fix XR SPI.
if (result.requiredFields != null)
{
var found = false;
foreach (var collection in result.requiredFields)
{
if (collection.field == StructFields.Attributes.instanceID)
{
found = true;
break;
}
}
if (!found)
{
result.requiredFields.Add(StructFields.Attributes.instanceID);
}
}
passes.Add(result);
}
ssd.passes = passes;
return ssd;
}
static void AddWorkflowModeControlToPass(ref PassDescriptor pass, BuiltInTarget target, WorkflowMode workflowMode)
{
if (target.allowMaterialOverride)
{
pass.keywords.Add(LitDefines.s_SpecularSetup);
}
else if (workflowMode == WorkflowMode.Specular)
{
pass.defines.Add(LitDefines.s_SpecularSetup, 1);
}
}
static void AddReceivesShadowsControlToPass(ref PassDescriptor pass, BuiltInTarget target, bool receives)
{
if (target.allowMaterialOverride)
{
pass.keywords.Add(LitDefines.s_TransparentReceivesShadows);
pass.keywords.Add(LitDefines.s_ShadowsSingleCascade);
pass.keywords.Add(LitDefines.s_ShadowsSplitSpheres);
pass.keywords.Add(LitDefines.s_ShadowsSoft);
}
else if (receives)
{
pass.defines.Add(LitDefines.s_TransparentReceivesShadows, 1);
pass.keywords.Add(LitDefines.s_ShadowsSingleCascade);
pass.keywords.Add(LitDefines.s_ShadowsSplitSpheres);
pass.keywords.Add(LitDefines.s_ShadowsSoft);
}
}
}
static class LitPasses
{
static readonly string s_ShaderPathMotionVectorCommon = $"{k_ShaderPath}/MotionVectorCommon.hlsl";
static readonly string s_ShaderPathMotionVectorPass = $"{k_ShaderPath}/MotionVectorPass.hlsl";
public static RenderStateDescriptor UberSwitchedCullRenderState(BuiltInTarget target)
{
if (target.allowMaterialOverride)
{
return RenderState.Cull(CoreRenderStates.Uniforms.cullMode);
}
else
{
return RenderState.Cull(CoreRenderStates.RenderFaceToCull(target.renderFace));
}
}
public static PassDescriptor MotionVectors(BuiltInTarget target)
{
var result = new PassDescriptor()
{
// Definition
displayName = "BuiltIn MotionVectors",
referenceName = "SHADERPASS_MOTION_VECTORS",
lightMode = "MotionVectors",
useInPreview = false,
// Template
passTemplatePath = BuiltInTarget.kTemplatePath,
sharedTemplateDirectories = BuiltInTarget.kSharedTemplateDirectories.Union
(
new string[]
{
k_TemplatePath,
"Packages/com.unity.shadergraph/Editor/Generation/Targets/BuiltIn/Editor/ShaderGraph"
}
).ToArray(),
// Port Mask
validVertexBlocks = new BlockFieldDescriptor[]
{
BlockFields.VertexDescription.Position,
},
validPixelBlocks = CoreBlockMasks.FragmentAlphaOnly,
// Fields
structs = CoreStructCollections.Default,
requiredFields = new()
{
// Needed for XR, but not sure if correct.
StructFields.Attributes.instanceID,
},
fieldDependencies = CoreFieldDependencies.Default,
// Conditional State
renderStates = new()
{
{ RenderState.ZTest(ZTest.LEqual) },
{ RenderState.ZWrite(ZWrite.On) },
{ UberSwitchedCullRenderState(target) },
// MVs write to the depth buffer causing z-fighting. Luckily, the depth texture has
// already been updated, and will not be updated before water renders.
{ RenderState.ColorMask("ColorMask RG\nOffset 1, 1") },
},
pragmas = new()
{
{ Pragma.Target(ShaderModel.Target35) }, // NOTE: SM 2.0 only GL
{ Pragma.MultiCompileInstancing },
{ Pragma.Vertex("vert") },
{ Pragma.Fragment("frag") },
},
defines = new() { CoreDefines.BuiltInTargetAPI },
keywords = new(),
includes = new()
{
// Pre-graph
{ CoreIncludes.CorePregraph },
{ CoreIncludes.ShaderGraphPregraph },
// Post-graph
{ s_ShaderPathMotionVectorCommon, IncludeLocation.Postgraph },
{ CoreIncludes.CorePostgraph },
{ s_ShaderPathMotionVectorPass, IncludeLocation.Postgraph },
},
// Custom Interpolator Support
customInterpolators = CoreCustomInterpDescriptors.Common,
};
// Only support time for now.
result.defines.Add(LitDefines.s_AutomaticTimeBasedMotionVectors, 1);
CorePasses.AddAlphaClipControlToPass(ref result, target);
return result;
}
}
internal static class LitDefines
{
public static readonly KeywordDescriptor s_AutomaticTimeBasedMotionVectors = new()
{
displayName = "Automatic Time-Based Motion Vectors",
referenceName = "AUTOMATIC_TIME_BASED_MOTION_VECTORS",
type = KeywordType.Boolean,
definition = KeywordDefinition.Predefined,
scope = KeywordScope.Local,
stages = KeywordShaderStage.Vertex,
};
public static readonly KeywordDescriptor s_SpecularSetup = new()
{
displayName = "Specular Setup",
referenceName = "_BUILTIN_SPECULAR_SETUP",
type = KeywordType.Boolean,
definition = KeywordDefinition.ShaderFeature,
scope = KeywordScope.Local,
stages = KeywordShaderStage.Fragment
};
public static readonly KeywordDescriptor s_TransparentReceivesShadows = new()
{
displayName = "Transparent Receives Shadows",
referenceName = "_BUILTIN_TRANSPARENT_RECEIVES_SHADOWS",
type = KeywordType.Boolean,
definition = KeywordDefinition.ShaderFeature,
scope = KeywordScope.Local,
stages = KeywordShaderStage.Fragment
};
public static readonly KeywordDescriptor s_ShadowsSingleCascade = new()
{
displayName = "Single Cascade Shadows",
referenceName = "SHADOWS_SINGLE_CASCADE",
type = KeywordType.Boolean,
definition = KeywordDefinition.MultiCompile,
scope = KeywordScope.Global,
stages = KeywordShaderStage.All,
};
public static readonly KeywordDescriptor s_ShadowsSoft = new()
{
displayName = "Soft Shadows",
referenceName = "SHADOWS_SOFT",
type = KeywordType.Boolean,
definition = KeywordDefinition.MultiCompile,
scope = KeywordScope.Global,
stages = KeywordShaderStage.All,
};
public static readonly KeywordDescriptor s_ShadowsSplitSpheres = new()
{
displayName = "Stable Fit Shadows",
referenceName = "SHADOWS_SPLIT_SPHERES",
type = KeywordType.Boolean,
definition = KeywordDefinition.MultiCompile,
scope = KeywordScope.Global,
stages = KeywordShaderStage.All,
};
}
}
}

View File

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

View File

@@ -10,7 +10,8 @@ namespace WaveHarmonic.Crest.Editor
{
abstract class TexturePreview : ObjectPreview
{
public static bool AnyActivePreviews { get; private set; }
public static TexturePreview s_ActiveInstance;
public bool Open { get; private set; }
UnityEditor.Editor _Editor;
RenderTexture _RenderTexture;
@@ -34,7 +35,11 @@ namespace WaveHarmonic.Crest.Editor
public override bool HasPreviewGUI()
{
AnyActivePreviews = false;
if (Event.current != null && Event.current.type == EventType.Layout)
{
Open = false;
}
return OriginalTexture;
}
@@ -61,7 +66,8 @@ namespace WaveHarmonic.Crest.Editor
public override void OnPreviewGUI(Rect rect, GUIStyle background)
{
AnyActivePreviews = true;
s_ActiveInstance = this;
Open = true;
// This check is in original.
if (Event.current.type == EventType.Repaint)
@@ -69,6 +75,18 @@ namespace WaveHarmonic.Crest.Editor
background.Draw(rect, false, false, false, false);
}
if (Texture is Cubemap)
{
if (_Editor == null || _Editor.target != Texture)
{
Object.DestroyImmediate(_Editor);
_Editor = UnityEditor.Editor.CreateEditor(Texture);
}
_Editor.DrawPreview(rect);
return;
}
var descriptor = Texture.GetDescriptor();
if (Helpers.RenderTextureNeedsUpdating(descriptor, _OriginalDescriptor))
@@ -111,6 +129,11 @@ namespace WaveHarmonic.Crest.Editor
{
OnPreviewGUI(rect, background);
if (Texture is Cubemap)
{
return;
}
// Show pixel value in preview.
_Slice = Development.Utility.GetPreviewSlice(_Editor, Texture);
var color = Development.Utility.InspectPixel(rect, OriginalTexture, Flipped, _Slice);

View File

@@ -1,4 +1,4 @@
// Crest Water System
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
// How to use:
@@ -79,7 +79,7 @@ namespace WaveHarmonic.Crest.Editor
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 void Suppressed(string _0, string _1, MessageType _2, Object _3 = null, FixValidation _4 = null, string _5 = null)
{
}