WuhuIslandTesting/Library/PackageCache/com.unity.render-pipelines.universal@8148.0.7-4/Editor/ShaderInjector/ShaderInjector.cs
2025-01-07 02:06:59 +01:00

510 lines
No EOL
13 KiB
C#

#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();
}
}
}
*/
/// <summary>
/// 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.
/// </summary>
public class ShaderInjector
{
public string outputFileDir;
public string inputFileDir;
public string[] injectionDirs;
public Dictionary<string, int> TagIndex;
public List<List<Tuple<int, string>>> injectionContent;
public Dictionary<int, int> 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<string, int>();
injectionContent = new List<List<Tuple<int, string>>>();
texcoordCounter = new Dictionary<int, int>();
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<SILexer.CommandInfo> 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<SILexer.CommandInfo> 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<int, string>>());
}
Tuple<int, string> cmdTuple = new Tuple<int, string>(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<SILexer.CommandInfo> 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<SILexer.CommandInfo> 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<SILexer.CommandInfo> 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<SILexer.CommandInfo> 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<ShaderInclude>(assetPath);
File.WriteAllText(dirOut, file2);
EditorUtility.SetDirty(fileObj);
//AssetDatabase.ImportAsset(assetPath);
fileObj = null;
return 0;
}
int InjectBlock(List<SILexer.CommandInfo> cmd, int index, ref StringBuilder outp)
{
string tag = cmd[index].parameters[0];
if (!TagIndex.ContainsKey(tag))
{
return 1;
}
int tagIndex = TagIndex[tag];
foreach (Tuple<int, string> 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<int, string>(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<Tuple<int, string>>());
}
currTagIndex = TagIndex[words[1]];
return 2;
}
else
{
return 0;
}
}
else
{
return 0;
}
}
*/
}
}
#endif