using System.Collections; using System.Collections.Generic; using UnityEditor; using UnityEngine; namespace Thry { public class MaterialLinker { private static Dictionary<(Material,string), List> linked_materials; private static void Load() { if (linked_materials == null) { linked_materials = new Dictionary<(Material,string), List>(); string raw = FileHelper.ReadFileIntoString(PATH.LINKED_MATERIALS_FILE); string[][] parsed = Parser.Deserialize(raw); if(parsed!=null) foreach (string[] material_cloud in parsed) { List materials = new List(); for (int i = 1; i < material_cloud.Length; i++) { string path = AssetDatabase.GUIDToAssetPath(material_cloud[i]); Material m = AssetDatabase.LoadAssetAtPath(path); if (m != null) materials.Add(m); } foreach (Material m in materials) if(linked_materials.ContainsKey((m, material_cloud[0])) == false) linked_materials.Add((m, material_cloud[0]), materials); } } } private static void Save() { List save_structre = new List(); HashSet<(Material,string)> has_already_been_saved = new HashSet<(Material,string)>(); foreach (KeyValuePair<(Material,string),List> link in linked_materials) { if (has_already_been_saved.Contains(link.Key)) continue; string[] value = new string[link.Value.Count + 1]; value[0] = link.Key.Item2; int i = 1; foreach (Material m in link.Value) { has_already_been_saved.Add((m, link.Key.Item2)); value[i++] = UnityHelper.GetGUID(m); } save_structre.Add(value); } FileHelper.WriteStringToFile(Parser.ObjectToString(save_structre),PATH.LINKED_MATERIALS_FILE); } public static bool IsLinked(MaterialProperty p) { Load(); return linked_materials.ContainsKey(((Material)p.targets[0], p.name)); } public static List GetLinked(MaterialProperty p) { return GetLinked((Material)p.targets[0], p); } public static List GetLinked(Material m, MaterialProperty p) { Load(); if (linked_materials.ContainsKey((m,p.name))) return linked_materials[(m,p.name)]; return null; } public static void Link(Material master, Material add_to, MaterialProperty p) { Load(); Debug.Log("link " + master.name + "," + add_to.name); bool containes_key1 = linked_materials.ContainsKey((master,p.name)); bool containes_key2 = linked_materials.ContainsKey((add_to,p.name)); if(containes_key1 && containes_key2) { Unlink(add_to, p); Link(master, add_to, p); return; } else if (containes_key1) AddToListIfMaterialAlreadyLinked(master, add_to, p); else if (containes_key2) AddToListIfMaterialAlreadyLinked(add_to, master, p); else { List value = new List(); value.Add(master); value.Add(add_to); linked_materials[(master,p.name)] = value; linked_materials[(add_to,p.name)] = value; } } private static void AddToListIfMaterialAlreadyLinked(Material existing, Material add, MaterialProperty p) { List value = linked_materials[(existing,p.name)]; value.Add(add); linked_materials[(add,p.name)] = value; } public static void Unlink(Material m, MaterialProperty p) { Load(); List value = linked_materials[(m,p.name)]; value.Remove(m); linked_materials.Remove((m,p.name)); } private static void UpdateLinkList(List new_linked_materials, MaterialProperty p) { var key = (p.targets[0] as Material, p.name); if (linked_materials.ContainsKey(key)) { List old_materials = linked_materials[key]; foreach (Material m in old_materials) linked_materials.Remove((m, p.name)); } foreach (Material m in new_linked_materials) linked_materials[(m, p.name)] = new_linked_materials; } public static void UnlinkAll(Material m) { List<(Material, string)> remove_keys = new List<(Material, string)>(); foreach (KeyValuePair<(Material,string), List> link_cloud in linked_materials) { if (link_cloud.Key.Item1 == m) { link_cloud.Value.Remove(m); remove_keys.Add(link_cloud.Key); } } foreach ((Material, string) k in remove_keys) linked_materials.Remove(k); RemoveEmptyLinks(); Save(); } private static void RemoveEmptyLinks() { List<(Material, string)> remove_keys = new List<(Material, string)>(); foreach (KeyValuePair<(Material,string), List> link_cloud in linked_materials) { if (link_cloud.Value.Count < 2) { link_cloud.Value.Clear(); remove_keys.Add(link_cloud.Key); } } foreach ((Material, string) k in remove_keys) linked_materials.Remove(k); } private static MaterialLinkerPopupWindow window; public static void Popup(Rect activeation_rect, List linked_materials, MaterialProperty p) { Vector2 pos = GUIUtility.GUIToScreenPoint(Event.current.mousePosition); pos.x = Mathf.Min(EditorWindow.focusedWindow.position.x + EditorWindow.focusedWindow.position.width - 250, pos.x); pos.y = Mathf.Min(EditorWindow.focusedWindow.position.y + EditorWindow.focusedWindow.position.height - 200, pos.y); Load(); if (window != null) window.Close(); window = ScriptableObject.CreateInstance(); window.position = new Rect(pos.x, pos.y, 250, 200); window.Init(linked_materials, p); window.ShowPopup(); } private class MaterialLinkerPopupWindow : EditorWindow { private Vector2 scrollPos; private List linked_materials; private MaterialProperty materialProperty; public void Init(List linked_materials, MaterialProperty p) { if (linked_materials == null) linked_materials = new List(); this.linked_materials = new List(linked_materials); string self_guid = UnityHelper.GetGUID((Material)p.targets[0]); for (int i = this.linked_materials.Count - 1; i >= 0; i--) { if (UnityHelper.GetGUID(this.linked_materials[i]) == self_guid) this.linked_materials.RemoveAt(i); } this.materialProperty = p; } public new Vector2 minSize = new Vector2(250, 200); void OnGUI() { GUILayout.Label("Linked Materials", EditorStyles.boldLabel); float listMaxHeight = this.position.height - 110; GuiHelper.DrawListField(linked_materials, listMaxHeight, ref scrollPos); GUILayout.Box("Drag and Drop new Material", EditorStyles.helpBox, GUILayout.MinHeight(30)); //Rect drag_rect = GUILayoutUtility.GetLastRect(); Rect lastRect = GUILayoutUtility.GetLastRect(); Rect drag_rect = new Rect(0, lastRect.y, Screen.width, Screen.height - lastRect.y - 30); Event e = Event.current; if ((e.type == EventType.DragPerform || e.type == EventType.DragUpdated) && drag_rect.Contains(e.mousePosition)) { DragAndDrop.visualMode = DragAndDropVisualMode.Copy; if (e.type == EventType.DragPerform) { DragAndDrop.AcceptDrag(); HanldeDropEvent(); } } if (GUI.Button(new Rect(0,this.position.height-30,this.position.width,30),"Done")) this.Close(); } public void HanldeDropEvent() { foreach (string path in DragAndDrop.paths) { if (AssetDatabase.GetMainAssetTypeAtPath(path) == typeof(Material)) { linked_materials.Add(AssetDatabase.LoadAssetAtPath(path)); } } } void Awake() { } void OnDestroy() { //add itself bool contains_itself = false; string self_guid = UnityHelper.GetGUID((Material)materialProperty.targets[0]); for (int i = linked_materials.Count - 1; i >= 0; i--) { if (UnityHelper.GetGUID(linked_materials[i]) == self_guid) contains_itself = true; if (linked_materials[i] == null) linked_materials.RemoveAt(i); } if (linked_materials.Count>0 && !contains_itself) linked_materials.Add((Material)materialProperty.targets[0]); UpdateLinkList(linked_materials, materialProperty); Save(); } } } }