升级水插件
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Crest Water System
|
||||
// Crest Water System
|
||||
// Copyright © 2024 Wave Harmonic. All rights reserved.
|
||||
|
||||
using UnityEditor;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Crest Water System
|
||||
// Crest Water System
|
||||
// Copyright © 2024 Wave Harmonic. All rights reserved.
|
||||
|
||||
using UnityEditor;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0c03a1671f6a84ed4ac42813e95a07d7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c4c99fd6915934b21a43efb6ca9915f7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user