| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524 | #if UNITY_2019_3_11 || UNITY_2019_3_12 || UNITY_2019_3_13 || UNITY_2019_3_14 || UNITY_2019_3_15 || UNITY_2019_4_OR_NEWER#define SERIALIZE_FIELD_MASKABLE#endifusing System.Collections;using System.Collections.Generic;using System.Runtime.CompilerServices;using Coffee.UIParticleExtensions;using UnityEngine;using UnityEngine.Rendering;using UnityEngine.Serialization;using UnityEngine.UI;[assembly: InternalsVisibleTo("Fort23.Editor")]namespace Coffee.UIExtensions{    /// <summary>    /// Render maskable and sortable particle effect ,without Camera, RenderTexture or Canvas.    /// </summary>    [ExecuteInEditMode]    [RequireComponent(typeof(RectTransform))]    [RequireComponent(typeof(CanvasRenderer))]    public class UIParticle : MaskableGraphic#if UNITY_EDITOR        , ISerializationCallbackReceiver#endif    {        [HideInInspector] [SerializeField] internal bool m_IsTrail = false;        [Tooltip("Ignore canvas scaler")] [SerializeField] [FormerlySerializedAs("m_IgnoreParent")]        bool m_IgnoreCanvasScaler = true;        [Tooltip("Particle effect scale")] [SerializeField]        float m_Scale = 100;        [Tooltip("Particle effect scale")] [SerializeField]        private Vector3 m_Scale3D;        [Tooltip("Animatable material properties. If you want to change the material properties of the ParticleSystem in Animation, enable it.")] [SerializeField]        internal AnimatableProperty[] m_AnimatableProperties = new AnimatableProperty[0];        [Tooltip("Particles")] [SerializeField]        private List<ParticleSystem> m_Particles = new List<ParticleSystem>();        [Tooltip("Shrink rendering by material on refresh.\nNOTE: Performance will be improved, but in some cases the rendering is not correct.")] [SerializeField]        bool m_ShrinkByMaterial = false;#if !SERIALIZE_FIELD_MASKABLE        [SerializeField] private bool m_Maskable = true;#endif        private bool _shouldBeRemoved;        private DrivenRectTransformTracker _tracker;        private Mesh _bakedMesh;        private readonly List<Material> _modifiedMaterials = new List<Material>();        private readonly List<Material> _maskMaterials = new List<Material>();        private readonly List<bool> _activeMeshIndices = new List<bool>();        private Vector3 _cachedPosition;        private static readonly List<Material> s_TempMaterials = new List<Material>(2);        private static MaterialPropertyBlock s_Mpb;        private static readonly List<Material> s_PrevMaskMaterials = new List<Material>();        private static readonly List<Material> s_PrevModifiedMaterials = new List<Material>();        private static readonly List<Component> s_Components = new List<Component>();        private static readonly List<ParticleSystem> s_ParticleSystems = new List<ParticleSystem>();        /// <summary>        /// Should this graphic be considered a target for raycasting?        /// </summary>        public override bool raycastTarget        {            get { return false; }            set { }        }        public bool ignoreCanvasScaler        {            get { return m_IgnoreCanvasScaler; }            set            {                // if (m_IgnoreCanvasScaler == value) return;                m_IgnoreCanvasScaler = value;                _tracker.Clear();                if (isActiveAndEnabled && m_IgnoreCanvasScaler)                    _tracker.Add(this, rectTransform, DrivenTransformProperties.Scale);            }        }        public bool shrinkByMaterial        {            get { return m_ShrinkByMaterial; }            set            {                if (m_ShrinkByMaterial == value) return;                m_ShrinkByMaterial = value;                RefreshParticles();            }        }        /// <summary>        /// Particle effect scale.        /// </summary>        public float scale        {            get { return m_Scale3D.x; }            set            {                m_Scale = Mathf.Max(0.001f, value);                m_Scale3D = new Vector3(m_Scale, m_Scale, m_Scale);            }        }        /// <summary>        /// Particle effect scale.        /// </summary>        public Vector3 scale3D        {            get { return m_Scale3D; }            set            {                if (m_Scale3D == value) return;                m_Scale3D.x = Mathf.Max(0.001f, value.x);                m_Scale3D.y = Mathf.Max(0.001f, value.y);                m_Scale3D.z = Mathf.Max(0.001f, value.z);            }        }        internal Mesh bakedMesh        {            get { return _bakedMesh; }        }        public List<ParticleSystem> particles        {            get { return m_Particles; }        }        public IEnumerable<Material> materials        {            get { return _modifiedMaterials; }        }        public override Material materialForRendering        {            get { return canvasRenderer.GetMaterial(0); }        }        public List<bool> activeMeshIndices        {            get { return _activeMeshIndices; }            set            {                if (_activeMeshIndices.SequenceEqualFast(value)) return;                _activeMeshIndices.Clear();                _activeMeshIndices.AddRange(value);                UpdateMaterial();            }        }        internal Vector3 cachedPosition        {            get { return _cachedPosition; }            set { _cachedPosition = value; }        }        public void Play()        {            particles.Exec(p => p.Play());        }        public void Pause()        {            particles.Exec(p => p.Pause());        }        public void Stop()        {            particles.Exec(p => p.Stop());        }        public void Clear()        {            particles.Exec(p => p.Clear());        }        public void SetParticleSystemInstance(GameObject instance)        {            SetParticleSystemInstance(instance, true);        }        public void SetParticleSystemInstance(GameObject instance, bool destroyOldParticles)        {            if (!instance) return;            foreach (Transform child in transform)            {                var go = child.gameObject;                go.SetActive(false);                if (!destroyOldParticles) continue;#if UNITY_EDITOR                if (!Application.isPlaying)                    DestroyImmediate(go);                else#endif                    Destroy(go);            }            var tr = instance.transform;            tr.SetParent(transform, false);            tr.localPosition = Vector3.zero;            RefreshParticles(instance);        }        public void SetParticleSystemPrefab(GameObject prefab)        {            if (!prefab) return;            SetParticleSystemInstance(Instantiate(prefab.gameObject), true);        }        public void RefreshParticles()        {            RefreshParticles(gameObject);        }        public void RefreshParticles(GameObject root)        {            if (!root) return;            root.GetComponentsInChildren(particles);            particles.RemoveAll(x => x.GetComponentInParent<UIParticle>() != this);            foreach (var ps in particles)            {                var tsa = ps.textureSheetAnimation;                if (tsa.mode == ParticleSystemAnimationMode.Sprites && tsa.uvChannelMask == (UVChannelFlags) 0)                    tsa.uvChannelMask = UVChannelFlags.UV0;            }            particles.Exec(p => p.GetComponent<ParticleSystemRenderer>().enabled = !enabled);            particles.SortForRendering(transform, m_ShrinkByMaterial);            SetMaterialDirty();        }        protected override void UpdateMaterial()        {            // Clear mask materials.            s_PrevMaskMaterials.AddRange(_maskMaterials);            _maskMaterials.Clear();            // Clear modified materials.            s_PrevModifiedMaterials.AddRange(_modifiedMaterials);            _modifiedMaterials.Clear();            // Recalculate stencil value.            if (m_ShouldRecalculateStencil)            {                var rootCanvas = MaskUtilities.FindRootSortOverrideCanvas(transform);                m_StencilValue = maskable ? MaskUtilities.GetStencilDepth(transform, rootCanvas) : 0;                m_ShouldRecalculateStencil = false;            }            // No mesh to render.            var count = activeMeshIndices.CountFast();            if (count == 0 || !isActiveAndEnabled || particles.Count == 0)            {                canvasRenderer.Clear();                ClearPreviousMaterials();                return;            }            //            GetComponents(typeof(IMaterialModifier), s_Components);            var materialCount = Mathf.Min(16, count);            canvasRenderer.materialCount = materialCount;            var j = 0;            for (var i = 0; i < particles.Count; i++)            {                if (materialCount <= j) break;                var ps = particles[i];                if (!ps) continue;                var r = ps.GetComponent<ParticleSystemRenderer>();                r.GetSharedMaterials(s_TempMaterials);                // Main                var index = i * 2;                if (activeMeshIndices.Count <= index) break;                if (activeMeshIndices[index] && 0 < s_TempMaterials.Count)                {                    var mat = GetModifiedMaterial(s_TempMaterials[0], ps.GetTextureForSprite());                    for (var k = 1; k < s_Components.Count; k++)                        mat = (s_Components[k] as IMaterialModifier).GetModifiedMaterial(mat);                    canvasRenderer.SetMaterial(mat, j);                    UpdateMaterialProperties(r, j);                    j++;                }                // Trails                index++;                if (activeMeshIndices.Count <= index || materialCount <= j) break;                if (activeMeshIndices[index] && 1 < s_TempMaterials.Count)                {                    var mat = GetModifiedMaterial(s_TempMaterials[1], null);                    for (var k = 1; k < s_Components.Count; k++)                        mat = (s_Components[k] as IMaterialModifier).GetModifiedMaterial(mat);                    canvasRenderer.SetMaterial(mat, j++);                }            }            ClearPreviousMaterials();        }        private void ClearPreviousMaterials()        {            foreach (var m in s_PrevMaskMaterials)                StencilMaterial.Remove(m);            s_PrevMaskMaterials.Clear();            foreach (var m in s_PrevModifiedMaterials)                ModifiedMaterial.Remove(m);            s_PrevModifiedMaterials.Clear();        }        private Material GetModifiedMaterial(Material baseMaterial, Texture2D texture)        {            if (0 < m_StencilValue)            {                baseMaterial = StencilMaterial.Add(baseMaterial, (1 << m_StencilValue) - 1, StencilOp.Keep, CompareFunction.Equal, ColorWriteMask.All, (1 << m_StencilValue) - 1, 0);                _maskMaterials.Add(baseMaterial);            }            if (texture == null && m_AnimatableProperties.Length == 0) return baseMaterial;            var id = m_AnimatableProperties.Length == 0 ? 0 : GetInstanceID();            baseMaterial = ModifiedMaterial.Add(baseMaterial, texture, id);            _modifiedMaterials.Add(baseMaterial);            return baseMaterial;        }        internal void UpdateMaterialProperties()        {            if (m_AnimatableProperties.Length == 0) return;            //            var count = activeMeshIndices.CountFast();            var materialCount = Mathf.Max(8, count);            canvasRenderer.materialCount = materialCount;            var j = 0;            for (var i = 0; i < particles.Count; i++)            {                if (materialCount <= j) break;                var ps = particles[i];                if (!ps) continue;                var r = ps.GetComponent<ParticleSystemRenderer>();                r.GetSharedMaterials(s_TempMaterials);                // Main                if (activeMeshIndices[i * 2] && 0 < s_TempMaterials.Count)                {                    UpdateMaterialProperties(r, j);                    j++;                }            }        }        internal void UpdateMaterialProperties(Renderer r, int index)        {            if (m_AnimatableProperties.Length == 0 || canvasRenderer.materialCount <= index) return;            r.GetPropertyBlock(s_Mpb ?? (s_Mpb = new MaterialPropertyBlock()));            if (s_Mpb.isEmpty) return;            // #41: Copy the value from MaterialPropertyBlock to CanvasRenderer            var mat = canvasRenderer.GetMaterial(index);            if (!mat) return;            foreach (var ap in m_AnimatableProperties)            {                ap.UpdateMaterialProperties(mat, s_Mpb);            }            s_Mpb.Clear();        }        /// <summary>        /// This function is called when the object becomes enabled and active.        /// </summary>        protected override void OnEnable()        {#if !SERIALIZE_FIELD_MASKABLE            maskable = m_Maskable;#endif            activeMeshIndices.Clear();            UIParticleUpdater.Register(this);            particles.Exec(p => p.GetComponent<ParticleSystemRenderer>().enabled = false);            if (isActiveAndEnabled && m_IgnoreCanvasScaler)            {                _tracker.Add(this, rectTransform, DrivenTransformProperties.Scale);            }            // Create objects.            _bakedMesh = MeshPool.Rent();            base.OnEnable();            InitializeIfNeeded();        }        private new IEnumerator Start()        {            // #147: ParticleSystem creates Particles in wrong position during prewarm            // #148: Particle Sub Emitter not showing when start game            var delayToPlay = particles.AnyFast(ps =>            {                ps.GetComponentsInChildren(false, s_ParticleSystems);                return s_ParticleSystems.AnyFast(p => p.isPlaying && (p.subEmitters.enabled || p.main.prewarm));            });            s_ParticleSystems.Clear();            if (!delayToPlay) yield break;            Stop();            Clear();            yield return null;            Play();        }        /// <summary>        /// This function is called when the behaviour becomes disabled.        /// </summary>        protected override void OnDisable()        {            UIParticleUpdater.Unregister(this);            if (!_shouldBeRemoved)                particles.Exec(p => p.GetComponent<ParticleSystemRenderer>().enabled = true);            _tracker.Clear();            // Destroy object.            MeshPool.Return(_bakedMesh);            _bakedMesh = null;            base.OnDisable();        }        /// <summary>        /// Call to update the geometry of the Graphic onto the CanvasRenderer.        /// </summary>        protected override void UpdateGeometry()        {        }        /// <summary>        /// Callback for when properties have been changed by animation.        /// </summary>        protected override void OnDidApplyAnimationProperties()        {        }        private void InitializeIfNeeded()        {            if (enabled && m_IsTrail)            {                UnityEngine.Debug.LogWarningFormat(this, "[UIParticle] The UIParticle component should be removed: {0}\nReason: UIParticle for trails is no longer needed.", name);                gameObject.hideFlags = HideFlags.None;                _shouldBeRemoved = true;                enabled = false;                return;            }            if (!this || particles.AnyFast()) return;            // refresh.#if UNITY_EDITOR            if (!Application.isPlaying)                UnityEditor.EditorApplication.delayCall += () =>                {                    if (this) RefreshParticles();                };            else#endif                RefreshParticles();        }#if UNITY_EDITOR        protected override void OnValidate()        {            SetLayoutDirty();            SetVerticesDirty();            m_ShouldRecalculateStencil = true;            RecalculateClipping();#if !SERIALIZE_FIELD_MASKABLE            maskable = m_Maskable;#endif        }        void ISerializationCallbackReceiver.OnBeforeSerialize()        {            if (Application.isPlaying) return;            InitializeIfNeeded();        }        void ISerializationCallbackReceiver.OnAfterDeserialize()        {            if (m_Scale3D == Vector3.zero)            {                scale = m_Scale;            }            UnityEditor.EditorApplication.delayCall += () =>            {                if (Application.isPlaying || !this) return;                InitializeIfNeeded();            };        }#endif    }}
 |