Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,4 +94,4 @@ git config --global core.symlinks true



*Auto-generated on Wed Oct 15 14:06:35 UTC 2025*
*Auto-generated on Wed Oct 22 05:43:29 UTC 2025*
1,534 changes: 1,534 additions & 0 deletions UnityToon_Generated_Test.shader

Large diffs are not rendered by default.

79 changes: 79 additions & 0 deletions com.unity.toonshader/Editor/README_ShaderGenerator.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Unity Toon Shader Generator

This tool helps maintain consistency between `UnityToon.shader` and `UnityToonTessellation.shader` by using a single source of truth for shared properties.

## Files

- **CommonPropertiesWithComments.txt**: Contains all shared properties between the two shader files with original comments preserved
- **TessellationProperties.txt**: Contains tessellation-specific properties only present in the tessellation shader
- **ShaderGenerator.cs**: Unity Editor script that generates the shader files from the property files

## How to Use

1. **Open the Shader Generator Window**:
- In Unity, go to `Tools > Unity Toon Shader > Generate Shader Files`
- This opens the Shader Generator window

2. **Edit Properties**:
- Click "Open Common Properties File" to edit shared properties (includes all original comments)
- Click "Open Tessellation Properties File" to edit tessellation-specific properties
- Make your changes to the property files

3. **Generate Shader Files**:
- Click "Generate Shader Files" button
- The tool will automatically:
- Create backups of the original shader files (`.backup` extension)
- Replace the Properties blocks in both shader files
- Preserve all other shader content (HLSLINCLUDE, SubShaders, etc.)

## Property File Format

The property files use the same format as ShaderLab Properties blocks, but without the `Properties { }` wrapper:

```
// Comments are supported
[HideInInspector] _simpleUI ("SimpleUI", Int ) = 0
[Enum(OFF, 0, ON, 1)] _isUnityToonshader("Material is touched by Unity Toon Shader", Int) = 1
_BaseColor ("BaseColor", Color) = (1,1,1,1)
```

## Benefits

- **Single Source of Truth**: All shared properties are defined in one place
- **Consistency**: Ensures both shader files have identical shared properties
- **Maintainability**: Easy to add, remove, or modify properties across both shaders
- **Backup Safety**: Original files are automatically backed up before generation
- **Preservation**: All non-Properties content and comments are preserved during generation
- **Comment Preservation**: All original comments from the Properties blocks are maintained

## Manual Generation (Alternative)

If you prefer to generate shaders manually or from command line, you can use the Python script:

```bash
cd /workspace
python3 generate_shaders.py
```

This will generate both shader files from the property files.

## Troubleshooting

- **Properties block not found**: Ensure the shader files have a valid `Properties { }` block
- **File not found errors**: Check that the property files exist in the correct paths
- **Generation fails**: Check the Unity Console for detailed error messages
- **Restore from backup**: If generation fails, you can restore from the `.backup` files

## File Structure

```
com.unity.toonshader/
├── Editor/
│ ├── ShaderGenerator.cs # Unity Editor script
│ └── README_ShaderGenerator.md # This file
└── Runtime/Integrated/Shaders/
├── CommonPropertiesWithComments.txt # Shared properties with comments
├── TessellationProperties.txt # Tessellation-specific properties
├── UnityToon.shader # Generated shader (regular)
└── UnityToonTessellation.shader # Generated shader (tessellation)
```
254 changes: 254 additions & 0 deletions com.unity.toonshader/Editor/ShaderGenerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
using System;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using UnityEditor;
using UnityEngine;

namespace UnityEditor.Rendering.Toon
{
/// <summary>
/// Shader generator for Unity Toon Shader that creates shader files from common properties.
/// This helps maintain consistency between UnityToon.shader and UnityToonTessellation.shader
/// by using a single source of truth for shared properties.
/// </summary>
public class ShaderGenerator : EditorWindow
{
private const string COMMON_PROPERTIES_PATH = "Assets/com.unity.toonshader/Runtime/Integrated/Shaders/CommonPropertiesWithComments.txt";
private const string TESSELATION_PROPERTIES_PATH = "Assets/com.unity.toonshader/Runtime/Integrated/Shaders/TessellationProperties.txt";
private const string UNITY_TOON_SHADER_PATH = "Assets/com.unity.toonshader/Runtime/Integrated/Shaders/UnityToon.shader";
private const string UNITY_TOON_TESSELATION_SHADER_PATH = "Assets/com.unity.toonshader/Runtime/Integrated/Shaders/UnityToonTessellation.shader";

[MenuItem("Tools/Unity Toon Shader/Generate Shader Files")]
public static void ShowWindow()
{
GetWindow<ShaderGenerator>("Shader Generator");
}

private void OnGUI()
{
GUILayout.Label("Unity Toon Shader Generator", EditorStyles.boldLabel);
GUILayout.Space(10);

GUILayout.Label("This tool generates UnityToon.shader and UnityToonTessellation.shader from common property files.", EditorStyles.helpBox);
GUILayout.Space(10);

if (GUILayout.Button("Generate Shader Files", GUILayout.Height(30)))
{
GenerateShaderFiles();
}

GUILayout.Space(10);

if (GUILayout.Button("Open Common Properties File"))
{
var asset = AssetDatabase.LoadAssetAtPath<TextAsset>(COMMON_PROPERTIES_PATH);
if (asset != null)
{
Selection.activeObject = asset;
EditorGUIUtility.PingObject(asset);
}
}

if (GUILayout.Button("Open Tessellation Properties File"))
{
var asset = AssetDatabase.LoadAssetAtPath<TextAsset>(TESSELATION_PROPERTIES_PATH);
if (asset != null)
{
Selection.activeObject = asset;
EditorGUIUtility.PingObject(asset);
}
}
}

private void GenerateShaderFiles()
{
try
{
// Read common properties
string commonProperties = ReadFile(COMMON_PROPERTIES_PATH);
if (string.IsNullOrEmpty(commonProperties))
{
Debug.LogError($"Failed to read common properties from {COMMON_PROPERTIES_PATH}");
return;
}

// Read tessellation properties
string tessellationProperties = ReadFile(TESSELATION_PROPERTIES_PATH);
if (string.IsNullOrEmpty(tessellationProperties))
{
Debug.LogError($"Failed to read tessellation properties from {TESSELATION_PROPERTIES_PATH}");
return;
}

// Generate UnityToon.shader
GenerateUnityToonShader(commonProperties);

// Generate UnityToonTessellation.shader
GenerateUnityToonTessellationShader(commonProperties, tessellationProperties);

AssetDatabase.Refresh();
Debug.Log("Shader files generated successfully!");
}
catch (Exception e)
{
Debug.LogError($"Error generating shader files: {e.Message}");
}
}

private void GenerateUnityToonShader(string commonProperties)
{
// Read the original shader file to preserve the rest of the content
string originalContent = ReadFile(UNITY_TOON_SHADER_PATH);
if (string.IsNullOrEmpty(originalContent))
{
Debug.LogError($"Failed to read original shader from {UNITY_TOON_SHADER_PATH}");
return;
}

// Create backup
CreateBackup(UNITY_TOON_SHADER_PATH);

// Replace the Properties block
string newContent = ReplacePropertiesBlock(originalContent, commonProperties, "");
WriteFile(UNITY_TOON_SHADER_PATH, newContent);
}

private void GenerateUnityToonTessellationShader(string commonProperties, string tessellationProperties)
{
// Read the original shader file to preserve the rest of the content
string originalContent = ReadFile(UNITY_TOON_TESSELATION_SHADER_PATH);
if (string.IsNullOrEmpty(originalContent))
{
Debug.LogError($"Failed to read original tessellation shader from {UNITY_TOON_TESSELATION_SHADER_PATH}");
return;
}

// Create backup
CreateBackup(UNITY_TOON_TESSELATION_SHADER_PATH);

// Replace the Properties block
string newContent = ReplacePropertiesBlock(originalContent, commonProperties, tessellationProperties);
WriteFile(UNITY_TOON_TESSELATION_SHADER_PATH, newContent);
}

private string ReplacePropertiesBlock(string originalContent, string commonProperties, string tessellationProperties)
{
// Find the Properties block using a more robust regex that handles nested braces
string propertiesPattern = @"Properties\s*\{";
Match startMatch = Regex.Match(originalContent, propertiesPattern);

if (!startMatch.Success)
{
Debug.LogError("Could not find Properties block start in shader file");
return originalContent;
}

// Find the matching closing brace
int startIndex = startMatch.Index;
int braceCount = 0;
int endIndex = startIndex;
bool foundStart = false;

for (int i = startIndex; i < originalContent.Length; i++)
{
if (originalContent[i] == '{')
{
braceCount++;
foundStart = true;
}
else if (originalContent[i] == '}')
{
braceCount--;
if (foundStart && braceCount == 0)
{
endIndex = i;
break;
}
}
}

if (braceCount != 0)
{
Debug.LogError("Could not find matching closing brace for Properties block");
return originalContent;
}

// Build new Properties block
StringBuilder newProperties = new StringBuilder();
newProperties.AppendLine(" Properties {");

// Add common properties
string[] commonLines = commonProperties.Split('\n');
int propertyCount = 0;
foreach (string line in commonLines)
{
if (!string.IsNullOrWhiteSpace(line))
{
newProperties.AppendLine($" {line.Trim()}");
if (!line.TrimStart().StartsWith("//"))
{
propertyCount++;
}
}
}

// Add tessellation properties if provided
if (!string.IsNullOrEmpty(tessellationProperties))
{
newProperties.AppendLine();
newProperties.AppendLine(" // Tessellation-specific properties");
string[] tessellationLines = tessellationProperties.Split('\n');
foreach (string line in tessellationLines)
{
if (!string.IsNullOrWhiteSpace(line))
{
newProperties.AppendLine($" {line.Trim()}");
if (!line.TrimStart().StartsWith("//"))
{
propertyCount++;
}
}
}
}

newProperties.AppendLine(" }");

Debug.Log($"Generated Properties block with {propertyCount} properties");

// Replace the Properties block in the original content
return originalContent.Substring(0, startIndex) + newProperties.ToString() + originalContent.Substring(endIndex + 1);
}

private void CreateBackup(string filePath)
{
string backupPath = filePath + ".backup";
if (File.Exists(filePath))
{
File.Copy(filePath, backupPath, true);
Debug.Log($"Created backup at {backupPath}");
}
}

private string ReadFile(string path)
{
if (File.Exists(path))
{
return File.ReadAllText(path);
}
return null;
}

private void WriteFile(string path, string content)
{
// Ensure directory exists
string directory = Path.GetDirectoryName(path);
if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}

File.WriteAllText(path, content);
}
}
}
Loading