using OpenTK.Graphics.OpenGL4; using OpenTK.Mathematics; namespace VoxelEngine.Rendering; public sealed class Shader : IDisposable { private readonly int _handle; private readonly Dictionary _uniforms = new(); public Shader(string vertexPath, string fragmentPath) { int vertex = CompileFromFile(ShaderType.VertexShader, vertexPath); int fragment = CompileFromFile(ShaderType.FragmentShader, fragmentPath); this._handle = GL.CreateProgram(); GL.AttachShader(this._handle, vertex); GL.AttachShader(this._handle, fragment); GL.LinkProgram(this._handle); GL.GetProgram(this._handle, GetProgramParameterName.LinkStatus, out int linked); if (linked == 0) throw new InvalidOperationException($"Program link failed: {GL.GetProgramInfoLog(this._handle)}"); GL.DetachShader(this._handle, vertex); GL.DetachShader(this._handle, fragment); GL.DeleteShader(vertex); GL.DeleteShader(fragment); } public void Use() => GL.UseProgram(this._handle); public void SetMatrix4(string name, Matrix4 value) => GL.UniformMatrix4(this.Location(name), true, ref value); public void SetInt(string name, int value) => GL.Uniform1(this.Location(name), value); public void SetFloat(string name, float value) => GL.Uniform1(this.Location(name), value); public void SetVector3(string name, Vector3 value) => GL.Uniform3(this.Location(name), value.X, value.Y, value.Z); public void SetVector2(string name, Vector2 value) => GL.Uniform2(this.Location(name), value.X, value.Y); private int Location(string name) { if (this._uniforms.TryGetValue(name, out int location)) return location; location = GL.GetUniformLocation(this._handle, name); this._uniforms[name] = location; return location; } private static int CompileFromFile(ShaderType type, string path) { string source = File.ReadAllText(path); int shader = GL.CreateShader(type); GL.ShaderSource(shader, source); GL.CompileShader(shader); GL.GetShader(shader, ShaderParameter.CompileStatus, out int compiled); if (compiled == 0) throw new InvalidOperationException($"{type} compile failed ({path}): {GL.GetShaderInfoLog(shader)}"); return shader; } public void Dispose() => GL.DeleteProgram(this._handle); }