Skip to content

Instantly share code, notes, and snippets.

@deccer
Created October 29, 2022 18:41
Show Gist options
  • Save deccer/b7561edce46cf8c98ac7dde4ffd36d82 to your computer and use it in GitHub Desktop.
Save deccer/b7561edce46cf8c98ac7dde4ffd36d82 to your computer and use it in GitHub Desktop.
public interface IShaderParser
{
string ParseShader(string? shaderSource);
}
internal sealed class ShaderParser : IShaderParser
{
private static readonly Regex _includeRegex =
new Regex("^[ ]*#[ ]*include[ ]+[\\\"<](?'include'.*)[\\\">].*", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private readonly IShaderIncludeHandler _includeHandler;
public ShaderParser(IShaderIncludeHandler includeHandler)
{
_includeHandler = includeHandler;
}
public string ParseShader(string? shaderSource)
{
var newShaderSourceLines = new StringBuilder();
var shaderSourceLines = shaderSource.Split("\n");
for (var i = 0; i < shaderSourceLines.Length; i++)
{
var shaderSourceLine = shaderSourceLines[i];
var match = _includeRegex.Match(shaderSourceLine);
if (match.Success)
{
var includeName = match.Groups["include"].Value;
var replaceWithInclude = _includeHandler.HandleInclude(shaderSource, includeName);
if (!string.IsNullOrEmpty(replaceWithInclude))
{
newShaderSourceLines.AppendLine(replaceWithInclude);
}
}
else
{
newShaderSourceLines.AppendLine(shaderSourceLine);
}
}
return newShaderSourceLines.ToString();
}
}
public interface IShaderIncludeHandler
{
string? HandleInclude(string? shaderSource, string? include);
}
public class FileShaderIncludeHandler : IShaderIncludeHandler
{
private readonly string _baseDirectory;
public FileShaderIncludeHandler()
{
_baseDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Shaders");
}
public string? HandleInclude(string? shaderSource, string? include)
{
if (string.IsNullOrEmpty(include))
{
return null;
}
var includeFilePath = Path.Combine(_baseDirectory, include);
return File.Exists(includeFilePath)
? File.ReadAllText(includeFilePath)
: null;
}
}
public class VirtualFileShaderIncludeHandler : IShaderIncludeHandler
{
public string? HandleInclude(string? shaderSource, string? include)
{
if (include == null)
{
return null;
}
if (include.EndsWith("virtual.glsl"))
{
var includeTypeName = string.Join(".", include.Split(".").Reverse().Skip(2).Reverse());
var includeType = Type.GetType(includeTypeName);
if (includeType == null)
{
return null;
}
return GenerateGlslFromType(includeType);
}
return null;
}
private string? GenerateGlslFromType(Type includeType)
{
var glsl = new StringBuilder();
glsl.AppendLine($"struct {includeType.Name}");
glsl.AppendLine("{");
var members = includeType
.GetFields(BindingFlags.Public)
.Select(field => new { Name = field.Name, Type = field.FieldType })
.Concat(includeType.GetProperties(BindingFlags.Public | BindingFlags.Instance).Select(property => new { Name = property.Name, Type = property.PropertyType }));
foreach (var member in members)
{
glsl.AppendLine($" {ToGlslType(member.Type)} {member.Name};");
}
glsl.AppendLine("};");
return glsl.ToString();
}
private string ToGlslType(Type type)
{
if (type == typeof(int))
{
return "int";
}
if (type == typeof(uint))
{
return "uint";
}
if (type == typeof(float))
{
return "float";
}
if (type == typeof(Point))
{
return "ivec2";
}
if (type == typeof(Int3))
{
return "ivec3";
}
if (type == typeof(Int4))
{
return "ivec4";
}
if (type == typeof(Vector2))
{
return "vec2";
}
if (type == typeof(Vector3))
{
return "vec3";
}
if (type == typeof(Vector4))
{
return "vec4";
}
return "INVALID";
}
}
public class CompositeShaderIncludeHandler : IShaderIncludeHandler
{
private readonly IShaderIncludeHandler[] _shaderIncludeHandlers;
public CompositeShaderIncludeHandler(params IShaderIncludeHandler[] shaderIncludeHandlers)
{
_shaderIncludeHandlers = shaderIncludeHandlers;
}
public string? HandleInclude(string? shaderSource, string? include)
{
return _shaderIncludeHandlers.Aggregate(string.Empty, (current, handler) => current + handler.HandleInclude(shaderSource, include));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment