Skip to content

Commit 05dcf14

Browse files
Refactor shader generation to use templates
This change introduces a new template-based approach for generating UnityToon shaders. It replaces the previous method of reading and modifying existing shader files with a more streamlined process that utilizes template files. This improves maintainability and reduces code duplication. Co-authored-by: sindharta.tanuwijaya <sindharta.tanuwijaya@unity3d.com>
1 parent df7ba9e commit 05dcf14

File tree

6 files changed

+258
-1277
lines changed

6 files changed

+258
-1277
lines changed

com.unity.toon-graphics-test/Editor/ShaderGenerator.cs

Lines changed: 152 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ public static class ShaderGenerator
2020
private const string COMMON_PROPERTIES_PATH = "Packages/com.unity.toonshader/Runtime/Shaders/Common/Parts~/CommonPropertiesPart.shader";
2121
private const string TESSELATION_PROPERTIES_PATH = "Packages/com.unity.toonshader/Runtime/Shaders/Common/Parts~/TessellationPropertiesPart.shader";
2222
private const string UNITY_TOON_SHADER_PATH = "Packages/com.unity.toonshader/Runtime/Integrated/Shaders/UnityToon.shader";
23+
private const string UNITY_TOON_SHADER_TEMPLATE_PATH = "Packages/com.unity.toonshader/Runtime/Shaders/Common/Parts~/UnityToon.shader.template";
2324
private const string UNITY_TOON_TESSELATION_SHADER_PATH = "Packages/com.unity.toonshader/Runtime/Integrated/Shaders/UnityToonTessellation.shader";
25+
private const string UNITY_TOON_TESSELATION_SHADER_TEMPLATE_PATH = "Packages/com.unity.toonshader/Runtime/Shaders/Common/Parts~/UnityToonTessellation.shader.template";
2426

2527
[MenuItem("Toon Shader/Generate Shader Files")]
2628
public static void GenerateShaderFilesMenu()
@@ -87,158 +89,41 @@ private static void GenerateShaderFilesInternal()
8789

8890
private static void GenerateUnityToonShader(string commonProperties, string autoCommentLine)
8991
{
90-
// Read the original shader file to preserve the rest of the content
91-
string originalContent = ReadFile(UNITY_TOON_SHADER_PATH);
92-
if (string.IsNullOrEmpty(originalContent))
93-
{
94-
originalContent = ReadFile(UNITY_TOON_SHADER_PATH + ".template");
95-
if (string.IsNullOrEmpty(originalContent))
96-
{
97-
Debug.LogError($"Failed to read original shader from {UNITY_TOON_SHADER_PATH}");
98-
return;
99-
}
100-
}
101-
// Replace the Properties block
102-
string newContent = ReplacePropertiesBlock(originalContent, commonProperties, string.Empty, autoCommentLine);
103-
WriteFile(UNITY_TOON_SHADER_PATH, newContent);
92+
GenerateShaderFromTemplate(UNITY_TOON_SHADER_TEMPLATE_PATH, UNITY_TOON_SHADER_PATH, commonProperties, string.Empty, autoCommentLine);
10493
}
105-
94+
10695
private static void GenerateUnityToonTessellationShader(string commonProperties, string tessellationProperties, string autoCommentLine)
10796
{
108-
// Read the original shader file to preserve the rest of the content
109-
string originalContent = ReadFile(UNITY_TOON_TESSELATION_SHADER_PATH);
110-
if (string.IsNullOrEmpty(originalContent))
111-
{
112-
originalContent = ReadFile(UNITY_TOON_TESSELATION_SHADER_PATH + ".template");
113-
if (string.IsNullOrEmpty(originalContent))
114-
{
115-
Debug.LogError($"Failed to read original tessellation shader from {UNITY_TOON_TESSELATION_SHADER_PATH}");
116-
return;
117-
}
118-
}
119-
// Replace the Properties block
120-
string newContent = ReplacePropertiesBlock(originalContent, commonProperties, tessellationProperties, autoCommentLine);
121-
WriteFile(UNITY_TOON_TESSELATION_SHADER_PATH, newContent);
97+
GenerateShaderFromTemplate(UNITY_TOON_TESSELATION_SHADER_TEMPLATE_PATH, UNITY_TOON_TESSELATION_SHADER_PATH, commonProperties, tessellationProperties, autoCommentLine);
12298
}
123-
124-
private static string ReplacePropertiesBlock(string originalContent, string commonProperties, string tessellationProperties, string autoCommentLine)
125-
{
126-
// Find the Properties block using a more robust regex that handles nested braces
127-
string propertiesPattern = @"Properties\s*\{";
128-
Match startMatch = Regex.Match(originalContent, propertiesPattern);
129-
130-
if (!startMatch.Success)
131-
{
132-
Debug.LogError("Could not find Properties block start in shader file");
133-
return originalContent;
134-
}
135-
136-
// Find the matching closing brace
137-
int startIndex = startMatch.Index;
138-
int braceCount = 0;
139-
int endIndex = startIndex;
140-
bool foundStart = false;
141-
142-
for (int i = startIndex; i < originalContent.Length; i++)
143-
{
144-
if (originalContent[i] == '{')
145-
{
146-
braceCount++;
147-
foundStart = true;
148-
}
149-
else if (originalContent[i] == '}')
150-
{
151-
braceCount--;
152-
if (foundStart && braceCount == 0)
153-
{
154-
endIndex = i;
155-
break;
156-
}
157-
}
158-
}
159-
160-
if (braceCount != 0)
161-
{
162-
Debug.LogError("Could not find matching closing brace for Properties block");
163-
return originalContent;
164-
}
165-
166-
int lineStartIndex = originalContent.LastIndexOf('\n', startIndex - 1);
167-
int baseIndentStart = lineStartIndex == -1 ? 0 : lineStartIndex + 1;
168-
string baseIndent = " ";
169-
string blockIndent = baseIndent + " ";
170-
171-
// Build new Properties block
172-
List<string> newProperties = new List<string>();
173-
Dictionary<string, int> propertyLineIndices = new Dictionary<string, int>(StringComparer.Ordinal);
174-
int propertyCount = 0;
175-
176-
newProperties.Add($"{baseIndent}Properties {{");
17799

178-
void AppendPropertyLine(string trimmedLine, bool allowOverride)
100+
private static void GenerateShaderFromTemplate(string templatePath, string outputPath, string commonProperties, string tessellationProperties, string autoCommentLine)
101+
{
102+
string templateContent = ReadFile(templatePath);
103+
if (string.IsNullOrEmpty(templateContent))
179104
{
180-
if (string.IsNullOrEmpty(trimmedLine))
181-
{
182-
newProperties.Add(string.Empty);
183-
return;
184-
}
185-
186-
string propertyLine = $"{blockIndent}{trimmedLine}";
187-
string propertyName = GetPropertyName(trimmedLine);
188-
189-
if (propertyName == null)
105+
templateContent = ReadFile(outputPath);
106+
if (string.IsNullOrEmpty(templateContent))
190107
{
191-
newProperties.Add(propertyLine);
108+
Debug.LogError($"Failed to read template or existing shader for {outputPath}");
192109
return;
193110
}
194-
195-
if (propertyLineIndices.TryGetValue(propertyName, out int existingIndex))
196-
{
197-
if (allowOverride)
198-
{
199-
newProperties[existingIndex] = propertyLine;
200-
return;
201-
}
202-
203-
string existingLine = newProperties[existingIndex];
204-
bool existingHidden = existingLine.Trim().StartsWith("[HideInInspector]", StringComparison.Ordinal);
205-
bool newHidden = trimmedLine.StartsWith("[HideInInspector]", StringComparison.Ordinal);
206-
207-
if (existingHidden && !newHidden)
208-
{
209-
newProperties[existingIndex] = propertyLine;
210-
}
211-
return;
212-
}
213-
214-
propertyLineIndices[propertyName] = newProperties.Count;
215-
propertyCount++;
216-
newProperties.Add(propertyLine);
217111
}
218112

219-
foreach (string line in commonProperties.Split('\n'))
220-
{
221-
AppendPropertyLine(line.Trim(), allowOverride: false);
222-
}
113+
templateContent = templateContent.TrimStart('\uFEFF');
223114

224-
if (!string.IsNullOrEmpty(tessellationProperties))
225-
{
226-
newProperties.Add(string.Empty);
227-
newProperties.Add($"{blockIndent}// Tessellation-specific properties");
228-
foreach (string line in tessellationProperties.Split('\n'))
229-
{
230-
AppendPropertyLine(line.Trim(), allowOverride: true);
231-
}
232-
}
115+
PropertySections sections = BuildPropertySections(commonProperties, tessellationProperties, " ");
233116

234-
newProperties.Add(baseIndent + "}");
117+
templateContent = ReplacePlaceholder(templateContent, " [COMMON_PROPERTIES]", sections.Common);
118+
templateContent = ReplacePlaceholder(templateContent, "[COMMON_PROPERTIES]", sections.Common);
235119

236-
Debug.Log($"Generated Properties block with {propertyCount} properties");
120+
templateContent = ReplacePlaceholder(templateContent, " [TESSELLATION_PROPERTIES]", sections.Tess);
121+
templateContent = ReplacePlaceholder(templateContent, "[TESSELLATION_PROPERTIES]", sections.Tess);
237122

238-
string newPropertiesText = string.Join("\n", newProperties);
123+
Debug.Log($"Generated properties with {sections.Count} entries for {outputPath}");
239124

240-
string updatedContent = originalContent.Substring(0, baseIndentStart) + newPropertiesText + originalContent.Substring(endIndex + 1);
241-
return ApplyAutoGeneratedComment(updatedContent, autoCommentLine);
125+
templateContent = ApplyAutoGeneratedComment(templateContent, autoCommentLine);
126+
WriteFile(outputPath, templateContent);
242127
}
243128

244129
private static string ExtractPropertiesBlockContent(string shaderContent)
@@ -395,5 +280,136 @@ private static string ApplyAutoGeneratedComment(string content, string commentLi
395280

396281
return result;
397282
}
283+
284+
private static string ReplacePlaceholder(string content, string placeholder, string replacement)
285+
{
286+
if (content.Contains(placeholder))
287+
{
288+
return content.Replace(placeholder, replacement ?? string.Empty);
289+
}
290+
291+
return content;
292+
}
293+
294+
private static PropertySections BuildPropertySections(string commonProperties, string tessellationProperties, string indent)
295+
{
296+
var entries = new List<PropertyEntry>();
297+
var nameToIndex = new Dictionary<string, int>(StringComparer.Ordinal);
298+
299+
void AddLines(string rawText, string source)
300+
{
301+
if (string.IsNullOrEmpty(rawText))
302+
{
303+
return;
304+
}
305+
306+
var lines = rawText.Split(new[] { '\n', '\r' }, StringSplitOptions.None);
307+
foreach (var rawLine in lines)
308+
{
309+
var trimmed = rawLine.Trim();
310+
var entry = new PropertyEntry
311+
{
312+
Line = trimmed,
313+
Source = source,
314+
Name = GetPropertyName(trimmed)
315+
};
316+
317+
if (trimmed.Length == 0)
318+
{
319+
entries.Add(entry);
320+
continue;
321+
}
322+
323+
if (!string.IsNullOrEmpty(entry.Name) && nameToIndex.TryGetValue(entry.Name, out int existingIndex))
324+
{
325+
entries[existingIndex] = entry;
326+
}
327+
else
328+
{
329+
entries.Add(entry);
330+
if (!string.IsNullOrEmpty(entry.Name))
331+
{
332+
nameToIndex[entry.Name] = entries.Count - 1;
333+
}
334+
}
335+
}
336+
}
337+
338+
AddLines(commonProperties, "common");
339+
AddLines(tessellationProperties, "tess");
340+
341+
int propertyCount = 0;
342+
var commonLines = new List<string>();
343+
var tessLines = new List<string>();
344+
345+
foreach (var entry in entries)
346+
{
347+
if (!string.IsNullOrEmpty(entry.Name))
348+
{
349+
propertyCount++;
350+
}
351+
352+
var target = entry.Source == "tess" ? tessLines : commonLines;
353+
if (string.IsNullOrEmpty(entry.Line))
354+
{
355+
target.Add(string.Empty);
356+
}
357+
else
358+
{
359+
target.Add(indent + entry.Line);
360+
}
361+
}
362+
363+
commonLines = CleanupLines(commonLines);
364+
tessLines = CleanupLines(tessLines);
365+
366+
return new PropertySections
367+
{
368+
Common = string.Join("\n", commonLines),
369+
Tess = string.Join("\n", tessLines),
370+
Count = propertyCount
371+
};
372+
}
373+
374+
private static List<string> CleanupLines(List<string> lines)
375+
{
376+
var result = new List<string>(lines);
377+
378+
while (result.Count > 0 && result[0].Length == 0)
379+
{
380+
result.RemoveAt(0);
381+
}
382+
383+
while (result.Count > 0 && result[result.Count - 1].Length == 0)
384+
{
385+
result.RemoveAt(result.Count - 1);
386+
}
387+
388+
var cleaned = new List<string>();
389+
foreach (var line in result)
390+
{
391+
if (line.Length == 0 && cleaned.Count > 0 && cleaned[cleaned.Count - 1].Length == 0)
392+
{
393+
continue;
394+
}
395+
cleaned.Add(line);
396+
}
397+
398+
return cleaned;
399+
}
400+
401+
private struct PropertySections
402+
{
403+
public string Common;
404+
public string Tess;
405+
public int Count;
406+
}
407+
408+
private class PropertyEntry
409+
{
410+
public string Line;
411+
public string Source;
412+
public string Name;
413+
}
398414
}
399415
}

com.unity.toonshader/Runtime/Integrated/Shaders/UnityToon.shader

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
1-
//Auto-generated on Wed Nov 05 08:37:28 UTC 2025
2-
//Unity Toon Shader
3-
//nobuyuki@unity3d.com
4-
//toshiyuki@unity3d.com (Intengrated)
5-
1+
//Auto-generated on Wed Nov 05 09:32:04 UTC 2025
62
Shader "Toon" {
7-
Properties {
3+
Properties
4+
{
85
// Common Properties for Unity Toon Shader
96
// This file contains all shared properties between UnityToon.shader and UnityToonTessellation.shader
107
// with comments preserved from the original files
@@ -35,7 +32,7 @@ Shader "Toon" {
3532
[Enum(OFF, 0, ON, 1, TRANSMODE, 2)] _ClippingMode("CliippingMode", int) = 0
3633

3734
// Cull and ZWrite Properties
38-
[Enum(OFF, 0, FRONT, 1, BACK, 2)] _CullMode("Cull Mode", int) = 2 //OFF/FRONT/BACK
35+
[HideInInspector] _CullMode("__cullmode", Float) = 2.0
3936
[Enum(OFF, 0, ONT, 1)] _ZWriteMode("ZWrite Mode", int) = 1 //OFF/ON
4037
[Enum(OFF, 0, ONT, 1)] _ZOverDrawMode("ZOver Draw Mode", Float) = 0 //OFF/ON
4138
_SPRDefaultUnlitColorMask("SPRDefaultUnlit Path Color Mask", int) = 15
@@ -57,7 +54,7 @@ Shader "Toon" {
5754
_BaseColor ("BaseColor", Color) = (1,1,1,1)
5855
//v.2.0.5 : Clipping/TransClipping for SSAO Problems in PostProcessing Stack.
5956
//If you want to go back the former SSAO results, comment out the below line.
60-
_Color ("Color", Color) = (1,1,1,1)
57+
_Color("Color", Color) = (1,1,1,1)
6158
//
6259
[Toggle(_)] _Is_LightColor_Base ("Is_LightColor_Base", Float ) = 1
6360

@@ -499,7 +496,8 @@ Shader "Toon" {
499496
//////////////////////////////////////////////////////////////////////////////
500497
//////////////////// End of HDRP material default values. ////////////////////
501498
//////////////////////////////////////////////////////////////////////////////
502-
}
499+
}
500+
503501

504502
HLSLINCLUDE
505503

0 commit comments

Comments
 (0)