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