using UnityEngine; using System.Collections.Generic; using MalbersAnimations.Events; using System; namespace MalbersAnimations.Utilities { /// Is used to change Materials on any Mesh Renderer using a list of Materials Items [AddComponentMenu("Malbers/Utilities/Mesh/Material Changer")] public class MaterialChanger : MonoBehaviour { public List materialList = new List(); public bool showMeshesList = true; public bool changeHidden = false; public bool random; /// All Material Changer Index Stored on a string separated by a space ' ' public string AllIndex { set { string[] getIndex = value.Split(' '); for (int i = 0; i < materialList.Count; i++) { if (getIndex.Length > i) { if (int.TryParse(getIndex[i], out int index)) { if (index == -1) continue; materialList[i].ChangeMaterial(index); } } } } get { string AllIndex = ""; for (int i = 0; i < materialList.Count; i++) { AllIndex += materialList[i].current.ToString() + " "; } AllIndex.Remove(AllIndex.Length - 1); //Remove the last space } return AllIndex; } } public MaterialItem this[int index] { get => materialList[index]; set => materialList[index] = value; } public int Count => materialList.Count; private void OnEnable() { foreach (var mat in materialList) { if (mat.Linked && mat.Master >= 0 && mat.Master < Count) //If the master material item is in range { materialList[mat.Master].OnMaterialChanged.AddListener(mat.ChangeMaterial); //Used for linked materials } } if (random) Randomize(); } private void OnDisable() { foreach (var mat in materialList) { if (mat.Linked && mat.Master >= 0 && mat.Master < Count) //If the master material item is in range { materialList[mat.Master].OnMaterialChanged.RemoveListener(mat.ChangeMaterial); //Used for linked materials } } } public virtual void Randomize() { foreach (var mat in materialList) { if (!mat.Linked) mat.ChangeMaterial(UnityEngine.Random.Range(0, mat.materials.Length)); } } /// Swap to the next or previous material on each Material Item public virtual void SetAllMaterials(bool Next = true) { foreach (var materialItem in materialList) { materialItem.ChangeMaterial(Next); } } /// Set all the MaterialItems on the List a specific Material using an Index /// the index on the Materials[], for each Material Item public virtual void SetAllMaterials(int index) { foreach (var mat in materialList) { mat.ChangeMaterial(index); } } /// Set a Material from the List of material inside the materialList... /// index of the Material List /// index a material on the MaterialList public virtual void SetMaterial(int indexList, int nextIndex) { if (indexList < 0) indexList = 0; indexList %= Count; if (materialList[indexList] != null) materialList[indexList].ChangeMaterial(nextIndex); } /// Set a Material from the List of material inside the materialList... /// /// public virtual void SetMaterial(int index, bool next = true) { if (index < 0) index = 0; index %= Count; if (materialList[index] != null) { materialList[index].ChangeMaterial(next); } } public virtual void SetMaterial(string name, int Index) { MaterialItem materialItem = materialList.Find(item => item.Name == name); if (materialItem != null) { materialItem.ChangeMaterial(Index); } else { Debug.LogWarning("No material Item Found with the name: " + name); } } public virtual void SetMaterial(string name, bool next = true) { MaterialItem materialItem = materialList.Find(item => item.Name == name); if (materialItem != null) { materialItem.ChangeMaterial(next); } else { Debug.LogWarning("No material Item Found with the name: " + name); } } /// Set all the MaterialItems on the List an External Material /// public virtual void SetAllMaterials(Material mat) { foreach (var MaterialItem in materialList) { MaterialItem.ChangeMaterial(mat); } } /// Swap to the Next material on a specific Material Item on the List using index /// index on the Material Item on the material list public virtual void NextMaterialItem(int index) { if (index < 0) index = 0; index %= Count; materialList[index].NextMaterial(); } /// Swap to the Next material on a specific Material Item on the List using the Name /// the Name used for the MaterialItem public virtual void NextMaterialItem(string name) { MaterialItem mat = materialList.Find(item => item.Name.ToUpper() == name.ToUpper()); if (mat != null) mat.NextMaterial(); } /// Returns the Current Index on the material list using the index of the slot public virtual int CurrentMaterialIndex(int index) { return materialList[index].current; } /// Returns the Current index of the material list slot using the index of the slot /// public virtual int CurrentMaterialIndex(string name) { int index = materialList.FindIndex(item => item.Name == name); return materialList[index].current; } #if UNITY_EDITOR [ContextMenu("Create Event Listeners")] void CreateListeners() { MEventListener listener = this.FindComponent(); if (listener == null) listener = transform.root.gameObject.AddComponent(); if (listener.Events == null) listener.Events = new List(); MEvent BlendS = MTools.GetInstance("Material Changer"); if (listener.Events.Find(item => item.Event == BlendS) == null) { var item = new MEventItemListener() { Event = BlendS, useVoid = false, useString = true, useInt = true }; UnityEditor.Events.UnityEventTools.AddPersistentListener(item.ResponseInt, NextMaterialItem); UnityEditor.Events.UnityEventTools.AddPersistentListener(item.ResponseString, NextMaterialItem); listener.Events.Add(item); UnityEditor.EditorUtility.SetDirty(listener); Debug.Log("Material Changer Added to the Event Listeners"); } MTools.SetDirty(listener); } #endif } /// Slot on the List of Materials Items [System.Serializable] public class MaterialItem { /// The name for the Material Item public string Name; /// The mesh renderer to use for the materials public Renderer mesh; /// The list of the Materials on the material Item public Material[] materials; /// Is the Material Item linked to another Material Item public bool Linked = false; /// If the material is Linked this is the Master ID public int Master = 0; /// Current Index Material Item (Which material is being used on the material List public int current = 0; /// Does this material Item has another meshes that uses the same Material? public bool HasLODs; /// List of other meshes that same Material? public Renderer[] LODs; /// Material ID (Used when a mesh have multiple materials) [Tooltip("Material ID (Used when a mesh have multiple materials) Default 0")] public int indexM = 0; public IntEvent OnMaterialChanged = new IntEvent(); #region Constructors public MaterialItem() { Name = "NameHere"; mesh = null; materials = new Material[0]; } public MaterialItem(MeshRenderer MR) { Name = "NameHere"; mesh = MR; materials = new Material[0]; } public MaterialItem(string name, MeshRenderer MR, Material[] mats) { Name = name; mesh = MR; materials = mats; } public MaterialItem(string name, MeshRenderer MR) { Name = name; mesh = MR; materials = new Material[0]; } #endregion /// Changes to the next material on the list..(Same as NextMaterial) public virtual void ChangeMaterial() { current++; //Move to the Next Material if (current < 0) current = 0; //If we get to lower than 0 clamp to zero current %= materials.Length; Material[] currentMaterial = mesh.sharedMaterials; //Create a copy of the Current materials if (materials[current] != null) //Check on the list that the next material is NOT Empty { currentMaterial[indexM] = materials[current]; //Change only the MaterialID on the Item mesh.sharedMaterials = currentMaterial; ChangeLOD(current); OnMaterialChanged.Invoke(current); } else { Debug.LogWarning("The Material on the Slot: " + current + " is empty"); } } public virtual void Set_by_BinaryIndex(int binaryCurrent) { int current = 0; for (int i = 0; i < materials.Length; i++) { if (MTools.IsBitActive(binaryCurrent, i)) { current = i; //find the first active bit and get the current break; } } ChangeMaterial(current); } internal void ChangeLOD(int index) { if (!HasLODs) return; foreach (var mesh in LODs) { if (mesh == null) break; Material[] currentMaterial = mesh.sharedMaterials; currentMaterial[indexM] = materials[current]; if (materials[current] != null) mesh.sharedMaterials = currentMaterial; } } internal void ChangeLOD(Material mat) { if (!HasLODs) return; Material[] currentMaterial = mesh.sharedMaterials; currentMaterial[indexM] = mat; foreach (var mesh in LODs) { mesh.sharedMaterials = currentMaterial; } } /// Changes to the Next material on the list.(Same as ChangeMaterial) public virtual void NextMaterial() { ChangeMaterial(); } /// Used for Change a specific material on the list using and Index. /// Index for the Material Array public virtual void ChangeMaterial(int index) { if ( materials.Length== 0) return; index = Mathf.Clamp(index, 0, materials.Length); var mat = materials[index]; if (mat != null) { Material[] currentMaterial = mesh.sharedMaterials; if (currentMaterial.Length - 1 < indexM) { Debug.LogWarning("The Meshes on the "+Name+" Material Item, does not have " + (indexM+1) + " Materials, please change the ID parameter to value lower than "+ currentMaterial.Length); return; } currentMaterial[indexM] = mat; mesh.sharedMaterials = currentMaterial; current = index; ChangeLOD(index); OnMaterialChanged.Invoke(current); } else { Debug.LogWarning("The material on the Slot: " + index + " is empty"); } } /// Changes to the previous material on the list. public virtual void PreviousMaterial() { current--; if (current < 0) current = materials.Length - 1; if (materials[current] != null) { Material[] currentMaterial = mesh.sharedMaterials; currentMaterial[indexM] = materials[current]; mesh.sharedMaterials = currentMaterial; ChangeLOD(current); OnMaterialChanged.Invoke(current); } else { Debug.LogWarning("The Material on the Slot: " + current + " is empty"); } } /// Changes to a specific External material public virtual void ChangeMaterial(Material mat) { Material[] currentMaterial = mesh.sharedMaterials; currentMaterial[indexM] = mat; mesh.sharedMaterials = currentMaterial; ChangeLOD(mat); } /// Changes to the Next or Previous material on the list /// true: Next, false: Previous public virtual void ChangeMaterial(bool Next) { if (Next) ChangeMaterial(); else PreviousMaterial(); } } }