#if UNITY_EDITOR using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Text; using UnityEditor; using UnityEngine; using Object = UnityEngine.Object; namespace lilToon { public class lilOptimizer { private const int TYPE_OFFSET = 8; internal static void OptimizeInputHLSL(Material[] materials, AnimationClip[] clips) { try { var dicT = new Dictionary(); var dicD = new Dictionary(); var dicF = new Dictionary(); var dicC = new Dictionary(); // Get materials foreach(var material in materials) { CheckMaterial(material, dicT, dicD, dicF, dicC); } // Get animations foreach(var clip in clips) { CheckAnimationClip(clip, dicT, dicD, dicF, dicC); } // Apply RewriteInputHLSL(dicT, dicD, dicF, dicC); } catch(Exception e) { Debug.LogException(e); Debug.Log("[lilToon] OptimizeInputHLSL() failed"); } } internal static string GetOptimizedText(Material[] materials, AnimationClip[] clips) { try { var dicT = new Dictionary(); var dicD = new Dictionary(); var dicF = new Dictionary(); var dicC = new Dictionary(); // Get materials foreach(var material in materials) { CheckMaterial(material, dicT, dicD, dicF, dicC); } // Get animations foreach(var clip in clips) { CheckAnimationClip(clip, dicT, dicD, dicF, dicC); } // Apply return RewriteInputHLSLText(dicT, dicD, dicF, dicC); } catch(Exception e) { Debug.LogException(e); Debug.Log("[lilToon] OptimizeInputHLSL() failed"); return e.ToString(); } } private static void CheckMaterial(Material material, Dictionary dicT, Dictionary dicD, Dictionary dicF, Dictionary dicC) { if(material == null || !CheckShaderIslilToon(material.shader)) return; var so = new SerializedObject(material); var savedProps = so.FindProperty("m_SavedProperties"); var texs = savedProps.FindPropertyRelative("m_TexEnvs"); Check(dicT, dicD, texs, material); var floats = savedProps.FindPropertyRelative("m_Floats"); Check(dicF, floats, material); var colors = savedProps.FindPropertyRelative("m_Colors"); Check(dicC, colors, material); } private static void CheckAnimationClip(AnimationClip clip, Dictionary dicT, Dictionary dicD, Dictionary dicF, Dictionary dicC) { if(clip == null) return; foreach(EditorCurveBinding binding in AnimationUtility.GetObjectReferenceCurveBindings(clip)) { foreach(ObjectReferenceKeyframe frame in AnimationUtility.GetObjectReferenceCurve(clip, binding)) { if(frame.value is Material) CheckMaterial((Material)frame.value, dicT, dicD, dicF, dicC); } } foreach(EditorCurveBinding binding in AnimationUtility.GetCurveBindings(clip)) { string propname = binding.propertyName; if(string.IsNullOrEmpty(propname) || !propname.Contains("material.")) continue; if(propname.Contains("_ST.")) { string name = propname.Substring(9, propname.Length - 14); dicD[name] = new STProp(){isVariable = true}; } else if(propname.EndsWith(".r") || propname.EndsWith(".g") || propname.EndsWith(".b") || propname.EndsWith(".a") || propname.EndsWith(".x") || propname.EndsWith(".y") || propname.EndsWith(".z") || propname.EndsWith(".w")) { string name = propname.Substring(9, propname.Length - 11); dicC[name] = new ColorProp(){isVariable = true}; } else { string name = propname.Substring(9, propname.Length - 9); dicF[name] = new FloatProp(){isVariable = true}; } } } private static void Check(Dictionary dic, Dictionary dicD, SerializedProperty props, Material material) { for(int i = 0; i < props.arraySize; i++) { var prop = props.GetArrayElementAtIndex(i); string name = prop.FindPropertyRelative("first").stringValue; if(!material.HasProperty(name)) continue; var prop2 = prop.FindPropertyRelative("second"); Object tex = prop2.FindPropertyRelative("m_Texture").objectReferenceValue; Vector2 scale = prop2.FindPropertyRelative("m_Scale").vector2Value; Vector2 offset = prop2.FindPropertyRelative("m_Offset").vector2Value; if(dic.ContainsKey(name)) { if(!dic[name].isVariable && dic[name].t != tex) dic[name] = new TexProp(){isVariable = true}; } else { dic[name] = new TexProp(){ isVariable = false, t = tex }; } if(dicD.ContainsKey(name)) { if(!dicD[name].isVariable) { var v = dicD[name]; if(v.s != scale || v.o != offset) dicD[name] = new STProp(){isVariable = true}; } } else { dicD[name] = new STProp(){ isVariable = false, s = scale, o = offset }; } } } private static void Check(Dictionary dic, SerializedProperty props, Material material) { for(int i = 0; i < props.arraySize; i++) { var prop = props.GetArrayElementAtIndex(i); string name = prop.FindPropertyRelative("first").stringValue; if(!material.HasProperty(name) || dic.ContainsKey(name) && dic[name].isVariable) continue; float fl = prop.FindPropertyRelative("second").floatValue; if(dic.ContainsKey(name)) { if(dic[name].f != fl) dic[name] = new FloatProp(){isVariable = true}; continue; } dic[name] = new FloatProp(){ isVariable = false, f = fl }; } } private static void Check(Dictionary dic, SerializedProperty props, Material material) { for(int i = 0; i < props.arraySize; i++) { var prop = props.GetArrayElementAtIndex(i); string name = prop.FindPropertyRelative("first").stringValue; if(!material.HasProperty(name) || dic.ContainsKey(name) && dic[name].isVariable) continue; Color color = prop.FindPropertyRelative("second").colorValue; if(dic.ContainsKey(name)) { if(dic[name].c != color) dic[name] = new ColorProp(){isVariable = true}; continue; } dic[name] = new ColorProp(){ isVariable = false, c = color }; } } private static void RewriteInputHLSL(Dictionary dicT, Dictionary dicD, Dictionary dicF, Dictionary dicC) { string optHLSL = RewriteInputHLSLText(dicT, dicD, dicF, dicC); string pathOpt = AssetDatabase.GUIDToAssetPath("571051a232e4af44a98389bda858df27"); var sw = new StreamWriter(pathOpt, false); sw.Write(optHLSL); sw.Close(); } private static string RewriteInputHLSLText(Dictionary dicT, Dictionary dicD, Dictionary dicF, Dictionary dicC) { if(dicT.Count == 0 && dicD.Count == 0 && dicF.Count == 0 && dicC.Count == 0) return null; string pathBase = AssetDatabase.GUIDToAssetPath("8ff7f7d9c86e1154fb3aac5a8a8681bb"); string pathOpt = AssetDatabase.GUIDToAssetPath("571051a232e4af44a98389bda858df27"); if(string.IsNullOrEmpty(pathBase) || string.IsNullOrEmpty(pathOpt) || !File.Exists(pathBase) || !File.Exists(pathOpt)) return null; var sb = new StringBuilder(); var sr = new StreamReader(pathBase); string line; while((line = sr.ReadLine()) != null) { int indEND = line.IndexOf(";"); if(indEND <= 0) { sb.AppendLine(line); continue; } int indF4 = line.IndexOf("float4 "); int indST = line.IndexOf("_ST;"); int indF = line.IndexOf("float "); int indI = line.IndexOf("uint "); int indB = line.IndexOf("lilBool "); if(indF4 >= 0) { indF4 += TYPE_OFFSET; string name = line.Substring(indF4, indEND - indF4); if(indST >= 0) { // Texture string texname = name.Substring(0,name.Length - 3); if(dicD.ContainsKey(texname) && !dicD[texname].isVariable) { var v = dicD[texname]; sb.AppendLine(GetIndent(indF4 - 8) + "#define " + name + " float4(" + LilF2S(v.s.x) + "," + LilF2S(v.s.y) + "," + LilF2S(v.o.x) + "," + LilF2S(v.o.y) + ")"); continue; } } else if(dicC.ContainsKey(name) && !dicC[name].isVariable) { var v = dicC[name]; Color c = line.Contains("Color") && !line.Contains("Emission") && PlayerSettings.colorSpace == ColorSpace.Linear ? v.c.linear : v.c; sb.AppendLine(GetIndent(indF4 - 8) + "#define " + name + " float4(" + LilF2S(c.r) + "," + LilF2S(c.g) + "," + LilF2S(c.b) + "," + LilF2S(c.a) + ")"); continue; } } else if(indF >= 0) { // Float indF += TYPE_OFFSET; string name = line.Substring(indF, indEND - indF); if(dicF.ContainsKey(name) && !dicF[name].isVariable) { sb.AppendLine(GetIndent(indF - 8) + "#define " + name + " (" + LilF2S(dicF[name].f) + ")"); continue; } } else if(indI >= 0) { // Int indI += TYPE_OFFSET; string name = line.Substring(indI, indEND - indI); if(dicF.ContainsKey(name) && !dicF[name].isVariable) { sb.AppendLine(GetIndent(indI - 8) + "#define " + name + " (" + (uint)dicF[name].f + ")"); continue; } } else if(indB >= 0) { // Bool indB += TYPE_OFFSET; string name = line.Substring(indB, indEND - indB); if(dicF.ContainsKey(name) && !dicF[name].isVariable) { sb.AppendLine(GetIndent(indB - 8) + "#define " + name + " (" + (uint)dicF[name].f + ")"); continue; } } sb.AppendLine(line); } sr.Close(); sb.Replace("\r\n", "\r"); sb.Replace("\n", "\r"); sb.Replace("\r", "\r\n"); return sb.ToString(); } internal static void ResetInputHLSL() { string pathBase = AssetDatabase.GUIDToAssetPath("8ff7f7d9c86e1154fb3aac5a8a8681bb"); string pathOpt = AssetDatabase.GUIDToAssetPath("571051a232e4af44a98389bda858df27"); if(string.IsNullOrEmpty(pathBase) || string.IsNullOrEmpty(pathOpt) || !File.Exists(pathBase) || !File.Exists(pathOpt)) return; var sw = new StreamWriter(pathOpt, false); var sr = new StreamReader(pathBase); sw.Write(sr.ReadToEnd()); sw.Close(); sr.Close(); } private static string GetIndent(int indent) { return new string(' ', indent); } private static bool CheckShaderIslilToon(Shader shader) { if(shader == null) return false; if(shader.name.Contains("lilToon")) return true; string shaderPath = AssetDatabase.GetAssetPath(shader); return !string.IsNullOrEmpty(shaderPath) && shaderPath.Contains(".lilcontainer"); } private static string LilF2S(float f){ return f.ToString(CultureInfo.InvariantCulture); } private struct TexProp { public bool isVariable; public Object t; } private struct STProp { public bool isVariable; public Vector2 s; public Vector2 o; } private struct FloatProp { public bool isVariable; public float f; } private struct ColorProp { public bool isVariable; public Color c; } } } #endif