363 lines
15 KiB
C#

#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<string, TexProp>();
var dicD = new Dictionary<string, STProp>();
var dicF = new Dictionary<string, FloatProp>();
var dicC = new Dictionary<string, ColorProp>();
// 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<string, TexProp>();
var dicD = new Dictionary<string, STProp>();
var dicF = new Dictionary<string, FloatProp>();
var dicC = new Dictionary<string, ColorProp>();
// 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<string, TexProp> dicT, Dictionary<string, STProp> dicD, Dictionary<string, FloatProp> dicF, Dictionary<string, ColorProp> 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<string, TexProp> dicT, Dictionary<string, STProp> dicD, Dictionary<string, FloatProp> dicF, Dictionary<string, ColorProp> 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<string, TexProp> dic, Dictionary<string, STProp> 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<string, FloatProp> 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<string, ColorProp> 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<string, TexProp> dicT, Dictionary<string, STProp> dicD, Dictionary<string, FloatProp> dicF, Dictionary<string, ColorProp> 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<string, TexProp> dicT, Dictionary<string, STProp> dicD, Dictionary<string, FloatProp> dicF, Dictionary<string, ColorProp> 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