// 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 } }