299 lines
8.3 KiB
C#
299 lines
8.3 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using ANX.Framework.Graphics;
|
|
using ANX.Framework.NonXNA;
|
|
using ANX.RenderSystem.GL3.Helpers;
|
|
using OpenTK.Graphics.OpenGL;
|
|
|
|
// This file is part of the ANX.Framework created by the
|
|
// "ANX.Framework developer group" and released under the Ms-PL license.
|
|
// For details see: http://anxframework.codeplex.com/license
|
|
|
|
namespace ANX.RenderSystem.GL3
|
|
{
|
|
/// <summary>
|
|
/// Native OpenGL Effect implementation.
|
|
/// http://wiki.delphigl.com/index.php/Tutorial_glsl
|
|
/// </summary>
|
|
public class EffectGL3 : INativeEffect
|
|
{
|
|
#region Private
|
|
private Effect managedEffect;
|
|
private ShaderData shaderData;
|
|
private List<EffectParameter> parameters;
|
|
private List<EffectTechnique> techniques;
|
|
internal bool IsDisposed;
|
|
|
|
internal EffectTechniqueGL3 CurrentTechnique
|
|
{
|
|
get
|
|
{
|
|
if (managedEffect.CurrentTechnique == null)
|
|
return null;
|
|
|
|
return managedEffect.CurrentTechnique.NativeTechnique as EffectTechniqueGL3;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Public
|
|
#region Techniques
|
|
public IEnumerable<EffectTechnique> Techniques
|
|
{
|
|
get
|
|
{
|
|
if (techniques.Count == 0)
|
|
{
|
|
Compile();
|
|
}
|
|
|
|
return techniques;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Parameters
|
|
public IEnumerable<EffectParameter> Parameters
|
|
{
|
|
get
|
|
{
|
|
if (techniques.Count == 0)
|
|
{
|
|
Compile();
|
|
}
|
|
|
|
return parameters;
|
|
}
|
|
}
|
|
#endregion
|
|
#endregion
|
|
|
|
#region Constructor
|
|
/// <summary>
|
|
/// Private helper constructor for the basic initialization.
|
|
/// </summary>
|
|
private EffectGL3(Effect setManagedEffect)
|
|
{
|
|
GraphicsResourceManager.UpdateResource(this, true);
|
|
|
|
parameters = new List<EffectParameter>();
|
|
techniques = new List<EffectTechnique>();
|
|
managedEffect = setManagedEffect;
|
|
}
|
|
|
|
~EffectGL3()
|
|
{
|
|
GraphicsResourceManager.UpdateResource(this, false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create a new effect instance of separate streams.
|
|
/// </summary>
|
|
/// <param name="vertexShaderByteCode">The vertex shader code.</param>
|
|
/// <param name="pixelShaderByteCode">The fragment shader code.</param>
|
|
public EffectGL3(Effect setManagedEffect, Stream vertexShaderByteCode, Stream pixelShaderByteCode)
|
|
: this(setManagedEffect)
|
|
{
|
|
// TODO: this is probably not right!
|
|
throw new NotImplementedException("TODO: implement effect constructor with vertex and fragment streams, check HOWTO...");
|
|
//CreateShader(ShaderHelper.LoadShaderCode(vertexShaderByteCode),
|
|
// ShaderHelper.LoadShaderCode(pixelShaderByteCode));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create a new effect instance of one streams.
|
|
/// </summary>
|
|
/// <param name="byteCode">The byte code of the shader.</param>
|
|
public EffectGL3(Effect setManagedEffect, Stream byteCode)
|
|
: this(setManagedEffect)
|
|
{
|
|
string source = ShaderHelper.LoadShaderCode(byteCode);
|
|
shaderData = ShaderHelper.ParseShaderCode(source);
|
|
}
|
|
#endregion
|
|
|
|
#region RecreateData
|
|
internal void RecreateData()
|
|
{
|
|
Compile();
|
|
}
|
|
#endregion
|
|
|
|
#region Compile
|
|
private void Compile()
|
|
{
|
|
parameters.Clear();
|
|
techniques.Clear();
|
|
Dictionary<string, int> vertexShaders = new Dictionary<string, int>();
|
|
Dictionary<string, int> fragmentShaders = new Dictionary<string, int>();
|
|
List<string> parameterNames = new List<string>();
|
|
|
|
#region Compile vertex shaders
|
|
foreach (string vertexName in shaderData.VertexShaderCodes.Keys)
|
|
{
|
|
string vertexSource = shaderData.VertexGlobalCode + shaderData.VertexShaderCodes[vertexName];
|
|
|
|
int vertexShader = GL.CreateShader(ShaderType.VertexShader);
|
|
string vertexError = CompileShader(vertexShader, vertexSource);
|
|
if (String.IsNullOrEmpty(vertexError) == false)
|
|
throw new InvalidDataException("Failed to compile the vertex shader '" + vertexName + "' cause of: " +
|
|
vertexError);
|
|
|
|
vertexShaders.Add(vertexName, vertexShader);
|
|
}
|
|
#endregion
|
|
|
|
#region Compile fragment shaders
|
|
foreach (string fragmentName in shaderData.FragmentShaderCodes.Keys)
|
|
{
|
|
string fragmentSource = shaderData.FragmentGlobalCode + shaderData.FragmentShaderCodes[fragmentName];
|
|
|
|
int fragmentShader = GL.CreateShader(ShaderType.FragmentShader);
|
|
string fragmentError = CompileShader(fragmentShader, fragmentSource);
|
|
if (String.IsNullOrEmpty(fragmentError) == false)
|
|
throw new InvalidDataException("Failed to compile the fragment shader '" + fragmentName + "' cause of: " +
|
|
fragmentError);
|
|
|
|
fragmentShaders.Add(fragmentName, fragmentShader);
|
|
}
|
|
#endregion
|
|
|
|
#region Compile programs
|
|
foreach (string programName in shaderData.Techniques.Keys)
|
|
{
|
|
string vertexName = shaderData.Techniques[programName].Key;
|
|
string fragmentName = shaderData.Techniques[programName].Value;
|
|
|
|
int programHandle = GL.CreateProgram();
|
|
ErrorHelper.Check("CreateProgram");
|
|
GL.AttachShader(programHandle, vertexShaders[vertexName]);
|
|
ErrorHelper.Check("AttachShader vertexShader");
|
|
GL.AttachShader(programHandle, fragmentShaders[fragmentName]);
|
|
ErrorHelper.Check("AttachShader fragmentShader");
|
|
GL.LinkProgram(programHandle);
|
|
|
|
int result;
|
|
GL.GetProgram(programHandle, ProgramParameter.LinkStatus, out result);
|
|
if (result == 0)
|
|
{
|
|
string programError;
|
|
GL.GetProgramInfoLog(programHandle, out programError);
|
|
throw new InvalidDataException("Failed to link the shader program '" +
|
|
programName + "' because of: " + programError);
|
|
}
|
|
|
|
EffectTechniqueGL3 technique = new EffectTechniqueGL3(managedEffect, programName, programHandle);
|
|
techniques.Add(new EffectTechnique(managedEffect, technique));
|
|
AddParametersFrom(programHandle, parameterNames, technique);
|
|
}
|
|
#endregion
|
|
}
|
|
#endregion
|
|
|
|
#region CompileShader
|
|
private string CompileShader(int shader, string source)
|
|
{
|
|
GL.ShaderSource(shader, source);
|
|
GL.CompileShader(shader);
|
|
|
|
int result;
|
|
GL.GetShader(shader, ShaderParameter.CompileStatus, out result);
|
|
if (result == 0)
|
|
{
|
|
string error = "";
|
|
GL.GetShaderInfoLog(shader, out error);
|
|
|
|
GL.DeleteShader(shader);
|
|
|
|
return error;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
#endregion
|
|
|
|
#region AddParametersFrom
|
|
private void AddParametersFrom(int programHandle, List<string> parameterNames,
|
|
EffectTechniqueGL3 technique)
|
|
{
|
|
int uniformCount;
|
|
GL.GetProgram(programHandle, ProgramParameter.ActiveUniforms,
|
|
out uniformCount);
|
|
ErrorHelper.Check("GetProgram ActiveUniforms");
|
|
|
|
for (int index = 0; index < uniformCount; index++)
|
|
{
|
|
string name = GL.GetActiveUniformName(programHandle, index);
|
|
ErrorHelper.Check("GetActiveUniformName name=" + name);
|
|
|
|
if (parameterNames.Contains(name) == false)
|
|
{
|
|
parameterNames.Add(name);
|
|
int uniformIndex = GL.GetUniformLocation(programHandle, name);
|
|
ErrorHelper.Check("GetUniformLocation name=" + name +
|
|
" uniformIndex=" + uniformIndex);
|
|
parameters.Add(new EffectParameter()
|
|
{
|
|
NativeParameter =
|
|
new EffectParameterGL3(technique, name, uniformIndex),
|
|
});
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Apply
|
|
public void Apply(GraphicsDevice graphicsDevice)
|
|
{
|
|
GL.UseProgram(CurrentTechnique.programHandle);
|
|
GraphicsDeviceWindowsGL3.activeEffect = this;
|
|
ErrorHelper.Check("UseProgram");
|
|
}
|
|
#endregion
|
|
|
|
#region Dispose
|
|
/// <summary>
|
|
/// Dispose the native shader data.
|
|
/// </summary>
|
|
public void Dispose()
|
|
{
|
|
if (IsDisposed == false)
|
|
{
|
|
IsDisposed = true;
|
|
DisposeResource();
|
|
}
|
|
}
|
|
|
|
internal void DisposeResource()
|
|
{
|
|
if (GraphicsDeviceWindowsGL3.IsContextCurrent == false)
|
|
{
|
|
return;
|
|
}
|
|
|
|
foreach (EffectTechnique technique in techniques)
|
|
{
|
|
int programHandle =
|
|
(technique.NativeTechnique as EffectTechniqueGL3).programHandle;
|
|
|
|
GL.DeleteProgram(programHandle);
|
|
ErrorHelper.Check("DeleteProgram");
|
|
|
|
int result;
|
|
GL.GetProgram(programHandle, ProgramParameter.DeleteStatus, out result);
|
|
if (result == 0)
|
|
{
|
|
string deleteError;
|
|
GL.GetProgramInfoLog(programHandle, out deleteError);
|
|
throw new Exception("Failed to delete the shader program '" +
|
|
technique.Name + "' because of: " + deleteError);
|
|
}
|
|
}
|
|
techniques.Clear();
|
|
parameters.Clear();
|
|
}
|
|
#endregion
|
|
}
|
|
}
|