200 lines
6.5 KiB
C#
200 lines
6.5 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using UnityEngine.UI;
|
|
|
|
namespace Plugins.Animate_UI_Materials
|
|
{
|
|
/// <summary>
|
|
/// Used in combination with GraphicMaterialOverride to modify and animate shader properties
|
|
/// The base class is used for the shared Editor script, and reacting to OnValidate events
|
|
/// </summary>
|
|
[ExecuteAlways]
|
|
public abstract class GraphicPropertyOverride : MonoBehaviour, IMaterialPropertyModifier
|
|
{
|
|
/// <summary>
|
|
/// The name of the shader property, serialized
|
|
/// </summary>
|
|
[SerializeField] protected string propertyName;
|
|
|
|
public virtual string DisplayName => propertyName;
|
|
|
|
/// <summary>
|
|
/// The id of the shader property
|
|
/// </summary>
|
|
/// <remarks> Should not be serialized, as it can change between game runs</remarks>
|
|
protected int PropertyId;
|
|
|
|
// Request a material update whenever the parent changes
|
|
void OnEnable()
|
|
{
|
|
SetMaterialDirty(true);
|
|
}
|
|
|
|
void OnDisable()
|
|
{
|
|
SetMaterialDirty();
|
|
}
|
|
|
|
#if UNITY_EDITOR // if in the unity editor, include unity editor callbacks
|
|
/// <summary>
|
|
/// On editor change, mark as dirty
|
|
/// </summary>
|
|
void OnValidate()
|
|
{
|
|
SetMaterialDirty(true);
|
|
}
|
|
#endif
|
|
|
|
/// <summary>
|
|
/// Try to retrieve and apply the default property value
|
|
/// If the source material cannot be found, reset to sensible defaults
|
|
/// </summary>
|
|
public abstract void ResetPropertyToDefault();
|
|
|
|
/// <summary>
|
|
/// Set the material as dirty
|
|
/// ApplyModifiedProperty will be called by the parent GraphicMaterialProperty
|
|
/// </summary>
|
|
/// <param name="renewId">If the GraphicPropertyOverride should try to get the shader property id, just to be safe</param>
|
|
public void SetMaterialDirty(bool renewId = false)
|
|
{
|
|
if (renewId || PropertyId == 0) PropertyId = Shader.PropertyToID(propertyName);
|
|
GraphicMaterialOverride parent = ParentOverride;
|
|
if (parent) parent.SetMaterialDirty();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Try to apply the GraphicPropertyOverride property to the material
|
|
/// Does not create a copy, only feed material instances to this
|
|
/// </summary>
|
|
/// <param name="material">The material to modify</param>
|
|
public abstract void ApplyModifiedProperty(Material material);
|
|
|
|
/// <summary>
|
|
/// The name of the shader property to override
|
|
/// Can be invalid if the shader has changed, or if the component was not setup
|
|
/// </summary>
|
|
public string PropertyName
|
|
{
|
|
get => propertyName;
|
|
set
|
|
{
|
|
propertyName = value;
|
|
SetMaterialDirty(true);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Try to get the Graphic component on the parent
|
|
/// </summary>
|
|
protected Graphic ParentGraphic => transform.parent ? transform.parent.GetComponent<Graphic>() : null;
|
|
|
|
/// <summary>
|
|
/// Try to get the GraphicMaterialOverride component on the parent
|
|
/// </summary>
|
|
protected GraphicMaterialOverride ParentOverride =>
|
|
transform.parent ? transform.parent.GetComponent<GraphicMaterialOverride>() : null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Template extension of GraphicPropertyOverride
|
|
/// Adds LateUpdate function to react to value changes
|
|
/// Compares value changes using EqualityComparer
|
|
/// Adds a SerializedField of type T
|
|
/// </summary>
|
|
/// <typeparam name="T"></typeparam>
|
|
public abstract class GraphicPropertyOverride<T> : GraphicPropertyOverride
|
|
{
|
|
/// <summary>
|
|
/// The serialized value, modified by the inspector or the animator
|
|
/// </summary>
|
|
[SerializeField] protected T propertyValue;
|
|
|
|
/// <summary>
|
|
/// The last known value, init the the type default (0, null, ...)
|
|
/// Used to check for changes
|
|
/// NonSerialized to prevent unity from serializing this
|
|
/// </summary>
|
|
[NonSerialized] T _previousValue;
|
|
|
|
/// <summary>
|
|
/// If _previousValue was set since last construction
|
|
/// </summary>
|
|
[NonSerialized] bool _previousValueIsInit;
|
|
|
|
/// <summary>
|
|
/// Checks if any changes happened just before rendering
|
|
/// Can be removed to optimize, since OnDidApplyAnimationProperties is doing the heavy lifting
|
|
/// But OnDidApplyAnimationProperties is undocumented, and will potentially change silently in the future
|
|
/// </summary>
|
|
void LateUpdate()
|
|
{
|
|
// If a previous value was recorded
|
|
// And it perfectly matches the current value
|
|
// Then ignore this update
|
|
if (_previousValueIsInit && EqualityComparer<T>.Default.Equals(propertyValue, _previousValue)) return;
|
|
|
|
_previousValueIsInit = true;
|
|
_previousValue = propertyValue;
|
|
SetMaterialDirty();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called by the animator system when a value is modified
|
|
/// </summary>
|
|
public void OnDidApplyAnimationProperties()
|
|
{
|
|
_previousValueIsInit = true;
|
|
_previousValue = propertyValue;
|
|
SetMaterialDirty();
|
|
}
|
|
|
|
/// <summary>
|
|
/// The value of the overriding property
|
|
/// Will react correctly when changed
|
|
/// </summary>
|
|
public T PropertyValue
|
|
{
|
|
get => propertyValue;
|
|
set
|
|
{
|
|
_previousValueIsInit = true;
|
|
_previousValue = propertyValue = value;
|
|
SetMaterialDirty();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Try to retrieve and apply the default property value
|
|
/// If the source material cannot be found, reset to sensible defaults
|
|
/// </summary>
|
|
public override void ResetPropertyToDefault()
|
|
{
|
|
// Try to get the associated Graphic component
|
|
Graphic graphic = ParentGraphic;
|
|
// If successful, get the material
|
|
Material material = graphic ? graphic.material : null;
|
|
// init the reset value to default
|
|
T value = default;
|
|
bool gotDefaultValue = false;
|
|
|
|
// If material was received, try to get the default value from the material
|
|
if (material) gotDefaultValue = GetDefaultValue(material, out value);
|
|
|
|
// Log a warning if we failed
|
|
if (!gotDefaultValue) Debug.LogWarning("Could not retrieve material default value", this);
|
|
|
|
// Set current value to what we managed to retrieve, and update
|
|
PropertyValue = value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Retrieve the default property value from the source material
|
|
/// </summary>
|
|
/// <param name="material">The source material</param>
|
|
/// <param name="defaultValue">The value from the material</param>
|
|
/// <returns>True if the value could be retrieved</returns>
|
|
public abstract bool GetDefaultValue(Material material, out T defaultValue);
|
|
}
|
|
} |