using System; using System.IO; using UnityEngine; using UnityEngine.Polybrush; namespace UnityEditor.Polybrush { static class ShaderMetaDataUtility { #pragma warning disable 0618 [Obsolete("Format is deprecated. Please use ShaderMetaDataUtility.LoadShaderMetaData and ShaderMetaDataUtility.SaveShaderMetaData.")] const string SHADER_ATTRIB_FILE_EXTENSION = "pbs.json"; /// /// Find a path to the Polybrush metadata for a shader. /// /// Shader associated with the metadata /// The path found, null if not found. [Obsolete("Please use ShaderMetaDataUtility.LoadShaderMetaData.")] internal static string FindPolybrushMetaDataForShader(Shader shader) { if (shader == null) return null; string path = AssetDatabase.GetAssetPath(shader); if (string.IsNullOrEmpty(path)) return null; string filename = Path.GetFileNameWithoutExtension(path); string directory = Path.GetDirectoryName(path); string[] paths = new string[] { string.Format("{0}/{1}.{2}", directory, PolyShaderUtil.GetMetaDataPath(shader), SHADER_ATTRIB_FILE_EXTENSION), string.Format("{0}/{1}.{2}", directory, filename, SHADER_ATTRIB_FILE_EXTENSION) }; // @todo verify that the json is actually valid foreach (string str in paths) { if (File.Exists(str)) { // remove `..` from path since `AssetDatabase.LoadAssetAtPath` doesn't like 'em string full = Path.GetFullPath(str).Replace("\\", "/"); string resolved = full.Replace(Application.dataPath, "Assets"); return resolved; } } return null; } /// /// Try to read AttributeLayouts from a .pbs.json file located at "path" /// /// Path of the file to read from /// AttributeLayoutContainer retrieved from the json /// true if it worked, false if the file doesn't exist or is empty [Obsolete("Please use ShaderMetaDataUtility.LoadShaderMetaData.")] public static bool TryReadAttributeLayoutsFromJsonFile(string path, out AttributeLayoutContainer container) { container = null; if (!File.Exists(path)) return false; string json = File.ReadAllText(path); if (string.IsNullOrEmpty(json)) return false; container = ScriptableObject.CreateInstance(); JsonUtility.FromJsonOverwrite(json, container); ResolveShaderReference(container); return true; } [Obsolete("Please use ShaderMetaDataUtility.LoadShaderMetaData.")] public static bool TryReadAttributeLayoutsFromJson(string jsonText, out AttributeLayoutContainer container) { container = ScriptableObject.CreateInstance(); JsonUtility.FromJsonOverwrite(jsonText, container); ResolveShaderReference(container); return true; } /// /// Store user-set shader attribute information. /// /// container that will have the shader and the metadata to write /// overwrite data if already existing /// log errors or not /// Returns the path written to on success, null otherwise. [Obsolete("Please use ShaderMetaDataUtility.SaveShaderMetaData.")] internal static string SaveMeshAttributesData(AttributeLayoutContainer container, bool overwrite = false, bool logErrors = true) { if (container == null) return string.Empty; return SaveMeshAttributesData(container.shader, container.attributes, overwrite); } /// /// Saves the metadata of the shader passed in parameters, can overwrite if necessary /// /// Shader associated with the metadata /// Metadata to write /// Will overwrite if already existing fileparam> /// Log errors or not [Obsolete("Please use ShaderMetaDataUtility.SaveShaderMetaData.")] internal static string SaveMeshAttributesData(Shader shader, AttributeLayout[] attributes, bool overwrite = false, bool logErrors = true) { if (shader == null || attributes == null) { if (logErrors) { Debug.LogError("Cannot save null attributes for shader."); } return null; } string path = FindPolybrushMetaDataForShader(shader); string shader_path = AssetDatabase.GetAssetPath(shader); string shader_directory = Path.GetDirectoryName(shader_path); string shader_filename = Path.GetFileNameWithoutExtension(path); // metadata didn't exist before if (string.IsNullOrEmpty(path)) { if (string.IsNullOrEmpty(shader_path)) { // how!? path = EditorUtility.SaveFilePanelInProject( "Save Polybrush Shader Attributes", shader_filename, SHADER_ATTRIB_FILE_EXTENSION, "Please enter a file name to save Polybrush shader metadata to."); if (string.IsNullOrEmpty(path)) { Debug.LogWarning(string.Format("Could not save Polybrush shader metadata. Please try again, possibly with a different file name or folder path.")); return null; } } else { shader_filename = Path.GetFileNameWithoutExtension(shader_path); path = string.Format("{0}/{1}.{2}", shader_directory, shader_filename, SHADER_ATTRIB_FILE_EXTENSION); } } if (!overwrite && File.Exists(path)) { // @todo Debug.LogWarning("shader metadata exists. calling function refuses to overwrite and lazy developer didn't add a save dialog here."); return null; } try { AttributeLayoutContainer container = AttributeLayoutContainer.Create(shader, attributes); string json = JsonUtility.ToJson(container, true); File.WriteAllText(path, json); //note: convert it here to be able to load it using AssetDatabase functions shader_filename = Path.GetFileNameWithoutExtension(shader_path); path = string.Format("{0}/{1}.{2}", shader_directory, shader_filename, SHADER_ATTRIB_FILE_EXTENSION); //------- return path; } catch (System.Exception e) { if (logErrors) { Debug.LogError("Failed saving Polybrush Shader MetaData\n" + e.ToString()); } return path; } } /// /// Searches only by looking for a compatibly named file in the same directory. /// /// Shader associated with the metadata /// result if any metadata found /// [Obsolete("Please use ShaderMetaDataUtility.LoadShaderMetaData.")] internal static bool FindMeshAttributesForShader(Shader shader, out AttributeLayoutContainer attributes) { attributes = null; string path = AssetDatabase.GetAssetPath(shader); string filename = Path.GetFileNameWithoutExtension(path); string directory = Path.GetDirectoryName(path); string[] paths = new string[] { string.Format("{0}/{1}.{2}", directory, filename, SHADER_ATTRIB_FILE_EXTENSION), string.Format("{0}/{1}.{2}", directory, PolyShaderUtil.GetMetaDataPath(shader), SHADER_ATTRIB_FILE_EXTENSION) }; foreach (string str in paths) { if (TryReadAttributeLayoutsFromJsonFile(str, out attributes)) return true; } return false; } [Obsolete("Method is deprecated. Please use ShaderMetaDataUtility.LoadShaderMetaData and ShaderMetaDataUtility.SaveShaderMetaData.")] static void ResolveShaderReference(AttributeLayoutContainer container) { container.shader = Shader.Find(container.shaderPath); } /// /// Store the shader's attributes in the new format. /// Erase the .pbs.json on success. /// internal static void ConvertMetaDataToNewFormat(Shader shader) { if (shader == null) throw new NullReferenceException("shader"); string path = ShaderMetaDataUtility.FindPolybrushMetaDataForShader(shader); // If not null, it means we have data stored with the old format. // Proceed to conversion. if (path != null) { AttributeLayoutContainer attributesContainer = ScriptableObject.CreateInstance(); ShaderMetaDataUtility.TryReadAttributeLayoutsFromJsonFile(path, out attributesContainer); if (attributesContainer != null) { ShaderMetaDataUtility.SaveShaderMetaData(shader, attributesContainer); FileUtil.DeleteFileOrDirectory(path); FileUtil.DeleteFileOrDirectory(path + ".meta"); AssetDatabase.Refresh(); } } } #pragma warning restore 0618 /// /// Check if the given shader is an asset we can work with. /// We will verify if it comes from the project by checking its importer. /// /// /// internal static bool IsValidShader(Shader shader) { if (shader == null) throw new ArgumentNullException("shader"); string path = AssetDatabase.GetAssetPath(shader); AssetImporter importer = AssetImporter.GetAtPath(path); return importer != null; } /// /// Deserialize the shader's attributes from UserData in the shader's importer. /// If none exists, it returns a new AttributeLayoutContainer instance. /// /// /// internal static AttributeLayoutContainer LoadShaderMetaData(Shader shader) { if (shader == null) throw new ArgumentNullException("shader"); string path = AssetDatabase.GetAssetPath(shader); AssetImporter importer = AssetImporter.GetAtPath(path); AttributeLayoutContainer data = AttributeLayoutContainer.Create(shader, null); JsonUtility.FromJsonOverwrite(importer.userData, data); return data; } /// /// Serialize the shader's attributes as UserData in the shader's importer. /// /// /// internal static void SaveShaderMetaData(Shader shader, AttributeLayoutContainer attributes) { if (shader == null) throw new ArgumentNullException("shader"); if (attributes == null) throw new ArgumentNullException("attributes"); string path = AssetDatabase.GetAssetPath(shader); AssetImporter importer = AssetImporter.GetAtPath(path); importer.userData = JsonUtility.ToJson(attributes); importer.SaveAndReimport(); } } }