using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace SharpGL.Shaders
{
public class ShaderProgram
{
private readonly Shader vertexShader = new Shader();
private readonly Shader fragmentShader = new Shader();
///
/// Creates the shader program.
///
/// The gl.
/// The vertex shader source.
/// The fragment shader source.
/// The attribute locations. This is an optional array of
/// uint attribute locations to their names.
///
public void Create(OpenGL gl, string vertexShaderSource, string fragmentShaderSource,
Dictionary attributeLocations)
{
// Create the shaders.
vertexShader.Create(gl, OpenGL.GL_VERTEX_SHADER, vertexShaderSource);
fragmentShader.Create(gl, OpenGL.GL_FRAGMENT_SHADER, fragmentShaderSource);
// Create the program, attach the shaders.
shaderProgramObject = gl.CreateProgram();
gl.AttachShader(shaderProgramObject, vertexShader.ShaderObject);
gl.AttachShader(shaderProgramObject, fragmentShader.ShaderObject);
// Before we link, bind any vertex attribute locations.
if (attributeLocations != null)
{
foreach (var vertexAttributeLocation in attributeLocations)
gl.BindAttribLocation(shaderProgramObject, vertexAttributeLocation.Key, vertexAttributeLocation.Value);
}
// Now we can link the program.
gl.LinkProgram(shaderProgramObject);
// Now that we've compiled and linked the shader, check it's link status. If it's not linked properly, we're
// going to throw an exception.
if (GetLinkStatus(gl) == false)
{
throw new ShaderCompilationException(string.Format("Failed to link shader program with ID {0}.", shaderProgramObject), GetInfoLog(gl));
}
}
public void Delete(OpenGL gl)
{
gl.DetachShader(shaderProgramObject, vertexShader.ShaderObject);
gl.DetachShader(shaderProgramObject, fragmentShader.ShaderObject);
vertexShader.Delete(gl);
fragmentShader.Delete(gl);
gl.DeleteProgram(shaderProgramObject);
shaderProgramObject = 0;
}
public int GetAttributeLocation(OpenGL gl, string attributeName)
{
return gl.GetAttribLocation(shaderProgramObject, attributeName);
}
public void BindAttributeLocation(OpenGL gl, uint location, string attribute)
{
gl.BindAttribLocation(shaderProgramObject, location, attribute);
}
public void Bind(OpenGL gl)
{
gl.UseProgram(shaderProgramObject);
}
public void Unbind(OpenGL gl)
{
gl.UseProgram(0);
}
public bool GetLinkStatus(OpenGL gl)
{
int[] parameters = new int[] { 0 };
gl.GetProgram(shaderProgramObject, OpenGL.GL_LINK_STATUS, parameters);
return parameters[0] == OpenGL.GL_TRUE;
}
public string GetInfoLog(OpenGL gl)
{
// Get the info log length.
int[] infoLength = new int[] { 0 };
gl.GetProgram(shaderProgramObject, OpenGL.GL_INFO_LOG_LENGTH, infoLength);
int bufSize = infoLength[0];
// Get the compile info.
StringBuilder il = new StringBuilder(bufSize);
gl.GetProgramInfoLog(shaderProgramObject, bufSize, IntPtr.Zero, il);
return il.ToString();
}
public void AssertValid(OpenGL gl)
{
if (vertexShader.GetCompileStatus(gl) == false)
throw new Exception(vertexShader.GetInfoLog(gl));
if (fragmentShader.GetCompileStatus(gl) == false)
throw new Exception(fragmentShader.GetInfoLog(gl));
if (GetLinkStatus(gl) == false)
throw new Exception(GetInfoLog(gl));
}
public void SetUniform1(OpenGL gl, string uniformName, float v1)
{
gl.Uniform1(GetUniformLocation(gl, uniformName), v1);
}
public void SetUniform3(OpenGL gl, string uniformName, float v1, float v2, float v3)
{
gl.Uniform3(GetUniformLocation(gl, uniformName), v1, v2, v3);
}
public void SetUniformMatrix3(OpenGL gl, string uniformName, float[] m)
{
gl.UniformMatrix3(GetUniformLocation(gl, uniformName), 1, false, m);
}
public void SetUniformMatrix4(OpenGL gl, string uniformName, float[] m)
{
gl.UniformMatrix4(GetUniformLocation(gl, uniformName), 1, false, m);
}
public int GetUniformLocation(OpenGL gl, string uniformName)
{
// If we don't have the uniform name in the dictionary, get it's
// location and add it.
if (uniformNamesToLocations.ContainsKey(uniformName) == false)
{
uniformNamesToLocations[uniformName] = gl.GetUniformLocation(shaderProgramObject, uniformName);
}
// Return the uniform location.
return uniformNamesToLocations[uniformName];
}
///
/// Gets the shader program object.
///
///
/// The shader program object.
///
public uint ShaderProgramObject
{
get { return shaderProgramObject; }
}
private uint shaderProgramObject;
///
/// A mapping of uniform names to locations. This allows us to very easily specify
/// uniform data by name, quickly looking up the location first if needed.
///
private readonly Dictionary uniformNamesToLocations = new Dictionary();
}
}