// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using UnityEngine;
using UnityEngine.Animations;
using UnityEngine.Playables;
#if GT_USE_UGUI
using UnityEngine.UI;
#endif // GT_USE_UGUI
namespace Microsoft.MixedReality.GraphicsTools
{
///
/// The base class for all CanvasMaterialAnimators generated via Assets > Graphics Tools > Generate Canvas Material Animator.
/// This behavior will expose all material properties of a Graphic's material so they can animated by Unity's animation system.
///
[ExecuteInEditMode]
#if GT_USE_UGUI
[RequireComponent(typeof(Graphic))]
#endif // GT_USE_UGUI
public abstract class CanvasMaterialAnimatorBase : MonoBehaviour, IAnimationWindowPreview
{
///
/// Flag to keep track of if the material properties are folded out (visible) or not.
///
public bool MaterialPropertiesFoldedOut
{
get => materialPropertiesFoldedOut;
set => materialPropertiesFoldedOut = value;
}
[Tooltip("Flag to keep track of if the material properties are folded out (visible) or not.")]
[SerializeField, NotKeyable, HideInInspector]
private bool materialPropertiesFoldedOut = false;
///
/// When animated should a new material be instantiated?
///
public bool UseInstanceMaterials
{
get { return instanceMaterials; }
set
{
if (isInitialized)
{
Debug.LogError("Cannot toggle UseInstanceMaterials after initialization.");
}
else
{
instanceMaterials = value;
}
}
}
[Tooltip("When animated should a new material be created?")]
[SerializeField, NotKeyable, HideInInspector]
private bool instanceMaterials = false;
///
/// Accessor to the material used during preview in editor.
///
public Material PreviewMaterial
{
get => previewMaterial;
private set => previewMaterial = value;
}
private Material previewMaterial = null;
#if GT_USE_UGUI
private Graphic graphic
{
get
{
if (cachedGraphic == null)
{
cachedGraphic = GetComponent();
}
return cachedGraphic;
}
}
private Graphic cachedGraphic;
#endif // GT_USE_UGUI
private bool isInitialized = false;
#if GT_USE_UGUI
private Material currentMaterial = null;
private Material sharedMaterial = null;
#endif // GT_USE_UGUI
#region MonoBehaviour Implementation
///
/// State clean up.
///
private void OnDestroy()
{
Terminate();
}
///
/// Event for when the animation system updates any serialized properties.
///
private void OnDidApplyAnimationProperties()
{
ApplyToMaterial();
}
///
/// Editor-only function that Unity calls when the script is loaded or a value changes in the Inspector.
///
private void OnValidate()
{
// If we are in the act of previewing apply any material changes from the inspector.
if (previewMaterial != null)
{
ApplyToMaterial();
}
}
#endregion MonoBehaviour Implementation
#region IAnimationWindowPreview Implementation
///
/// Notification callback when the Animation window starts previewing an AnimationClip.
///
public void StartPreview()
{
#if GT_USE_UGUI
previewMaterial = graphic.material;
#endif // GT_USE_UGUI
}
///
/// Notification callback when the Animation window stops previewing an AnimationClip.
///
public void StopPreview()
{
#if GT_USE_UGUI
Terminate();
graphic.material = previewMaterial;
previewMaterial = null;
#endif // GT_USE_UGUI
}
///
/// Notification callback when the Animation Window updates its PlayableGraph before sampling an AnimationClip.
///
public void UpdatePreviewGraph(PlayableGraph graph)
{
// Intentionally blank to implement the interface.
}
///
/// The Animation window calls this function when it samples an AnimationClip for the first time.
///
public Playable BuildPreviewGraph(PlayableGraph graph, Playable inputPlayable)
{
// Intentionally blank to implement the interface.
return inputPlayable;
}
#endregion IAnimationWindowPreview Implementation
#region BaseCanvasMaterialAnimator Implementation
///
/// Initializes all material properties based on the default material.
///
private void Initialize()
{
#if GT_USE_UGUI
Material material = graphic.material;
sharedMaterial = material;
if (material != null)
{
if (material.shader.name == GetTargetShaderName())
{
InitializeFromMaterial(material);
if (UseInstanceMaterials)
{
currentMaterial = MaterialInstance.Instance(material);
graphic.material = currentMaterial;
}
else
{
currentMaterial = material;
MaterialRestorer.Capture(material);
}
isInitialized = true;
}
else
{
Debug.LogErrorFormat("Failed to initialize CanvasMaterialAnimator. Expected shader {0} but using {1}.", GetTargetShaderName(), material.shader.name);
}
}
#endif // GT_USE_UGUI
}
///
/// Destroys any assets this created in initialize.
///
private void Terminate()
{
#if GT_USE_UGUI
if (UseInstanceMaterials)
{
if (currentMaterial != null)
{
if (Application.isPlaying)
{
Destroy(currentMaterial);
}
else
{
DestroyImmediate(currentMaterial);
}
currentMaterial = null;
}
}
else
{
MaterialRestorer.Restore(currentMaterial);
}
isInitialized = false;
#endif
}
///
/// Call this method after any properties have been modified via code and they need to be applied to the current material.
///
public void ApplyToMaterial()
{
#if GT_USE_UGUI
if (!isInitialized)
{
Initialize();
}
Material material = graphic.material;
if (material != null)
{
// Release the previous material and obtain the current.
if (material != currentMaterial)
{
Terminate();
Initialize();
}
ApplyToMaterial(material);
}
#endif
}
///
/// Call this method when the animator is idle and you wish to return the graphic's material to its default state.
/// This will restore any batching behavior before animating.
///
public void RestoreToSharedMaterial()
{
#if GT_USE_UGUI
Terminate();
graphic.material = sharedMaterial;
sharedMaterial = null;
#endif
}
///
/// This method will extract all material properties from the material passed in to apply default values to
/// the serialized properties of the current CanvasMaterialAnimator.
///
public abstract void InitializeFromMaterial(Material material);
///
/// This method will apply all serialized material properties on the current CanvasMaterialAnimator to the material passed in.
///
public abstract void ApplyToMaterial(Material material);
///
/// Returns the name of the shader this class was generated from.
///
public abstract string GetTargetShaderName();
#endregion BaseCanvasMaterialAnimator Implementation
}
}