using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEditor; using System.IO; namespace UVViwer { internal class PostProcessingMesh : AssetPostprocessor { void OnPreprocessModel() { if (ModelUVViewer.s_Editor == null) return; ModelImporter modelImporter = assetImporter as ModelImporter; string path = modelImporter.assetPath; string GUID = AssetDatabase.AssetPathToGUID(path); if (ModelUVViewer.s_Guid == GUID) { ModelUVViewer.ForceRepaint(); } } //void OnPostprocessModel(GameObject go) //{ // //return; // Mesh mesh = go.GetComponent().sharedMesh; // if (mesh) // { // Vector2[] uvs = mesh.uv2; // for (int i = 0; i < uvs.Length; ++i) // { // Vector2 uv = uvs[i]; // uv.x = Mathf.Clamp(uv.x, 0, 1); // uv.y = Mathf.Clamp(uv.y, 0, 1); // uvs[i] = uv; // } // mesh.uv2 = uvs; // } //} } public class ModelUVViewer : EditorWindow { [MenuItem("Assets/Model UV Viewer", true)] static bool TryOpenModelUVViewer() { string[] guids = Selection.assetGUIDs; if (guids == null || guids.Length == 0) return false; for (int i = 0; i < guids.Length; ++i) { string guid = guids[i]; string path = AssetDatabase.GUIDToAssetPath(guid); string extension = Path.GetExtension(path).ToLower(); if (extension != ".fbx") return false; } return true; } [MenuItem("Assets/Model UV Viewer", false)] static void OpenModelUVViewer() { string[] guids = Selection.assetGUIDs; s_Mesh.Clear(); s_MeshToAsset = new Dictionary(); foreach (string guid in guids) { string path = AssetDatabase.GUIDToAssetPath(guid); Mesh mesh = AssetDatabase.LoadAssetAtPath(path, typeof(Mesh)) as Mesh; if (mesh != null) { s_Mesh.Add(mesh); s_MeshToAsset.Add(mesh.GetHashCode(), guid); } } #if UNITY_2020 || UNITY_2021 || UNITY_2019 if (!HasOpenInstances() || s_Editor == null) #else if (s_Editor == null) #endif { s_Editor = GetWindow(); s_Editor.titleContent = new GUIContent("Model UV Viewer"); s_Editor.Show(); } s_Editor.Focus(); s_Selcted = string.Empty; s_TargetTexture = null; if (s_Mat == null) { s_Mat = new Material(Shader.Find("UI/Default")); } if (s_UVMesh == null) { s_UVMesh = new Mesh(); } s_HashCode = 0; s_TillingOffset = new Rect(0, 0, 1, 1); } public static ModelUVViewer s_Editor; static List s_Mesh = new List(); static string s_Selcted; static int s_HashCode; public static string s_Guid { get { if (s_HashCode == 0) return string.Empty; string guid; if (s_MeshToAsset.TryGetValue(s_HashCode, out guid)) return guid; return string.Empty; } } static Texture2D s_TargetTexture; static int s_Size = 500; static Vector2[] s_UV; // 性能太低 //static Vector2[] s_UV //{ // get // { // Mesh mesh = GetMeshByHashCode(s_HashCode); // if (mesh == null) // return null; // return GetMeshUV(mesh, s_UVIndex); // } //} static int s_UVIndex; static Material s_Mat; static Mesh s_UVMesh; static Rect s_TillingOffset; static GUIStyle s_SelectStyle; static GUIStyle s_AlignRightText; public static Dictionary s_MeshToAsset = new Dictionary(); static IEnumerator s_ReloadUV; static bool s_HasWarning { get { return s_Warning != null && s_Warning != string.Empty && s_Warning.Length > 0; } } static string s_Warning; private void OnGUI() { if (s_Editor == null) return; if (s_Mesh == null || s_Mesh.Count == 0) return; if (s_SelectStyle == null) { s_SelectStyle = new GUIStyle(EditorStyles.miniButtonMid); s_SelectStyle.normal.background = EditorStyles.miniButtonMid.active.background; s_SelectStyle.normal.scaledBackgrounds = EditorStyles.miniButtonMid.active.scaledBackgrounds; } if (s_AlignRightText == null) { s_AlignRightText = new GUIStyle(EditorStyles.label); s_AlignRightText.alignment = TextAnchor.MiddleRight; } if (Event.current.isScrollWheel) { Vector2 delta = Event.current.delta; float dy = delta.y; s_Size -= (int)dy * 10; s_Size = Mathf.Clamp(s_Size, 350, 1000); this.Repaint(); } int xOff = 10; int yOff = 10; EditorGUILayout.Space(); yOff = (int)EditorGUILayout.GetControlRect().y; for (int i = 0; i < s_Mesh.Count; ++i) { Mesh mesh = s_Mesh[i]; if (mesh == null) continue; s_Mesh[i] = ShowMeshItem(mesh, i, new Rect(xOff, yOff, 200, 16)); yOff += 25; } GUI.Label(new Rect(xOff, yOff, 100, 20), "选择贴图:"); yOff += 5; s_TargetTexture = EditorGUI.ObjectField(new Rect(xOff + 100, yOff, 60, 60), s_TargetTexture, typeof(Texture2D), false) as Texture2D; Vector2 tilling = new Vector2(s_TillingOffset.width, s_TillingOffset.height); Vector2 offset = new Vector2(s_TillingOffset.x, s_TillingOffset.y); tilling = EditorGUI.Vector2Field(new Rect(xOff + 170, yOff - 5, 200, 20), "Tilling", tilling); offset = EditorGUI.Vector2Field(new Rect(xOff + 170, yOff + 30, 200, 20), "Offset", offset); s_TillingOffset.x = offset.x; s_TillingOffset.y = offset.y; s_TillingOffset.width = tilling.x; s_TillingOffset.height = tilling.y; yOff += 75; if (s_HasWarning) { EditorGUI.HelpBox(new Rect(xOff, yOff, position.width - 20, 40), s_Warning, MessageType.Warning); yOff += 45; } s_Size = EditorGUI.IntSlider(new Rect(xOff, yOff, 200, 20), s_Size, 350, 1000); EditorGUI.LabelField(new Rect(xOff + 203, yOff, 50, 20), "texels"); yOff += 25; int previewYOff = yOff; if (s_TargetTexture != null) GUI.DrawTextureWithTexCoords(new Rect(xOff, yOff, s_Size, s_Size), s_TargetTexture, s_TillingOffset, false); // EditorGUI.DrawPreviewTexture(new Rect(xOff, yOff, s_Size, s_Size), s_TargetTexture/*, s_TillingOffset, false*/); else GUI.DrawTextureWithTexCoords(new Rect(xOff, yOff, s_Size, s_Size), Texture2D.blackTexture, s_TillingOffset, false); if (s_Selcted != string.Empty && s_Selcted != "") { int hashCode = int.Parse(s_Selcted.Substring(0, s_Selcted.LastIndexOf("_"))); Mesh mesh = GetMeshByHashCode(hashCode); if (mesh != null) { string uvi = s_Selcted.Substring(s_Selcted.LastIndexOf("_") + 1, s_Selcted.Length - s_Selcted.LastIndexOf("_") - 1); GUI.Label(new Rect(xOff + s_Size - 200, yOff - 25, 200, 20), mesh.name + "." + uvi, s_AlignRightText); yOff += 25; if (s_UV != null && s_UV.Length > 0 && s_Mat != null) { if (s_UVMesh == null) s_UVMesh = new Mesh(); if (s_UVMesh.vertices == null || s_UVMesh.vertices.Length == 0) { Vector3[] vertices = new Vector3[mesh.vertices.Length]; for (int i = 0; i < vertices.Length; ++i) { Vector2 uv = s_UV[i]; if ((uv.x > 1 || uv.y > 1 || uv.x < 0 || uv.y < 0) && !s_HasWarning) s_Warning = "这套UV有超过[0,1]范围的值"; vertices[i] = new Vector3(uv.x, 1 - uv.y, 0); } s_UVMesh.vertices = vertices; s_UVMesh.triangles = mesh.triangles; s_UVMesh.UploadMeshData(true); } s_Mat.SetPass(0); s_Mat.SetColor("_Color", Color.yellow); GL.wireframe = true; Graphics.DrawMeshNow(s_UVMesh, Matrix4x4.TRS(new Vector3(xOff, previewYOff, 0), Quaternion.identity, new Vector3(s_Size, s_Size, 1))); GL.wireframe = false; } } } if (s_ReloadUV != null) { if (!s_ReloadUV.MoveNext()) { s_ReloadUV = null; } } } private Mesh ShowMeshItem(Mesh mesh, int index, Rect rect) { Mesh newMesh = EditorGUI.ObjectField(rect, mesh, typeof(Mesh), false) as Mesh; if (newMesh != mesh) return newMesh; float xOff = rect.x + rect.width + 10; float yOff = rect.y; int hashCode = mesh.GetHashCode(); xOff = ShowUVButton(1, hashCode, "uv", mesh.uv, new Rect(xOff, yOff, 50, 16)); xOff = ShowUVButton(2, hashCode, "uv2", mesh.uv2, new Rect(xOff, yOff, 50, 16)); xOff = ShowUVButton(3, hashCode, "uv3", mesh.uv3, new Rect(xOff, yOff, 50, 16)); xOff = ShowUVButton(4, hashCode, "uv4", mesh.uv4, new Rect(xOff, yOff, 50, 16)); #if UNITY_2020 || UNITY_2021 || UNITY_2019 xOff = ShowUVButton(5, hashCode, "uv5", mesh.uv5, new Rect(xOff, yOff, 50, 20)); xOff = ShowUVButton(6, hashCode, "uv6", mesh.uv6, new Rect(xOff, yOff, 50, 20)); xOff = ShowUVButton(7, hashCode, "uv7", mesh.uv7, new Rect(xOff, yOff, 50, 20)); xOff = ShowUVButton(8, hashCode, "uv8", mesh.uv8, new Rect(xOff, yOff, 50, 20)); #endif return mesh; } private float ShowUVButton(int index, int hashCode, string name, Vector2[] uv, Rect rect) { if (uv == null || uv.Length == 0) { rect.x += 50; return rect.x; } bool isThis = s_Selcted == hashCode + "_" + name; #if UNITY_2020 || UNITY_2021 || UNITY_2019 Color c = GUI.color; if (isThis) GUI.color = Color.gray; if (GUI.Button(rect, name, EditorStyles.miniButtonMid)) #else if (GUI.Button(rect, name, isThis ? s_SelectStyle : EditorStyles.miniButtonMid)) #endif { if (isThis) { s_UV = null; s_UVIndex = 0; s_Selcted = string.Empty; s_HashCode = 0; s_UVMesh = null; s_Warning = ""; } else { s_UV = uv; s_UVIndex = index; s_HashCode = hashCode; s_Selcted = hashCode + "_" + name; s_UVMesh = null; s_Warning = ""; } } #if UNITY_2020 || UNITY_2021 || UNITY_2019 GUI.color = c; #endif rect.x += 50; return rect.x; } static Vector2[] GetMeshUV(Mesh mesh, int i) { switch (i) { case 1: return mesh.uv; case 2: return mesh.uv2; case 3: return mesh.uv3; case 4: return mesh.uv4; #if UNITY_2020 || UNITY_2021 || UNITY_2019 case 5: return mesh.uv5; case 6: return mesh.uv6; case 7: return mesh.uv7; case 8: return mesh.uv8; #endif default: Debug.LogError("InValid uv index"); return null; } } static Mesh GetMeshByHashCode(int hashCode) { foreach (var mesh in s_Mesh) { if (mesh.GetHashCode() == hashCode) { return mesh; } } return null; } public static void ForceRepaint() { if (s_Editor != null) { s_Editor.Repaint(); s_ReloadUV = ReloadUVNextFrame(); s_ReloadUV.MoveNext(); } } // 需要等到导入mesh之后的下一帧才能得到最新的UV数据 static IEnumerator ReloadUVNextFrame() { yield return null; Mesh mesh = GetMeshByHashCode(s_HashCode); if (mesh != null) { s_UVMesh = null; s_UV = GetMeshUV(mesh, s_UVIndex); s_Warning = ""; } } } }