diff --git a/GlitchyEngine/src/Renderer/Effect.bf b/GlitchyEngine/src/Renderer/Effect.bf index 25e8ff9..62c08a2 100644 --- a/GlitchyEngine/src/Renderer/Effect.bf +++ b/GlitchyEngine/src/Renderer/Effect.bf @@ -1,10 +1,11 @@ using System; +using System.IO; namespace GlitchyEngine.Renderer { public class Effect : RefCounted { - protected GraphicsContext _context; // Todo:??? + protected GraphicsContext _context ~ _?.ReleaseRef(); internal VertexShader _vs ~ _?.ReleaseRef(); internal PixelShader _ps ~ _?.ReleaseRef(); @@ -41,18 +42,24 @@ namespace GlitchyEngine.Renderer [Obsolete("Will be removed in the future", false)] public this() { - } public this(GraphicsContext context, String filename, String vsEntry, String psEntry) { - let vs = Shader.FromFile!(context, filename, vsEntry); - VertexShader = vs; - vs.ReleaseRef(); + _context = context..AddRef(); + CompileFromFile(filename, vsEntry, psEntry); + } - let ps = Shader.FromFile!(context, filename, psEntry); - PixelShader = ps; - ps.ReleaseRef(); + public this(GraphicsContext context, String filename) + { + _context = context..AddRef(); + + String fileContent = scope String(); + String vsName = scope String(); + String psName = scope String(); + + ProcessFile(filename, fileContent, vsName, psName); + Compile(fileContent, vsName, psName); } public this(String vsPath, String vsEntry, String psPath, String psEntry) @@ -60,6 +67,93 @@ namespace GlitchyEngine.Renderer Compile(vsPath, vsEntry, psPath, psEntry); } + private void CompileFromFile(String filename, String vsEntry, String psEntry) + { + let vs = Shader.FromFile!(_context, filename, vsEntry); + VertexShader = vs; + vs.ReleaseRef(); + let ps = Shader.FromFile!(_context, filename, psEntry); + PixelShader = ps; + ps.ReleaseRef(); + } + + private void Compile(String fileContent, String vsEntry, String psEntry) + { + // TODO: vsEntry and psEntry could be empty (which is a valid case.) + let vs = new VertexShader(_context, fileContent, vsEntry); + VertexShader = vs; + vs.ReleaseRef(); + let ps = new PixelShader(_context, fileContent, psEntry); + PixelShader = ps; + ps.ReleaseRef(); + } + + const String effectKeyword = "#effect"; + + /** + * Loads the effect file and extracts the names of the vertex- and pixel-shader. + * @param filename The path of the effect file. + * @param fileContent The preprocessed effect file. + * @param vsName The string that will receive the vertex shader entry point. + * @param psName The string that will receive the pixel shader entry point. + */ + private static void ProcessFile(String filename, String fileContent, String vsName, String psName) + { + File.ReadAllText(filename, fileContent, true); + // append line ending just in case the file doesn't end with one. + fileContent.Append('\n'); + + int effectIndex = fileContent.IndexOf(effectKeyword, true); + + Log.EngineLogger.Assert(effectIndex >= 0, "Could not find #effect preprocessor directive."); + + int lineEndIndex = fileContent.IndexOf('\n', effectIndex + effectKeyword.Length); + + // String containing the #effect directive + StringView effectDirective = fileContent.Substring(effectIndex, lineEndIndex - effectIndex); + + int paramStartIndex = effectDirective.IndexOf('['); + + Log.EngineLogger.Assert(paramStartIndex >= 0, "Expected '[' after \"#effect\""); + + StringView effectToBracket = effectDirective.Substring(effectKeyword.Length, paramStartIndex - effectKeyword.Length); + + // Make sure there is only whitespace between "#effect" and "[" + Log.EngineLogger.Assert(effectToBracket.IsWhiteSpace, "Expected '[' after \"#effect\""); + + int paramEndIndex = effectDirective.IndexOf(']'); + + Log.EngineLogger.Assert(paramEndIndex >= 0, "Expected ']'"); + + StringView parameters = effectDirective.Substring(paramStartIndex + 1, paramEndIndex - paramStartIndex - 1); + + for(StringView parameter in parameters.Split(',')) + { + int indexOfEquals = parameter.IndexOf("="); + + Log.EngineLogger.Assert(indexOfEquals >= 0, "Expected '='"); + + StringView paramName = parameter.Substring(0, indexOfEquals); + paramName.Trim(); + + StringView paramValue = parameter.Substring(indexOfEquals + 1); + paramValue.Trim(); + + switch(paramName) + { + case "VS", "VertexShader": + vsName.Append(paramValue); + case "PS", "PixelShader": + psName.Append(paramValue); + default: + Log.EngineLogger.Assert(false, scope $"Unknown parameter name \"{paramName}\"."); + } + } + + // remove preprocessor directive from string so that the compiler wont try process it + fileContent.Remove(effectIndex, lineEndIndex - effectIndex); + } + protected extern void Compile(String vsPath, String vsEntry, String psPath, String psEntry); } } diff --git a/Sandbox/content/Shaders/basicShader.hlsl b/Sandbox/content/Shaders/basicShader.hlsl index 3d9d8df..72e7f0d 100644 --- a/Sandbox/content/Shaders/basicShader.hlsl +++ b/Sandbox/content/Shaders/basicShader.hlsl @@ -43,4 +43,6 @@ PS_IN VS(VS_IN input) float4 PS(PS_IN input) : SV_TARGET { return input.Color * BaseColor; -} \ No newline at end of file +} + +#effect[VS=VS,PS=PS] diff --git a/Sandbox/content/Shaders/textureShader.hlsl b/Sandbox/content/Shaders/textureShader.hlsl index b2feb65..d471cba 100644 --- a/Sandbox/content/Shaders/textureShader.hlsl +++ b/Sandbox/content/Shaders/textureShader.hlsl @@ -50,4 +50,6 @@ PS_IN VS(VS_IN input) float4 PS(PS_IN input) : SV_TARGET { return input.Color * ColorTexture.Sample(TextureSampler, input.TexCoord); -} \ No newline at end of file +} + +#effect[VS=VS,PS=PS] diff --git a/Sandbox/src/SandboxApp.bf b/Sandbox/src/SandboxApp.bf index b3ecf89..72e1258 100644 --- a/Sandbox/src/SandboxApp.bf +++ b/Sandbox/src/SandboxApp.bf @@ -86,9 +86,9 @@ namespace Sandbox { _context = Application.Get().Window.Context..AddRef(); - _effect = new Effect(_context, "content\\Shaders\\basicShader.hlsl", "VS", "PS"); + _effect = new Effect(_context, "content\\Shaders\\basicShader.hlsl"); - _textureEffect = new Effect(_context, "content\\Shaders\\textureShader.hlsl", "VS", "PS"); + _textureEffect = new Effect(_context, "content\\Shaders\\textureShader.hlsl"); // Create Input Layout