rabidus-test/Assets/Amazing Assets/Advanced Dissolve/Editor/Utilities/AdvancedDissolveGenerateSha...

519 lines
20 KiB
C#
Raw Permalink Normal View History

2023-09-18 20:09:22 +03:00
using System.Linq;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.IO;
namespace AmazingAssets.AdvancedDissolveEditor
{
public class GenerateShader : Editor
{
enum SHADER_PASS { Unknown, Pass, UniversalForward, ShadowCaster, META, Meta, ScenePickingPass, SceneSelectionPass, DepthOnly, DepthForwardOnly, DepthNormals, DepthNormalsOnly, GBuffer, MotionVectors, Forward, ForwardOnly, FullScreenDebug, IndirectDXR, VisibilityDXR, ForwardDXR, GBufferDXR, PathTracingDXR, RayTracingPrepass, TransparentDepthPrepass }
[MenuItem("Assets/Amazing Assets/Advanced Dissolve/Generate Shader", false, 4201)]
static public void Menu()
{
Generate(Selection.activeObject);
}
[MenuItem("Assets/Amazing Assets/Advanced Dissolve/Generate Shader", true, 4201)]
static public bool Validate_Menu()
{
if (Selection.activeObject == null)
return false;
string path = AssetDatabase.GetAssetPath(Selection.activeObject);
if (string.IsNullOrEmpty(path))
return false;
if (Path.GetExtension(path).ToLowerInvariant() != ".shader")
return false;
if (string.IsNullOrEmpty(GUIUtility.systemCopyBuffer) || GUIUtility.systemCopyBuffer.IndexOf("Shader \"") != 0)
return false;
return true;
}
static bool IsAssetReady(Object obj)
{
if (obj == null)
return false;
string path = AssetDatabase.GetAssetPath(obj);
if (string.IsNullOrEmpty(path))
{
Debug.LogError("Unknown asset type.\n");
return false;
}
if (Path.GetExtension(path).ToLowerInvariant() != ".shader")
{
Debug.LogError("Asset is not a valid shader.\n");
return false;
}
Shader shader = (Shader)AssetDatabase.LoadAssetAtPath(path, typeof(Shader));
if (shader == null)
{
Debug.LogError("Asset is not a valid shader.\n");
return false;
}
if (string.IsNullOrEmpty(GUIUtility.systemCopyBuffer) || GUIUtility.systemCopyBuffer.IndexOf("Shader \"") != 0)
{
Debug.LogError("System copy buffer does not hold 'shader' code.\n");
return false;
}
return true;
}
static void Generate(Object sourceShaderAsset)
{
if (IsAssetReady(sourceShaderAsset) == false)
return;
string sourceShaderAssetPath = AssetDatabase.GetAssetPath(sourceShaderAsset);
//Write system copy buffer into a file
CreateShaderAssetFile(sourceShaderAssetPath, new List<string> { GUIUtility.systemCopyBuffer });
List<string> newShaderFile = File.ReadAllLines(sourceShaderAssetPath).ToList();
//1) Change shader Name
//2) Add properties
//3) Add shader code
//4) Add custom editor
//5) Save
//1
if (ChangeShaderName(sourceShaderAssetPath, newShaderFile) == false)
{
Debug.LogError("Problem with shader renaming.\n");
return;
}
//2
if (AddProperties(newShaderFile) == false)
{
Debug.LogError("Shader has no properties.\n");
return;
}
//3
if (AddShaderCode(newShaderFile) == false)
{
Debug.LogError("Problems with shader generating.\n");
return;
}
//4
AddCustomEditor(newShaderFile);
//5
CreateShaderAssetFile(sourceShaderAssetPath, newShaderFile);
}
static string GetCustomEditorName(string defaultCustomEditor)
{
switch(AmazingAssets.AdvancedDissolveEditor.Utilities.GetProjectRenderPipeline())
{
case Utilities.RenderPipeline.HighDefinition:
{
if (defaultCustomEditor.Contains("Rendering.HighDefinition.HDUnlitGUI"))
return defaultCustomEditor.Replace("Rendering.HighDefinition.HDUnlitGUI", "AmazingAssets.AdvancedDissolveEditor.ShaderGraph.AdvancedDissolve_ShaderGraphGUI");
if (defaultCustomEditor.Contains("Rendering.HighDefinition.DecalShaderGraphGUI"))
return defaultCustomEditor.Replace("Rendering.HighDefinition.DecalShaderGraphGUI", "AmazingAssets.AdvancedDissolveEditor.ShaderGraph.AdvancedDissolve_ShaderGraphGUI");
if (defaultCustomEditor.Contains("Rendering.HighDefinition.LightingShaderGraphGUI"))
return defaultCustomEditor.Replace("Rendering.HighDefinition.LightingShaderGraphGUI", "AmazingAssets.AdvancedDissolveEditor.ShaderGraph.AdvancedDissolve_ShaderGraphGUI");
if (defaultCustomEditor.Contains("Rendering.HighDefinition.LitShaderGraphGUI"))
return defaultCustomEditor.Replace("Rendering.HighDefinition.LitShaderGraphGUI", "AmazingAssets.AdvancedDissolveEditor.ShaderGraph.AdvancedDissolve_ShaderGraphGUI");
} break;
case Utilities.RenderPipeline.Universal:
{
if (defaultCustomEditor.Contains("ShaderGraphUnlitGUI"))
return "CustomEditorForRenderPipeline \"UnityEditor.AdvancedDissolve_ShaderGraphUnlitGUI\" \"UnityEngine.Rendering.Universal.UniversalRenderPipelineAsset\"";
else if (defaultCustomEditor.Contains("ShaderGraphLitGUI"))
return "CustomEditorForRenderPipeline \"UnityEditor.AdvancedDissolve_ShaderGraphLitGUI\" \"UnityEngine.Rendering.Universal.UniversalRenderPipelineAsset\"";
} break;
}
Debug.LogError("Undefined custom material editor.\n");
return string.Empty;
}
static bool ChangeShaderName(string sourceShaderAssetPath, List<string> newShaderFile)
{
//Get source shader name
string originalName = Path.GetFileNameWithoutExtension(sourceShaderAssetPath);
//Shader "name" <-- find this line and set new shader name
//{
// Properties
// {
// ...
// ...
// ...
// }
for (int i = 0; i < newShaderFile.Count; i++)
{
if (newShaderFile[i].Contains("Shader \""))
{
newShaderFile[i] = "Shader \"" + "Amazing Assets/Advanced Dissolve/Shader Graph/" + originalName + "\"";
return true;
}
}
return false;
}
static bool AddProperties(List<string> newShaderFile)
{
//Properties
// { <-- find this line ID and add Dissolve properties below it
// ...
// ...
// ...
// }
//SubShader
//{
int propertiesLineID = -1;
for (int i = 0; i < newShaderFile.Count; i++)
{
if (newShaderFile[i].Trim() == "Properties")
{
propertiesLineID = i + 1;
break;
}
}
if (propertiesLineID == -1)
return false;
string mateiralPropertiesFilePath = Path.Combine(Utilities.GetPathToTheAssetInstallationtFolder(), "Editor", "Utilities", "AdvancedDissolveProperties.txt");
List<string> propertyFile = File.ReadAllLines(mateiralPropertiesFilePath).ToList();
propertyFile.Add(System.Environment.NewLine);
newShaderFile.InsertRange(propertiesLineID + 1, propertyFile);
return true;
}
static void AddCustomEditor(List<string> newShaderFile)
{
//Find defult "CustomEditor" and replace it
//Loop of 10 iterations is enough to find "CustomEditor"
int customEditroLineID = -1;
for (int i = newShaderFile.Count - 1; i >= newShaderFile.Count - 10; i -= 1)
{
if (newShaderFile[i].Contains("CustomEditorForRenderPipeline"))
{
customEditroLineID = i;
break;
}
}
if (customEditroLineID != -1)
{
string newCustomEditor = GetCustomEditorName(newShaderFile[customEditroLineID]);
//Comment old editor
newShaderFile[customEditroLineID] = "//" + newShaderFile[customEditroLineID];
//Add new editor
newShaderFile.Insert(customEditroLineID + 1, newCustomEditor);
}
//No "CustomEditor" detected
else
{
//Find the end of a shader file
for (int i = newShaderFile.Count - 1; i >= newShaderFile.Count - 10; i -= 1)
{
if (newShaderFile[i].Trim() == "}")
{
newShaderFile.Insert(i, string.Format(" CustomEditor \"AmazingAssets.AdvancedDissolveEditor.ShaderGraph.{0}\"", GetCustomEditorName(string.Empty)));
break;
}
}
}
}
static bool AddShaderCode(List<string> newShaderFile)
{
// SurfaceDescription SurfaceDescriptionFunction(SurfaceDescriptionInputs IN) <-- find this line ID and add Dissolve keywords above it
//{
// SurfaceDescription surface = (SurfaceDescription)0;
// ...
// ...
// ... SG_AdvancedDissolve_XXXXXXXX({0}, {1}, {2}); <-- find this line, extract {0} parameter and then disalbe this line
// ...
// ...
// return surface; <-- find this line ID and add Dissolve core methods above it
//}
if (Utilities.GetProjectRenderPipeline() == Utilities.RenderPipeline.BuiltIn)
return false;
string pathToDefinesCGINC = Path.Combine(Utilities.GetPathToTheAssetInstallationtFolder(), "Shaders", "cginc", "Defines.cginc");
string pathToCoreCGINC = Path.Combine(Utilities.GetPathToTheAssetInstallationtFolder(), "Shaders", "cginc", "Core.cginc");
string pathToShaderKeywordsFile = Path.Combine(Utilities.GetPathToTheAssetInstallationtFolder(), "Editor", "Utilities", "AdvancedDissolveKeywords.txt");
pathToDefinesCGINC = "#include \"" + pathToDefinesCGINC.Replace(Path.DirectorySeparatorChar, '/') + "\"";
pathToCoreCGINC = "#include \"" + pathToCoreCGINC.Replace(Path.DirectorySeparatorChar, '/') + "\"";
List<string> keywordsFile = File.ReadAllLines(pathToShaderKeywordsFile).ToList();
keywordsFile.RemoveAll(x => x.Contains("_AD_EDGE_UV_DISTORTION_SOURCE_CUSTOM_MAP"));
List<string> defines = new List<string>();
defines.Add(System.Environment.NewLine);
defines.AddRange(keywordsFile);
defines.Add(System.Environment.NewLine);
defines.Add("#define ADVANCED_DISSOLVE_SHADER_GRAPH");
if(Utilities.GetProjectRenderPipeline() == Utilities.RenderPipeline.Universal)
defines.Add("#define ADVANCED_DISSOLVE_UNIVERSAL_RENDER_PIPELINE");
else
defines.Add("#define ADVANCED_DISSOLVE_HIGH_DEFINITION_RENDER_PIPELINE");
defines.Add(pathToDefinesCGINC);
defines.Add("/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////");
defines.Add(System.Environment.NewLine);
List<string> coreData = new List<string>();
coreData.Add(System.Environment.NewLine);
coreData.Add("//Advanced Dissolve");
coreData.Add(pathToCoreCGINC);
coreData.Add(System.Environment.NewLine);
SHADER_PASS shaderPass = SHADER_PASS.Unknown;
bool definesAdded = false;
bool coreDataAdded = false;
bool codeAdded = false;
string customCutoutAlpha = "1";
string customEdgeColor = "1";
string surfaceAlbedo = string.Empty;
bool useEmission = false;
for (int i = 0; i < newShaderFile.Count; i++)
{
//Detect current shader pass
GetShaderPassFromString(newShaderFile[i], ref shaderPass);
//Add keywords
if (newShaderFile[i].Contains("CBUFFER_START(UnityPerMaterial)"))
{
newShaderFile.InsertRange(i, defines);
i += defines.Count;
//Define meta pass
if (shaderPass == SHADER_PASS.META || shaderPass == SHADER_PASS.Meta)
{
newShaderFile.Insert(i - 4, "#define ADVANCED_DISSOLVE_META_PASS");
//Reset
shaderPass = SHADER_PASS.Unknown;
i += 1;
}
definesAdded = true;
}
//Add core data
if (newShaderFile[i].Contains("SurfaceDescription SurfaceDescriptionFunction(SurfaceDescriptionInputs IN)"))
{
GetSurfaceProperties(newShaderFile, i, out surfaceAlbedo, out useEmission);
newShaderFile.InsertRange(i, coreData);
i += coreData.Count;
coreDataAdded = true;
}
//Find Custom Cutout Alpha & Custom Edge Color
if (newShaderFile[i].Contains("SG_AdvancedDissolve_") && newShaderFile[i].Contains("void ") == false)
{
//Just make sure this line is what we are looking for
if (newShaderFile[i].Contains("(") && newShaderFile[i].Contains(")") && newShaderFile[i].Contains(",") && newShaderFile[i].Contains(";"))
{
//SG_AdvancedDissolve_40a0ec735cfda6043bd217c139a901ab(_9D6BCFCD_Out_2, _258F41d_Out_2, _AdvancedDissolve_21DC4B79, _AdvancedDissolve_21DC4B79_Out_3);
// ↑ ↑ ↑
// | | |
// | | |
//index1 - - - - - - - - - - - - - - - - - - - - - - - | |
// | |
//index2 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
// |
//index3 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int index1 = newShaderFile[i].IndexOf("(");
int index2 = newShaderFile[i].IndexOf(",");
customCutoutAlpha = newShaderFile[i].Substring(index1 + 1, index2 - index1 - 1).Trim();
if(newShaderFile[i].Contains("float4") == false) //User has provied Custom Edge Color
{
int index3 = newShaderFile[i].IndexOf(",", newShaderFile[i].IndexOf(",") + 1);
customEdgeColor = newShaderFile[i].Substring(index2 + 1, index3 - index2 - 1).Trim();
}
}
}
//Add core method
if (newShaderFile[i].Contains("return surface;"))
{
newShaderFile[i] = string.Empty;
List<string> code = new List<string>();
code.Add(System.Environment.NewLine);
code.Add("//" + shaderPass.ToString());
code.Add(GetShaderCode(customCutoutAlpha, customEdgeColor, surfaceAlbedo, useEmission));
code.Add(System.Environment.NewLine);
code.Add("return surface;");
newShaderFile.InsertRange(i, code);
i += code.Count;
codeAdded = true;
}
}
//Make sure shader always has '_ALPHATEST_ON' keyword
for (int i = 0; i < newShaderFile.Count; i++)
{
if (newShaderFile[i].Contains("#pragma") && newShaderFile[i].Contains("shader_feature") && newShaderFile[i].Contains("_ALPHATEST_ON"))
{
newShaderFile[i] = "#define _ALPHATEST_ON 1";
}
}
return definesAdded && coreDataAdded && codeAdded ? true : false;
}
static void CreateShaderAssetFile(string sourceShaderAssetPath, List<string> newShaderFile)
{
File.WriteAllLines(sourceShaderAssetPath, newShaderFile);
AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate);
}
static string GetShaderCode(string customCutoutAlpha, string customEdgeColor, string albedo, bool useEmission)
{
string code = "AdvancedDissolveShaderGraph(IN.uv0.xy, IN.ObjectSpacePosition, IN.WorldSpacePosition, IN.AbsoluteWorldSpacePosition, IN.ObjectSpaceNormal, IN.WorldSpaceNormal, " + customCutoutAlpha + ", " + customEdgeColor;
code += string.Format(", {0}{1}surface.Alpha, surface.AlphaClipThreshold);",
string.IsNullOrEmpty(albedo) ? string.Empty : ("surface." + albedo + ", "),
useEmission ? ("surface.Emission, ") : string.Empty);
return code;
}
static void GetSurfaceProperties(List<string> newShaderFile, int startIndex, out string albedo, out bool emission)
{
albedo = string.Empty;
emission = false;
for (int i = startIndex; i < newShaderFile.Count; i++)
{
if (newShaderFile[i].Contains("surface.Albedo = "))
albedo = "Albedo";
if (newShaderFile[i].Contains("surface.BaseColor = "))
albedo = "BaseColor";
if (newShaderFile[i].Contains("surface.Color = "))
albedo = "Color";
if (newShaderFile[i].Contains("surface.Emission = "))
emission = true;
if (newShaderFile[i].Contains("return surface;"))
return;
}
Debug.LogError("Uknown Surface Properties");
}
static void GetShaderPassFromString(string line, ref SHADER_PASS shaderPass)
{
//Name "Depth Only" <------ example
if (string.IsNullOrEmpty(line) || line.Contains("Name \"") == false)
return;
//Remove "Name", ", space
line = line.Replace("Name", string.Empty).Replace("\"", string.Empty).Replace(" ", string.Empty);
foreach (SHADER_PASS pass in System.Enum.GetValues(typeof(SHADER_PASS)))
{
if (line == pass.ToString())
{
shaderPass = pass;
return;
}
}
Debug.LogError("Unknown Shader Pass: " + line);
shaderPass = SHADER_PASS.Unknown;
}
}
}