diff options
| author | yum <yum.food.vr@gmail.com> | 2024-09-18 23:36:06 -0700 |
|---|---|---|
| committer | yum <yum.food.vr@gmail.com> | 2024-09-18 23:36:06 -0700 |
| commit | 9c3b0eb22f65c158462894ed849ce9866446abd0 (patch) | |
| tree | ec9651be78542f4485c053cb9e5ee56c45727997 /Editor | |
| parent | b904835dc91db76627ecb08636cd5c339a07a1df (diff) | |
Move inliner under Editor/
Fixes upload breakage.
Diffstat (limited to 'Editor')
| -rw-r--r-- | Editor/shader_inliner.cs | 207 |
1 files changed, 207 insertions, 0 deletions
diff --git a/Editor/shader_inliner.cs b/Editor/shader_inliner.cs new file mode 100644 index 0000000..cecf9b8 --- /dev/null +++ b/Editor/shader_inliner.cs @@ -0,0 +1,207 @@ +// !! AI ARTIFACT !! +// This code was originally generated by Claude 3.5 Sonnet. +// I wanted to write this tooling like I want a fucking hole in the head so I +// kindly asked Claude to write it for me. It's shitty and poorly designed, but +// it works well enough for my purposes. +// It has been slightly tweaked by me, and validated on *this* codebase. It is +// provided with no warranty. +using UnityEngine; +using UnityEditor; +using System.IO; +using System.Text.RegularExpressions; +using System.Collections.Generic; +using System.Linq; + +public class ShaderInliner : EditorWindow +{ + private string inputShaderPath; + private string outputShaderPath; + + [MenuItem("Tools/yum_food/Shader Inliner")] + public static void ShowWindow() + { + GetWindow<ShaderInliner>("Shader Inliner"); + } + + private void OnGUI() + { + GUILayout.Label("Shader Inliner", EditorStyles.boldLabel); + + inputShaderPath = EditorGUILayout.TextField("Input Shader Path", inputShaderPath); + if (GUILayout.Button("Select Input Shader")) + { + inputShaderPath = EditorUtility.OpenFilePanel("Select Shader", "", "shader"); + } + + if (GUILayout.Button("Inline Shader")) + { + if (string.IsNullOrEmpty(inputShaderPath)) + { + EditorUtility.DisplayDialog("Error", "Please select an input shader.", "OK"); + return; + } + + InlineShader(); + } + } + + private void InlineShader() + { + string shaderContent = File.ReadAllText(inputShaderPath); + string inlinedShader = ProcessShader(shaderContent, Path.GetDirectoryName(inputShaderPath)); + + string fileName = Path.GetFileNameWithoutExtension(inputShaderPath); + outputShaderPath = Path.Combine(Path.GetDirectoryName(inputShaderPath), $"{fileName}_inlined.shader"); + File.WriteAllText(outputShaderPath, inlinedShader); + + AssetDatabase.Refresh(); + EditorUtility.DisplayDialog("Success", $"Inlined shader saved to:\n{outputShaderPath}", "OK"); + } + + private string ProcessShader(string content, string basePath) + { + // Update shader name + content = Regex.Replace(content, @"Shader\s+""(.+?)""", match => + { + string shaderName = match.Groups[1].Value; + return $"Shader \"{shaderName}_inlined\""; + }); + + // Process each Pass independently + content = Regex.Replace(content, @"(CGPROGRAM.*?ENDCG)", match => + { + return ProcessPass(match.Value, basePath); + }, RegexOptions.Singleline); + + // Check for mismatched preprocessor macros in the entire shader + CheckMismatchedMacros(content); + + return content; + } + + private string ProcessPass(string passContent, string basePath) + { + HashSet<string> includedFiles = new HashSet<string>(); + + string pattern = @"#include\s+""(.+?)"""; + return Regex.Replace(passContent, pattern, match => + { + string includePath = match.Groups[1].Value; + string fullPath = Path.Combine(basePath, includePath); + + if (File.Exists(fullPath)) + { + if (!includedFiles.Contains(fullPath)) + { + includedFiles.Add(fullPath); + string includeContent = File.ReadAllText(fullPath); + return ProcessInclude(includeContent, Path.GetDirectoryName(fullPath), includedFiles); + } + else + { + return "// Already included in this pass: " + includePath; + } + } + else + { + Debug.LogWarning($"Include file not found: {fullPath}"); + return match.Value; + } + }); + } + + private string ProcessInclude(string content, string basePath, HashSet<string> includedFiles) + { + string pattern = @"#include\s+""(.+?)"""; + return Regex.Replace(content, pattern, match => + { + string includePath = match.Groups[1].Value; + string fullPath = Path.Combine(basePath, includePath); + + if (File.Exists(fullPath)) + { + if (!includedFiles.Contains(fullPath)) + { + includedFiles.Add(fullPath); + string includeContent = File.ReadAllText(fullPath); + return ProcessInclude(includeContent, Path.GetDirectoryName(fullPath), includedFiles); + } + else + { + return "// Already included in this pass: " + includePath; + } + } + else + { + Debug.LogWarning($"Include file not found: {fullPath}"); + return match.Value; + } + }); + } + + private void CheckMismatchedMacros(string content) + { + var stack = new Stack<string>(); + var lines = content.Split('\n'); + var macroPattern = @"^\s*#(if|ifdef|ifndef|elif|else|endif|if\s+defined)"; + + for (int i = 0; i < lines.Length; i++) + { + var line = lines[i].Trim(); + var match = Regex.Match(line, macroPattern); + + if (match.Success) + { + var directive = match.Groups[1].Value; + + switch (directive) + { + case "if": + case "ifdef": + case "ifndef": + case "if defined": + stack.Push(directive); + break; + case "elif": + if (stack.Count == 0 || (stack.Peek() != "if" && stack.Peek() != "elif")) + { + Debug.LogError($"Mismatched #elif at line {i + 1}"); + } + else + { + stack.Pop(); + stack.Push("elif"); + } + break; + case "else": + if (stack.Count == 0 || (stack.Peek() != "if" && stack.Peek() != "elif")) + { + Debug.LogError($"Mismatched #else at line {i + 1}"); + } + else + { + stack.Pop(); + stack.Push("else"); + } + break; + case "endif": + if (stack.Count == 0) + { + Debug.LogError($"Mismatched #endif at line {i + 1}"); + } + else + { + stack.Pop(); + } + break; + } + } + } + + if (stack.Count > 0) + { + Debug.LogError($"Unclosed preprocessor directives: {string.Join(", ", stack)}"); + } + } +} + |
