yum-archive/2ner
A toon shader for Unity's BIRP.
git clone https://git.yummers.dev/yum-archive/2ner
5f84c37
master
1// !! AI ARTIFACT !! 2// This code was originally generated by Claude 3.5 Sonnet. 3// I wanted to write this tooling like I want a fucking hole in the head so I 4// kindly asked Claude to write it for me. It's shitty and poorly designed, but 5// it works well enough for my purposes. 6// It has been slightly tweaked by me, and validated on *this* codebase. It is 7// provided with no warranty. 8using UnityEngine ; 9using UnityEditor ; 10using System . IO ; 11using System . Text . RegularExpressions ; 12using System . Collections . Generic ; 13using System . Linq ; 14 15public class ShaderInlinerV2 : EditorWindow 16{ 17private string inputShaderPath ; 18private string outputShaderPath ; 19private int maxOutputLines = 10000000 ; // 10 million lines limit 20private int currentLineCount = 0 ; 21 22[ MenuItem ( "Tools/yum_food/Shader Inliner (v2)" )] 23public static void ShowWindow () 24{ 25GetWindow < ShaderInlinerV2 > ( "Shader Inliner" ); 26} 27 28private void OnGUI () 29{ 30GUILayout . Label ( "Shader Inliner" , EditorStyles . boldLabel ); 31 32inputShaderPath = EditorGUILayout . TextField ( "Input Shader Path" , inputShaderPath ); 33if ( GUILayout . Button ( "Select Input Shader" )) 34{ 35inputShaderPath = EditorUtility . OpenFilePanel ( "Select Shader" , "" , "shader" ); 36} 37 38maxOutputLines = EditorGUILayout . IntField ( "Max Output Lines" , maxOutputLines ); 39 40if ( GUILayout . Button ( "Inline Shader" )) 41{ 42if ( string . IsNullOrEmpty ( inputShaderPath )) 43{ 44EditorUtility . DisplayDialog ( "Error" , "Please select an input shader." , "OK" ); 45return ; 46} 47 48InlineShader (); 49} 50} 51 52private void InlineShader () 53{ 54currentLineCount = 0 ; 55string shaderContent = File . ReadAllText ( inputShaderPath ); 56string inlinedShader = ProcessShader ( shaderContent , Path . GetDirectoryName ( inputShaderPath )); 57 58string fileName = Path . GetFileNameWithoutExtension ( inputShaderPath ); 59outputShaderPath = Path . Combine ( Path . GetDirectoryName ( inputShaderPath ), $" { fileName } _inlined.shader" ); 60File . WriteAllText ( outputShaderPath , inlinedShader ); 61 62AssetDatabase . Refresh (); 63EditorUtility . DisplayDialog ( "Success" , 64$"Inlined shader saved to:\n { outputShaderPath } \nTotal lines: { currentLineCount } " , "OK" ); 65} 66 67private string ProcessShader ( string content , string basePath ) 68{ 69// Update shader name 70content = Regex . Replace ( content , @"Shader\s+""(.+?)""" , match=> 71{ 72string shaderName = match . Groups [ 1 ]. Value ; 73return $"Shader \" { shaderName } _inlined\"" ; 74}); 75 76// Count initial lines 77currentLineCount += content . Split ( '\n' ). Length ; 78if ( currentLineCount > maxOutputLines ) 79{ 80Debug . LogError ( $"Maximum line count exceeded: { currentLineCount } " ); 81return content ; 82} 83 84// Process all includes, regardless of whether they're in CGPROGRAM blocks 85content = ProcessIncludes ( content , basePath ); 86 87// Check for mismatched preprocessor macros in the entire shader 88CheckMismatchedMacros ( content ); 89 90return content ; 91} 92 93private string ProcessIncludes ( string content , string basePath ) 94{ 95string pattern = @"#include\s+""(.+?)""" ; 96return Regex . Replace ( content , pattern , match=> 97{ 98string includePath = match . Groups [ 1 ]. Value ; 99string fullPath = Path . Combine ( basePath , includePath ); 100 101if ( File . Exists ( fullPath )) 102{ 103string includeContent = File . ReadAllText ( fullPath ); 104 105// Count the lines in the included file 106int includeLines = includeContent . Split ( '\n' ). Length ; 107currentLineCount += includeLines ; 108 109// Check if we've exceeded the line limit 110if ( currentLineCount > maxOutputLines ) 111{ 112Debug . LogError ( $"Maximum line count exceeded ( { currentLineCount } lines) while including: { includePath } " ); 113return $"// ERROR: Maximum line count exceeded while including: { includePath } " ; 114} 115 116// Process includes recursively 117return ProcessIncludes ( includeContent , Path . GetDirectoryName ( fullPath )); 118} 119else 120{ 121Debug . LogWarning ( $"Include file not found: { fullPath } " ); 122return match . Value ; 123} 124}); 125} 126 127private void CheckMismatchedMacros ( string content ) 128{ 129var stack = new Stack < string > (); 130var lines = content . Split ( '\n' ); 131var macroPattern = @"^\s*#(if|ifdef|ifndef|elif|else|endif|if\s+defined)" ; 132 133for ( int i = 0 ; i < lines . Length ; i ++ ) 134{ 135var line = lines [ i ]. Trim (); 136var match = Regex . Match ( line , macroPattern ); 137 138if ( match . Success ) 139{ 140var directive = match . Groups [ 1 ]. Value ; 141 142switch ( directive ) 143{ 144case "if" : 145case "ifdef" : 146case "ifndef" : 147case "if defined" : 148stack . Push ( directive ); 149break ; 150case "elif" : 151if ( stack . Count == 0 || ( stack . Peek () != "if" && stack . Peek () != "elif" )) 152{ 153Debug . LogError ( $"Mismatched #elif at line { i + 1 } " ); 154} 155else 156{ 157stack . Pop (); 158stack . Push ( "elif" ); 159} 160break ; 161case "else" : 162if ( stack . Count == 0 || ( stack . Peek () != "if" && stack . Peek () != "elif" )) 163{ 164Debug . LogError ( $"Mismatched #else at line { i + 1 } " ); 165} 166else 167{ 168stack . Pop (); 169stack . Push ( "else" ); 170} 171break ; 172case "endif" : 173if ( stack . Count == 0 ) 174{ 175Debug . LogError ( $"Mismatched #endif at line { i + 1 } " ); 176} 177else 178{ 179stack . Pop (); 180} 181break ; 182} 183} 184} 185 186if ( stack . Count > 0 ) 187{ 188Debug . LogError ( $"Unclosed preprocessor directives: { string . Join ( ", " , stack )} " ); 189} 190} 191} 192