#if UNITY_EDITOR using System; using System.Collections.Generic; using System.IO; using System.Text; using UnityEditor; using UnityEngine; namespace SLZ.Bonelab { /* public class CodegenDebugWindow : EditorWindow { public ShaderInclude inputFile; public ShaderInclude outputFile; public SerializedObject thisObj; public SerializedProperty inputProp; public SerializedProperty outputProp; public SerializedProperty injectionProp; public ShaderInclude[] injectionFiles; public ShaderInjector cg; [MenuItem("Tools/LitMAS Codegen Debug")] static void Init() { CodegenDebugWindow window = (CodegenDebugWindow)GetWindow(typeof(CodegenDebugWindow)); window.Show(); } private void OnEnable() { cg = new ShaderInjector(); thisObj = new SerializedObject(this); inputProp = thisObj.FindProperty("inputFile"); outputProp = thisObj.FindProperty("outputFile"); injectionProp = thisObj.FindProperty("injectionFiles"); } private void OnGUI() { EditorGUILayout.ObjectField(inputProp); EditorGUILayout.ObjectField(outputProp); EditorGUILayout.PropertyField(injectionProp, true); thisObj.ApplyModifiedProperties(); if (GUILayout.Button("Test")) { string appPath = Path.GetDirectoryName(Application.dataPath); cg.inputFileDir = Path.Combine(appPath, AssetDatabase.GetAssetPath(inputFile)); cg.outputFileDir = Path.Combine(appPath, AssetDatabase.GetAssetPath(outputFile)); cg.injectionDirs = new string[injectionFiles.Length]; for (int i = 0; i < injectionFiles.Length; i++) { cg.injectionDirs[i] = Path.Combine(appPath, AssetDatabase.GetAssetPath(injectionFiles[i])); } Debug.Log("Begin injection"); cg.CreateShader(); } } } */ /// /// This script is a horrible cludge slapped together quickly to solve the issue of having to maintain many different /// versions of LitMAS, most of which is just duplicate code with minor variations. Goes through a set of specified include /// files looking for blocks of code contained in blocks guarded by //#!INJECT_BEGIN (name) (order) and //#!INJECT_END. It /// then looks for lines in the base file which begin with //!#INJECT_POINT (name) and pastes the code from all the blocks with /// the same at that point. /// public class ShaderInjector { public string outputFileDir; public string inputFileDir; public string[] injectionDirs; public Dictionary TagIndex; public List>> injectionContent; public Dictionary texcoordCounter; public string texcoord = " {0} {1} : TEXCOORD{2};\n"; public string blockHeader = "// Begin Injection {0} from {1} ----------------------------------------------------------\n"; public string blockEnder = "// End Injection {0} from {1} ----------------------------------------------------------\n"; const string warningHeader = "/*-----------------------------------------------------------------------------------------------------*\n" + " *-----------------------------------------------------------------------------------------------------*\n" + " * WARNING: THIS FILE WAS CREATED WITH SHADERINJECTOR, AND SHOULD NOT BE EDITED DIRECTLY. MODIFY THE *\n" + " * BASE INCLUDE AND INJECTED FILES INSTEAD, AND REGENERATE!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! *\n" + " *-----------------------------------------------------------------------------------------------------*\n" + " *-----------------------------------------------------------------------------------------------------*/\n\n"; public void CreateShader() { TagIndex = new Dictionary(); injectionContent = new List>>(); texcoordCounter = new Dictionary(); foreach (string injDir in injectionDirs) { int status = ReadInjectionFile(injDir); if (status != 0) return; } sortInjectionContent(); InjectBlocksIntoFile(inputFileDir, outputFileDir); } private int ReadInjectionFile(string injDir) { string file = ""; if (ReadFile(injDir, out file) != 0) { return 1; } Debug.Log(file.Length); SILexer lexer = new SILexer(); List cmd = lexer.LexFile(ref file); if (cmd == null || cmd.Count == 0) { Debug.LogError("ShaderInjector: No injection commands in file at " + injDir); return 1; } SIParser parser = new SIParser(injDir); if (parser.validateInjection(cmd) != 0) //validation will handle printing errors { return 1; } string fileName = Path.GetFileName(injDir); readInjectionCommands(cmd, ref file, fileName); return 0; } private void readInjectionCommands(List cmd, ref string file, string fileName) { for (int i = 0; i < cmd.Count; i++) { if (cmd[i].cmdType == SILexicon.CmdType.InjectBegin) { string tag = cmd[i].parameters[0]; int order = Int32.Parse(cmd[i].parameters[1]); //parser should make sure this is an int string block = ReadBlock(cmd, i, ref file, fileName); if (!TagIndex.ContainsKey(tag)) { TagIndex.Add(tag, TagIndex.Count); injectionContent.Add(new List>()); } Tuple cmdTuple = new Tuple(order, block); injectionContent[TagIndex[tag]].Add(cmdTuple); } } } private string CreateTexcoord(SILexer.CommandInfo cmd) { int counter; int register = Int32.Parse(cmd.parameters[2]); if (texcoordCounter.TryGetValue(register, out counter)) { counter = texcoordCounter[register] + 1; texcoordCounter[register] = counter; } else { counter = 0; texcoordCounter.Add(register, counter); } return String.Format(texcoord, cmd.parameters[0], cmd.parameters[1], counter); } private string ReadBlock(List cmd, int index, ref string file, string fileName = null) { int i = index; StringBuilder block = new StringBuilder(); if (fileName != null) { block.Append(string.Format(blockHeader, cmd[i].parameters[0], fileName)); } int beginBlock = cmd[i].endIndex + 1; int j = i + 1; int endBlock; while (cmd[j].cmdType != SILexicon.CmdType.InjectEnd && j < cmd.Count) { endBlock = cmd[j].beginIndex; block.Append(file.Substring(beginBlock, Mathf.Max(endBlock - beginBlock, 0))); if (cmd[j].cmdType == SILexicon.CmdType.TexcoordCounter) { beginBlock = cmd[j].beginIndex; } else { beginBlock = cmd[j].endIndex + 1; } j++; } endBlock = cmd[j].beginIndex; block.Append(file.Substring(beginBlock, endBlock - beginBlock)); if (fileName != null) { block.Append(string.Format(blockEnder, cmd[i].parameters[0], fileName)); } return block.ToString(); } private int ReadFile(string injDir, out string file) { file = ""; try { file = File.ReadAllText(injDir); } catch (ArgumentException) { Debug.LogError("ShaderInjector: Invalid file directory: " + injDir); return 1; } catch (PathTooLongException) { Debug.LogError("ShaderInjector: File directory too long: " + injDir); return 1; } catch (FileNotFoundException) { Debug.LogError("ShaderInjector: File does not exist at path: " + injDir); return 1; } catch (DirectoryNotFoundException) { Debug.LogError("ShaderInjector: Directory does not exist: " + injDir); return 1; } catch (IOException) { Debug.LogError("ShaderInjector: I/O error attempting to read file at: " + injDir); return 1; } return 0; } void sortInjectionContent() { foreach (var tagContent in injectionContent) { tagContent.Sort((x, y) => x.Item1.CompareTo(y.Item1)); } } void ParseSecondPass(ref StringBuilder sb, ref string file, ref List cmd) { sb.Append(file.Substring(0, cmd[0].beginIndex)); int i = 0; for (; i < cmd.Count; i++) { switch (cmd[i].cmdType) { case SILexicon.CmdType.TexcoordCounter: sb.Append(CreateTexcoord(cmd[i])); Debug.Log(cmd[i].parameters[1]); break; } int beginIndex = cmd[i].endIndex + 1; int endIndex = i != cmd.Count - 1 ? cmd[i + 1].beginIndex : file.Length; sb.Append(file.Substring(beginIndex, endIndex - beginIndex)); } } int InjectBlocksIntoFile(string dirIn, string dirOut) { string file = ""; if (ReadFile(dirIn, out file) != 0) { return 1; } SILexer lexer = new SILexer(); List cmd = lexer.LexFile(ref file); if (cmd == null || cmd.Count == 0) { Debug.LogError("ShaderInjector: No injection commands in file at " + dirIn); return 1; } SIParser parser = new SIParser(dirIn); if (parser.validateBase(cmd) != 0) { return 1; } StringBuilder OutputFile = new StringBuilder(); OutputFile.Append(warningHeader); OutputFile.Append(file.Substring(0, cmd[0].beginIndex)); int i = 0; int useDefault = 0; //bool skipBlock = false; for (; i < cmd.Count; i++) { switch (cmd[i].cmdType) { case SILexicon.CmdType.InjectPoint: useDefault = InjectBlock(cmd, i, ref OutputFile); break; case SILexicon.CmdType.InjectEnd: break; case SILexicon.CmdType.InjectDefault: if (useDefault == 0) { ++i; while (cmd[i].cmdType != SILexicon.CmdType.InjectEnd) { ++i; } useDefault = 0; } break; case SILexicon.CmdType.TexcoordCounter: OutputFile.Append( file.Substring(cmd[i].beginIndex, cmd[i].endIndex - cmd[i].beginIndex + 1)); break; } int beginIndex = cmd[i].endIndex + 1; int endIndex = i != cmd.Count - 1 ? cmd[i + 1].beginIndex : file.Length; OutputFile.Append(file.Substring(beginIndex, endIndex - beginIndex)); } //OutputFile.Append(file.Substring(0, cmd[0].beginIndex)); string file2 = OutputFile.ToString(); List cmd2 = lexer.LexFile(ref file2); if (cmd2 != null && cmd2.Count > 0) { if (parser.validateBase(cmd2) != 0) { return 1; } StringBuilder OutputFile2 = new StringBuilder(); ParseSecondPass(ref OutputFile2, ref file2, ref cmd2); file2 = OutputFile2.ToString(); } string assetPath = dirOut.Substring(Path.GetDirectoryName(Application.dataPath).Length + 1); ShaderInclude fileObj = AssetDatabase.LoadAssetAtPath(assetPath); File.WriteAllText(dirOut, file2); EditorUtility.SetDirty(fileObj); //AssetDatabase.ImportAsset(assetPath); fileObj = null; return 0; } int InjectBlock(List cmd, int index, ref StringBuilder outp) { string tag = cmd[index].parameters[0]; if (!TagIndex.ContainsKey(tag)) { return 1; } int tagIndex = TagIndex[tag]; foreach (Tuple block in injectionContent[tagIndex]) { outp.Append(block.Item2); } return 0; } /* private int readInjectionFile(string injectionDir) { try { using (StreamReader inj = new StreamReader(injectionDir)) { string line; bool isReadingInjection = false; int lineNum = 0; int currentTagIndex = -1; int currentTagOrder = 0; StringBuilder injectBlock = new StringBuilder(); while ((line = inj.ReadLine()) != null) { string lineClipped = line.TrimStart(); if (!isReadingInjection) { int injectTagStatus = ParseInjectBlock(lineClipped, lineNum, injectionDir, ref currentTagIndex, ref currentTagOrder); switch (injectTagStatus) { case 0: break; case 1: return 1; case 2: isReadingInjection = true; break; } } else { if (lineClipped.StartsWith(prefix)) { if (injectEnd.Equals(lineClipped.Substring(prefix.Length,injectEnd.Length))) { injectionContent[currentTagIndex].Add(new Tuple(currentTagOrder, injectBlock.ToString())); isReadingInjection = false; injectBlock.Clear(); } } else { injectBlock.AppendLine(line); } } lineNum++; } injectBlock = null; if (isReadingInjection) { Debug.LogError("Reached end of file while reading injection block"); return 1; } } return 0; } catch (FileNotFoundException e) { Debug.LogError(e.Message); return 1; } } int ParseInjectBlock(string line, int lineNum, string injectionDir, ref int currTagIndex, ref int currTagOrder) { if (line.StartsWith(prefix)) { Debug.Log(line); if (injectBegin.Equals(line.Substring(prefix.Length,injectBegin.Length))) { string[] words; words = line.Split(new char[0], 4, System.StringSplitOptions.RemoveEmptyEntries); if (words.Length < 3) { Debug.LogError("Malformed injection tag at line " + lineNum + " in file " + injectionDir); return 1; } try { currTagOrder = Int32.Parse(words[2]); } catch (FormatException e) { Debug.LogError("Malformed injection tag at line " + lineNum + " in file " + injectionDir + ", could not parse " + words[2] + " as number \n" + e.Message); return 1; } if (!TagIndex.ContainsKey(words[1])) { TagIndex.Add(words[1], TagIndex.Count); injectionContent.Add(new List>()); } currTagIndex = TagIndex[words[1]]; return 2; } else { return 0; } } else { return 0; } } */ } } #endif