// Copyright (C) 2019-2021 Alexander Bogarsukov. All rights reserved. // See the LICENSE.md file in the project root for more information. using System; using System.Collections; using System.Collections.Generic; using System.Text; using UnityEngine; namespace UnityFx.Outline { /// /// A collection of instances that share outline settings. An /// can only belong to one at time. /// /// /// [Serializable] public sealed class OutlineLayer : ICollection, IReadOnlyCollection, IOutlineSettings { #region data [SerializeField, HideInInspector] private OutlineSettingsInstance _settings = new OutlineSettingsInstance(); [SerializeField, HideInInspector] private string _name; [SerializeField, HideInInspector] private bool _enabled = true; [SerializeField, HideInInspector] private bool _mergeLayerObjects; private OutlineLayerCollection _parentCollection; private Dictionary _outlineObjects = new Dictionary(); private List _mergedRenderers; #endregion #region interface /// /// Gets the layer name. /// public string Name { get { if (string.IsNullOrEmpty(_name)) { return "OutlineLayer #" + Index.ToString(); } return _name; } } /// /// Gets or sets a value indicating whether the layer is enabled. /// /// public bool Enabled { get { return _enabled; } set { _enabled = value; } } /// /// Gets or sets a value indicating whether layer game objects should be trated as one. /// public bool MergeLayerObjects { get { return _mergeLayerObjects; } set { _mergeLayerObjects = value; } } /// /// Gets index of the layer in parent collection. /// public int Index { get { if (_parentCollection != null) { return _parentCollection.IndexOf(this); } return -1; } } /// /// Gets or sets outline settings. Set this to non- value to share settings with other components. /// public OutlineSettings OutlineSettings { get { return _settings.OutlineSettings; } set { _settings.OutlineSettings = value; } } /// /// Initializes a new instance of the class. /// public OutlineLayer() { } /// /// Initializes a new instance of the class. /// internal OutlineLayer(OutlineLayerCollection parentCollection) { _parentCollection = parentCollection; } /// /// Initializes a new instance of the class. /// public OutlineLayer(string name) { _name = name; } /// /// Initializes a new instance of the class. /// /// Thrown if is . public OutlineLayer(OutlineSettings settings) { if (settings is null) { throw new ArgumentNullException(nameof(settings)); } _settings.OutlineSettings = settings; } /// /// Initializes a new instance of the class. /// /// Thrown if is . public OutlineLayer(string name, OutlineSettings settings) { if (settings is null) { throw new ArgumentNullException(nameof(settings)); } _name = name; _settings.OutlineSettings = settings; } /// /// Attempts to get renderers assosiated with the specified . /// /// Thrown if is . public bool TryGetRenderers(GameObject go, out ICollection renderers) { if (go is null) { throw new ArgumentNullException(nameof(go)); } if (_outlineObjects.TryGetValue(go, out var result)) { renderers = result; return true; } renderers = null; return false; } /// /// Gets the objects for rendering. /// public void GetRenderObjects(IList renderObjects) { if (_enabled) { if (_mergeLayerObjects) { renderObjects.Add(new OutlineRenderObject(GetRenderers(), this, Name)); } else { foreach (var kvp in _outlineObjects) { var go = kvp.Key; if (go && go.activeInHierarchy) { renderObjects.Add(new OutlineRenderObject(kvp.Value.GetList(), _settings, go.name)); } } } } } /// /// Gets all layer renderers. /// public IReadOnlyList GetRenderers() { if (_enabled) { if (_mergedRenderers != null) { _mergedRenderers.Clear(); } else { _mergedRenderers = new List(); } foreach (var kvp in _outlineObjects) { var go = kvp.Key; if (go && go.activeInHierarchy) { var rl = kvp.Value.GetList(); for (var i = 0; i < rl.Count; i++) { _mergedRenderers.Add(rl[i]); } } } return _mergedRenderers; } return Array.Empty(); } #endregion #region internals internal string NameTag { get { return _name; } set { _name = value; } } internal OutlineLayerCollection ParentCollection => _parentCollection; internal void UpdateRenderers(int ignoreLayers) { foreach (var renderers in _outlineObjects.Values) { renderers.Reset(false, ignoreLayers); } } internal void Reset() { _outlineObjects.Clear(); } internal void SetCollection(OutlineLayerCollection collection) { if (_parentCollection == null || collection == null || _parentCollection == collection) { _parentCollection = collection; } else { throw new InvalidOperationException("OutlineLayer can only belong to a single OutlineLayerCollection."); } } #endregion #region IOutlineSettings /// public Color OutlineColor { get { return _settings.OutlineColor; } set { _settings.OutlineColor = value; } } /// public int OutlineWidth { get { return _settings.OutlineWidth; } set { _settings.OutlineWidth = value; } } /// public float OutlineIntensity { get { return _settings.OutlineIntensity; } set { _settings.OutlineIntensity = value; } } /// public float OutlineAlphaCutoff { get { return _settings.OutlineAlphaCutoff; } set { _settings.OutlineAlphaCutoff = value; } } /// public OutlineRenderFlags OutlineRenderMode { get { return _settings.OutlineRenderMode; } set { _settings.OutlineRenderMode = value; } } #endregion #region ICollection /// public int Count => _outlineObjects.Count; /// public bool IsReadOnly => false; /// public void Add(GameObject go) { if (go is null) { throw new ArgumentNullException(nameof(go)); } if (!_outlineObjects.ContainsKey(go)) { var renderers = new OutlineRendererCollection(go); renderers.Reset(false, _parentCollection.IgnoreLayerMask); _outlineObjects.Add(go, renderers); } } /// public bool Remove(GameObject go) { if (go is null) { return false; } return _outlineObjects.Remove(go); } /// public bool Contains(GameObject go) { if (go is null) { return false; } return _outlineObjects.ContainsKey(go); } /// public void Clear() { _outlineObjects.Clear(); } /// public void CopyTo(GameObject[] array, int arrayIndex) { _outlineObjects.Keys.CopyTo(array, arrayIndex); } #endregion #region IEnumerable /// public IEnumerator GetEnumerator() { return _outlineObjects.Keys.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return _outlineObjects.Keys.GetEnumerator(); } #endregion #region IEquatable /// public bool Equals(IOutlineSettings other) { return OutlineSettings.Equals(this, other); } #endregion #region Object /// public override string ToString() { var text = new StringBuilder(); if (string.IsNullOrEmpty(_name)) { text.Append("OutlineLayer"); } else { text.Append(_name); } if (_parentCollection != null) { text.Append(" #"); text.Append(_parentCollection.IndexOf(this)); } if (_outlineObjects.Count > 0) { text.Append(" ("); foreach (var go in _outlineObjects.Keys) { text.Append(go.name); text.Append(", "); } text.Remove(text.Length - 2, 2); text.Append(")"); } return string.Format("{0}", text); } /// public override bool Equals(object other) { return OutlineSettings.Equals(this, other as IOutlineSettings); } /// public override int GetHashCode() { return base.GetHashCode(); } #endregion #region implementation #endregion } }