using System; using System.Collections.Generic; using UnityEngine; using Object = UnityEngine.Object; namespace FairyGUI { [Flags] public enum MaterialFlags { Clipped = 1, SoftClipped = 2, StencilTest = 4, AlphaMask = 8, Grayed = 16, ColorFilter = 32 } /// /// Every texture-shader combination has a MaterialManager. /// public class MaterialManager { public event Action onCreateNewMaterial; public bool firstMaterialInFrame; NTexture _texture; Shader _shader; List _addKeywords; Dictionary> _materials; bool _combineTexture; class MaterialRef { public Material material; public int frame; public BlendMode blendMode; public uint group; } const int internalKeywordsCount = 6; static string[] internalKeywords = new[] { "CLIPPED", "SOFT_CLIPPED", null, "ALPHA_MASK", "GRAYED", "COLOR_FILTER" }; /// /// /// /// /// internal MaterialManager(NTexture texture, Shader shader) { _texture = texture; _shader = shader; _materials = new Dictionary>(); _combineTexture = texture.alphaTexture != null; } /// /// /// /// /// public int GetFlagsByKeywords(IList keywords) { if (_addKeywords == null) _addKeywords = new List(); int flags = 0; for (int i = 0; i < keywords.Count; i++) { string s = keywords[i]; if (string.IsNullOrEmpty(s)) continue; int j = _addKeywords.IndexOf(s); if (j == -1) { j = _addKeywords.Count; _addKeywords.Add(s); } flags += (1 << (j + internalKeywordsCount)); } return flags; } /// /// /// /// /// /// /// public Material GetMaterial(int flags, BlendMode blendMode, uint group) { if (blendMode != BlendMode.Normal && BlendModeUtils.Factors[(int)blendMode].pma) flags |= (int)MaterialFlags.ColorFilter; List items; if (!_materials.TryGetValue(flags, out items)) { items = new List(); _materials[flags] = items; } int frameId = Time.frameCount; int cnt = items.Count; MaterialRef result = null; for (int i = 0; i < cnt; i++) { MaterialRef item = items[i]; if (item.group == group && item.blendMode == blendMode) { if (item.frame != frameId) { firstMaterialInFrame = true; item.frame = frameId; } else firstMaterialInFrame = false; if (_combineTexture) item.material.SetTexture(ShaderConfig.ID_AlphaTex, _texture.alphaTexture); return item.material; } else if (result == null && (item.frame > frameId || item.frame < frameId - 1)) //collect materials if it is unused in last frame result = item; } if (result == null) { result = new MaterialRef() { material = CreateMaterial(flags) }; items.Add(result); } else if (_combineTexture) result.material.SetTexture(ShaderConfig.ID_AlphaTex, _texture.alphaTexture); if (result.blendMode != blendMode) { BlendModeUtils.Apply(result.material, blendMode); result.blendMode = blendMode; } result.group = group; result.frame = frameId; firstMaterialInFrame = true; return result.material; } /// /// /// /// Material CreateMaterial(int flags) { Material mat = new Material(_shader); mat.mainTexture = _texture.nativeTexture; if (_texture.alphaTexture != null) { mat.EnableKeyword("COMBINED"); mat.SetTexture(ShaderConfig.ID_AlphaTex, _texture.alphaTexture); } for (int i = 0; i < internalKeywordsCount; i++) { if ((flags & (1 << i)) != 0) { string s = internalKeywords[i]; if (s != null) mat.EnableKeyword(s); } } if (_addKeywords != null) { int keywordCnt = _addKeywords.Count; for (int i = 0; i < keywordCnt; i++) { if ((flags & (1 << (i + internalKeywordsCount))) != 0) mat.EnableKeyword(_addKeywords[i]); } } mat.hideFlags = DisplayObject.hideFlags; if (onCreateNewMaterial != null) onCreateNewMaterial(mat); return mat; } /// /// /// public void DestroyMaterials() { var iter = _materials.GetEnumerator(); while (iter.MoveNext()) { List items = iter.Current.Value; if (Application.isPlaying) { int cnt = items.Count; for (int j = 0; j < cnt; j++) Object.Destroy(items[j].material); } else { int cnt = items.Count; for (int j = 0; j < cnt; j++) Object.DestroyImmediate(items[j].material); } items.Clear(); } iter.Dispose(); } /// /// /// public void RefreshMaterials() { _combineTexture = _texture.alphaTexture != null; var iter = _materials.GetEnumerator(); while (iter.MoveNext()) { List items = iter.Current.Value; int cnt = items.Count; for (int j = 0; j < cnt; j++) { Material mat = items[j].material; mat.mainTexture = _texture.nativeTexture; if (_combineTexture) { mat.EnableKeyword("COMBINED"); mat.SetTexture(ShaderConfig.ID_AlphaTex, _texture.alphaTexture); } } } iter.Dispose(); } } }